From c924678a893874a1b22ca0562dcd4ff8f2d8247c Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:24:25 +0100 Subject: [PATCH 001/102] Added functionalities for checking version and usage (--v and --help commands) with other optimizations. --- bin/cli.js | 102 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 40 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 916cfb0d..b50f40ab 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -13,99 +13,121 @@ See LICENSE file in root for details. *******************************************************************************/ -import main from '../lib/index.js'; +/** + * @overview This module serves as the entry point for the Highcharts Export + * Server. It provides functionality for starting the server, performing batch + * or single chart exports, and managing configuration options. It supports both + * CLI and server-based usage, allowing flexible integration for generating + * Highcharts exports in various environments. + */ + +import { singleExport, batchExport } from '../lib/chart.js'; +import { setOptions } from '../lib/config.js'; +import { initExport } from '../lib/index.js'; +import { log, logWithStack } from '../lib/logger.js'; +import { manualConfig } from '../lib/prompt.js'; +import { shutdownCleanUp } from '../lib/resourceRelease.js'; +import { printLicense, printVersion } from '../lib/utils.js'; +import { printUsage } from '../lib/schemas/config.js'; +import { startServer } from '../lib/server/server.js'; import ExportError from '../lib/errors/ExportError.js'; /** * The primary function to initiate the server or perform the direct export. + * Logs an error if it occurs during the execution and gracefully shut down + * the process. * - * @throws {ExportError} Throws an ExportError if no valid options are provided. - * @throws {Error} Throws an Error if an unexpected error occurs during - * execution. + * @async + * @function start + * + * @throws {ExportError} Throws an `ExportError` if no valid options are + * provided. */ -const start = async () => { +async function start() { try { // Get the CLI arguments const args = process.argv; - // Print the usage information if no arguments supplied + // Display license information if requested + if (['-v', '--v'].includes(args[args.length - 1])) { + // Print logo with the version and license information + return printLicense(); + } + + // Display help information if requested + if (['-h', '--h', '-help', '--help'].includes(args[args.length - 1])) { + // Print CLI usage information + return printUsage(); + } + + // Print CLI usage information if no arguments supplied if (args.length <= 2) { - main.log( + printUsage(); + return log( 2, - '[cli] The number of provided arguments is too small. Please refer to the section below.' + '[cli] The number of provided arguments is too small. Please refer to the help section above.' ); - return main.printUsage(); } // Set the options, keeping the priority order of setting values: - // 1. Options from the lib/schemas/config.js file - // 2. Options from a custom JSON file (loaded by the --loadConfig argument) - // 3. Options from the environment variables (the .env file) + // 1. Options from the `lib/schemas/config.js` file + // 2. Options from a custom JSON file (loaded by the `loadConfig` argument) + // 3. Options from the environment variables (the `.env` file) // 4. Options from the CLI - const options = main.setOptions(null, args); + const options = setOptions(null, args, true); // If all options correctly parsed if (options) { - // Print initial logo or text - main.printLogo(options.other.noLogo); + // Print initial logo or text with the version + printVersion(options.other.noLogo); // In this case we want to prepare config manually if (options.customLogic.createConfig) { - return main.manualConfig(options.customLogic.createConfig); + return manualConfig(options.customLogic.createConfig); } // Start server if (options.server.enable) { // Init the export mechanism for the server configuration - await main.initExport(options); + await initExport(options); // Run the server - await main.startServer(options.server); + await startServer(options.server); } else { // Perform batch exports if (options.export.batch) { - // If not set explicitly, use default option for batch exports - if (!args.includes('--minWorkers', '--maxWorkers')) { - options.pool = { - ...options.pool, - minWorkers: 2, - maxWorkers: 25 - }; - } - // Init a pool for the batch exports - await main.initExport(options); + await initExport(options); // Start batch exports - await main.batchExport(options); + await batchExport(options); } else { // No need for multiple workers in case of a single CLI export - options.pool = { - ...options.pool, - minWorkers: 1, - maxWorkers: 1 - }; + options.pool.minWorkers = 1; + options.pool.maxWorkers = 1; // Init a pool for one export - await main.initExport(options); + await initExport(options); // Start a single export - await main.singleExport(options); + await singleExport(options); } } } else { throw new ExportError( - '[cli] No valid options provided. Please check your input and try again.' + '[cli] No valid options provided. Please check your input and try again.', + 400 ); } } catch (error) { // Log the error with stack - main.logWithStack(1, error); + logWithStack(1, error); // Gracefully shut down the process - await main.shutdownCleanUp(1); + await shutdownCleanUp(1); } -}; +} +// Start the export server process start(); From 2c21ec2fef0aac86d9771051a4eb27275f406188 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:25:03 +0100 Subject: [PATCH 002/102] Added separate HTTP errors and made some corrections. --- lib/errors/ExportError.js | 45 ++++++++++++++++++++++++++- lib/errors/HttpError.js | 42 ++++++++++++++++++++++--- lib/errors/NoCorrectBodyError.js | 33 ++++++++++++++++++++ lib/errors/NoCorrectChartDataError.js | 33 ++++++++++++++++++++ lib/errors/NoCorrectResultError.js | 33 ++++++++++++++++++++ lib/errors/PrivateRangeUrlError.js | 33 ++++++++++++++++++++ lib/errors/ValidationError.js | 33 ++++++++++++++++++++ 7 files changed, 246 insertions(+), 6 deletions(-) create mode 100644 lib/errors/NoCorrectBodyError.js create mode 100644 lib/errors/NoCorrectChartDataError.js create mode 100644 lib/errors/NoCorrectResultError.js create mode 100644 lib/errors/PrivateRangeUrlError.js create mode 100644 lib/errors/ValidationError.js diff --git a/lib/errors/ExportError.js b/lib/errors/ExportError.js index be659551..04683ab6 100644 --- a/lib/errors/ExportError.js +++ b/lib/errors/ExportError.js @@ -1,22 +1,65 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +/** + * A custom error class for handling export-related errors. Extends the native + * `Error` class to include additional properties like status code and stack + * trace details. + */ class ExportError extends Error { - constructor(message) { + /** + * Creates an instance of `ExportError`. + * + * @param {string} message - The error message to be displayed. + * @param {number} statusCode - Optional HTTP status code associated + * with the error (e.g., 400, 500). + */ + constructor(message, statusCode) { super(); + this.message = message; this.stackMessage = message; + + if (statusCode) { + this.statusCode = statusCode; + } } + /** + * Sets additional error details based on an existing error object. + * + * @param {Error} error - An error object containing details to populate + * the `ExportError` instance. + * + * @returns {ExportError} The updated instance of the `ExportError` class. + */ setError(error) { this.error = error; + if (error.name) { this.name = error.name; } + if (error.statusCode) { this.statusCode = error.statusCode; } + if (error.stack) { this.stackMessage = error.message; this.stack = error.stack; } + return this; } } diff --git a/lib/errors/HttpError.js b/lib/errors/HttpError.js index b995a4d2..a14143ce 100644 --- a/lib/errors/HttpError.js +++ b/lib/errors/HttpError.js @@ -1,13 +1,45 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + import ExportError from './ExportError.js'; +/** + * A custom HTTP error class that extends `ExportError`. Used to handle errors + * with HTTP status codes. + */ class HttpError extends ExportError { - constructor(message, status) { - super(message); - this.status = this.statusCode = status; + /** + * Creates an instance of `HttpError`. + * + * @param {string} message - The error message to be displayed. + * @param {number} statusCode - Optional HTTP status code associated + * with the error (e.g., 400, 500). + */ + constructor(message, statusCode) { + super(message, statusCode); } - setStatus(status) { - this.status = status; + /** + * Sets or updates the HTTP status code for the error. + * + * @param {number} statusCode - The HTTP status code to assign to the error. + * + * @returns {HttpError} The updated instance of the `HttpError` class. + */ + setStatus(statusCode) { + this.statusCode = statusCode; + return this; } } diff --git a/lib/errors/NoCorrectBodyError.js b/lib/errors/NoCorrectBodyError.js new file mode 100644 index 00000000..0a037b5f --- /dev/null +++ b/lib/errors/NoCorrectBodyError.js @@ -0,0 +1,33 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +import HttpError from './HttpError.js'; + +/** + * The `NoCorrectBodyError` error class that extends `HttpError`. Used to handle + * errors related to incorrect request's body. + */ +class NoCorrectBodyError extends HttpError { + /** + * Creates an instance of `NoCorrectBodyError`. + */ + constructor() { + super( + "The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.", + 400 + ); + } +} + +export default NoCorrectBodyError; diff --git a/lib/errors/NoCorrectChartDataError.js b/lib/errors/NoCorrectChartDataError.js new file mode 100644 index 00000000..9b64956a --- /dev/null +++ b/lib/errors/NoCorrectChartDataError.js @@ -0,0 +1,33 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +import HttpError from './HttpError.js'; + +/** + * The `NoCorrectChartDataError` error class that extends `HttpError`. Used + * to handle errors related to incorrect chart's data. + */ +class NoCorrectChartDataError extends HttpError { + /** + * Creates an instance of `NoCorrectChartDataError`. + */ + constructor() { + super( + "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 + ); + } +} + +export default NoCorrectChartDataError; diff --git a/lib/errors/NoCorrectResultError.js b/lib/errors/NoCorrectResultError.js new file mode 100644 index 00000000..6edfac76 --- /dev/null +++ b/lib/errors/NoCorrectResultError.js @@ -0,0 +1,33 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +import HttpError from './HttpError.js'; + +/** + * The `NoCorrectResultError` error class that extends `HttpError`. Used + * to handle errors related to unexpected return of the export result. + */ +class NoCorrectResultError extends HttpError { + /** + * Creates an instance of `NoCorrectResultError`. + */ + constructor() { + super( + 'Unexpected return of the export result from the chart generation. Please check your request data.', + 400 + ); + } +} + +export default NoCorrectResultError; diff --git a/lib/errors/PrivateRangeUrlError.js b/lib/errors/PrivateRangeUrlError.js new file mode 100644 index 00000000..c064f718 --- /dev/null +++ b/lib/errors/PrivateRangeUrlError.js @@ -0,0 +1,33 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +import HttpError from './HttpError.js'; + +/** + * The `PrivateRangeUrlError` error class that extends `HttpError`. Used + * to handle errors related to private range url. + */ +class PrivateRangeUrlError extends HttpError { + /** + * Creates an instance of `PrivateRangeUrlError`. + */ + constructor() { + super( + "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 + ); + } +} + +export default PrivateRangeUrlError; diff --git a/lib/errors/ValidationError.js b/lib/errors/ValidationError.js new file mode 100644 index 00000000..7418a4f3 --- /dev/null +++ b/lib/errors/ValidationError.js @@ -0,0 +1,33 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +import HttpError from './HttpError.js'; + +/** + * The `ValidationError` error class that extends `HttpError`. Used to handle + * errors related to validation of provided options. + */ +class ValidationError extends HttpError { + /** + * Creates an instance of `ValidationError`. + */ + constructor() { + super( + 'The provided options are not correct. Please check if your data is of the correct types.', + 400 + ); + } +} + +export default ValidationError; From cd9fccea318708159383bbfde7f751ff11729aa2 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:25:14 +0100 Subject: [PATCH 003/102] Revision and overhaul of the default config options. --- lib/schemas/config.js | 1478 ++++++++++++++++++++--------------------- 1 file changed, 716 insertions(+), 762 deletions(-) diff --git a/lib/schemas/config.js b/lib/schemas/config.js index 50600dfd..b3a90e35 100644 --- a/lib/schemas/config.js +++ b/lib/schemas/config.js @@ -12,86 +12,27 @@ See LICENSE file in root for details. *******************************************************************************/ -// Possible names for Highcharts scripts -export const scriptsNames = { - core: ['highcharts', 'highcharts-more', 'highcharts-3d'], - modules: [ - 'stock', - 'map', - 'gantt', - 'exporting', - 'parallel-coordinates', - 'accessibility', - // 'annotations-advanced', - 'boost-canvas', - 'boost', - 'data', - 'data-tools', - 'draggable-points', - 'static-scale', - 'broken-axis', - 'heatmap', - 'tilemap', - 'tiledwebmap', - 'timeline', - 'treemap', - 'treegraph', - 'item-series', - 'drilldown', - 'histogram-bellcurve', - 'bullet', - 'funnel', - 'funnel3d', - 'geoheatmap', - 'pyramid3d', - 'networkgraph', - 'overlapping-datalabels', - 'pareto', - 'pattern-fill', - 'pictorial', - 'price-indicator', - 'sankey', - 'arc-diagram', - 'dependency-wheel', - 'series-label', - 'series-on-point', - 'solid-gauge', - 'sonification', - // 'stock-tools', - 'streamgraph', - 'sunburst', - 'variable-pie', - 'variwide', - 'vector', - 'venn', - 'windbarb', - 'wordcloud', - 'xrange', - 'no-data-to-display', - 'drag-panes', - 'debugger', - 'dumbbell', - 'lollipop', - 'cylinder', - 'organization', - 'dotplot', - 'marker-clusters', - 'hollowcandlestick', - 'heikinashi', - 'flowmap', - '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' - ] -}; +/** + * @overview Configuration management module for the Highcharts Export Server. + * Provides default configurations that support environment variables, CLI + * arguments, and interactive prompts for customization and adaptability. + * Additionally, it maps legacy options to modern structures, generates nested + * argument mappings, and displays CLI usage information. + */ -// This is the configuration object with all options and their default values, -// also from the .env file if one exists +/** + * The configuration object containing all available options, organized + * by sections. + * + * This object includes: + * - Default values for each option + * - Data types for validation + * - Names of corresponding environment variables + * - Descriptions of each property + * - [Optional] Corresponding CLI argument names for CLI usage + * - [Optional] Legacy names from the previous PhantomJS-based server + * - [Optional] Information used for prompts in interactive configuration + */ export const defaultConfig = { puppeteer: { args: { @@ -144,1031 +85,1044 @@ export const defaultConfig = { '--process-per-tab', '--use-mock-keychain' ], - type: 'string[]', - description: 'Arguments array to send to Puppeteer.' + types: ['string[]'], + envLink: 'PUPPETEER_ARGS', + cliName: 'puppeteerArgs', + description: 'Array of Puppeteer arguments', + promptOptions: { + type: 'list', + separator: ';' + } } }, highcharts: { version: { value: 'latest', - type: 'string', + types: ['string'], envLink: 'HIGHCHARTS_VERSION', - description: 'The Highcharts version to be used.' + description: 'Highcharts version', + promptOptions: { + type: 'text' + } }, - cdnURL: { - value: 'https://code.highcharts.com/', - type: 'string', + cdnUrl: { + value: 'https://code.highcharts.com', + types: ['string'], envLink: 'HIGHCHARTS_CDN_URL', - description: 'The CDN URL for Highcharts scripts to be used.' + description: 'CDN URL for Highcharts scripts', + promptOptions: { + type: 'text' + } + }, + forceFetch: { + value: false, + types: ['boolean'], + envLink: 'HIGHCHARTS_FORCE_FETCH', + description: 'Flag to refetch scripts after each server rerun', + promptOptions: { + type: 'toggle' + } + }, + cachePath: { + value: '.cache', + types: ['string'], + envLink: 'HIGHCHARTS_CACHE_PATH', + description: 'Directory path for cached Highcharts scripts', + promptOptions: { + type: 'text' + } }, coreScripts: { - value: scriptsNames.core, - type: 'string[]', + value: ['highcharts', 'highcharts-more', 'highcharts-3d'], + types: ['string[]'], envLink: 'HIGHCHARTS_CORE_SCRIPTS', - description: 'The core Highcharts scripts to fetch.' + description: 'Highcharts core scripts to fetch', + promptOptions: { + type: 'multiselect', + instructions: 'Space: Select specific, A: Select all, Enter: Confirm' + } }, moduleScripts: { - value: scriptsNames.modules, - type: 'string[]', + value: [ + 'stock', + 'map', + 'gantt', + 'exporting', + 'parallel-coordinates', + 'accessibility', + // 'annotations-advanced', + 'boost-canvas', + 'boost', + 'data', + 'data-tools', + 'draggable-points', + 'static-scale', + 'broken-axis', + 'heatmap', + 'tilemap', + 'tiledwebmap', + 'timeline', + 'treemap', + 'treegraph', + 'item-series', + 'drilldown', + 'histogram-bellcurve', + 'bullet', + 'funnel', + 'funnel3d', + 'geoheatmap', + 'pyramid3d', + 'networkgraph', + 'overlapping-datalabels', + 'pareto', + 'pattern-fill', + 'pictorial', + 'price-indicator', + 'sankey', + 'arc-diagram', + 'dependency-wheel', + 'series-label', + 'series-on-point', + 'solid-gauge', + 'sonification', + // 'stock-tools', + 'streamgraph', + 'sunburst', + 'variable-pie', + 'variwide', + 'vector', + 'venn', + 'windbarb', + 'wordcloud', + 'xrange', + 'no-data-to-display', + 'drag-panes', + 'debugger', + 'dumbbell', + 'lollipop', + 'cylinder', + 'organization', + 'dotplot', + 'marker-clusters', + 'hollowcandlestick', + 'heikinashi', + 'flowmap', + 'export-data', + 'navigator', + 'textpath' + ], + types: ['string[]'], envLink: 'HIGHCHARTS_MODULE_SCRIPTS', - description: 'The modules of Highcharts to fetch.' + description: 'Highcharts module scripts to fetch', + promptOptions: { + type: 'multiselect', + instructions: 'Space: Select specific, A: Select all, Enter: Confirm' + } }, indicatorScripts: { - value: scriptsNames.indicators, - type: 'string[]', + value: ['indicators-all'], + types: ['string[]'], envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS', - description: 'The indicators of Highcharts to fetch.' + description: 'Highcharts indicator scripts to fetch', + promptOptions: { + type: 'multiselect', + instructions: 'Space: Select specific, A: Select all, Enter: Confirm' + } }, customScripts: { - value: scriptsNames.custom, - type: 'string[]', - description: 'Additional custom scripts or dependencies to fetch.' - }, - forceFetch: { - value: false, - 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.' + value: [ + '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' + ], + types: ['string[]'], + envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS', + description: 'Additional custom scripts or dependencies to fetch', + promptOptions: { + type: 'list', + separator: ';' + } } }, export: { infile: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], + envLink: 'EXPORT_INFILE', 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.' + 'Input filename with type, formatted correctly as JSON or SVG', + promptOptions: { + type: 'text' + } }, instr: { - value: false, - type: 'string', + value: null, + types: ['object', 'string', 'null'], + envLink: 'EXPORT_INSTR', description: - 'Input, provided in the form of a stringified JSON or SVG file, will override the --infile option.' + 'Overrides the `infile` with JSON, stringified JSON, or SVG input', + promptOptions: { + type: 'text' + } }, options: { - value: false, - type: 'string', - description: 'An alias for the --instr option.' + value: null, + types: ['object', 'string', 'null'], + envLink: 'EXPORT_OPTIONS', + description: 'Alias for the `instr` option', + promptOptions: { + type: 'text' + } + }, + svg: { + value: null, + types: ['string', 'null'], + envLink: 'EXPORT_SVG', + description: 'SVG string representation of the chart to render', + promptOptions: { + type: 'text' + } }, outfile: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], + envLink: 'EXPORT_OUTFILE', description: - 'The output filename along with a type (jpeg, png, pdf, or svg). This will ignore the --type flag.' + 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option', + promptOptions: { + type: 'text' + } }, type: { value: 'png', - type: 'string', + types: ['string'], envLink: 'EXPORT_TYPE', - description: 'The file export format. It can be jpeg, png, pdf, or svg.' + description: 'File export format. Can be jpeg, png, pdf, or svg', + promptOptions: { + type: 'select', + hint: 'Default: png', + choices: ['png', 'jpeg', 'pdf', 'svg'] + } }, constr: { value: 'chart', - type: 'string', + types: ['string'], envLink: 'EXPORT_CONSTR', description: - 'The constructor to use. Can be chart, stockChart, mapChart, or ganttChart.' + 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart', + promptOptions: { + type: 'select', + hint: 'Default: chart', + choices: ['chart', 'stockChart', 'mapChart', 'ganttChart'] + } + }, + b64: { + value: false, + types: ['boolean'], + envLink: 'EXPORT_B64', + description: + 'Whether or not to the chart should be received in base64 format instead of binary', + promptOptions: { + type: 'toggle' + } + }, + noDownload: { + value: false, + types: ['boolean'], + envLink: 'EXPORT_NO_DOWNLOAD', + description: + 'Whether or not to include or exclude attachment headers in the response', + promptOptions: { + type: 'toggle' + } }, defaultHeight: { value: 400, - type: 'number', + types: ['number'], envLink: 'EXPORT_DEFAULT_HEIGHT', - description: - 'the default height of the exported chart. Used when no value is set.' + description: 'Default height of the exported chart if not set', + promptOptions: { + type: 'number' + } }, defaultWidth: { value: 600, - type: 'number', + types: ['number'], envLink: 'EXPORT_DEFAULT_WIDTH', - description: - 'The default width of the exported chart. Used when no value is set.' + description: 'Default width of the exported chart if not set', + promptOptions: { + type: 'number' + } }, defaultScale: { value: 1, - type: 'number', + types: ['number'], envLink: 'EXPORT_DEFAULT_SCALE', description: - 'The default scale of the exported chart. Used when no value is set.' + 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0', + promptOptions: { + type: 'number', + min: 0.1, + max: 5 + } }, height: { - value: false, - type: 'number', - description: - 'The height of the exported chart, overriding the option in the chart settings.' + value: null, + types: ['number', 'null'], + envLink: 'EXPORT_HEIGHT', + description: 'Height of the exported chart, overrides chart settings', + promptOptions: { + type: 'number' + } }, width: { - value: false, - type: 'number', - description: - 'The width of the exported chart, overriding the option in the chart settings.' + value: null, + types: ['number', 'null'], + envLink: 'EXPORT_WIDTH', + description: 'Width of the exported chart, overrides chart settings', + promptOptions: { + type: 'number' + } }, scale: { - value: false, - type: 'number', + value: null, + types: ['number', 'null'], + envLink: 'EXPORT_SCALE', description: - 'The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0.' + 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0', + promptOptions: { + type: 'number' + } }, globalOptions: { - value: false, - type: 'string', + value: null, + types: ['object', 'string', 'null'], + envLink: 'EXPORT_GLOBAL_OPTIONS', description: - 'Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions.' + 'JSON, stringified JSON or filename with global options for Highcharts.setOptions', + promptOptions: { + type: 'text' + } }, themeOptions: { - value: false, - type: 'string', + value: null, + types: ['object', 'string', 'null'], + envLink: 'EXPORT_THEME_OPTIONS', description: - 'Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions.' + 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions', + promptOptions: { + type: 'text' + } }, batch: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], + envLink: 'EXPORT_BATCH', description: - 'Initiates a batch job with a string containing input/output pairs: "in=out;in=out;...".' + 'Batch job string with input/output pairs: "in=out;in=out;..."', + promptOptions: { + type: 'text' + } }, rasterizationTimeout: { value: 1500, - type: 'number', + types: ['number'], envLink: 'EXPORT_RASTERIZATION_TIMEOUT', - description: - 'The duration in milliseconds to wait for rendering a webpage.' + description: 'Milliseconds to wait for webpage rendering', + promptOptions: { + type: 'number' + } } }, customLogic: { allowCodeExecution: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION', description: - 'Controls whether the execution of arbitrary code is allowed during the exporting process.' + 'Allows or disallows execution of arbitrary code during exporting', + promptOptions: { + type: 'toggle' + } }, allowFileResources: { value: false, - type: 'boolean', + types: ['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.' + 'Allows or disallows injection of filesystem resources (disabled in server mode)', + promptOptions: { + type: 'toggle' + } }, customCode: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], + envLink: 'CUSTOM_LOGIC_CUSTOM_CODE', 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.' + 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename', + promptOptions: { + type: 'text' + } }, callback: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], + envLink: 'CUSTOM_LOGIC_CALLBACK', description: - 'JavaScript code to run during construction. It can be a function or a filename with the .js extension.' + 'JavaScript code to run during construction. Can be a function or a .js filename', + promptOptions: { + type: 'text' + } }, resources: { - value: false, - type: 'string', + value: null, + types: ['object', 'string', 'null'], + envLink: 'CUSTOM_LOGIC_RESOURCES', description: - 'Additional resource in the form of a stringified JSON, which may contain files, js, and css sections.' + 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections', + promptOptions: { + type: 'text' + } }, loadConfig: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], + envLink: 'CUSTOM_LOGIC_LOAD_CONFIG', legacyName: 'fromFile', - description: 'A file containing a pre-defined configuration to use.' + description: 'File with a pre-defined configuration to use', + promptOptions: { + type: 'text' + } }, createConfig: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], + envLink: 'CUSTOM_LOGIC_CREATE_CONFIG', description: - 'Enables setting options through a prompt and saving them in a provided config file.' + 'Prompt-based option setting, saved to a provided config file', + promptOptions: { + type: 'text' + } } }, server: { enable: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'SERVER_ENABLE', cliName: 'enableServer', - description: - 'When set to true, the server starts on the local IP address 0.0.0.0.' + description: 'Starts the server when true', + promptOptions: { + type: 'toggle' + } }, host: { value: '0.0.0.0', - type: 'string', + types: ['string'], envLink: 'SERVER_HOST', - description: - 'The hostname of the server. Additionally, it starts a server on the provided hostname.' + description: 'Hostname of the server', + promptOptions: { + type: 'text' + } }, port: { value: 7801, - type: 'number', + types: ['number'], envLink: 'SERVER_PORT', - description: 'The server port when enabled.' + description: 'Port number for the server', + promptOptions: { + type: 'number' + } }, benchmarking: { value: false, - type: 'boolean', + types: ['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.' + 'Displays or not action durations in milliseconds during server requests', + promptOptions: { + type: 'toggle' + } }, proxy: { host: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], envLink: 'SERVER_PROXY_HOST', cliName: 'proxyHost', - description: 'The host of the proxy server to use, if it exists.' + description: 'Host of the proxy server, if applicable', + promptOptions: { + type: 'text' + } }, port: { - value: 8080, - type: 'number', + value: null, + types: ['number', 'null'], envLink: 'SERVER_PROXY_PORT', cliName: 'proxyPort', - description: 'The port of the proxy server to use, if it exists.' + description: 'Port of the proxy server, if applicable', + promptOptions: { + type: 'number' + } }, timeout: { value: 5000, - type: 'number', + types: ['number'], envLink: 'SERVER_PROXY_TIMEOUT', cliName: 'proxyTimeout', - description: 'The timeout for the proxy server to use, if it exists.' + description: + 'Timeout in milliseconds for the proxy server, if applicable', + promptOptions: { + type: 'number' + } } }, rateLimiting: { enable: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'SERVER_RATE_LIMITING_ENABLE', cliName: 'enableRateLimiting', - description: 'Enables rate limiting for the server.' + description: 'Enables or disables rate limiting on the server', + promptOptions: { + type: 'toggle' + } }, maxRequests: { value: 10, - type: 'number', + types: ['number'], envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS', legacyName: 'rateLimit', - description: 'The maximum number of requests allowed in one minute.' + description: 'Maximum number of requests allowed per minute', + promptOptions: { + type: 'number' + } }, window: { value: 1, - type: 'number', + types: ['number'], envLink: 'SERVER_RATE_LIMITING_WINDOW', - description: 'The time window, in minutes, for the rate limiting.' + description: 'Time window in minutes for rate limiting', + promptOptions: { + type: 'number' + } }, delay: { value: 0, - type: 'number', + types: ['number'], envLink: 'SERVER_RATE_LIMITING_DELAY', description: - 'The delay duration for each successive request before reaching the maximum limit.' + 'Delay duration between successive requests before reaching the limit', + promptOptions: { + type: 'number' + } }, trustProxy: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY', - description: 'Set this to true if the server is behind a load balancer.' + description: 'Set to true if the server is behind a load balancer', + promptOptions: { + type: 'toggle' + } }, skipKey: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], envLink: 'SERVER_RATE_LIMITING_SKIP_KEY', - description: - 'Allows bypassing the rate limiter and should be provided with the skipToken argument.' + description: 'Key to bypass the rate limiter, used with `skipToken`', + promptOptions: { + type: 'text' + } }, skipToken: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN', - description: - 'Allows bypassing the rate limiter and should be provided with the skipKey argument.' + description: 'Token to bypass the rate limiter, used with `skipKey`', + promptOptions: { + type: 'text' + } } }, ssl: { enable: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'SERVER_SSL_ENABLE', cliName: 'enableSsl', - description: 'Enables or disables the SSL protocol.' + description: 'Enables or disables SSL protocol', + promptOptions: { + type: 'toggle' + } }, force: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'SERVER_SSL_FORCE', cliName: 'sslForce', legacyName: 'sslOnly', - description: - 'When set to true, the server is forced to serve only over HTTPS.' + description: 'Forces the server to use HTTPS only when true', + promptOptions: { + type: 'toggle' + } }, port: { value: 443, - type: 'number', + types: ['number'], envLink: 'SERVER_SSL_PORT', cliName: 'sslPort', - description: 'The port on which to run the SSL server.' + description: 'Port for the SSL server', + promptOptions: { + type: 'number' + } }, certPath: { - value: false, - type: 'string', + value: null, + types: ['string', 'null'], envLink: 'SERVER_SSL_CERT_PATH', + cliName: 'sslCertPath', legacyName: 'sslPath', - description: 'The path to the SSL certificate/key file.' + description: 'Path to the SSL certificate/key file', + promptOptions: { + type: 'text' + } } } }, pool: { minWorkers: { value: 4, - type: 'number', + types: ['number'], envLink: 'POOL_MIN_WORKERS', - description: 'The number of minimum and initial pool workers to spawn.' + description: 'Minimum and initial number of pool workers to spawn', + promptOptions: { + type: 'number' + } }, maxWorkers: { value: 8, - type: 'number', + types: ['number'], envLink: 'POOL_MAX_WORKERS', legacyName: 'workers', - description: 'The number of maximum pool workers to spawn.' + description: 'Maximum number of pool workers to spawn', + promptOptions: { + type: 'number' + } }, workLimit: { value: 40, - type: 'number', + types: ['number'], envLink: 'POOL_WORK_LIMIT', - description: - 'The number of work pieces that can be performed before restarting the worker process.' + description: 'Number of tasks a worker can handle before restarting', + promptOptions: { + type: 'number' + } }, acquireTimeout: { value: 5000, - type: 'number', + types: ['number'], envLink: 'POOL_ACQUIRE_TIMEOUT', - description: - 'The duration, in milliseconds, to wait for acquiring a resource.' + description: 'Timeout in milliseconds for acquiring a resource', + promptOptions: { + type: 'number' + } }, createTimeout: { value: 5000, - type: 'number', + types: ['number'], envLink: 'POOL_CREATE_TIMEOUT', - description: - 'The duration, in milliseconds, to wait for creating a resource.' + description: 'Timeout in milliseconds for creating a resource', + promptOptions: { + type: 'number' + } }, destroyTimeout: { value: 5000, - type: 'number', + types: ['number'], envLink: 'POOL_DESTROY_TIMEOUT', - description: - 'The duration, in milliseconds, to wait for destroying a resource.' + description: 'Timeout in milliseconds for destroying a resource', + promptOptions: { + type: 'number' + } }, idleTimeout: { value: 30000, - type: 'number', + types: ['number'], envLink: 'POOL_IDLE_TIMEOUT', - description: - 'The duration, in milliseconds, after which an idle resource is destroyed.' + description: 'Timeout in milliseconds for destroying idle resources', + promptOptions: { + type: 'number' + } }, createRetryInterval: { value: 200, - type: 'number', + types: ['number'], envLink: 'POOL_CREATE_RETRY_INTERVAL', description: - 'The duration, in milliseconds, to wait before retrying the create process in case of a failure.' + 'Interval in milliseconds before retrying resource creation on failure', + promptOptions: { + type: 'number' + } }, reaperInterval: { value: 1000, - type: 'number', + types: ['number'], envLink: 'POOL_REAPER_INTERVAL', description: - 'The duration, in milliseconds, after which the check for idle resources to destroy is triggered.' + 'Interval in milliseconds to check and destroy idle resources', + promptOptions: { + type: 'number' + } }, benchmarking: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'POOL_BENCHMARKING', cliName: 'poolBenchmarking', - description: - 'Indicate whether to show statistics for the pool of resources or not.' + description: 'Shows statistics for the pool of resources', + promptOptions: { + type: 'toggle' + } } }, logging: { level: { value: 4, - type: 'number', + types: ['number'], envLink: 'LOGGING_LEVEL', cliName: 'logLevel', - description: 'The logging level to be used.' + description: 'Logging verbosity level', + promptOptions: { + type: 'number', + round: 0, + min: 0, + max: 5 + } }, file: { value: 'highcharts-export-server.log', - type: 'string', + types: ['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.' + 'Log file name. Requires `logToFile` and `logDest` to be set', + promptOptions: { + type: 'text' + } }, dest: { - value: 'log/', - type: 'string', + value: 'log', + types: ['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.' + description: 'Path to store log files. Requires `logToFile` to be set', + promptOptions: { + type: 'text' + } }, toConsole: { value: true, - type: 'boolean', + types: ['boolean'], envLink: 'LOGGING_TO_CONSOLE', cliName: 'logToConsole', - description: 'Enables or disables showing logs in the console.' + description: 'Enables or disables console logging', + promptOptions: { + type: 'toggle' + } }, toFile: { value: true, - type: 'boolean', + types: ['boolean'], envLink: 'LOGGING_TO_FILE', cliName: 'logToFile', - description: - 'Enables or disables creation of the log directory and saving the log into a .log file.' + description: 'Enables or disables logging to a file', + promptOptions: { + type: 'toggle' + } } }, ui: { enable: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'UI_ENABLE', cliName: 'enableUi', - description: - 'Enables or disables the user interface (UI) for the export server.' + description: 'Enables or disables the UI for the export server', + promptOptions: { + type: 'toggle' + } }, route: { value: '/', - type: 'string', + types: ['string'], envLink: 'UI_ROUTE', cliName: 'uiRoute', - description: - 'The endpoint route to which the user interface (UI) should be attached.' + description: 'The endpoint route for the UI', + promptOptions: { + type: 'text' + } } }, other: { nodeEnv: { value: 'production', - type: 'string', + types: ['string'], envLink: 'OTHER_NODE_ENV', - description: 'The type of Node.js environment.' + description: 'The Node.js environment type', + promptOptions: { + type: 'text' + } }, listenToProcessExits: { value: true, - type: 'boolean', + types: ['boolean'], envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS', - description: 'Decides whether or not to attach process.exit handlers.' + description: 'Whether or not to attach process.exit handlers', + promptOptions: { + type: 'toggle' + } }, noLogo: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'OTHER_NO_LOGO', - description: - 'Skip printing the logo on a startup. Will be replaced by a simple text.' + description: 'Display or skip printing the logo on startup', + promptOptions: { + type: 'toggle' + } }, hardResetPage: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'OTHER_HARD_RESET_PAGE', - description: 'Decides if the page content should be reset entirely.' + description: 'Whether or not to reset the page content entirely', + promptOptions: { + type: 'toggle' + } }, browserShellMode: { value: true, - type: 'boolean', + types: ['boolean'], envLink: 'OTHER_BROWSER_SHELL_MODE', - description: 'Decides if the browser runs in the shell mode.' + description: 'Whether or not to set the browser to run in shell mode', + promptOptions: { + type: 'toggle' + } } }, debug: { enable: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'DEBUG_ENABLE', cliName: 'enableDebug', - description: 'Enables or disables debug mode for the underlying browser.' + description: 'Enables or disables debug mode for the underlying browser', + promptOptions: { + type: 'toggle' + } }, headless: { - value: true, - type: 'boolean', + value: false, + types: ['boolean'], envLink: 'DEBUG_HEADLESS', description: - 'Controls the mode in which the browser is launched when in the debug mode.' + 'Whether or not to set the browser to run in headless mode during debugging', + promptOptions: { + type: 'toggle' + } }, devtools: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'DEBUG_DEVTOOLS', - description: - 'Decides whether to enable DevTools when the browser is in a headful state.' + description: 'Enables or disables DevTools in headful mode', + promptOptions: { + type: 'toggle' + } }, listenToConsole: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'DEBUG_LISTEN_TO_CONSOLE', description: - 'Decides whether to enable a listener for console messages sent from the browser.' + 'Enables or disables listening to console messages from the browser', + promptOptions: { + type: 'toggle' + } }, dumpio: { value: false, - type: 'boolean', + types: ['boolean'], envLink: 'DEBUG_DUMPIO', description: - 'Redirects browser process stdout and stderr to process.stdout and process.stderr.' + 'Redirects or not browser stdout and stderr to process.stdout and process.stderr', + promptOptions: { + type: 'toggle' + } }, slowMo: { value: 0, - type: 'number', + types: ['number'], envLink: 'DEBUG_SLOW_MO', - description: - 'Slows down Puppeteer operations by the specified number of milliseconds.' + description: 'Delays Puppeteer operations by the specified milliseconds', + promptOptions: { + type: 'number' + } }, debuggingPort: { value: 9222, - type: 'number', + types: ['number'], envLink: 'DEBUG_DEBUGGING_PORT', - description: 'Specifies the debugging port.' + description: 'Port used for debugging', + promptOptions: { + type: 'number' + } } } }; -// The config descriptions object for the prompts functionality. It contains -// information like: -// * Type of a prompt -// * Name of an option -// * Short description of a chosen option -// * Initial value -export const promptsConfig = { - puppeteer: [ - { - type: 'list', - name: 'args', - message: 'Puppeteer arguments', - initial: defaultConfig.puppeteer.args.value.join(','), - separator: ',' - } - ], - highcharts: [ - { - type: 'text', - name: 'version', - message: 'Highcharts version', - initial: defaultConfig.highcharts.version.value - }, - { - type: 'text', - name: 'cdnURL', - message: 'The URL of CDN', - initial: defaultConfig.highcharts.cdnURL.value - }, - { - type: 'multiselect', - name: 'coreScripts', - message: 'Available core scripts', - instructions: 'Space: Select specific, A: Select all, Enter: Confirm.', - choices: defaultConfig.highcharts.coreScripts.value - }, - { - type: 'multiselect', - name: 'moduleScripts', - message: 'Available module scripts', - instructions: 'Space: Select specific, A: Select all, Enter: Confirm.', - choices: defaultConfig.highcharts.moduleScripts.value - }, - { - type: 'multiselect', - name: 'indicatorScripts', - message: 'Available indicator scripts', - instructions: 'Space: Select specific, A: Select all, Enter: Confirm.', - choices: defaultConfig.highcharts.indicatorScripts.value - }, - { - type: 'list', - name: 'customScripts', - message: 'Custom scripts', - initial: defaultConfig.highcharts.customScripts.value.join(','), - separator: ',' - }, - { - type: 'toggle', - name: 'forceFetch', - message: 'Force re-fetch the scripts', - initial: defaultConfig.highcharts.forceFetch.value - }, - { - type: 'text', - name: 'cachePath', - message: 'The path to the cache directory', - initial: defaultConfig.highcharts.cachePath.value - } - ], - export: [ - { - type: 'select', - name: 'type', - message: 'The default export file type', - hint: `Default: ${defaultConfig.export.type.value}`, - initial: 0, - choices: ['png', 'jpeg', 'pdf', 'svg'] - }, - { - type: 'select', - name: 'constr', - message: 'The default constructor for Highcharts', - hint: `Default: ${defaultConfig.export.constr.value}`, - initial: 0, - choices: ['chart', 'stockChart', 'mapChart', 'ganttChart'] - }, - { - type: 'number', - name: 'defaultHeight', - message: 'The default fallback height of the exported chart', - initial: defaultConfig.export.defaultHeight.value - }, - { - type: 'number', - name: 'defaultWidth', - message: 'The default fallback width of the exported chart', - initial: defaultConfig.export.defaultWidth.value - }, - { - type: 'number', - name: 'defaultScale', - message: 'The default fallback scale of the exported chart', - initial: defaultConfig.export.defaultScale.value, - min: 0.1, - max: 5 - }, - { - type: 'number', - name: 'rasterizationTimeout', - message: 'The rendering webpage timeout in milliseconds', - initial: defaultConfig.export.rasterizationTimeout.value - } - ], - customLogic: [ - { - type: 'toggle', - name: 'allowCodeExecution', - message: 'Enable execution of custom code', - initial: defaultConfig.customLogic.allowCodeExecution.value - }, - { - type: 'toggle', - name: 'allowFileResources', - message: 'Enable file resources', - initial: defaultConfig.customLogic.allowFileResources.value - } - ], - server: [ - { - type: 'toggle', - name: 'enable', - message: 'Starts the server on 0.0.0.0', - initial: defaultConfig.server.enable.value - }, - { - type: 'text', - name: 'host', - message: 'Server hostname', - initial: defaultConfig.server.host.value - }, - { - type: 'number', - name: 'port', - message: 'Server port', - initial: defaultConfig.server.port.value - }, - { - type: 'toggle', - name: 'benchmarking', - message: 'Enable server benchmarking', - initial: defaultConfig.server.benchmarking.value - }, - { - type: 'text', - name: 'proxy.host', - message: 'The host of the proxy server to use', - initial: defaultConfig.server.proxy.host.value - }, - { - type: 'number', - name: 'proxy.port', - message: 'The port of the proxy server to use', - initial: defaultConfig.server.proxy.port.value - }, - { - type: 'number', - name: 'proxy.timeout', - message: 'The timeout for the proxy server to use', - initial: defaultConfig.server.proxy.timeout.value - }, - { - type: 'toggle', - name: 'rateLimiting.enable', - message: 'Enable rate limiting', - initial: defaultConfig.server.rateLimiting.enable.value - }, - { - type: 'number', - name: 'rateLimiting.maxRequests', - message: 'The maximum requests allowed per minute', - initial: defaultConfig.server.rateLimiting.maxRequests.value - }, - { - type: 'number', - name: 'rateLimiting.window', - message: 'The rate-limiting time window in minutes', - initial: defaultConfig.server.rateLimiting.window.value - }, - { - type: 'number', - name: 'rateLimiting.delay', - message: - 'The delay for each successive request before reaching the maximum', - initial: defaultConfig.server.rateLimiting.delay.value - }, - { - type: 'toggle', - name: 'rateLimiting.trustProxy', - message: 'Set to true if behind a load balancer', - initial: defaultConfig.server.rateLimiting.trustProxy.value - }, - { - type: 'text', - name: 'rateLimiting.skipKey', - message: - 'Allows bypassing the rate limiter when provided with the skipToken argument', - initial: defaultConfig.server.rateLimiting.skipKey.value - }, - { - type: 'text', - name: 'rateLimiting.skipToken', - message: - 'Allows bypassing the rate limiter when provided with the skipKey argument', - initial: defaultConfig.server.rateLimiting.skipToken.value - }, - { - type: 'toggle', - name: 'ssl.enable', - message: 'Enable SSL protocol', - initial: defaultConfig.server.ssl.enable.value - }, - { - type: 'toggle', - name: 'ssl.force', - message: 'Force serving only over HTTPS', - initial: defaultConfig.server.ssl.force.value - }, - { - type: 'number', - name: 'ssl.port', - message: 'SSL server port', - initial: defaultConfig.server.ssl.port.value - }, - { - type: 'text', - name: 'ssl.certPath', - message: 'The path to find the SSL certificate/key', - initial: defaultConfig.server.ssl.certPath.value - } - ], - pool: [ - { - type: 'number', - name: 'minWorkers', - message: 'The initial number of workers to spawn', - initial: defaultConfig.pool.minWorkers.value - }, - { - type: 'number', - name: 'maxWorkers', - message: 'The maximum number of workers to spawn', - initial: defaultConfig.pool.maxWorkers.value - }, - { - type: 'number', - name: 'workLimit', - message: - 'The pieces of work that can be performed before restarting a Puppeteer process', - initial: defaultConfig.pool.workLimit.value - }, - { - type: 'number', - name: 'acquireTimeout', - message: 'The number of milliseconds to wait for acquiring a resource', - initial: defaultConfig.pool.acquireTimeout.value - }, - { - type: 'number', - name: 'createTimeout', - message: 'The number of milliseconds to wait for creating a resource', - initial: defaultConfig.pool.createTimeout.value - }, - { - type: 'number', - name: 'destroyTimeout', - message: 'The number of milliseconds to wait for destroying a resource', - initial: defaultConfig.pool.destroyTimeout.value - }, - { - type: 'number', - name: 'idleTimeout', - message: 'The number of milliseconds after an idle resource is destroyed', - initial: defaultConfig.pool.idleTimeout.value - }, - { - type: 'number', - name: 'createRetryInterval', - message: - 'The retry interval in milliseconds after a create process fails', - initial: defaultConfig.pool.createRetryInterval.value - }, - { - type: 'number', - name: 'reaperInterval', - message: - 'The reaper interval in milliseconds after triggering the check for idle resources to destroy', - initial: defaultConfig.pool.reaperInterval.value - }, - { - type: 'toggle', - name: 'benchmarking', - message: 'Enable benchmarking for a resource pool', - initial: defaultConfig.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: defaultConfig.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: defaultConfig.logging.file.value - }, - { - type: 'text', - name: 'dest', - message: 'The path to a log file when the file logging is enabled', - initial: defaultConfig.logging.dest.value - }, - { - type: 'toggle', - name: 'toConsole', - message: 'Enable logging to the console', - initial: defaultConfig.logging.toConsole.value - }, - { - type: 'toggle', - name: 'toFile', - message: 'Enables logging to a file', - initial: defaultConfig.logging.toFile.value - } - ], - ui: [ - { - type: 'toggle', - name: 'enable', - message: 'Enable UI for the export server', - initial: defaultConfig.ui.enable.value - }, - { - type: 'text', - name: 'route', - message: 'A route to attach the UI', - initial: defaultConfig.ui.route.value - } - ], - other: [ - { - type: 'text', - name: 'nodeEnv', - message: 'The type of Node.js environment', - initial: defaultConfig.other.nodeEnv.value - }, - { - type: 'toggle', - name: 'listenToProcessExits', - message: 'Set to false to skip attaching process.exit handlers', - initial: defaultConfig.other.listenToProcessExits.value - }, - { - type: 'toggle', - name: 'noLogo', - message: 'Skip printing the logo on startup. Replaced by simple text', - initial: defaultConfig.other.noLogo.value - }, - { - type: 'toggle', - name: 'hardResetPage', - message: 'Decides if the page content should be reset entirely', - initial: defaultConfig.other.hardResetPage.value - }, - { - type: 'toggle', - name: 'browserShellMode', - message: 'Decides if the browser runs in the shell mode', - initial: defaultConfig.other.browserShellMode.value - } - ], - debug: [ - { - type: 'toggle', - name: 'enable', - message: 'Enables debug mode for the browser instance', - initial: defaultConfig.debug.enable.value - }, - { - type: 'toggle', - name: 'headless', - message: 'The mode setting for the browser', - initial: defaultConfig.debug.headless.value - }, - { - type: 'toggle', - name: 'devtools', - message: 'The DevTools for the headful browser', - initial: defaultConfig.debug.devtools.value - }, - { - type: 'toggle', - name: 'listenToConsole', - message: 'The event listener for console messages from the browser', - initial: defaultConfig.debug.listenToConsole.value - }, - { - type: 'toggle', - name: 'dumpio', - message: 'Redirects the browser stdout and stderr to NodeJS process', - initial: defaultConfig.debug.dumpio.value - }, - { - type: 'number', - name: 'slowMo', - message: 'Puppeteer operations slow down in milliseconds', - initial: defaultConfig.debug.slowMo.value - }, - { - type: 'number', - name: 'debuggingPort', - message: 'The port number for debugging', - initial: defaultConfig.debug.debuggingPort.value - } - ] -}; +// Argument nesting level of all export server options +export const nestedArgs = _createNestedArgs(defaultConfig); -// Absolute props that, in case of merging recursively, need to be force merged -export const absoluteProps = [ - 'options', - 'globalOptions', - 'themeOptions', - 'resources', - 'payload' -]; +/** + * Maps old-structured configuration options (PhantomJS) to a new format + * (Puppeteer). This function converts flat, old-structured options into + * a new, nested configuration format based on a predefined mapping + * (`nestedArgs`). The new format is used for Puppeteer, while the old format + * was used for PhantomJS. + * + * @function mapToNewConfig + * + * @param {Object} oldOptions - The old, flat configuration options + * to be converted. + * + * @returns {Object} A new object containing options structured according + * to the mapping defined in `nestedArgs`. + */ +export function mapToNewConfig(oldOptions) { + // An object for the new structured options + const newOptions = {}; -// Argument nesting level of all export server options -export const nestedArgs = {}; + // Iterate over each key-value pair in the old-structured options + for (const [key, value] of Object.entries(oldOptions)) { + // If there is a nested mapping, split it into a properties chain + const propertiesChain = nestedArgs[key] ? nestedArgs[key].split('.') : []; + + // If it is the last property in the chain, assign the value, otherwise, + // create or reuse the nested object + propertiesChain.reduce( + (obj, prop, index) => + (obj[prop] = + propertiesChain.length - 1 === index ? value : obj[prop] || {}), + newOptions + ); + } + + // Return the new, structured options object + return newOptions; +} /** - * Recursively creates a chain of nested arguments from an object. + * Prints usage information for CLI arguments, displaying available options + * and their descriptions. It can list properties recursively if categories + * contain nested options. * - * @param {Object} obj - The object containing nested arguments. - * @param {string} propChain - The current chain of nested properties - * (used internally during recursion). + * @function printUsage */ -const createNestedArgs = (obj, propChain = '') => { - Object.keys(obj).forEach((k) => { - if (!['puppeteer', 'highcharts'].includes(k)) { - const entry = obj[k]; - if (typeof entry.value === 'undefined') { - // Go deeper in the nested arguments - createNestedArgs(entry, `${propChain}.${k}`); - } else { - // Create the chain of nested arguments - nestedArgs[entry.cliName || k] = `${propChain}.${k}`.substring(1); +export function printUsage() { + // Display README and general usage information + console.log( + '\nUsage of CLI arguments:'.bold, + '\n-----', + `\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}`, + '\n' + ); - // Support for the legacy, PhantomJS properties names - if (entry.legacyName !== undefined) { - nestedArgs[entry.legacyName] = `${propChain}.${k}`.substring(1); - } + // Iterate through each category in the `defaultConfig` and display usage info + Object.keys(defaultConfig).forEach((category) => { + console.log(`${category.toUpperCase()}`.red); + _cycleCategories(defaultConfig[category]); + }); +} + +/** + * Recursively generates a mapping of nested argument chains from a nested + * config object. This function traverses a nested object and creates a mapping + * where each key is an argument name (either from `cliName`, `legacyName`, + * or the original key) and each value is a string representing the chain + * of nested properties leading to that argument. + * + * @function _createNestedArgs + * + * @param {Object} config - The nested configuration object containing argument. + * @param {Object} [nestedArgs={}] - The accumulator object for storing + * the resulting argument chains. + * @param {string} [propChain=''] - The current chain of nested properties, + * used internally during recursion. + * + * @returns {Object} An object mapping argument names to their corresponding + * nested property chains. + */ +function _createNestedArgs(config, nestedArgs = {}, propChain = '') { + Object.keys(config).forEach((key) => { + // Get the specific section + const entry = config[key]; + + // Check if there is still more depth to traverse + if (typeof entry.value === 'undefined') { + // Recurse into deeper levels of nested arguments + _createNestedArgs(entry, nestedArgs, `${propChain}.${key}`); + } else { + // Create the chain of nested arguments + nestedArgs[entry.cliName || key] = `${propChain}.${key}`.substring(1); + + // Support for the legacy, PhantomJS properties names + if (entry.legacyName !== undefined) { + nestedArgs[entry.legacyName] = `${propChain}.${key}`.substring(1); } } }); -}; -createNestedArgs(defaultConfig); + // Return the object with nested argument chains + return nestedArgs; +} + +/** + * Recursively traverses the options object to print the usage information + * for each option category and individual option. + * + * @function _cycleCategories + * + * @param {Object} options - The options object containing CLI options. + * It may include nested categories and individual options. + */ +function _cycleCategories(options) { + for (const [name, option] of Object.entries(options)) { + // If the current entry is a category and not a leaf option, recurse into it + if (!Object.prototype.hasOwnProperty.call(option, 'value')) { + _cycleCategories(option); + } else { + const descName = ` --${option.cliName || name} ${ + ('<' + option.types.join('|') + '>').green + }\n`; + + // Display correctly aligned messages + console.log( + descName, + `[Default: ${String(option.value).bold}]`.blue, + '-', + `${option.description}.`, + '\n' + ); + } + } +} + +export default { + defaultConfig, + nestedArgs, + mapToNewConfig, + printUsage +}; From 39e8b5b3e91426e0861111f2bbce10cf2c7b128c Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:25:24 +0100 Subject: [PATCH 004/102] Optimization of the browser module. --- lib/browser.js | 283 ++++++++++++++++++++++++++++++------------------- 1 file changed, 174 insertions(+), 109 deletions(-) diff --git a/lib/browser.js b/lib/browser.js index 866be4f9..c05a1474 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -12,8 +12,16 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * @overview This module provides functions for managing Puppeteer browser + * instances, creating and clearing pages, injecting custom resources, + * and setting up Highcharts for server-side rendering. The module ensures that + * resources are correctly managed and can handle failures during operations + * like launching the browser or creating new pages. + */ + import { readFileSync } from 'fs'; -import path from 'path'; +import { join } from 'path'; import puppeteer from 'puppeteer'; @@ -28,20 +36,23 @@ import ExportError from './errors/ExportError.js'; // Get the template for the page const template = readFileSync(__dirname + '/templates/template.html', 'utf8'); +// To save the browser let browser; /** * Retrieves the existing Puppeteer browser instance. * - * @returns {Promise} A Promise resolving to the Puppeteer browser + * @function getBrowser + * + * @returns {Promise} A Promise resolving to the Puppeteer browser * instance. * - * @throws {ExportError} Throws an ExportError if no valid browser has been - * created. + * @throws {ExportError} Throws an `ExportError` if no valid browser + * has been created. */ -export function get() { +export function getBrowser() { if (!browser) { - throw new ExportError('[browser] No valid browser has been created.'); + throw new ExportError('[browser] No valid browser has been created.', 500); } return browser; } @@ -49,24 +60,30 @@ export function get() { /** * Creates a Puppeteer browser instance with the specified arguments. * - * @param {Array} puppeteerArgs - Additional arguments for Puppeteer launch. + * @async + * @function createBrowser * - * @returns {Promise} A Promise resolving to the Puppeteer browser + * @param {Array.} [puppeteerArg=[]] - Additional arguments + * for Puppeteer launch. The default value is an empty array. + * + * @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. + * @throws {ExportError} Throws an `ExportError` if max retries to open + * a browser instance are reached, or if no browser instance is found after + * retries. */ -export async function create(puppeteerArgs) { - // Get debug and other options +export async function createBrowser(puppeteerArgs = []) { + // Get `debug` and `other` options const { debug, other } = getOptions(); // Get the debug options const { enable: enabledDebug, ...debugOptions } = debug; + // Launch options for the browser instance const launchOptions = { headless: other.browserShellMode ? 'shell' : true, - userDataDir: './tmp/', + userDataDir: 'tmp', args: puppeteerArgs, handleSIGINT: false, handleSIGTERM: false, @@ -86,6 +103,8 @@ export async function create(puppeteerArgs) { 3, `[browser] Attempting to get a browser instance (try ${++tryCount}).` ); + + // Launch the browser browser = await puppeteer.launch(launchOptions); } catch (error) { logWithStack( @@ -119,12 +138,13 @@ export async function create(puppeteerArgs) { } } catch (error) { throw new ExportError( - '[browser] Maximum retries to open a browser instance reached.' + '[browser] Maximum retries to open a browser instance reached.', + 500 ).setError(error); } if (!browser) { - throw new ExportError('[browser] Cannot find a browser to open.'); + throw new ExportError('[browser] Cannot find a browser to open.', 500); } } @@ -135,71 +155,102 @@ export async function create(puppeteerArgs) { /** * Closes the Puppeteer browser instance if it is connected. * + * @async + * @function closeBrowser + * * @returns {Promise} A Promise resolving to true after the browser * is closed. */ -export async function close() { - // Close the browser when connnected - if (browser?.connected) { +export async function closeBrowser() { + // Close the browser when connected + if (browser && browser.connected) { await browser.close(); } + browser = null; log(4, '[browser] Closed the browser.'); } /** - * Creates a new Puppeteer Page within an existing browser instance. + * Creates a new Puppeteer page within an existing browser instance. + * If the browser instance is not available, returns false. The function creates + * a new page, disables caching, sets content using `_setPageContent()`, + * and returns the created Puppeteer page. * - * If the browser instance is not available, returns false. + * @async + * @function newPage * - * The function creates a new page, disables caching, sets content using - * setPageContent(), and returns the created Puppeteer Page. + * @param {Object} poolResource - The pool resource that contians page and id. * - * @returns {(boolean|object)} Returns false if the browser instance is not - * available, or a Puppeteer Page object representing the newly created page. + * @returns {(boolean|object)} Returns false if the browser instance + * is not available, or a Puppeteer Page object representing the newly created + * page. + * + * @throws {ExportError} Throws an `ExportError` if no valid browser + * has been connected or if a page is invalid or closed. */ -export async function newPage() { - if (!browser) { - return false; +export async function newPage(poolResource) { + const startDate = new Date().getTime(); + + // Throw an error in case of no connected browser + if (!browser || !browser.connected) { + throw new ExportError(`[browser] Browser is not yet connected.`, 500); } // Create a page - const page = await browser.newPage(); + poolResource.page = await browser.newPage(); // Disable cache - await page.setCacheEnabled(false); + await poolResource.page.setCacheEnabled(false); // Set the content - await setPageContent(page); + await _setPageContent(poolResource.page); // Set page events - setPageEvents(page); + _setPageEvents(poolResource); + + // Check if the page is correctly created + if (!poolResource.page || poolResource.page.isClosed()) { + throw new ExportError('The page is invalid or closed.', 400); + } + + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${ + new Date().getTime() - startDate + }ms.` + ); - return page; + // Return the resource with a ready to use page + return poolResource; } /** - * Clears the content of a Puppeteer Page based on the specified mode. + * Clears the content of a Puppeteer Page based on the specified mode. Logs + * thrown error if clearing the page content fails. * - * @param {Object} page - The Puppeteer Page object to be cleared. - * @param {boolean} hardReset - A flag indicating the type of clearing + * @async + * @function clearPage + * + * @param {Object} poolResource - The pool resource that contians page and id. + * @param {boolean} [hardReset=false] - A flag indicating the type of clearing * to be performed. If true, navigates to 'about:blank' and resets content * and scripts. If false, clears the body content by setting a predefined HTML * structure. - * - * @throws {Error} Logs thrown error if clearing the page content fails. */ -export async function clearPage(page, hardReset = false) { +export async function clearPage(poolResource, hardReset = false) { try { - if (page && !page.isClosed()) { + if (poolResource.page && !poolResource.page.isClosed()) { if (hardReset) { // Navigate to about:blank - await page.goto('about:blank', { waitUntil: 'domcontentloaded' }); + await poolResource.page.goto('about:blank', { + waitUntil: 'domcontentloaded' + }); // Set the content and and scripts again - await setPageContent(page); + await _setPageContent(poolResource.page); } else { // Clear body content - await page.evaluate(() => { + await poolResource.page.evaluate(() => { document.body.innerHTML = '
'; }); @@ -210,10 +261,11 @@ export async function clearPage(page, hardReset = false) { logWithStack( 2, error, - '[browser] Could not clear the content of the page.' + `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.` ); + // Set the `workLimit` to exceeded in order to recreate the resource + poolResource.workCount = getOptions().pool.workLimit + 1; } - return false; } @@ -221,19 +273,22 @@ export async function clearPage(page, hardReset = false) { * Adds custom JS and CSS resources to a Puppeteer Page based on the specified * options. * - * @param {Object} page - The Puppeteer Page object to which resources will be - * added. - * @param {Object} options - All options and configuration. + * @async + * @function addPageResources + * + * @param {Object} page - The Puppeteer Page object to which resources will + * be added. + * @param {Object} customLogicOptions - The custom logic options. * - * @returns {Promise>} - Promise resolving to an array of injected + * @returns {Promise>} Promise resolving to an array of injected * resources. */ -export async function addPageResources(page, options) { +export async function addPageResources(page, customLogicOptions) { // Injected resources array const injectedResources = []; // Use resources - const resources = options.customLogic.resources; + const resources = customLogicOptions.resources; if (resources) { const injectedJs = []; @@ -293,9 +348,9 @@ export async function addPageResources(page, options) { injectedCss.push({ url: cssImportPath }); - } else if (options.customLogic.allowFileResources) { + } else if (customLogicOptions.allowFileResources) { injectedCss.push({ - path: path.join(__dirname, cssImportPath) + path: join(__dirname, cssImportPath) }); } } @@ -325,51 +380,58 @@ export async function addPageResources(page, options) { * injected resources and resets CSS and script tags on the page. Additionally, * it destroys previously existing charts. * + * @async + * @function clearPageResources + * * @param {Object} page - The Puppeteer Page object from which resources will * be cleared. - * @param {Array} injectedResources - Array of injected resources + * @param {Array.} injectedResources - Array of injected resources * to be cleared. */ export async function clearPageResources(page, injectedResources) { - for (const resource of injectedResources) { - await resource.dispose(); - } + try { + for (const resource of injectedResources) { + await resource.dispose(); + } - // Destroy old charts after export is done and reset all CSS and script tags - await page.evaluate(() => { - // We are not guaranteed that Highcharts is loaded, e,g, when doing SVG - // exports - if (typeof Highcharts !== 'undefined') { - // eslint-disable-next-line no-undef - const oldCharts = Highcharts.charts; - - // Check in any already existing charts - if (Array.isArray(oldCharts) && oldCharts.length) { - // Destroy old charts - for (const oldChart of oldCharts) { - oldChart && oldChart.destroy(); - // eslint-disable-next-line no-undef - Highcharts.charts.shift(); + // Destroy old charts after export is done and reset all CSS and script tags + await page.evaluate(() => { + // We are not guaranteed that Highcharts is loaded, e,g, when doing SVG + // exports + if (typeof Highcharts !== 'undefined') { + // eslint-disable-next-line no-undef + const oldCharts = Highcharts.charts; + + // Check in any already existing charts + if (Array.isArray(oldCharts) && oldCharts.length) { + // Destroy old charts + for (const oldChart of oldCharts) { + oldChart && oldChart.destroy(); + // eslint-disable-next-line no-undef + Highcharts.charts.shift(); + } } } - } - // eslint-disable-next-line no-undef - const [...scriptsToRemove] = document.getElementsByTagName('script'); - // eslint-disable-next-line no-undef - const [, ...stylesToRemove] = document.getElementsByTagName('style'); - // eslint-disable-next-line no-undef - const [...linksToRemove] = document.getElementsByTagName('link'); - - // Remove tags - for (const element of [ - ...scriptsToRemove, - ...stylesToRemove, - ...linksToRemove - ]) { - element.remove(); - } - }); + // eslint-disable-next-line no-undef + const [...scriptsToRemove] = document.getElementsByTagName('script'); + // eslint-disable-next-line no-undef + const [, ...stylesToRemove] = document.getElementsByTagName('style'); + // eslint-disable-next-line no-undef + const [...linksToRemove] = document.getElementsByTagName('link'); + + // Remove tags + for (const element of [ + ...scriptsToRemove, + ...stylesToRemove, + ...linksToRemove + ]) { + element.remove(); + } + }); + } catch (error) { + logWithStack(1, error, `[browser] Could not clear page's resources.`); + } } /** @@ -377,14 +439,17 @@ export async function clearPageResources(page, injectedResources) { * and additional scripts. Also, sets the pageerror in order to catch * and display errors from the window context. * + * @async + * @function _setPageContent + * * @param {Object} page - The Puppeteer Page object for which the content * is being set. */ -async function setPageContent(page) { +async function _setPageContent(page) { await page.setContent(template, { waitUntil: 'domcontentloaded' }); // Add all registered Higcharts scripts, quite demanding - await page.addScriptTag({ path: `${getCachePath()}/sources.js` }); + await page.addScriptTag({ path: join(getCachePath(), 'sources.js') }); // Set the initial animObject await page.evaluate(setupHighcharts); @@ -393,30 +458,23 @@ async function setPageContent(page) { /** * Set events for a Puppeteer Page. * - * @param {Object} page - The Puppeteer Page object to set events to. + * @function _setPageEvents + * + * @param {Object} poolResource - The pool resource that contians page and id. */ -function setPageEvents(page) { - // Get debug options - const { debug } = getOptions(); - - // Set the console listener, if needed - if (debug.enable && debug.listenToConsole) { - page.on('console', (message) => { - console.log(`[debug] ${message.text()}`); - }); - } +function _setPageEvents(poolResource) { + // Get `debug` and `pool` options + const { debug, pool } = getOptions(); // Set the pageerror listener - page.on('pageerror', async (error) => { + poolResource.page.on('pageerror', async (error) => { // It would seem like this may fire at the same time or shortly before // a page is closed. - if (page.isClosed()) { + if (poolResource.page.isClosed()) { return; } - // TODO: Consider adding a switch here that turns on log(0) logging - // on page errors. - await page.$eval( + await poolResource.page.$eval( '#container', (element, errorMessage) => { // eslint-disable-next-line no-undef @@ -427,12 +485,19 @@ function setPageEvents(page) { `

Chart input data error:

${error.toString()}` ); }); + + // Set the console listener, if needed + if (debug.enable && debug.listenToConsole) { + poolResource.page.on('console', (message) => { + console.log(`[debug] ${message.text()}`); + }); + } } export default { - get, - create, - close, + getBrowser, + createBrowser, + closeBrowser, newPage, clearPage, addPageResources, From 0ad7f57677425e99257cc6c66f7ca6593003834a Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:25:33 +0100 Subject: [PATCH 005/102] Optimization of the cache module. --- lib/cache.js | 519 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 303 insertions(+), 216 deletions(-) diff --git a/lib/cache.js b/lib/cache.js index cd712a15..bf83a913 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -12,116 +12,269 @@ See LICENSE file in root for details. *******************************************************************************/ -// The cache manager manages the Highcharts library and its dependencies. -// The cache itself is stored in .cache, and is checked by the config system -// before starting the service +/** + * @overview The cache manager is responsible for handling and managing + * the Highcharts library along with its dependencies. It ensures that these + * resources are stored and retrieved efficiently to optimize performance + * and reduce redundant network requests. The cache is stored in the `.cache` + * directory, which serves as a dedicated folder for keeping cached files. + */ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; -import { join } from 'path'; +import { isAbsolute, join } from 'path'; import { HttpsProxyAgent } from 'https-proxy-agent'; import { getOptions } from './config.js'; -import { envs } from './envs.js'; import { fetch } from './fetch.js'; import { log } from './logger.js'; import { __dirname } from './utils.js'; import ExportError from './errors/ExportError.js'; +// The initial cache template const cache = { - cdnURL: 'https://code.highcharts.com/', + cdnUrl: 'https://code.highcharts.com', activeManifest: {}, sources: '', hcVersion: '' }; /** - * Extracts and caches the Highcharts version from the sources string. + * Checks the cache for Highcharts dependencies, updates the cache if needed, + * and loads the sources. + * + * @async + * @function checkAndUpdateCache + * + * @param {Object} highchartsOptions - Object containing highcharts options. + * @param {Object} serverProxyOptions - Object containing server options. + * + * @returns {Promise} A Promise that resolves once the cache is checked + * and updated. + */ +export async function checkAndUpdateCache( + highchartsOptions, + serverProxyOptions +) { + let fetchedModules; + + // Get the cache path + const cachePath = getCachePath(); + + // Prepare paths to manifest and sources from the .cache folder + const manifestPath = join(cachePath, 'manifest.json'); + const sourcePath = join(cachePath, 'sources.js'); + + // Create the cache destination if it doesn't exist already + !existsSync(cachePath) && mkdirSync(cachePath, { recursive: true }); + + // Fetch all the scripts either if manifest.json does not exist + // or if the forceFetch option is enabled + if (!existsSync(manifestPath) || highchartsOptions.forceFetch) { + log(3, '[cache] Fetching and caching Highcharts dependencies.'); + fetchedModules = await _updateCache( + highchartsOptions, + serverProxyOptions, + sourcePath + ); + } else { + let requestUpdate = false; + + // Read the manifest JSON + const manifest = JSON.parse(readFileSync(manifestPath)); + + // Check if the modules is an array, if so, we rewrite it to a map to make + // it easier to resolve modules. + if (manifest.modules && Array.isArray(manifest.modules)) { + const moduleMap = {}; + manifest.modules.forEach((m) => (moduleMap[m] = 1)); + manifest.modules = moduleMap; + } + + const { coreScripts, moduleScripts, indicatorScripts } = highchartsOptions; + const numberOfModules = + coreScripts.length + moduleScripts.length + indicatorScripts.length; + + // Compare the loaded highcharts config with the contents in cache. + // If there are changes, fetch requested modules and products, + // and bake them into a giant blob. Save the blob. + if (manifest.version !== highchartsOptions.version) { + log( + 2, + '[cache] A Highcharts version mismatch in the cache, need to re-fetch.' + ); + requestUpdate = true; + } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) { + log( + 2, + '[cache] The cache and the requested modules do not match, need to re-fetch.' + ); + requestUpdate = true; + } else { + // Check each module, if anything is missing refetch everything + requestUpdate = (moduleScripts || []).some((moduleName) => { + if (!manifest.modules[moduleName]) { + log( + 2, + `[cache] The ${moduleName} is missing in the cache, need to re-fetch.` + ); + return true; + } + }); + } + + if (requestUpdate) { + fetchedModules = await _updateCache( + highchartsOptions, + serverProxyOptions, + sourcePath + ); + } else { + log(3, '[cache] Dependency cache is up to date, proceeding.'); + + // Load the sources + cache.sources = readFileSync(sourcePath, 'utf8'); + + // Get current modules map + fetchedModules = manifest.modules; + + // Extract and save version of currently used Highcharts + cache.hcVersion = extractVersion(cache.sources); + } + } + + // Finally, save the new manifest, which is basically our current config + // in a slightly different format + await _saveConfigToManifest(highchartsOptions, fetchedModules); +} + +/** + * Gets the version of Highcharts from the cache. + * + * @function getHighchartsVersion + * + * @returns {string} The cached Highcharts version. + */ +export function getHighchartsVersion() { + return cache.hcVersion; +} + +/** + * Updates the Highcharts version in the applied configuration and checks + * the cache for the new version. + * + * @async + * @function updateHighchartsVersion + * + * @param {string} newVersion - The new Highcharts version to be applied. + * + * @returns {Promise<(Object|boolean)>} A Promise resolving to the updated + * configuration with the new version, or false if no applied configuration + * exists. + */ +export async function updateHighchartsVersion(newVersion) { + // Get the reference to the global options to update to the new version + const options = getOptions(true); + + // Set to the new version + options.highcharts.version = newVersion; + + // Check if cache needs to be updated + await checkAndUpdateCache(options.highcharts, options.server.proxy); +} + +/** + * Extracts Highcharts version from the cache's sources string. + * + * @function extractVersion + * + * @param {Object} cacheSources - The cache sources object. * * @returns {string} The extracted Highcharts version. */ -export const extractVersion = (cache) => { - return cache.sources - .substring(0, cache.sources.indexOf('*/')) +export function extractVersion(cacheSources) { + return cacheSources + .substring(0, cacheSources.indexOf('*/')) .replace('/*', '') .replace('*/', '') .replace(/\n/g, '') .trim(); -}; +} /** * Extracts the Highcharts module name based on the scriptPath. + * + * @function extractModuleName + * + * @param {string} scriptPath - The path of the script from which the module + * name will be extracted. + * + * @returns {string} The extracted module name. */ -export const extractModuleName = (scriptPath) => { +export function extractModuleName(scriptPath) { return scriptPath.replace( /(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi, '' ); -}; +} /** - * Saves the provided configuration and fetched modules to the cache manifest - * file. + * Retrieves the current cache object. * - * @param {object} config - Highcharts-related configuration object. - * @param {object} fetchedModules - An object that contains mapped names of - * fetched Highcharts modules to use. + * @function getCache * - * @throws {ExportError} Throws an ExportError if an error occurs while writing - * the cache manifest. + * @returns {Object} The cache object containing various cached data. */ -export const saveConfigToManifest = async (config, fetchedModules) => { - const newManifest = { - version: config.version, - modules: fetchedModules || {} - }; +export function getCache() { + return cache; +} - // Update cache object with the current modules - cache.activeManifest = newManifest; +/** + * Gets the cache path for Highcharts. + * + * @function getCachePath + * + * @returns {string} The path to the cache directory for Highcharts. + */ +export function getCachePath() { + const cachePathOption = getOptions().highcharts.cachePath; - log(3, '[cache] Writing a new manifest.'); - try { - writeFileSync( - join(__dirname, config.cachePath, 'manifest.json'), - JSON.stringify(newManifest), - 'utf8' - ); - } catch (error) { - throw new ExportError('[cache] Error writing the cache manifest.').setError( - error - ); - } -}; + return isAbsolute(cachePathOption) // #562 + ? cachePathOption + : join(__dirname, cachePathOption); +} /** - * Fetches a single script and updates the fetchedModules accordingly. + * Fetches a single script and updates the `fetchedModules` accordingly. + * + * @async + * @function _fetchAndProcessScript * * @param {string} script - A path to script to get. * @param {Object} requestOptions - Additional options for the proxy agent * to use for a request. * @param {Object} fetchedModules - An object which tracks which Highcharts * modules have been fetched. - * @param {boolean} shouldThrowError - A flag to indicate if the error should be - * thrown. This should be used only for the core scripts. + * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error + * should be thrown. This should be used only for the core scripts. * * @returns {Promise} A Promise resolving to the text representation * of the fetched script. * - * @throws {ExportError} Throws an ExportError if there is a problem with - * fetching the script. + * @throws {ExportError} Throws an `ExportError` if there is a problem + * with fetching the script. */ -export const fetchAndProcessScript = async ( +async function _fetchAndProcessScript( script, requestOptions, fetchedModules, shouldThrowError = false -) => { +) { // Get rid of the .js from the custom strings if (script.endsWith('.js')) { script = script.substring(0, script.length - 3); } - log(4, `[cache] Fetching script - ${script}.js`); // Fetch the script @@ -133,13 +286,13 @@ export const fetchAndProcessScript = async ( const moduleName = extractModuleName(script); fetchedModules[moduleName] = 1; } - return response.text; } if (shouldThrowError) { throw new ExportError( - `Could not fetch the ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).` + `Could not fetch the ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`, + 404 ).setError(response); } else { log( @@ -149,29 +302,73 @@ export const fetchAndProcessScript = async ( } return ''; -}; +} + +/** + * Saves the provided configuration and fetched modules to the cache manifest + * file. + * + * @async + * @function _saveConfigToManifest + * + * @param {Object} highchartsOptions - Highcharts-related configuration object. + * @param {Object} [fetchedModules={}] - An object that contains mapped names + * of fetched Highcharts modules to use. + * + * @throws {ExportError} Throws an `ExportError` if an error occurs while + * writing the cache manifest. + */ +async function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) { + const newManifest = { + version: highchartsOptions.version, + modules: fetchedModules + }; + + // Update cache object with the current modules + cache.activeManifest = newManifest; + + log(3, '[cache] Writing a new manifest.'); + try { + writeFileSync( + join(getCachePath(), 'manifest.json'), + JSON.stringify(newManifest), + 'utf8' + ); + } catch (error) { + throw new ExportError( + '[cache] Error writing the cache manifest.', + 500 + ).setError(error); + } +} /** * Fetches Highcharts scripts and customScripts from the given CDNs. * + * @async + * @function _fetchScripts + * * @param {string} coreScripts - Array of Highcharts core scripts to fetch. * @param {string} moduleScripts - Array of Highcharts modules to fetch. * @param {string} customScripts - Array of custom script paths to fetch * (full URLs). - * @param {object} proxyOptions - Options for the proxy agent to use for - * a request. - * @param {object} fetchedModules - An object which tracks which Highcharts + * @param {Object} proxyOptions - Options for the proxy agent to use + * for a request. + * @param {Object} fetchedModules - An object which tracks which Highcharts * modules have been fetched. * * @returns {Promise} The fetched scripts content joined. + * + * @throws {ExportError} Throws an `ExportError` if an error occurs while + * setting an HTTP Agent for proxy. */ -export const fetchScripts = async ( +async function _fetchScripts( coreScripts, moduleScripts, customScripts, proxyOptions, fetchedModules -) => { +) { // Configure proxy if exists let proxyAgent; const proxyHost = proxyOptions.host; @@ -185,9 +382,10 @@ export const fetchScripts = async ( port: proxyPort }); } catch (error) { - throw new ExportError('[cache] Could not create a Proxy Agent.').setError( - error - ); + throw new ExportError( + '[cache] Could not create a Proxy Agent.', + 500 + ).setError(error); } } @@ -195,212 +393,101 @@ export const fetchScripts = async ( const requestOptions = proxyAgent ? { agent: proxyAgent, - timeout: envs.SERVER_PROXY_TIMEOUT + timeout: proxyOptions.timeout } : {}; const allFetchPromises = [ ...coreScripts.map((script) => - fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true) + _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true) ), ...moduleScripts.map((script) => - fetchAndProcessScript(`${script}`, requestOptions, fetchedModules) + _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules) ), ...customScripts.map((script) => - fetchAndProcessScript(`${script}`, requestOptions) + _fetchAndProcessScript(`${script}`, requestOptions) ) ]; const fetchedScripts = await Promise.all(allFetchPromises); return fetchedScripts.join(';\n'); -}; +} /** * Updates the local cache with Highcharts scripts and their versions. * - * @param {Object} options - Object containing all options. + * @async + * @function _updateCache + * + * @param {Object} highchartsOptions - Object containing Highcharts options. + * @param {Object} serverProxyOptions - Object containing server proxy options. * @param {string} sourcePath - The path to the source file in the cache. * - * @returns {Promise} A Promise resolving to an object representing + * @returns {Promise} A Promise resolving to an object representing * the fetched modules. * - * @throws {ExportError} Throws an ExportError if there is an issue updating + * @throws {ExportError} Throws an `ExportError` if there is an issue updating * the local Highcharts cache. */ -export const updateCache = async ( - highchartsOptions, - proxyOptions, - sourcePath -) => { - const version = highchartsOptions.version; - const hcVersion = version === 'latest' || !version ? '' : `${version}/`; - const cdnURL = highchartsOptions.cdnURL || cache.cdnURL; - - log( - 3, - `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.` - ); +async function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) { + // Get Highcharts version for scripts + const hcVersion = + highchartsOptions.version === 'latest' + ? '' + : `${highchartsOptions.version}`; + + // Get the CDN url for scripts + const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl; - const fetchedModules = {}; try { - cache.sources = await fetchScripts( + const fetchedModules = {}; + + log( + 3, + `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.` + ); + + cache.sources = await _fetchScripts( [ - ...highchartsOptions.coreScripts.map((c) => `${cdnURL}${hcVersion}${c}`) + ...highchartsOptions.coreScripts.map( + (c) => `${cdnUrl}/${hcVersion}/${c}` + ) ], [ ...highchartsOptions.moduleScripts.map((m) => m === 'map' - ? `${cdnURL}maps/${hcVersion}modules/${m}` - : `${cdnURL}${hcVersion}modules/${m}` + ? `${cdnUrl}/maps/${hcVersion}/modules/${m}` + : `${cdnUrl}/${hcVersion}/modules/${m}` ), ...highchartsOptions.indicatorScripts.map( - (i) => `${cdnURL}stock/${hcVersion}indicators/${i}` + (i) => `${cdnUrl}/stock/${hcVersion}/indicators/${i}` ) ], highchartsOptions.customScripts, - proxyOptions, + serverProxyOptions, fetchedModules ); - cache.hcVersion = extractVersion(cache); + // Extract and save version of currently used Highcharts + cache.hcVersion = extractVersion(cache.sources); // Save the fetched modules into caches' source JSON writeFileSync(sourcePath, cache.sources); return fetchedModules; } catch (error) { throw new ExportError( - '[cache] Unable to update the local Highcharts cache.' + '[cache] Unable to update the local Highcharts cache.', + 500 ).setError(error); } -}; - -/** - * Updates the Highcharts version in the applied configuration and checks - * the cache for the new version. - * - * @param {string} newVersion - The new Highcharts version to be applied. - * - * @returns {Promise<(object|boolean)>} A Promise resolving to the updated - * configuration with the new version, or false if no applied configuration - * exists. - */ -export const updateVersion = async (newVersion) => { - const options = getOptions(); - if (options?.highcharts) { - options.highcharts.version = newVersion; - } - await checkAndUpdateCache(options); -}; - -/** - * Checks the cache for Highcharts dependencies, updates the cache if needed, - * and loads the sources. - * - * @param {Object} options - Object containing all options. - * - * @returns {Promise} A Promise that resolves once the cache is checked - * and updated. - * - * @throws {ExportError} Throws an ExportError if there is an issue updating - * or reading the cache. - */ -export const checkAndUpdateCache = async (options) => { - const { highcharts, server } = options; - const cachePath = join(__dirname, highcharts.cachePath); - - let fetchedModules; - // Prepare paths to manifest and sources from the .cache folder - const manifestPath = join(cachePath, 'manifest.json'); - const sourcePath = join(cachePath, 'sources.js'); - - // Create the cache destination if it doesn't exist already - !existsSync(cachePath) && mkdirSync(cachePath); - - // Fetch all the scripts either if manifest.json does not exist - // or if the forceFetch option is enabled - if (!existsSync(manifestPath) || highcharts.forceFetch) { - log(3, '[cache] Fetching and caching Highcharts dependencies.'); - fetchedModules = await updateCache(highcharts, server.proxy, sourcePath); - } else { - let requestUpdate = false; - - // Read the manifest JSON - const manifest = JSON.parse(readFileSync(manifestPath)); - - // Check if the modules is an array, if so, we rewrite it to a map to make - // it easier to resolve modules. - if (manifest.modules && Array.isArray(manifest.modules)) { - const moduleMap = {}; - manifest.modules.forEach((m) => (moduleMap[m] = 1)); - manifest.modules = moduleMap; - } - - const { coreScripts, moduleScripts, indicatorScripts } = highcharts; - const numberOfModules = - coreScripts.length + moduleScripts.length + indicatorScripts.length; - - // Compare the loaded highcharts config with the contents in cache. - // If there are changes, fetch requested modules and products, - // and bake them into a giant blob. Save the blob. - if (manifest.version !== highcharts.version) { - log( - 2, - '[cache] A Highcharts version mismatch in the cache, need to re-fetch.' - ); - requestUpdate = true; - } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) { - log( - 2, - '[cache] The cache and the requested modules do not match, need to re-fetch.' - ); - requestUpdate = true; - } else { - // Check each module, if anything is missing refetch everything - requestUpdate = (moduleScripts || []).some((moduleName) => { - if (!manifest.modules[moduleName]) { - log( - 2, - `[cache] The ${moduleName} is missing in the cache, need to re-fetch.` - ); - return true; - } - }); - } - - if (requestUpdate) { - fetchedModules = await updateCache(highcharts, server.proxy, sourcePath); - } else { - log(3, '[cache] Dependency cache is up to date, proceeding.'); - - // Load the sources - cache.sources = readFileSync(sourcePath, 'utf8'); - - // Get current modules map - fetchedModules = manifest.modules; - - cache.hcVersion = extractVersion(cache); - } - } - - // Finally, save the new manifest, which is basically our current config - // in a slightly different format - await saveConfigToManifest(highcharts, fetchedModules); -}; - -export const getCachePath = () => - join(__dirname, getOptions().highcharts.cachePath); - -export const getCache = () => cache; - -export const highcharts = () => cache.sources; - -export const version = () => cache.hcVersion; +} export default { checkAndUpdateCache, - getCachePath, - updateVersion, + getHighchartsVersion, + updateHighchartsVersion, + extractVersion, + extractModuleName, getCache, - highcharts, - version + getCachePath }; From c5b14e1bdbd8ae784bfc96938ae7b84662adc229 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:25:52 +0100 Subject: [PATCH 006/102] Modification, corrections and separation exporting logic into smaller and more readable functions. --- lib/chart.js | 964 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 608 insertions(+), 356 deletions(-) diff --git a/lib/chart.js b/lib/chart.js index 47b28f8d..8c1bfe61 100644 --- a/lib/chart.js +++ b/lib/chart.js @@ -12,107 +12,132 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * @overview This module provides functions to manage the exporting + * of Highcharts configurations into various output formats such as images + * and SVGs. It supports single and batch export operations and allows extensive + * customization through options passed from configurations or APIs. + */ + import { readFileSync, writeFileSync } from 'fs'; -import { getOptions, initExportSettings } from './config.js'; +import { getOptions } from './config.js'; import { log, logWithStack } from './logger.js'; -import { killPool, postWork, stats } from './pool.js'; +import { killPool, postWork, getPoolStats } from './pool.js'; +import { sanitize } from './sanitize.js'; import { fixType, - handleResources, isCorrectJSON, optionsStringify, roundNumber, - toBoolean, wrapAround } from './utils.js'; -import { sanitize } from './sanitize.js'; + import ExportError from './errors/ExportError.js'; let allowCodeExecution = false; /** - * Starts an export process. The `settings` contains final options gathered - * from all possible sources (config, env, cli, json). The `endCallback` is - * called when the export is completed, with an error object as the first - * argument and the second containing the base64 respresentation of a chart. + * Starts an export process. The `options` object contains final options + * gathered from all possible sources (config, custom json, env, cli). + * The `endCallback` is called when the export is completed, with the `error` + * object as the first argument and the `data` object as the second, + * which contains the base64 respresentation of a chart. * - * @param {Object} settings - The settings object containing export - * configuration. - * @param {function} endCallback - The callback function to be invoked upon + * @async + * @function startExport + * + * @param {Object} [options=getOptions()] - The `options` object containing + * configuration for a custom export. The default value is the global options + * of the export server instance. + * @param {Function} endCallback - The callback function to be invoked upon * finalizing work or upon error occurance of the exporting process. * - * @returns {void} This function does not return a value directly; instead, - * it communicates results via the endCallback. + * @returns {void} This function does not return a value directly. Instead, + * it communicates results via the `endCallback`. + * + * @throws {ExportError} Throws an `ExportError` if there is a problem with + * processing input of any type. */ -export const startExport = async (settings, endCallback) => { +export async function startExport(options = getOptions(), endCallback) { // Starting exporting process message log(4, '[chart] Starting the exporting process.'); - // Initialize options - const options = initExportSettings(settings, getOptions()); - // Get the export options const exportOptions = options.export; - // If SVG is an input (argument can be sent only by the request) - if (options.payload?.svg && options.payload.svg !== '') { + // Export using SVG as an input + if (exportOptions.svg !== null) { try { - log(4, '[chart] Attempting to export from a SVG input.'); + log(4, '[chart] Attempting to export from an SVG input.'); - const result = exportAsString( - sanitize(options.payload.svg), // #209 + // Export from an SVG string + const result = _exportAsString( + sanitize(exportOptions.svg), // #209 options, endCallback ); - ++stats.exportFromSvgAttempts; + // SVG export attempt counter + ++getPoolStats().exportFromSvgAttempts; + + // Return SVG export result return result; } catch (error) { return endCallback( - new ExportError('[chart] Error loading SVG input.').setError(error) + new ExportError('[chart] Error loading an SVG input.', 400).setError( + error + ) ); } } - // Export using options from the file - if (exportOptions.infile && exportOptions.infile.length) { - // Try to read the file to get the string representation + // Export using options from the file as an input + if (exportOptions.infile !== null) { try { - log(4, '[chart] Attempting to export from an input file.'); - options.export.instr = readFileSync(exportOptions.infile, 'utf8'); - return exportAsString(options.export.instr.trim(), options, endCallback); + log(4, '[chart] Attempting to export from a file input.'); + + // Try to read the file to get the string representation + exportOptions.instr = readFileSync(exportOptions.infile, 'utf8'); + + // Export from a file options + return _exportAsString(exportOptions.instr, options, endCallback); } catch (error) { return endCallback( - new ExportError('[chart] Error loading input file.').setError(error) + new ExportError('[chart] Error loading a file input.', 400).setError( + error + ) ); } } - // Export with options from the raw representation - if ( - (exportOptions.instr && exportOptions.instr !== '') || - (exportOptions.options && exportOptions.options !== '') - ) { + // Export using options from the raw representation as an input + if (exportOptions.instr !== null || exportOptions.options !== null) { + // If not found, make sure that the instr gets the value from the options + if (!exportOptions.instr && exportOptions.options) { + exportOptions.instr = exportOptions.options; + } + try { log(4, '[chart] Attempting to export from a raw input.'); // Perform a direct inject when forced - if (toBoolean(options.customLogic?.allowCodeExecution)) { - return doStraightInject(options, endCallback); + if (options.customLogic.allowCodeExecution) { + return _doStraightInject(options, endCallback); } - // Either try to parse to JSON first or do the direct export - return typeof exportOptions.instr === 'string' - ? exportAsString(exportOptions.instr.trim(), options, endCallback) - : doExport( - options, - exportOptions.instr || exportOptions.options, - endCallback - ); + // Export from stringified options + if (typeof exportOptions.instr === 'string') { + return _exportAsString(exportOptions.instr, options, endCallback); + } + + // Export from object options + return _prepareExport(options, endCallback, exportOptions.instr, null); } catch (error) { return endCallback( - new ExportError('[chart] Error loading raw input.').setError(error) + new ExportError('[chart] Error loading a raw input.', 400).setError( + error + ) ); } } @@ -120,30 +145,76 @@ export const startExport = async (settings, endCallback) => { // No input specified, pass an error message to the callback return endCallback( new ExportError( - `[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.` + `[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.`, + 400 ) ); -}; +} + +/** + * Starts a single export process based on the specified options. + * + * @async + * @function singleExport + * + * @param {Object} [options=getOptions()] - The `options` object containing + * configuration for a custom export. The default value is the global options + * of the export server instance. + * + * @returns {Promise} A Promise that resolves once the single export + * process is completed. + * + * @throws {ExportError} Throws an `ExportError` if an error occurs during + * the single export process. + */ +export async function singleExport(options = getOptions()) { + // Use `instr` or its alias, `options` + options.export.instr = options.export.instr || options.export.options; + + // Perform an export + await startExport(options, async (error, data) => { + // Exit process when error exists + if (error) { + throw error; + } + + // Get the `outfile` and `type` for a chart + const { outfile, type } = data.options.export; + + // Save the base64 from a buffer to a correct image format + writeFileSync( + outfile || `chart.${type}`, + type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result + ); + + // Kill pool and close browser after finishing single export + await killPool(); + }); +} /** * Starts a batch export process for multiple charts based on the information - * in the batch option. The batch is a string in the following format: - * "infile1.json=outfile1.png;infile2.json=outfile2.png;..." + * in the `batch` option. The `batch` is a string in the following format: + * "infile1.json=outfile1.png;infile2.json=outfile2.png;...". * - * @param {Object} options - The options object containing configuration for - * a batch export. + * @async + * @function batchExport + * + * @param {Object} [options=getOptions()] - The `options` object containing + * configuration for a custom export. The default value is the global options + * of the export server instance. * * @returns {Promise} A Promise that resolves once the batch export * process is completed. * - * @throws {ExportError} Throws an ExportError if an error occurs during + * @throws {ExportError} Throws an `ExportError` if an error occurs during * any of the batch export process. */ -export const batchExport = async (options) => { +export async function batchExport(options = getOptions()) { const batchFunctions = []; - // Split and pair the --batch arguments - for (let pair of options.export.batch.split(';')) { + // Split and pair the `batch` arguments + for (let pair of options.export.batch.split(';') || []) { pair = pair.split('='); if (pair.length === 2) { batchFunctions.push( @@ -156,18 +227,19 @@ export const batchExport = async (options) => { outfile: pair[1] } }, - (error, info) => { + (error, data) => { // Throw an error if (error) { throw error; } + // Get the `outfile` and `type` for a chart + const { outfile, type } = data.options.export; + // Save the base64 from a buffer to a correct image file writeFileSync( - info.options.export.outfile, - info.options.export.type !== 'svg' - ? Buffer.from(info.result, 'base64') - : info.result + outfile, + type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result ); } ) @@ -183,297 +255,66 @@ export const batchExport = async (options) => { await killPool(); } catch (error) { throw new ExportError( - '[chart] Error encountered during batch export.' + '[chart] Error encountered during batch export.', + 400 ).setError(error); } -}; +} /** - * Starts a single export process based on the specified options. - * - * @param {Object} options - The options object containing configuration for - * a single export. + * Retrieves and returns the current status of the code execution permission. * - * @returns {Promise} A Promise that resolves once the single export - * process is completed. + * @function getAllowCodeExecution * - * @throws {ExportError} Throws an ExportError if an error occurs during - * the single export process. + * @returns {boolean} The value of the global `allowCodeExecution` option. */ -export const singleExport = async (options) => { - // Use instr or its alias, options - options.export.instr = options.export.instr || options.export.options; - - // Perform an export - await startExport(options, async (error, info) => { - // Exit process when error - if (error) { - throw error; - } - - const { outfile, type } = info.options.export; - - // Save the base64 from a buffer to a correct image file - writeFileSync( - outfile || `chart.${type}`, - type !== 'svg' ? Buffer.from(info.result, 'base64') : info.result - ); - - // Kill pool and close browser after finishing single export - await killPool(); - }); -}; - -/** - * Determines the size and scale for chart export based on the provided options. - * - * @param {Object} options - The options object containing configuration for - * chart export. - * - * @returns {Object} An object containing the calculated height, width, - * and scale for the chart export. - */ -export const findChartSize = (options) => { - const { chart, exporting } = - options.export?.options || isCorrectJSON(options.export?.instr); - - // See if globalOptions holds chart or exporting size - const globalOptions = isCorrectJSON(options.export?.globalOptions); - - // Secure scale value - let scale = - options.export?.scale || - exporting?.scale || - globalOptions?.exporting?.scale || - options.export?.defaultScale || - 1; - - // the scale cannot be lower than 0.1 and cannot be higher than 5.0 - scale = Math.max(0.1, Math.min(scale, 5.0)); - - // we want to round the numbers like 0.23234 -> 0.23 - scale = roundNumber(scale, 2); - - // Find chart size and scale - const size = { - height: - options.export?.height || - exporting?.sourceHeight || - chart?.height || - globalOptions?.exporting?.sourceHeight || - globalOptions?.chart?.height || - options.export?.defaultHeight || - 400, - width: - options.export?.width || - exporting?.sourceWidth || - chart?.width || - globalOptions?.exporting?.sourceWidth || - globalOptions?.chart?.width || - options.export?.defaultWidth || - 600, - scale - }; - - // Get rid of potential px and % - for (let [param, value] of Object.entries(size)) { - size[param] = - typeof value === 'string' ? +value.replace(/px|%/gi, '') : value; - } - return size; -}; +export function getAllowCodeExecution() { + return allowCodeExecution; +} /** - * Function for finalizing options before export. + * Sets the code execution permission based on the provided boolean value. * - * @param {Object} options - The options object containing configuration for - * the export process. - * @param {Object} chartJson - The JSON representation of the chart. - * @param {Function} endCallback - The callback function to be called upon - * completion or error. - * @param {string} svg - The SVG representation of the chart. + * @function setAllowCodeExecution * - * @returns {Promise} A Promise that resolves once the export process - * is completed. + * @param {boolean} value - The value to be assigned to the global + * `allowCodeExecution` option. */ -const doExport = async (options, chartJson, endCallback, svg) => { - let { export: exportOptions, customLogic: customLogicOptions } = options; - - const allowCodeExecutionScoped = - typeof customLogicOptions.allowCodeExecution === 'boolean' - ? customLogicOptions.allowCodeExecution - : allowCodeExecution; - - if (!customLogicOptions) { - customLogicOptions = options.customLogic = {}; - } else if (allowCodeExecutionScoped) { - if (typeof options.customLogic.resources === 'string') { - // Process resources - options.customLogic.resources = handleResources( - options.customLogic.resources, - toBoolean(options.customLogic.allowFileResources) - ); - } else if (!options.customLogic.resources) { - try { - const resources = readFileSync('resources.json', 'utf8'); - options.customLogic.resources = handleResources( - resources, - toBoolean(options.customLogic.allowFileResources) - ); - } catch (error) { - logWithStack( - 2, - error, - `[chart] Unable to load the default resources.json file.` - ); - } - } - } - - // If the allowCodeExecution flag isn't set, we should refuse the usage - // of callback, resources, and custom code. Additionally, the worker will - // refuse to run arbitrary JavaScript. Prioritized should be the scoped - // option, then we should take a look at the overall pool option. - if (!allowCodeExecutionScoped && customLogicOptions) { - if ( - customLogicOptions.callback || - customLogicOptions.resources || - customLogicOptions.customCode - ) { - // Send back a friendly message saying that the exporter does not support - // these settings. - return endCallback( - new ExportError( - `[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server.` - ) - ); - } - - // Reset all additional custom code - customLogicOptions.callback = false; - customLogicOptions.resources = false; - customLogicOptions.customCode = false; - } - - // Clean properties to keep it lean and mean - if (chartJson) { - chartJson.chart = chartJson.chart || {}; - chartJson.exporting = chartJson.exporting || {}; - chartJson.exporting.enabled = false; - } - - exportOptions.constr = exportOptions.constr || 'chart'; - exportOptions.type = fixType(exportOptions.type, exportOptions.outfile); - if (exportOptions.type === 'svg') { - exportOptions.width = false; - } - - // Prepare global and theme options - ['globalOptions', 'themeOptions'].forEach((optionsName) => { - try { - if (exportOptions && exportOptions[optionsName]) { - if ( - typeof exportOptions[optionsName] === 'string' && - exportOptions[optionsName].endsWith('.json') - ) { - exportOptions[optionsName] = isCorrectJSON( - readFileSync(exportOptions[optionsName], 'utf8'), - true - ); - } else { - exportOptions[optionsName] = isCorrectJSON( - exportOptions[optionsName], - true - ); - } - } - } catch (error) { - exportOptions[optionsName] = {}; - logWithStack(2, error, `[chart] The '${optionsName}' cannot be loaded.`); - } - }); - - // Prepare the customCode - if (customLogicOptions.allowCodeExecution) { - try { - customLogicOptions.customCode = wrapAround( - customLogicOptions.customCode, - customLogicOptions.allowFileResources - ); - } catch (error) { - logWithStack(2, error, `[chart] The 'customCode' cannot be loaded.`); - } - } - - // Get the callback - if ( - customLogicOptions && - customLogicOptions.callback && - customLogicOptions.callback?.indexOf('{') < 0 - ) { - // The allowFileResources is always set to false for HTTP requests to avoid - // injecting arbitrary files from the fs - if (customLogicOptions.allowFileResources) { - try { - customLogicOptions.callback = readFileSync( - customLogicOptions.callback, - 'utf8' - ); - } catch (error) { - customLogicOptions.callback = false; - logWithStack(2, error, `[chart] The 'callback' cannot be loaded.`); - } - } else { - customLogicOptions.callback = false; - } - } - - // Size search - options.export = { - ...options.export, - ...findChartSize(options) - }; - - // Post the work to the pool - try { - const result = await postWork( - exportOptions.strInj || chartJson || svg, - options - ); - return endCallback(false, result); - } catch (error) { - return endCallback(error); - } -}; +export function setAllowCodeExecution(value) { + allowCodeExecution = value; +} /** * Performs a direct inject of options before export. The function attempts * to stringify the provided options and removes unnecessary characters, - * ensuring a clean and formatted input. The resulting string is saved as - * a "stright inject" string in the export options. It then invokes the - * doExport function with the updated options. + * ensuring a clean and formatted input. The resulting string is saved + * as a 'stright inject' string in the export options. It then invokes + * the `_prepareExport` function with the updated options. * - * IMPORTANT: Dangerous and must be used deliberately by someone who sets up - * a server (see the --allowCodeExecution option). + * IMPORTANT: Dangerous and must be used deliberately by someone who sets + * up a server (see the `allowCodeExecution` option). * - * @param {Object} options - The export options containing the input + * @function _doStraightInject + * + * @param {Object} options - The `options` object containing the input * to be injected. - * @param {function} endCallback - The callback function to be invoked + * @param {Function} endCallback - The callback function to be invoked * at the end of the process. * * @returns {Promise} A Promise that resolves with the result of the export * operation or rejects with an error if any issues occur during the process. */ -const doStraightInject = (options, endCallback) => { +function _doStraightInject(options, endCallback) { try { let strInj; - let instr = options.export.instr || options.export.options; + let instr = options.export.instr; if (typeof instr !== 'string') { // Try to stringify options strInj = instr = optionsStringify( instr, - options.customLogic?.allowCodeExecution + options.customLogic.allowCodeExecution, + false ); } strInj = instr.replaceAll(/\t|\n|\r/g, '').trim(); @@ -485,82 +326,493 @@ const doStraightInject = (options, endCallback) => { // Save as stright inject string options.export.strInj = strInj; - return doExport(options, false, endCallback); + + // Call the `_prepareExport` function with the straight injected string + return _prepareExport(options, endCallback, null, null); } catch (error) { return endCallback( new ExportError( - `[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.` + (options.export?.requestId + ? `[chart] Malformed input detected for the request: ${options.export?.requestId}. ` + : '[chart] Malformed input detected. ') + + 'Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you are using SVG, it is unescaped.', + 400 ).setError(error) ); } -}; +} /** - * Exports a string based on the provided options and invokes an end callback. + * Exports a string based on the provided options and invokes the end callback. + * + * @function _exportAsString * * @param {string} stringToExport - The string content to be exported. - * @param {Object} options - Export options, including customLogic with - * allowCodeExecution flag. + * @param {Object} options - The `options` object containing general options + * configuration. * @param {Function} endCallback - Callback function to be invoked at the end * of the export process. * - * @returns {any} Result of the export process or an error if encountered. + * @returns {unknown} Result of the export process or an error if encountered. */ -const exportAsString = (stringToExport, options, endCallback) => { - const { allowCodeExecution } = options.customLogic; - +function _exportAsString(stringToExport, options, endCallback) { // Check if it is SVG if ( stringToExport.indexOf('= 0 || stringToExport.indexOf('= 0 ) { log(4, '[chart] Parsing input as SVG.'); - return doExport(options, false, endCallback, stringToExport); + + // Call the `_prepareExport` function with an SVG string + return _prepareExport(options, endCallback, null, stringToExport); } try { - // Try to parse to JSON and call the doExport function + log(4, '[chart] Parsing input from a file.'); + + // Try to parse to JSON const chartJSON = JSON.parse(stringToExport.replaceAll(/\t|\n|\r/g, ' ')); - // If a correct JSON, do the export - return doExport(options, chartJSON, endCallback); + if (!chartJSON || typeof chartJSON !== 'object') { + return endCallback( + new ExportError( + '[chart] Invalid configuration provided - the options must be an object, not a string.', + 400 + ) + ); + } + + // Call the `_prepareExport` function with a chart JSON options + return _prepareExport(options, endCallback, chartJSON, null); } catch (error) { - // Not a valid JSON - if (toBoolean(allowCodeExecution)) { - return doStraightInject(options, endCallback); + // If not a valid JSON, try to inject stingified version + if (options.customLogic.allowCodeExecution) { + return _doStraightInject(options, endCallback); } else { - // Do not allow straight injection without the allowCodeExecution flag + // Do not allow straight injection if the `allowCodeExecution` is false return endCallback( new ExportError( - '[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.' + '[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` options set to true.', + 403 ).setError(error) ); } } -}; +} + +/** + * Function for finalizing options before export. + * + * @async + * @function _prepareExport + * + * @param {Object} options - The `options` object containing configuration + * for the export process. + * @param {Function} endCallback - The callback function to be called upon + * completion or error. + * @param {Object} json - The JSON representation of the chart. + * @param {string} svg - The SVG representation of the chart. + * + * @returns {Promise} A Promise that resolves once the export process + * is completed. + */ +async function _prepareExport(options, endCallback, json, svg) { + const { export: exportOptions, customLogic: customLogicOptions } = options; + + const allowCodeExecutionScoped = + customLogicOptions.allowCodeExecution || allowCodeExecution; + + // Prepare the custom logic (`customCode`, `callback`, `resources`) options + try { + _handleCustomLogic(customLogicOptions, allowCodeExecutionScoped); + } catch (error) { + return endCallback(error); + } + + // Prepare the `globalOptions` and `themeOptions` options + _handleGlobalAndTheme( + exportOptions, + customLogicOptions.allowFileResources, + allowCodeExecutionScoped + ); + + // Prepare the `type` option + exportOptions.type = fixType(exportOptions.type, exportOptions.outfile); + + // Prepare the `chart` and `exporting` properties to keep it lean and mean + if (json) { + json.chart = json.chart || {}; + json.exporting = json.exporting || {}; + json.exporting.enabled = false; + } + + // Set the `width` option to null in case of exporting to an SVG + if (exportOptions.type === 'svg') { + exportOptions.width = null; + } + + // Prepare the `height`, `width` and `scale` options + options.export = { + ...exportOptions, + ..._findChartSize(exportOptions) + }; + + // Post the work to the pool + try { + const result = await postWork(exportOptions.strInj || json || svg, options); + return endCallback(null, result); + } catch (error) { + return endCallback(error); + } +} /** - * Retrieves and returns the current status of code execution permission. + * Calculates the `height`, `width` and `scale` for chart exports based + * on the provided export options. + * + * The function prioritizes values in the following order: + * 1. The `height`, `width`, `scale` from the `exportOptions`. + * 2. Options from the chart configuration (`chart` and `exporting` sections). + * 3. Options from the global options (`chart` and `exporting` sections). + * 4. Options from the theme options (`chart` and `exporting` sections). + * 5. The `defaultHeight`, `defaultWidth`, `defaultScale` from the + * `exportOptions`. + * 6. Fallback values (`height` = 400, `width` = 600, `scale` = 1). * - * @returns {any} The value of allowCodeExecution. + * @function _findChartSize + * + * @param {Object} exportOptions - The `exportOptions` object containing + * export configuration. + * + * @returns {Object} An object containing the calculated `height`, `width` + * and `scale` values for the chart export. */ -export const getAllowCodeExecution = () => allowCodeExecution; +function _findChartSize(exportOptions) { + // Check the `options` and `instr` for chart and exporting sections + const { chart: optionsChart, exporting: optionsExporting } = + exportOptions.options || isCorrectJSON(exportOptions.instr) || false; + + // Check the `globalOptions` for chart and exporting sections + const { chart: globalOptionsChart, exporting: globalOptionsExporting } = + isCorrectJSON(exportOptions.globalOptions) || false; + + // Check the `themeOptions` for chart and exporting sections + const { chart: themeOptionsChart, exporting: themeOptionsExporting } = + isCorrectJSON(exportOptions.themeOptions) || false; + + // Find the `scale` value: + // - It cannot be lower than 0.1 + // - It cannot be higher than 5.0 + // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23) + const scale = roundNumber( + Math.max( + 0.1, + Math.min( + exportOptions.scale || + optionsExporting?.scale || + globalOptionsExporting?.scale || + themeOptionsExporting?.scale || + exportOptions.defaultScale || + 1, + 5.0 + ) + ), + 2 + ); + + // Find the `height` value + const height = + exportOptions.height || + optionsExporting?.sourceHeight || + optionsChart?.height || + globalOptionsExporting?.sourceHeight || + globalOptionsChart?.height || + themeOptionsExporting?.sourceHeight || + themeOptionsChart?.height || + exportOptions.defaultHeight || + 400; + + // Find the `width` value + const width = + exportOptions.width || + optionsExporting?.sourceWidth || + optionsChart?.width || + globalOptionsExporting?.sourceWidth || + globalOptionsChart?.width || + themeOptionsExporting?.sourceWidth || + themeOptionsChart?.width || + exportOptions.defaultWidth || + 600; + + // Gather `height`, `width` and `scale` information + const size = { height, width, scale }; + + // Get rid of potential px and % + for (let [param, value] of Object.entries(size)) { + size[param] = + typeof value === 'string' ? +value.replace(/px|%/gi, '') : value; + } + + // Return the size object + return size; +} /** - * Sets the code execution permission based on the provided boolean value. + * Handles the execution of custom logic options, including loading `resources`, + * `customCode`, and `callback`. If code execution is allowed, it processes + * the custom logic options accordingly. If code execution is not allowed, + * it disables the usage of resources, custom code and callback. + * + * @function _handleCustomLogic + * + * @param {Object} customLogicOptions - Options containing custom logic settings + * such as `resources`, `customCode` and `callback`. + * @param {boolean} allowCodeExecution - A flag indicating whether code + * execution is allowed. * - * @param {any} value - The value to be converted and assigned - * to allowCodeExecution. + * @throws {ExportError} Throws an `ExportError` if code execution + * is not allowed but custom logic options are still provided. */ -export const setAllowCodeExecution = (value) => { - allowCodeExecution = toBoolean(value); -}; +function _handleCustomLogic(customLogicOptions, allowCodeExecution) { + // In case of allowing code execution + if (allowCodeExecution) { + // Process the `resources` + if (typeof customLogicOptions.resources === 'string') { + // Custom stringified resources + customLogicOptions.resources = _handleResources( + customLogicOptions.resources, + customLogicOptions.allowFileResources, + true + ); + } else if (!customLogicOptions.resources) { + try { + // Load the default one + const resources = readFileSync('resources.json', 'utf8'); + customLogicOptions.resources = _handleResources( + resources, + customLogicOptions.allowFileResources, + true + ); + } catch (error) { + log(2, `[chart] Unable to load the default resources.json file.`); + } + } + + // Process the `customCode` + try { + // Try to load custom code and wrap around it in a self invoking function + customLogicOptions.customCode = wrapAround( + customLogicOptions.customCode, + customLogicOptions.allowFileResources + ); + } catch (error) { + logWithStack(2, error, `[chart] The 'customCode' cannot be loaded.`); + } + + // Process the `callback` + if ( + customLogicOptions.callback && + customLogicOptions.callback.endsWith('.js') + ) { + // The `allowFileResources` is always set to false for HTTP requests + // to avoid injecting arbitrary files from the fs + if (customLogicOptions.allowFileResources) { + try { + customLogicOptions.callback = readFileSync( + customLogicOptions.callback, + 'utf8' + ); + } catch (error) { + customLogicOptions.callback = null; + logWithStack(2, error, `[chart] The 'callback' cannot be loaded.`); + } + } else { + customLogicOptions.callback = null; + } + } + + // Check if there is the `customCode` present + if ([null, undefined].includes(customLogicOptions.customCode)) { + log(3, `[cli] No custom code found.`); + } + + // Check if there is the `callback` present + if ([null, undefined].includes(customLogicOptions.callback)) { + log(3, `[cli] No callback found.`); + } + + // Check if there is the `resources` present + if ([null, undefined].includes(customLogicOptions.resources)) { + log(3, `[cli] No resources found.`); + } + } else { + // If the `allowCodeExecution` flag is not set, we should refuse the usage + // of `callback`, `resources`, and `customCode`. Additionally, the worker + // will refuse to run arbitrary JavaScript. Prioritized should be the scoped + // option + if ( + customLogicOptions.callback || + customLogicOptions.resources || + customLogicOptions.customCode + ) { + // Send a message saying that the exporter does not support these settings + throw new ExportError( + `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`, + 403 + ); + } + + // Reset all additional custom code + customLogicOptions.callback = null; + customLogicOptions.resources = null; + customLogicOptions.customCode = null; + } +} + +/** + * Handles and validates resources for export. + * + * @function _handleResources + * + * @param {(Object|string|null)} [resources=null] - The resources to be handled. + * Can be either a JSON object, stringified JSON, a path to a JSON file or null. + * @param {boolean} allowFileResources - Whether to allow loading resources + * from files. + * @param {boolean} allowCodeExecution - Whether to allow executing arbitrary + * JS code. + * + * @returns {(Object|null)} The handled resources or null if no valid resources + * are found. + */ +function _handleResources( + resources = null, + allowFileResources, + allowCodeExecution +) { + // List of allowed sections in the resources JSON + const allowedProps = ['js', 'css', 'files']; + + let handledResources = resources; + let correctResources = false; + + // Try to load resources from a file + if (allowFileResources && resources.endsWith('.json')) { + try { + handledResources = isCorrectJSON( + readFileSync(resources, 'utf8'), + false, + allowCodeExecution + ); + } catch { + return null; + } + } else { + // Try to get JSON + handledResources = isCorrectJSON(resources, false, allowCodeExecution); + + // Get rid of the files section + if (handledResources && !allowFileResources) { + delete handledResources.files; + } + } + + // Filter from unnecessary properties + for (const propName in handledResources) { + if (!allowedProps.includes(propName)) { + delete handledResources[propName]; + } else if (!correctResources) { + correctResources = true; + } + } + + // Check if at least one of allowed properties is present + if (!correctResources) { + return null; + } + + // Handle files section + if (handledResources.files) { + handledResources.files = handledResources.files.map((item) => item.trim()); + if (!handledResources.files || handledResources.files.length <= 0) { + delete handledResources.files; + } + } + + // Return resources + return handledResources; +} + +/** + * Handles the loading and validation of the `globalOptions` and `themeOptions` + * in the export options. If the option is a string and references a JSON file + * (when `allowFileResources` is true), it reads and parses the file. Otherwise, + * it attempts to parse the string or object as JSON. If any errors occur during + * this process, the option is set to null. If there is an error loading + * or parsing the `globalOptions` or `themeOptions`, the error is logged + * and the option is set to null. + * + * @function _handleGlobalAndTheme + * + * @param {Object} exportOptions - The `exportOptions` object containing + * the `globalOptions` and `themeOptions`. + * @param {boolean} allowFileResources - A flag indicating whether file + * resources (e.g. the .json files) are allowed. + * @param {boolean} allowCodeExecution - A flag indicating whether executing + * code (e.g., within JSON or theme options) is allowed. + */ +function _handleGlobalAndTheme( + exportOptions, + allowFileResources, + allowCodeExecution +) { + // Check the `globalOptions` and `themeOptions` options + ['globalOptions', 'themeOptions'].forEach((optionsName) => { + try { + // Check if the option exists + if (exportOptions[optionsName]) { + // Check if it is a string + if (typeof exportOptions[optionsName] === 'string') { + // Check if it is a file name with the .json extension + if ( + allowFileResources && + exportOptions[optionsName].endsWith('.json') + ) { + // Check if the file content can be a JSON, and save it as a string + exportOptions[optionsName] = isCorrectJSON( + readFileSync(exportOptions[optionsName], 'utf8'), + true, + allowCodeExecution + ); + } else { + // Check if the value can be a JSON, and save it as a string + exportOptions[optionsName] = isCorrectJSON( + exportOptions[optionsName], + true, + allowCodeExecution + ); + } + } else { + // Check if the value can be a JSON, and save it as an object + exportOptions[optionsName] = isCorrectJSON( + exportOptions[optionsName], + true, + allowCodeExecution + ); + } + } + } catch (error) { + logWithStack(2, error, `[chart] The '${optionsName}' cannot be loaded.`); + + // In case of an error, set the option with null + exportOptions[optionsName] = null; + } + }); +} export default { - batchExport, + startExport, singleExport, + batchExport, getAllowCodeExecution, - setAllowCodeExecution, - startExport, - findChartSize + setAllowCodeExecution }; From 92624534b553a988e5d422a433ea92e1a85b84d4 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:26:04 +0100 Subject: [PATCH 007/102] Complete optimization the way options from all sources are set, get and validated. --- lib/config.js | 547 +++++++++++++++++--------------------------------- 1 file changed, 184 insertions(+), 363 deletions(-) diff --git a/lib/config.js b/lib/config.js index 36b9243e..27c4ebd0 100644 --- a/lib/config.js +++ b/lib/config.js @@ -12,446 +12,267 @@ See LICENSE file in root for details. *******************************************************************************/ -import { existsSync, readFileSync, promises as fsPromises } from 'fs'; +/** + * @overview Manages configuration for the Highcharts Export Server by loading + * and merging options from multiple sources, such as default settings, + * environment variables, user-provided options, and command-line arguments. + * Ensures the export server's global options are up-to-date with the highest + * priority values. Provides functions for accessing and updating configuration. + */ -import prompts from 'prompts'; +import { readFileSync } from 'fs'; -import { - absoluteProps, - defaultConfig, - nestedArgs, - promptsConfig -} from './schemas/config.js'; -import { envs } from './envs.js'; import { log, logWithStack } from './logger.js'; -import { deepCopy, isObject, printUsage, toBoolean } from './utils.js'; +import { envs } from './envs.js'; +import { deepCopy } from './utils.js'; +import { defaultConfig, nestedArgs } from './schemas/config.js'; -let generalOptions = {}; +// Sets the global options with initial values from the default config +const globalOptions = _initGlobalOptions(defaultConfig); /** - * Retrieves and returns the general options for the export process. + * Gets the global options of the export server instance. * - * @returns {Object} The general options object. + * @function getOptions + * + * @param {boolean} [getGlobal = false] - Optional parameter to decide whether + * to return the reference to the global options of the server instance object + * or return a copy of it. + * + * @returns {Object} The global options object of the server instance. */ -export const getOptions = () => generalOptions; +export function getOptions(getGlobal = false) { + return getGlobal ? globalOptions : deepCopy(globalOptions); +} /** - * Initializes and sets the general options for the server instace, keeping - * the principle of the options load priority. It accepts optional userOptions - * and args from the CLI. + * Sets the general options of the export server instance, keeping the principle + * of the options load priority from all available sources. It accepts optional + * `customOptions` object and `cliArgs` array with arguments from the CLI. These + * options will be validated and applied if provided. * - * @param {Object} userOptions - User-provided options for customization. - * @param {Array} args - Command-line arguments for additional configuration - * (CLI usage). + * @function setOptions * - * @returns {Object} The updated general options object. + * @param {Object} [customOptions={}] - Optional custom options for additional + * configuration. + * @param {Array.} [cliArgs=[]] - Optional command line arguments + * for additional configuration. + * @param {boolean} [modifyGlobal = false] - Optional parameter to decide + * whether to update and return the reference to the global options + * of the server instance object or return a copy of it. + * + * @returns {Object} The updated general options object, reflecting the merged + * configuration from all available sources. */ -export const setOptions = (userOptions, args) => { - // Only for the CLI usage - if (args?.length) { - // Get the additional options from the custom JSON file - generalOptions = loadConfigFile(args); - } - - // Update the default config with a correct option values - updateDefaultConfig(defaultConfig, generalOptions); +export function setOptions( + customOptions = {}, + cliArgs = [], + modifyGlobal = false +) { + // Object for options loaded via the `loadConfig` option + let configOptions = {}; - // Set values for server's options and returns them - generalOptions = initOptions(defaultConfig); - - // Apply user options if there are any - if (userOptions) { - // Merge user options - generalOptions = mergeConfigOptions( - generalOptions, - userOptions, - absoluteProps - ); - } + // Object for options from the CLI + let cliOptions = {}; // Only for the CLI usage - if (args?.length) { - // Pair provided arguments - generalOptions = pairArgumentValue(generalOptions, args, defaultConfig); + if (cliArgs.length) { + // Get options from the custom JSON loaded via the `loadConfig` + configOptions = _loadConfigFile(cliArgs); } - // Return final general options - return generalOptions; -}; - -/** - * Allows manual configuration based on specified prompts and saves - * the configuration to a file. - * - * @param {string} configFileName - The name of the configuration file. - * - * @returns {Promise} A Promise that resolves to true once the manual - * configuration is completed and saved. - */ -export const manualConfig = async (configFileName) => { - // Prepare a config object - let configFile = {}; - - // Check if provided config file exists - if (existsSync(configFileName)) { - configFile = JSON.parse(readFileSync(configFileName, 'utf8')); + // Only for the CLI usage + if (cliArgs.length) { + // Get options from the CLI + cliOptions = _pairArgumentValue(nestedArgs, cliArgs); } - // Question about a configuration category - const onSubmit = async (p, categories) => { - let questionsCounter = 0; - let allQuestions = []; - - // Create a corresponding property in the manualConfig object - for (const section of categories) { - // Mark each option with a section - promptsConfig[section] = promptsConfig[section].map((option) => ({ - ...option, - section - })); - - // Collect the questions - allQuestions = [...allQuestions, ...promptsConfig[section]]; - } + // Get the reference to the global options object or a copy of the object + const generalOptions = modifyGlobal ? globalOptions : deepCopy(globalOptions); - await prompts(allQuestions, { - onSubmit: async (prompt, answer) => { - // Get the default module scripts - if (prompt.name === 'moduleScripts') { - answer = answer.length - ? answer.map((module) => prompt.choices[module]) - : prompt.choices; - - configFile[prompt.section][prompt.name] = answer; - } else { - configFile[prompt.section] = recursiveProps( - Object.assign({}, configFile[prompt.section] || {}), - prompt.name.split('.'), - prompt.choices ? prompt.choices[answer] : answer - ); - } - - if (++questionsCounter === allQuestions.length) { - try { - await fsPromises.writeFile( - configFileName, - JSON.stringify(configFile, null, 2), - 'utf8' - ); - } catch (error) { - logWithStack( - 1, - error, - `[config] An error occurred while creating the ${configFileName} file.` - ); - } - return true; - } - } - }); - - return true; - }; - - // Find the categories - const choices = Object.keys(promptsConfig).map((choice) => ({ - title: `${choice} options`, - value: choice - })); - - // Category prompt - return prompts( - { - type: 'multiselect', - name: 'category', - message: 'Which category do you want to configure?', - hint: 'Space: Select specific, A: Select all, Enter: Confirm.', - instructions: '', - choices - }, - { onSubmit } + // Update values of the general options with values from each source possible + _updateOptions( + defaultConfig, + generalOptions, + configOptions, + customOptions, + cliOptions ); -}; + + // Return options + return generalOptions; +} /** - * Maps old-structured (PhantomJS) options to a new configuration format - * (Puppeteer). + * Initializes and returns global options object based on provided + * configuration, setting values from nested properties recursively. * - * @param {Object} oldOptions - Old-structured options to be mapped. + * @function _initGlobalOptions * - * @returns {Object} New options structured based on the defined nestedArgs - * mapping. + * @param {Object} config - Configuration to be used for initializing options. + * + * @returns {Object} Initialized options object. */ -export const mapToNewConfig = (oldOptions) => { - const newOptions = {}; - // Cycle through old-structured options - for (const [key, value] of Object.entries(oldOptions)) { - const propertiesChain = nestedArgs[key] ? nestedArgs[key].split('.') : []; - - // Populate object in correct properties levels - propertiesChain.reduce( - (obj, prop, index) => - (obj[prop] = - propertiesChain.length - 1 === index ? value : obj[prop] || {}), - newOptions - ); +function _initGlobalOptions(config) { + const options = {}; + for (const [name, item] of Object.entries(config)) { + options[name] = Object.prototype.hasOwnProperty.call(item, 'value') + ? item.value + : _initGlobalOptions(item); } - return newOptions; -}; + return options; +} /** - * Merges two sets of configuration options, considering absolute properties. + * Updates options object with values from various sources, following a specific + * prioritization order. The function checks for values in the following order + * of precedence: the `loadConfig` configuration options, environment variables, + * custom options, and CLI options. * - * @param {Object} options - Original configuration options. - * @param {Object} newOptions - New configuration options to be merged. - * @param {Array} absoluteProps - List of properties that should - * not be recursively merged. + * @function _updateOptions * - * @returns {Object} Merged configuration options. + * @param {Object} config - The configuration object, which includes the initial + * settings and metadata for each option. This object is used to determine + * the structure and default values for the options. + * @param {Object} options - The options object that will be updated with values + * from other sources. + * @param {Object} configOpt - The configuration options object, which may + * provide values to override defaults. + * @param {Object} customOpt - The custom options object, typically containing + * user-defined values that may override configuration options. + * @param {Object} cliOpt - The CLI options object, which includes values + * provided through command-line arguments and has the highest precedence among + * options. */ -export const mergeConfigOptions = (options, newOptions, absoluteProps = []) => { - const mergedOptions = deepCopy(options); - - for (const [key, value] of Object.entries(newOptions)) { - mergedOptions[key] = - isObject(value) && - !absoluteProps.includes(key) && - mergedOptions[key] !== undefined - ? mergeConfigOptions(mergedOptions[key], value, absoluteProps) - : value !== undefined - ? value - : mergedOptions[key]; - } +function _updateOptions(config, options, configOpt, customOpt, cliOpt) { + Object.keys(config).forEach((key) => { + // Get the config entry of a specific option + const entry = config[key]; - return mergedOptions; -}; + // Gather values for the options from every possible source, if exists + const configVal = configOpt && configOpt[key]; + const customVal = customOpt && customOpt[key]; + const cliVal = cliOpt && cliOpt[key]; -/** - * Initializes export settings based on provided exportOptions - * and generalOptions. - * - * @param {Object} exportOptions - Options specific to the export process. - * @param {Object} generalOptions - General configuration options. - * - * @returns {Object} Initialized export settings. - */ -export const initExportSettings = (exportOptions, generalOptions = {}) => { - let options = {}; - - if (exportOptions.svg) { - options = deepCopy(generalOptions); - options.export.type = exportOptions.type || exportOptions.export.type; - options.export.scale = exportOptions.scale || exportOptions.export.scale; - options.export.outfile = - exportOptions.outfile || exportOptions.export.outfile; - options.payload = { - svg: exportOptions.svg - }; - } else { - options = mergeConfigOptions( - generalOptions, - exportOptions, - // Omit going down recursively with the belows - absoluteProps - ); - } + // If the value not found, need to go deeper + if (typeof entry.value === 'undefined') { + _updateOptions(entry, options[key], configVal, customVal, cliVal); + } else { + // If a value from custom JSON options exists, it take precedence + if (configVal !== undefined && configVal !== null) { + options[key] = configVal; + } - options.export.outfile = - options.export?.outfile || `chart.${options.export?.type || 'png'}`; - return options; -}; + // If a value from environment variables exists, it take precedence + const envVal = envs[entry.envLink]; + if (entry.envLink in envs && envVal !== undefined && envVal !== null) { + options[key] = envVal; + } + + // If a value from user options exists, it take precedence + if (customVal !== undefined && customVal !== null) { + options[key] = customVal; + } + + // If a value from CLI options exists, it take precedence + if (cliVal !== undefined && cliVal !== null) { + options[key] = cliVal; + } + } + }); +} /** - * Loads additional configuration from a specified file using - * the --loadConfig option. + * Loads additional configuration from a specified file provided via + * the `--loadConfig` option in the command-line arguments. + * + * @function _loadConfigFile * - * @param {Array} args - Command-line arguments to check for - * the --loadConfig option. + * @param {Array.} cliArgs - Command-line arguments to search + * for the `--loadConfig` option and the corresponding file path. * - * @returns {Object} Additional configuration loaded from the specified file, - * or an empty object if not found or invalid. + * @returns {Object} The additional configuration loaded from the specified + * file, or an empty object if the file is not found, invalid, or an error + * occurs. */ -function loadConfigFile(args) { - // Check if the --loadConfig option was used - const configIndex = args.findIndex( +function _loadConfigFile(cliArgs) { + // Check if the `loadConfig` option was used + const configIndex = cliArgs.findIndex( (arg) => arg.replace(/-/g, '') === 'loadConfig' ); - // Check if the --loadConfig has a value - if (configIndex > -1 && args[configIndex + 1]) { - const fileName = args[configIndex + 1]; + // Get the `loadConfig` option value + const configFileName = configIndex > -1 && cliArgs[configIndex + 1]; + + // Check if the `loadConfig` is present and has a correct value + if (configFileName) { try { - // Check if an additional config file is a correct JSON file - if (fileName && fileName.endsWith('.json')) { - // Load an optional custom JSON config file - return JSON.parse(readFileSync(fileName)); - } + // Load an optional custom JSON config file + return JSON.parse(readFileSync(configFileName)); } catch (error) { logWithStack( 2, error, - `[config] Unable to load the configuration from the ${fileName} file.` + `[config] Unable to load the configuration from the ${configFileName} file.` ); } } - // No additional options to return return {}; } /** - * Updates the default configuration object with values from a custom object - * and environment variables. + * Parses command-line arguments and pairs each argument with its corresponding + * option in the configuration. The values are structured into a nested options + * object, based on predefined mappings. * - * @param {Object} configObj - The default configuration object. - * @param {Object} customObj - Custom configuration object to override defaults. - * @param {string} propChain - Property chain for tracking nested properties - * during recursion. - */ -function updateDefaultConfig(configObj, customObj = {}, propChain = '') { - Object.keys(configObj).forEach((key) => { - const entry = configObj[key]; - const customValue = customObj && customObj[key]; - - if (typeof entry.value === 'undefined') { - updateDefaultConfig(entry, customValue, `${propChain}.${key}`); - } else { - // If a value from a custom JSON exists, it take precedence - if (customValue !== undefined) { - entry.value = customValue; - } - - // If a value from an env variable exists, it take precedence - if (entry.envLink in envs && envs[entry.envLink] !== undefined) { - entry.value = envs[entry.envLink]; - } - } - }); -} - -/** - * Initializes options object based on provided items, setting values from - * nested properties recursively. + * @function _pairArgumentValue * - * @param {Object} items - Configuration items to be used for initializing - * options. + * @param {Array.} nestedArgs - An array of nesting level for all + * general options. + * @param {Array.} cliArgs - An array of command-line arguments + * containing options and their associated values. * - * @returns {Object} Initialized options object. + * @returns {Object} An updated options object where each option from + * the command-line is paired with its value, structured into nested objects + * as defined. */ -function initOptions(items) { - let options = {}; - for (const [name, item] of Object.entries(items)) { - options[name] = Object.prototype.hasOwnProperty.call(item, 'value') - ? item.value - : initOptions(item); - } - return options; -} +function _pairArgumentValue(nestedArgs, cliArgs) { + // An empty object to collect and structurize data from the args + const cliOptions = {}; -/** - * Pairs argument values with corresponding options in the configuration, - * updating the options object. - * - * @param {Object} options - Configuration options object to be updated. - * @param {Array} args - Command-line arguments containing values for specific - * options. - * @param {Object} defaultConfig - Default configuration object for reference. - * - * @returns {Object} Updated options object. - */ -function pairArgumentValue(options, args, defaultConfig) { - let showUsage = false; - for (let i = 0; i < args.length; i++) { - const option = args[i].replace(/-/g, ''); + // Cycle through all CLI args and filter them + for (let i = 0; i < cliArgs.length; i++) { + const option = cliArgs[i].replace(/-/g, ''); // Find the right place for property's value const propertiesChain = nestedArgs[option] ? nestedArgs[option].split('.') : []; - // Get the correct type for CLI args which are passed as strings - let argumentType; - propertiesChain.reduce((obj, prop, index) => { - if (propertiesChain.length - 1 === index) { - argumentType = obj[prop].type; - } - return obj[prop]; - }, defaultConfig); - + // Create options object with values from CLI for later parsing and merging propertiesChain.reduce((obj, prop, index) => { if (propertiesChain.length - 1 === index) { - // Finds an option and set a corresponding value - if (typeof obj[prop] !== 'undefined') { - if (args[++i]) { - if (argumentType === 'boolean') { - obj[prop] = toBoolean(args[i]); - } else if (argumentType === 'number') { - obj[prop] = +args[i]; - } else if (argumentType.indexOf(']') >= 0) { - obj[prop] = args[i].split(','); - } else { - obj[prop] = args[i]; - } - } else { - log( - 2, - `[config] Missing value for the '${option}' argument. Using the default value.` - ); - showUsage = true; - } + const value = cliArgs[++i]; + if (!value) { + log( + 2, + `[config] Missing value for the '${option}' argument. Using the default value.` + ); } + obj[prop] = value || null; + } else if (obj[prop] === undefined) { + obj[prop] = {}; } return obj[prop]; - }, options); - } - - // Display the usage for the reference if needed - if (showUsage) { - printUsage(defaultConfig); - } - - return options; -} - -/** - * Recursively updates properties in an object based on nested names and assigns - * the final value. - * - * @param {Object} objectToUpdate - The object to be updated. - * @param {Array} nestedNames - Array of nested property names. - * @param {any} value - The final value to be assigned. - * - * @returns {Object} Updated object with assigned values. - */ -function recursiveProps(objectToUpdate, nestedNames, value) { - while (nestedNames.length > 1) { - const propName = nestedNames.shift(); - - // Create a property in object if it doesn't exist - if (!Object.prototype.hasOwnProperty.call(objectToUpdate, propName)) { - objectToUpdate[propName] = {}; - } - - // Call function again if there still names to go - objectToUpdate[propName] = recursiveProps( - Object.assign({}, objectToUpdate[propName]), - nestedNames, - value - ); - - return objectToUpdate; + }, cliOptions); } - // Assign the final value - objectToUpdate[nestedNames[0]] = value; - return objectToUpdate; + // Return parsed CLI options + return cliOptions; } export default { getOptions, - setOptions, - manualConfig, - mapToNewConfig, - mergeConfigOptions, - initExportSettings + setOptions }; From bf9e711bdee7fcf955fa6b4d2c125e3623a47b3e Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:26:12 +0100 Subject: [PATCH 008/102] Added missing envs for the validation. --- lib/envs.js | 55 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/lib/envs.js b/lib/envs.js index 885d7f07..c3ab2f49 100644 --- a/lib/envs.js +++ b/lib/envs.js @@ -1,9 +1,22 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + /** - * @fileoverview - * This file is responsible for parsing the environment variables with the 'zod' - * library. The parsed environment variables are then exported to be used - * in the application as "envs". We should not use process.env directly - * in the application as these would not be parsed properly. + * @overview This file is responsible for parsing the environment variables + * with the 'zod' library. The parsed environment variables are then exported + * to be used in the application as "envs". We should not use the `process.env` + * directly in the application as these would not be parsed properly. * * The environment variables are parsed and validated only once when * the application starts. We should write a custom validator or a transformer @@ -13,7 +26,7 @@ import dotenv from 'dotenv'; import { z } from 'zod'; -import { scriptsNames } from './schemas/config.js'; +import { defaultConfig } from './schemas/config.js'; // Load .env into environment variables dotenv.config(); @@ -96,6 +109,9 @@ const v = { }; export const Config = z.object({ + // puppeteer + PUPPETEER_ARGS: v.string(), + // highcharts HIGHCHARTS_VERSION: z .string() @@ -120,24 +136,45 @@ export const Config = z.object({ }) ) .transform((value) => (value !== '' ? value : undefined)), - HIGHCHARTS_CORE_SCRIPTS: v.array(scriptsNames.core), - HIGHCHARTS_MODULE_SCRIPTS: v.array(scriptsNames.modules), - HIGHCHARTS_INDICATOR_SCRIPTS: v.array(scriptsNames.indicators), HIGHCHARTS_FORCE_FETCH: v.boolean(), HIGHCHARTS_CACHE_PATH: v.string(), HIGHCHARTS_ADMIN_TOKEN: v.string(), + HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts), + HIGHCHARTS_MODULE_SCRIPTS: v.array(defaultConfig.highcharts.moduleScripts), + HIGHCHARTS_INDICATOR_SCRIPTS: v.array( + defaultConfig.highcharts.indicatorScripts + ), + HIGHCHARTS_CUSTOM_SCRIPTS: v.array(defaultConfig.highcharts.customScripts), // export + EXPORT_INFILE: v.string(), + EXPORT_INSTR: v.string(), + EXPORT_OPTIONS: v.string(), + EXPORT_SVG: v.string(), + EXPORT_OUTFILE: v.string(), EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']), EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']), + EXPORT_B64: v.boolean(), + EXPORT_NO_DOWNLOAD: v.boolean(), + EXPORT_HEIGHT: v.positiveNum(), + EXPORT_WIDTH: v.positiveNum(), + EXPORT_SCALE: v.positiveNum(), EXPORT_DEFAULT_HEIGHT: v.positiveNum(), EXPORT_DEFAULT_WIDTH: v.positiveNum(), EXPORT_DEFAULT_SCALE: v.positiveNum(), + EXPORT_GLOBAL_OPTIONS: v.string(), + EXPORT_THEME_OPTIONS: v.string(), + EXPORT_BATCH: v.string(), EXPORT_RASTERIZATION_TIMEOUT: v.nonNegativeNum(), // custom CUSTOM_LOGIC_ALLOW_CODE_EXECUTION: v.boolean(), CUSTOM_LOGIC_ALLOW_FILE_RESOURCES: v.boolean(), + CUSTOM_LOGIC_CUSTOM_CODE: v.string(), + CUSTOM_LOGIC_CALLBACK: v.string(), + CUSTOM_LOGIC_RESOURCES: v.string(), + CUSTOM_LOGIC_LOAD_CONFIG: v.string(), + CUSTOM_LOGIC_CREATE_CONFIG: v.string(), // server SERVER_ENABLE: v.boolean(), From 8b92d05426c95ed8b889e18419057d5fa29125e4 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:26:20 +0100 Subject: [PATCH 009/102] Optimization of the export module. --- lib/export.js | 282 ++++++++++++++++++++++++++++---------------------- 1 file changed, 157 insertions(+), 125 deletions(-) diff --git a/lib/export.js b/lib/export.js index a3c69b31..33a10eee 100644 --- a/lib/export.js +++ b/lib/export.js @@ -12,136 +12,38 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * @overview This module handles chart export functionality using Puppeteer. + * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module + * manages resources, sets up the export environment, and processes chart + * configurations or SVG inputs for rendering. Exports to a chart from a page + * using Puppeteer. + */ + import { addPageResources, clearPageResources } from './browser.js'; import { getCache } from './cache.js'; import { triggerExport } from './highcharts.js'; import { log } from './logger.js'; -import svgTemplate from './../templates/svgExport/svgExport.js'; +import svgTemplate from '../templates/svgExport/svgExport.js'; import ExportError from './errors/ExportError.js'; -/** - * Retrieves the clipping region coordinates of the specified page element with - * the id 'chart-container'. - * - * @param {Object} page - Puppeteer page object. - * - * @returns {Promise} Promise resolving to an object containing - * x, y, width, and height properties. - */ -const getClipRegion = (page) => - page.$eval('#chart-container', (element) => { - const { x, y, width, height } = element.getBoundingClientRect(); - return { - x, - y, - width, - height: Math.trunc(height > 1 ? height : 500) - }; - }); - -/** - * Creates an image using Puppeteer's page screenshot functionality with - * specified options. - * - * @param {Object} page - Puppeteer page object. - * @param {string} type - Image type. - * @param {string} encoding - Image encoding. - * @param {Object} clip - Clipping region coordinates. - * @param {number} rasterizationTimeout - Timeout for rasterization - * in milliseconds. - * - * @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 - ) - ) - ]); - -/** - * Creates a PDF using Puppeteer's page pdf functionality with specified - * options. - * - * @param {Object} page - Puppeteer page object. - * @param {number} height - PDF height. - * @param {number} width - PDF width. - * @param {string} encoding - PDF encoding. - * - * @returns {Promise} Promise resolving to the PDF buffer. - */ -const createPDF = async ( - page, - height, - width, - encoding, - rasterizationTimeout -) => { - await page.emulateMediaType('screen'); - - return page.pdf({ - // This will remove an extra empty page in PDF exports - height: height + 1, - width, - encoding, - timeout: rasterizationTimeout || 1500 - }); -}; - -/** - * Creates an SVG string by evaluating the outerHTML of the first 'svg' element - * inside an element with the id 'container'. - * - * @param {Object} page - Puppeteer page object. - * - * @returns {Promise} Promise resolving to the SVG string. - */ -const createSVG = (page) => - page.$eval('#container svg:first-of-type', (element) => element.outerHTML); - -/** - * Sets the specified chart and options as configuration into the triggerExport - * function within the window context using page.evaluate. - * - * @param {Object} page - Puppeteer page object. - * @param {any} chart - The chart object to be configured. - * @param {Object} options - Configuration options for the chart. - * - * @returns {Promise} Promise resolving after the configuration is set. - */ -const setAsConfig = async (page, chart, options, displayErrors) => - page.evaluate(triggerExport, chart, options, displayErrors); - /** * Exports to a chart from a page using Puppeteer. * + * @async + * @function puppeteerExport + * * @param {Object} page - Puppeteer page object. - * @param {any} chart - The chart object or SVG configuration to be exported. + * @param {unknown} chart - The chart object or SVG configuration + * to be exported. * @param {Object} options - Export options and configuration. * - * @returns {Promise} Promise resolving to - * the exported data or rejecting with an ExportError. + * @returns {Promise<(string|Buffer|ExportError)>} Promise resolving + * to the exported data or rejecting with an `ExportError`. */ -export default async (page, chart, options) => { +export async function puppeteerExport(page, chart, options) { // Injected resources array (additional JS and CSS) let injectedResources = []; @@ -179,7 +81,7 @@ export default async (page, chart, options) => { // Need to perform straight inject if (exportOptions.strInj) { // Injection based configuration export - await setAsConfig( + await _setAsConfig( page, { chart: { @@ -195,14 +97,14 @@ export default async (page, chart, options) => { chart.chart.height = exportOptions.height; chart.chart.width = exportOptions.width; - await setAsConfig(page, chart, options, displayErrors); + await _setAsConfig(page, chart, options, displayErrors); } } // Keeps track of all resources added on the page with addXXXTag. etc // It's VITAL that all added resources ends up here so we can clear things // out when doing a new export in the same page! - injectedResources = await addPageResources(page, options); + injectedResources = await addPageResources(page, options.customLogic); // Get the real chart size and set the zoom accordingly const size = isSVG @@ -233,8 +135,8 @@ export default async (page, chart, options) => { // 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 + // 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; @@ -253,7 +155,7 @@ export default async (page, chart, options) => { ); // Get the clip region for the page - const { x, y } = await getClipRegion(page); + const { x, y } = await _getClipRegion(page); // Set the final viewport now that we have the real height await page.setViewport({ @@ -266,10 +168,10 @@ export default async (page, chart, options) => { // Rasterization process if (exportOptions.type === 'svg') { // SVG - data = await createSVG(page); + data = await _createSVG(page); } else if (['png', 'jpeg'].includes(exportOptions.type)) { // PNG or JPEG - data = await createImage( + data = await _createImage( page, exportOptions.type, 'base64', @@ -283,7 +185,7 @@ export default async (page, chart, options) => { ); } else if (exportOptions.type === 'pdf') { // PDF - data = await createPDF( + data = await _createPDF( page, viewportHeight, viewportWidth, @@ -292,7 +194,8 @@ export default async (page, chart, options) => { ); } else { throw new ExportError( - `[export] Unsupported output format ${exportOptions.type}.` + `[export] Unsupported output format ${exportOptions.type}.`, + 400 ); } @@ -303,4 +206,133 @@ export default async (page, chart, options) => { await clearPageResources(page, injectedResources); return error; } +} + +/** + * Retrieves the clipping region coordinates of the specified page element with + * the id 'chart-container'. + * + * @async + * @function _getClipRegion + * + * @param {Object} page - Puppeteer page object. + * + * @returns {Promise} Promise resolving to an object containing + * x, y, width, and height properties. + */ +async function _getClipRegion(page) { + return page.$eval('#chart-container', (element) => { + const { x, y, width, height } = element.getBoundingClientRect(); + return { + x, + y, + width, + height: Math.trunc(height > 1 ? height : 500) + }; + }); +} + +/** + * Sets the specified chart and options as configuration into the triggerExport + * function within the window context using page.evaluate. + * + * @async + * @function _setAsConfig + * + * @param {Object} page - Puppeteer page object. + * @param {unknown} chart - The chart object to be configured. + * @param {Object} options - Configuration options for the chart. + * @param {boolean} displayErrors - A flag indicating whether to display errors. + * + * @returns {Promise} Promise resolving after the configuration is set. + */ +async function _setAsConfig(page, chart, options, displayErrors) { + return page.evaluate(triggerExport, chart, options, displayErrors); +} + +/** + * Creates an image using Puppeteer's page screenshot functionality with + * specified options. + * + * @async + * @function _createImage + * + * @param {Object} page - Puppeteer page object. + * @param {string} type - Image type. + * @param {string} encoding - Image encoding. + * @param {Object} clip - Clipping region coordinates. + * @param {number} rasterizationTimeout - Timeout for rasterization + * in milliseconds. + * + * @returns {Promise} Promise resolving to the image buffer or rejecting + * with an ExportError for timeout. + */ +async function _createImage(page, type, encoding, clip, rasterizationTimeout) { + return Promise.race([ + page.screenshot({ + type, + encoding, + clip, + captureBeyondViewport: true, + fullPage: false, + optimizeForSpeed: true, + ...(type !== 'png' ? { quality: 80 } : {}), + + // Always render on a transparent page if the expected type format is PNG + omitBackground: type == 'png' // #447, #463 + }), + new Promise((_resolve, reject) => + setTimeout( + () => reject(new ExportError('Rasterization timeout', 408)), + rasterizationTimeout || 1500 + ) + ) + ]); +} + +/** + * Creates a PDF using Puppeteer's page PDF functionality with specified + * options. + * + * @async + * @function _createPDF + * + * @param {Object} page - Puppeteer page object. + * @param {number} height - PDF height. + * @param {number} width - PDF width. + * @param {string} encoding - PDF encoding. + * + * @returns {Promise} Promise resolving to the PDF buffer. + */ +async function _createPDF(page, height, width, encoding, rasterizationTimeout) { + await page.emulateMediaType('screen'); + return page.pdf({ + // This will remove an extra empty page in PDF exports + height: height + 1, + width, + encoding, + timeout: rasterizationTimeout || 1500 + }); +} + +/** + * Creates an SVG string by evaluating the outerHTML of the first 'svg' element + * inside an element with the id 'container'. + * + * @async + * @function _createSVG + * + * @param {Object} page - Puppeteer page object. + * + * @returns {Promise} Promise resolving to the SVG string. + */ +async function _createSVG(page) { + return page.$eval( + '#container svg:first-of-type', + (element) => element.outerHTML + ); +} + +export default { + puppeteerExport }; From eae2efb85015d65764edfb1dfdcea3fe7c60fcd6 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:26:31 +0100 Subject: [PATCH 010/102] Optimization of the fetch module. --- lib/fetch.js | 114 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 45 deletions(-) diff --git a/lib/fetch.js b/lib/fetch.js index 25a6b90b..b5784e69 100644 --- a/lib/fetch.js +++ b/lib/fetch.js @@ -1,49 +1,57 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + /** - * This module exports two functions: fetch (for GET requests) and post (for POST requests). + * @overview HTTP utility module for fetching and posting data. Supports both + * HTTP and HTTPS protocols, providing methods to make GET and POST requests + * with customizable options. Includes protocol determination based on URL + * and augments response objects with a 'text' property for easier data access. */ import http from 'http'; import https from 'https'; -/** - * Returns the HTTP or HTTPS protocol module based on the provided URL. - * - * @param {string} url - The URL to determine the protocol. - * - * @returns {Object} The HTTP or HTTPS protocol module (http or https). - */ -const getProtocol = (url) => (url.startsWith('https') ? https : http); - /** * Fetches data from the specified URL using either HTTP or HTTPS protocol. * + * @async + * @function fetch + * * @param {string} url - The URL to fetch data from. - * @param {Object} requestOptions - Options for the HTTP request (optional). + * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request. * - * @returns {Promise} Promise resolving to the HTTP response object - * with added 'text' property or rejecting with an error. + * @returns {Promise} Promise resolving to the HTTP/HTTPS response + * object with added 'text' property or rejecting with an error. */ -async function fetch(url, requestOptions = {}) { +export async function fetch(url, requestOptions = {}) { return new Promise((resolve, reject) => { - const protocol = getProtocol(url); - - protocol - .get(url, requestOptions, (res) => { + _getProtocolModule(url) + .get(url, requestOptions, (response) => { let data = ''; - // A chunk of data has been received. - res.on('data', (chunk) => { + // A chunk of data has been received + response.on('data', (chunk) => { data += chunk; }); - // The whole response has been received. - res.on('end', () => { + // The whole response has been received + response.on('end', () => { if (!data) { reject('Nothing was fetched from the URL.'); } - - res.text = data; - resolve(res); + response.text = data; + resolve(response); }); }) .on('error', (error) => { @@ -56,17 +64,18 @@ async function fetch(url, requestOptions = {}) { * Sends a POST request to the specified URL with the provided JSON body using * either HTTP or HTTPS protocol. * + * @async + * @function post + * * @param {string} url - The URL to send the POST request to. - * @param {Object} body - The JSON body to include in the POST request - * (optional, default is an empty object). - * @param {Object} requestOptions - Options for the HTTP request (optional). + * @param {Object} [body={}] - The JSON body to include in the POST request. + * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request. * - * @returns {Promise} Promise resolving to the HTTP response object with - * added 'text' property or rejecting with an error. + * @returns {Promise} Promise resolving to the HTTP/HTTPS response + * object with added 'text' property or rejecting with an error. */ -async function post(url, body = {}, requestOptions = {}) { +export async function post(url, body = {}, requestOptions = {}) { return new Promise((resolve, reject) => { - const protocol = getProtocol(url); const data = JSON.stringify(body); // Set default headers and merge with requestOptions @@ -81,20 +90,20 @@ async function post(url, body = {}, requestOptions = {}) { requestOptions ); - const req = protocol - .request(url, options, (res) => { + const request = _getProtocolModule(url) + .request(url, options, (response) => { let responseData = ''; - // A chunk of data has been received. - res.on('data', (chunk) => { + // A chunk of data has been received + response.on('data', (chunk) => { responseData += chunk; }); - // The whole response has been received. - res.on('end', () => { + // The whole response has been received + response.on('end', () => { try { - res.text = responseData; - resolve(res); + response.text = responseData; + resolve(response); } catch (error) { reject(error); } @@ -104,11 +113,26 @@ async function post(url, body = {}, requestOptions = {}) { reject(error); }); - // Write the request body and end the request. - req.write(data); - req.end(); + // Write the request body and end the request + request.write(data); + request.end(); }); } -export default fetch; -export { fetch, post }; +/** + * Returns the HTTP or HTTPS protocol module based on the provided URL. + * + * @function _getProtocolModule + * + * @param {string} url - The URL to determine the protocol. + * + * @returns {Object} The HTTP or HTTPS protocol module (http or https). + */ +function _getProtocolModule(url) { + return url.startsWith('https') ? https : http; +} + +export default { + fetch, + post +}; From 82cc719f9974ce7ab29892f11037fa44173a1eb7 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:26:44 +0100 Subject: [PATCH 011/102] Optimization of the highcharts module. --- lib/highcharts.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/highcharts.js b/lib/highcharts.js index 57fb58c0..7f5ab54a 100644 --- a/lib/highcharts.js +++ b/lib/highcharts.js @@ -15,7 +15,17 @@ See LICENSE file in root for details. /* eslint-disable no-undef */ /** - * Setting the animObject. Called when initing the page. + * @overview Provides methods for initializing Highcharts with customized + * animation settings and triggering the creation of Highcharts charts with + * export-specific configurations. Supports dynamic option merging, custom logic + * injection, and control over rendering behaviors. Designed to accommodate + * export workflows and custom user requirements. Used by the Puppeteer page. + */ + +/** + * Setting the `Highcharts.animObject` function. Called when initing the page. + * + * @function setupHighcharts */ export function setupHighcharts() { Highcharts.animObject = function () { @@ -26,8 +36,11 @@ export function setupHighcharts() { /** * Creates the actual chart. * - * @param {object} chartOptions - The options for the Highcharts chart. - * @param {object} options - The export options. + * @async + * @function triggerExport + * + * @param {Object} chartOptions - The options for the Highcharts chart. + * @param {Object} options - The general export server options. * @param {boolean} displayErrors - A flag indicating whether to display errors. */ export async function triggerExport(chartOptions, options, displayErrors) { @@ -142,3 +155,8 @@ export async function triggerExport(chartOptions, options, displayErrors) { // Empty the custom global options object Highcharts.setOptionsObj = {}; } + +export default { + setupHighcharts, + triggerExport +}; From 87eaf5c06fe76529293c658966b6eeb1395d3371 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:26:52 +0100 Subject: [PATCH 012/102] Optimization of the index module. --- lib/index.js | 118 ++++++++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/lib/index.js b/lib/index.js index a825e688..24daefc8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -12,34 +12,77 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * @overview Core module for initializing and managing the Highcharts Export + * Server. Provides functionalities for configuring exports, setting up server + * operations, logging, scripts caching, resource pooling, and graceful process + * cleanup. + */ + import 'colors'; import { checkAndUpdateCache } from './cache.js'; import { - batchExport, - setAllowCodeExecution, + startExport, singleExport, - startExport + batchExport, + setAllowCodeExecution } from './chart.js'; -import { mapToNewConfig, manualConfig, setOptions } from './config.js'; +import { getOptions, setOptions } from './config.js'; import { - initLogging, log, logWithStack, + initLogging, setLogLevel, enableFileLogging } from './logger.js'; import { initPool, killPool } from './pool.js'; import { shutdownCleanUp } from './resourceRelease.js'; +import { mapToNewConfig } from './schemas/config.js'; import server, { startServer } from './server/server.js'; -import { printLogo, printUsage } from './utils.js'; + +/** + * Initializes the export process. Tasks such as configuring logging, checking + * the cache and sources and initializing the pool of resources happen during + * this stage. This function must be called before attempting to export charts + * or set up a server. The `options` parameter is an object that contains all + * possible options. If the object is not provided, the default general options + * will be retrieved using the `getOptions` function. + * + * @async + * @function initExport + * + * @param {Object} [options=getOptions()] - The `options` object containing + * configuration for a custom export. The default value is the global options + * of the export server instance. + */ +export async function initExport(options = getOptions()) { + // Set the `allowCodeExecution` per export module scope + setAllowCodeExecution(options.customLogic.allowCodeExecution); + + // Init the logging + initLogging(options.logging); + + // Attach process' exit listeners + if (options.other.listenToProcessExits) { + _attachProcessExitListeners(); + } + + // Check if cache needs to be updated + await checkAndUpdateCache(options.highcharts, options.server.proxy); + + // Init the pool + await initPool(options.pool, options.puppeteer.args); +} /** * Attaches exit listeners to the process, ensuring proper cleanup of resources - * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM', and - * 'uncaughtException' events. + * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM' + * and 'uncaughtException' events. + * + * @function _attachProcessExitListeners */ -const attachProcessExitListeners = () => { +function _attachProcessExitListeners() { log(3, '[process] Attaching exit listeners to the process.'); // Handler for the 'exit' @@ -70,66 +113,27 @@ const attachProcessExitListeners = () => { logWithStack(1, error, `The ${name} error.`); await shutdownCleanUp(1); }); -}; - -/** - * Initializes the export process. Tasks such as configuring logging, checking - * cache and sources, and initializing the pool of resources happen during - * 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. - * - * @param {Object} options - All export options. - * - * @returns {Promise} Promise resolving to the updated export options. - */ -const initExport = async (options) => { - // Set the allowCodeExecution per export module scope - setAllowCodeExecution( - options.customLogic && options.customLogic.allowCodeExecution - ); - - // Init the logging - initLogging(options.logging); - - // Attach process' exit listeners - if (options.other.listenToProcessExits) { - attachProcessExitListeners(); - } - - // Check if cache needs to be updated - await checkAndUpdateCache(options); - - // Init the pool - await initPool({ - pool: options.pool || { - minWorkers: 1, - maxWorkers: 1 - }, - puppeteerArgs: options.puppeteer.args || [] - }); - - // Return updated options - return options; -}; +} export default { // Server server, startServer, + // Options + getOptions, + setOptions, + // Exporting initExport, + startExport, singleExport, batchExport, - startExport, // Pool initPool, killPool, - // Other - setOptions, - shutdownCleanUp, - // Logs log, logWithStack, @@ -137,8 +141,6 @@ export default { enableFileLogging, // Utils - mapToNewConfig, - manualConfig, - printLogo, - printUsage + shutdownCleanUp, + mapToNewConfig }; From b2e34d4aaf6e470ae44f491292f8ac0b9a35f8b0 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:27:01 +0100 Subject: [PATCH 013/102] Optimization of the logger module. --- lib/logger.js | 219 ++++++++++++++++++++++++-------------------------- 1 file changed, 105 insertions(+), 114 deletions(-) diff --git a/lib/logger.js b/lib/logger.js index 8d7374fd..82f0be67 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -12,7 +12,17 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * @overview A module for managing logging functionality with customizable + * log levels, console and file logging options, and error handling support. + * The module also ensures that file-based logs are stored in a structured + * directory, creating the necessary paths automatically if they do not exist. + */ + import { appendFile, existsSync, mkdirSync } from 'fs'; +import { join } from 'path'; + +import { getNewDate } from './utils.js'; // The available colors const colors = ['red', 'yellow', 'blue', 'gray', 'green']; @@ -23,6 +33,8 @@ let logging = { toConsole: true, toFile: false, pathCreated: false, + // Full path to the log file + pathToLog: '', // Log levels levelsDesc: [ { @@ -45,51 +57,42 @@ let logging = { title: 'benchmark', color: colors[4] } - ], - // Log listeners - listeners: [] + ] }; /** - * Logs the provided texts to a file, if file logging is enabled. It creates - * the necessary directory structure if not already created and appends the - * content, including an optional prefix, to the specified log file. + * Initializes logging with the specified logging configuration. * - * @param {string[]} texts - An array of texts to be logged. - * @param {string} prefix - An optional prefix to be added to each log entry. + * @function initLogging + * + * @param {Object} loggingOptions - Object containing logging options. */ -const logToFile = (texts, prefix) => { - if (!logging.pathCreated) { - // Create if does not exist - !existsSync(logging.dest) && mkdirSync(logging.dest); - - // We now assume the path is available, e.g. it's the responsibility - // of the user to create the path with the correct access rights. - logging.pathCreated = true; +export function initLogging(loggingOptions) { + // Set all the logging options on our logging module object + for (const [key, value] of Object.entries(loggingOptions)) { + logging[key] = value; } - // Add the content to a file - appendFile( - `${logging.dest}${logging.file}`, - [prefix].concat(texts).join(' ') + '\n', - (error) => { - if (error) { - console.log(`[logger] Unable to write to log file: ${error}`); - logging.toFile = false; - } - } - ); -}; + // Set the log level + setLogLevel(loggingOptions.level); + + // Set the log file path and name + if (loggingOptions.toFile) { + enableFileLogging(loggingOptions.dest, loggingOptions.file); + } +} /** * Logs a message. Accepts a variable amount of arguments. Arguments after - * `level` will be passed directly to console.log, and/or will be joined + * the `level` will be passed directly to `console.log`, and/or will be joined * and appended to the log file. * - * @param {any} args - An array of arguments where the first is the log level - * and the rest are strings to build a message with. + * @function log + * + * @param {...unknown} args - An array of arguments where the first is the log + * level and the rest are strings to build a message with. */ -export const log = (...args) => { +export function log(...args) { const [newLevel, ...texts] = args; // Current logging options @@ -103,16 +106,8 @@ export const log = (...args) => { return; } - // Get rid of the GMT text information - const newDate = new Date().toString().split('(')[0].trim(); - // Create a message's prefix - const prefix = `${newDate} [${levelsDesc[newLevel - 1].title}] -`; - - // Call available log listeners - logging.listeners.forEach((fn) => { - fn(prefix, texts.join(' ')); - }); + const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`; // Log to console if (logging.toConsole) { @@ -124,20 +119,22 @@ export const log = (...args) => { // Log to file if (logging.toFile) { - logToFile(texts, prefix); + _logToFile(texts, prefix); } -}; +} /** * Logs an error message with its stack trace. Optionally, a custom message * can be provided. * - * @param {number} level - The log level. + * @function logWithStack + * + * @param {number} newLevel - The log level. * @param {Error} error - The error object. - * @param {string} customMessage - An optional custom message to be logged along - * with the error. + * @param {string} customMessage - An optional custom message to be logged + * along with the error. */ -export const logWithStack = (newLevel, error, customMessage) => { +export function logWithStack(newLevel, error, customMessage) { // Get the main message const mainMessage = customMessage || error.message; @@ -149,117 +146,111 @@ export const logWithStack = (newLevel, error, customMessage) => { return; } - // Get rid of the GMT text information - const newDate = new Date().toString().split('(')[0].trim(); - // Create a message's prefix - const prefix = `${newDate} [${levelsDesc[newLevel - 1].title}] -`; + const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`; - // If the customMessage exists, we want to display the whole stack message + // If the `customMessage` exists, we want to display the whole stack message const stackMessage = - error.message !== error.stackMessage || error.stackMessage === undefined + error && + (error.message !== error.stackMessage || error.stackMessage === undefined ? error.stack - : error.stack.split('\n').slice(1).join('\n'); + : error.stack.split('\n').slice(1).join('\n')); - // Combine custom message or error message with error stack message - const texts = [mainMessage, '\n', stackMessage]; + // Combine custom message or error message with error stack message, if exists + const texts = [mainMessage]; + if (stackMessage) { + texts.push('\n', stackMessage); + } + + // Log to file + if (logging.toFile) { + _logToFile(texts, prefix); + } // Log to console if (logging.toConsole) { console.log.apply( undefined, [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat([ - mainMessage[colors[newLevel - 1]], - '\n', - stackMessage + texts.shift()[colors[newLevel - 1]], + ...texts ]) ); } - - // Call available log listeners - logging.listeners.forEach((fn) => { - fn(prefix, texts.join(' ')); - }); - - // Log to file - if (logging.toFile) { - logToFile(texts, prefix); - } -}; +} /** * Sets the log level to the specified value. Log levels are (0 = no logging, - * 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark) + * 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark). + * + * @function setLogLevel * * @param {number} newLevel - The new log level to be set. */ -export const setLogLevel = (newLevel) => { +export function setLogLevel(newLevel) { if (newLevel >= 0 && newLevel <= logging.levelsDesc.length) { logging.level = newLevel; } -}; +} /** * Enables file logging with the specified destination and log file. * - * @param {string} logDest - The destination path for log files. - * @param {string} logFile - The log file name. + * @function enableFileLogging + * + * @param {string} dest - The destination path for log files. + * @param {string} file - The log file name. */ -export const enableFileLogging = (logDest, logFile) => { +export function enableFileLogging(dest, file) { // Update logging options logging = { ...logging, - dest: logDest || logging.dest, - file: logFile || logging.file, + dest, + file, toFile: true }; - - if (logging.dest.length === 0) { - return log(1, '[logger] File logging initialization: no path supplied.'); - } - - if (!logging.dest.endsWith('/')) { - logging.dest += '/'; - } -}; +} /** - * Initializes logging with the specified logging configuration. + * Logs the provided texts to a file, if file logging is enabled. It creates + * the necessary directory structure if not already created and appends + * the content, including an optional prefix, to the specified log file. + * + * @function _logToFile * - * @param {Object} loggingOptions - The logging configuration object. + * @param {Array.} texts - An array of texts to be logged. + * @param {string} prefix - An optional prefix to be added to each log entry. */ -export const initLogging = (loggingOptions) => { - // Set all the logging options on our logging module object - for (const [key, value] of Object.entries(loggingOptions)) { - logging[key] = value; - } +function _logToFile(texts, prefix) { + if (!logging.pathCreated) { + // Create if does not exist + !existsSync(logging.dest) && mkdirSync(logging.dest); - // Set the log level - setLogLevel(loggingOptions && parseInt(loggingOptions.level)); + // Create the full path + logging.pathToLog = join(logging.dest, logging.file); - // Set the log file path and name - if (loggingOptions && loggingOptions.dest && loggingOptions.toFile) { - enableFileLogging( - loggingOptions.dest, - loggingOptions.file || 'highcharts-export-server.log' - ); + // We now assume the path is available, e.g. it's the responsibility + // of the user to create the path with the correct access rights. + logging.pathCreated = true; } -}; -/** - * Adds a listener function to the logging system. - * - * @param {function} fn - The listener function to be added. - */ -export const listen = (fn) => { - logging.listeners.push(fn); -}; + // Add the content to a file + appendFile( + logging.pathToLog, + [prefix].concat(texts).join(' ') + '\n', + (error) => { + if (error) { + console.log(`[logger] Unable to write to log file: ${error}`); + logging.toFile = false; + } + } + ); +} export default { + initLogging, log, logWithStack, setLogLevel, - enableFileLogging, - initLogging, - listen + enableFileLogging }; From 13af74766f32df98fdbe81bebeec18d551989f6c Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:27:17 +0100 Subject: [PATCH 014/102] Removed the poolConfig in favor of the global pool options, created a function for the factory object, and expanded stats info. --- lib/pool.js | 497 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 316 insertions(+), 181 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 5b5f09e5..cd4fe30e 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -12,18 +12,22 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * @overview This module provides a worker pool implementation for managing + * browser instances and pages, specifically designed for use with + * the Highcharts Export Server. It optimizes resource usage and performance + * by maintaining a pool of workers that can handle concurrent export tasks + * using Puppeteer. + */ + import { Pool } from 'tarn'; import { v4 as uuid } from 'uuid'; -import { - create as createBrowser, - close as closeBrowser, - newPage, - clearPage -} from './browser.js'; -import puppeteerExport from './export.js'; +import { createBrowser, closeBrowser, newPage, clearPage } from './browser.js'; +import { getOptions } from './config.js'; +import { puppeteerExport } from './export.js'; import { log, logWithStack } from './logger.js'; -import { measureTime } from './utils.js'; +import { getNewDateTime, measureTime } from './utils.js'; import ExportError from './errors/ExportError.js'; @@ -31,7 +35,7 @@ import ExportError from './errors/ExportError.js'; let pool = false; // Pool statistics -export const stats = { +const poolStats = { performedExports: 0, exportAttempts: 0, exportFromSvgAttempts: 0, @@ -40,125 +44,27 @@ export const stats = { spentAverage: 0 }; -let poolConfig = {}; - -const factory = { - /** - * Creates a new worker page for the export pool. - * - * @returns {Object} - An object containing the worker ID, a reference to the - * browser page, and initial work count. - * - * @throws {ExportError} - If there's an error during the creation of the new - * page. - */ - create: async () => { - let page = false; - - const id = uuid(); - const startDate = new Date().getTime(); - - try { - page = await newPage(); - - if (!page || page.isClosed()) { - throw new ExportError('The page is invalid or closed.'); - } - - log( - 3, - `[pool] Successfully created a worker ${id} - took ${ - new Date().getTime() - startDate - } ms.` - ); - } catch (error) { - throw new ExportError( - 'Error encountered when creating a new page.' - ).setError(error); - } - - return { - id, - page, - // Try to distribute the initial work count - workCount: Math.round(Math.random() * (poolConfig.workLimit / 2)) - }; - }, - - /** - * Validates a worker page in the export pool, checking if it has exceeded - * the work limit. - * - * @param {Object} workerHandle - The handle to the worker, containing the - * worker's ID, a reference to the browser page, and work count. - * - * @returns {boolean} - Returns true if the worker is valid and within - * the work limit; otherwise, returns false. - */ - validate: async (workerHandle) => { - // NOTE: In certain cases acquiring throws a TargetCloseError, which may - // be caused by two things: - // - The page is closed and attempted to be reused. - // - Lost contact with the browser - // What we're seeing in logs is that successive exports typically - // succeeds, and the server recovers, indicating that it's likely - // the first case. This is an attempt at allievating the issue by - // simply not validating the worker if the page is null or closed. - // - // The actual result from when this happened, was that a worker would - // be completely locked, stopping it from being acquired until - // its work count reached the limit. - if (!workerHandle.page || workerHandle.page?.isClosed()) { - return false; - } - - if ( - poolConfig.workLimit && - ++workerHandle.workCount > poolConfig.workLimit - ) { - log( - 3, - `[pool] Worker failed validation: exceeded work limit (limit is ${poolConfig.workLimit}).` - ); - return false; - } - return true; - }, - - /** - * Destroys a worker entry in the export pool, closing its associated page. - * - * @param {Object} workerHandle - The handle to the worker, containing - * the worker's ID and a reference to the browser page. - */ - destroy: async (workerHandle) => { - log(3, `[pool] Destroying pool entry ${workerHandle.id}.`); - - if (workerHandle.page && !workerHandle.page.isClosed()) { - await workerHandle.page.close(); - } - } - - // log: (message, level) => log(1, '[tarn] ' + message) -}; - /** * Initializes the export pool with the provided configuration, creating * a browser instance and setting up worker resources. * - * @param {Object} config - Configuration options for the export pool along - * with custom puppeteer arguments for the puppeteer.launch function. + * @async + * @function initPool + * + * @param {Object} poolOptions - Object containing pool options. + * @param {Array.} puppeteerArgs - Array of custom puppeteer arguments + * for the puppeteer.launch function. + * + * @throws {ExportError} Throws an `ExportError` if could not create the pool + * of workers. */ -export const initPool = async (config) => { - // For the module scope usage - poolConfig = config && config.pool ? { ...config.pool } : {}; - +export async function initPool(poolOptions = getOptions().pool, puppeteerArgs) { // Create a browser instance with the puppeteer arguments - await createBrowser(config.puppeteerArgs); + await createBrowser(puppeteerArgs); log( 3, - `[pool] Initializing pool with workers: min ${poolConfig.minWorkers}, max ${poolConfig.maxWorkers}.` + `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.` ); if (pool) { @@ -168,44 +74,48 @@ export const initPool = async (config) => { ); } - if (parseInt(poolConfig.minWorkers) > parseInt(poolConfig.maxWorkers)) { - poolConfig.minWorkers = poolConfig.maxWorkers; + // Keep an eye on a correct min and max workers number + if (poolOptions.minWorkers > poolOptions.maxWorkers) { + poolOptions.minWorkers = poolOptions.maxWorkers; } try { // Create a pool along with a minimal number of resources pool = new Pool({ // Get the create/validate/destroy/log functions - ...factory, - min: parseInt(poolConfig.minWorkers), - max: parseInt(poolConfig.maxWorkers), - acquireTimeoutMillis: poolConfig.acquireTimeout, - createTimeoutMillis: poolConfig.createTimeout, - destroyTimeoutMillis: poolConfig.destroyTimeout, - idleTimeoutMillis: poolConfig.idleTimeout, - createRetryIntervalMillis: poolConfig.createRetryInterval, - reapIntervalMillis: poolConfig.reaperInterval, + ..._factory(poolOptions), + min: poolOptions.minWorkers, + max: poolOptions.maxWorkers, + acquireTimeoutMillis: poolOptions.acquireTimeout, + createTimeoutMillis: poolOptions.createTimeout, + destroyTimeoutMillis: poolOptions.destroyTimeout, + idleTimeoutMillis: poolOptions.idleTimeout, + createRetryIntervalMillis: poolOptions.createRetryInterval, + reapIntervalMillis: poolOptions.reaperInterval, propagateCreateError: false }); // Set events pool.on('release', async (resource) => { // Clear page - const r = await clearPage(resource.page, false); + const clearStatus = await clearPage(resource, false); log( 4, - `[pool] Releasing a worker with ID ${resource.id}. Clear page status: ${r}.` + `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.` ); }); - pool.on('destroySuccess', (eventId, resource) => { - log(4, `[pool] Destroyed a worker with ID ${resource.id}.`); + pool.on('destroySuccess', (_eventId, resource) => { + log( + 4, + `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.` + ); resource.page = null; }); const initialResources = []; // Create an initial number of resources - for (let i = 0; i < poolConfig.minWorkers; i++) { + for (let i = 0; i < poolOptions.minWorkers; i++) { try { const resource = await pool.acquire().promise; initialResources.push(resource); @@ -225,15 +135,19 @@ export const initPool = async (config) => { ); } catch (error) { throw new ExportError( - '[pool] Could not create the pool of workers.' + '[pool] Could not create the pool of workers.', + 500 ).setError(error); } -}; +} /** * Kills all workers in the pool, destroys the pool, and closes the browser * instance. * + * @async + * @function killPool + * * @returns {Promise} A promise that resolves after the workers are * killed, the pool is destroyed, and the browser is closed. */ @@ -252,6 +166,7 @@ export async function killPool() { await pool.destroy(); log(4, '[browser] Destroyed the pool of resources.'); } + pool = null; } // Close the browser instance @@ -263,27 +178,34 @@ export async function killPool() { * handle from the pool, performs the export using puppeteer, and releases * the worker handle back to the pool. * + * @async + * @function postWork + * * @param {string} chart - The chart data or configuration to be exported. * @param {Object} options - Export options and configuration. * * @returns {Promise} A promise that resolves with the export resultand * options. * - * @throws {ExportError} If an error occurs during the export process. + * @throws {ExportError} Throws an `ExportError` if an error occurs during + * the export process. */ -export const postWork = async (chart, options) => { +export async function postWork(chart, options) { let workerHandle; try { log(4, '[pool] Work received, starting to process.'); - ++stats.exportAttempts; - if (poolConfig.benchmarking) { + ++poolStats.exportAttempts; + if (getOptions().pool.benchmarking) { getPoolInfo(); } if (!pool) { - throw new ExportError('Work received, but pool has not been started.'); + throw new ExportError( + 'Work received, but pool has not been started.', + 500 + ); } // Acquire the worker along with the id of resource and work count @@ -297,7 +219,7 @@ export const postWork = async (chart, options) => { log( 5, options.payload?.requestId - ? `[benchmark] Request with ID ${options.payload?.requestId} -` + ? `[benchmark] Request: ${options.payload?.requestId} -` : '[benchmark]', `Acquired a worker handle: ${acquireCounter()}ms.` ); @@ -305,23 +227,30 @@ export const postWork = async (chart, options) => { } catch (error) { throw new ExportError( (options.payload?.requestId - ? `For request with ID ${options.payload?.requestId} - ` + ? `Request: ${options.payload?.requestId} - ` : '') + - `Error encountered when acquiring an available entry: ${acquireCounter()}ms.` + `Error encountered when acquiring an available entry: ${acquireCounter()}ms.`, + 400 ).setError(error); } log(4, '[pool] Acquired a worker handle.'); if (!workerHandle.page) { + // Set the `workLimit` to exceeded in order to recreate the resource + workerHandle.workCount = options.pool.workLimit + 1; throw new ExportError( - 'Resolved worker page is invalid: the pool setup is wonky.' + 'Resolved worker page is invalid: the pool setup is wonky.', + 400 ); } // Save the start time - let workStart = new Date().getTime(); + let workStart = getNewDateTime(); - log(4, `[pool] Starting work on pool entry with ID ${workerHandle.id}.`); + log( + 4, + `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.` + ); // Perform an export on a puppeteer level const exportCounter = measureTime(); @@ -329,18 +258,21 @@ export const postWork = async (chart, options) => { // Check if it's an error if (result instanceof Error) { - // NOTE: If there's a rasterization timeout, we want need to flush the page. - // This is because the page may be in a state where it's waiting for - // the screenshot to finish even though the timeout has occured. - // Which of course causes a lot of issues with the event system, - // and page consistency. + // NOTE: + // If there's a rasterization timeout, we want need to flush the page. + // This is because the page may be in a state where it's waiting for + // the screenshot to finish even though the timeout has occured. + // Which of course causes a lot of issues with the event system, + // and page consistency. // - // NOTE: Only page.screenshot will throw this, timeouts for PDF's are - // handled by the page.pdf function itself. + // NOTE: + // Only page.screenshot will throw this, timeouts for PDF's are + // handled by the page.pdf function itself. // - // ...yes, this is ugly. + // ...yes, this is ugly. if (result.message === 'Rasterization timeout') { - workerHandle.workCount = poolConfig.workLimit + 1; + // Set the `workLimit` to exceeded in order to recreate the resource + workerHandle.workCount = options.pool.workLimit + 1; workerHandle.page = null; } @@ -354,7 +286,7 @@ export const postWork = async (chart, options) => { } else { throw new ExportError( (options.payload?.requestId - ? `For request with ID ${options.payload?.requestId} - ` + ? `Request: ${options.payload?.requestId} - ` : '') + `Error encountered during export: ${exportCounter()}ms.` ).setError(result); } @@ -365,7 +297,7 @@ export const postWork = async (chart, options) => { log( 5, options.payload?.requestId - ? `[benchmark] Request with ID ${options.payload?.requestId} -` + ? `[benchmark] Request: ${options.payload?.requestId} -` : '[benchmark]', `Exported a chart sucessfully: ${exportCounter()}ms.` ); @@ -376,10 +308,11 @@ export const postWork = async (chart, options) => { // Used for statistics in averageTime and processedWorkCount, which // in turn is used by the /health route. - const workEnd = new Date().getTime(); + const workEnd = getNewDateTime(); const exportTime = workEnd - workStart; - stats.timeSpent += exportTime; - stats.spentAverage = stats.timeSpent / ++stats.performedExports; + + poolStats.timeSpent += exportTime; + poolStats.spentAverage = poolStats.timeSpent / ++poolStats.performedExports; log(4, `[pool] Work completed in ${exportTime} ms.`); @@ -389,7 +322,7 @@ export const postWork = async (chart, options) => { options }; } catch (error) { - ++stats.droppedExports; + ++poolStats.droppedExports; if (workerHandle) { pool.release(workerHandle); @@ -399,45 +332,247 @@ export const postWork = async (chart, options) => { error ); } -}; +} /** * Retrieves the current pool instance. * - * @returns {Object|null} The current pool instance if initialized, or null + * @function getPool + * + * @returns {(Object|null)} The current pool instance if initialized, or null * if the pool has not been created. */ -export const getPool = () => pool; +export function getPool() { + return pool; +} + +/** + * Gets the statistic of a pool instace about exports. + * + * @function getPoolStats + * + * @returns {Object} The current pool statistics. + */ +export function getPoolStats() { + return poolStats; +} /** * Retrieves pool information in JSON format, including minimum and maximum * workers, available workers, workers in use, and pending acquire requests. * + * @function getPoolInfoJSON + * * @returns {Object} Pool information in JSON format. */ -export const getPoolInfoJSON = () => ({ - min: pool.min, - max: pool.max, - all: pool.numFree() + pool.numUsed(), - available: pool.numFree(), - used: pool.numUsed(), - pending: pool.numPendingAcquires() -}); +export function getPoolInfoJSON() { + return { + min: pool.min, + max: pool.max, + used: pool.numUsed(), + available: pool.numFree(), + allCreated: pool.numUsed() + pool.numFree(), + pendingAcquires: pool.numPendingAcquires(), + pendingCreates: pool.numPendingCreates(), + pendingValidations: pool.numPendingValidations(), + pendingDestroys: pool.pendingDestroys.length, + absoluteAll: + pool.numUsed() + + pool.numFree() + + pool.numPendingAcquires() + + pool.numPendingCreates() + + pool.numPendingValidations() + + pool.pendingDestroys.length + }; +} /** * Logs information about the current state of the pool, including the minimum * and maximum workers, available workers, workers in use, and pending acquire * requests. + * + * @function getPoolInfo */ export function getPoolInfo() { - const { min, max, all, available, used, pending } = getPoolInfoJSON(); + const { + min, + max, + used, + available, + allCreated, + pendingAcquires, + pendingCreates, + pendingValidations, + pendingDestroys, + absoluteAll + } = getPoolInfoJSON(); log(5, `[pool] The minimum number of resources allowed by pool: ${min}.`); log(5, `[pool] The maximum number of resources allowed by pool: ${max}.`); - log(5, `[pool] The number of all created resources: ${all}.`); - log(5, `[pool] The number of available resources: ${available}.`); - log(5, `[pool] The number of acquired resources: ${used}.`); - log(5, `[pool] The number of resources waiting to be acquired: ${pending}.`); + log(5, `[pool] The number of used resources: ${used}.`); + log(5, `[pool] The number of free resources: ${available}.`); + log( + 5, + `[pool] The number of all created (used and free) resources: ${allCreated}.` + ); + log( + 5, + `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.` + ); + log( + 5, + `[pool] The number of resources waiting to be created: ${pendingCreates}.` + ); + log( + 5, + `[pool] The number of resources waiting to be validated: ${pendingValidations}.` + ); + log( + 5, + `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.` + ); + log(5, `[pool] The number of all resources: ${absoluteAll}.`); +} + +/** + * Factory function that returns an object with create, validate and destroy + * functions for the pool instance. + * + * @function _factory + * + * @param {Object} poolOptions - Object containing pool options. + */ +function _factory(poolOptions) { + return { + /** + * Creates a new worker page for the export pool. + * + * @async + * @function create + * + * @returns {Object} An object containing the worker ID, a reference to the + * browser page, and initial work count. + * + * @throws {ExportError} Throws an `ExportError` if there is an error during + * the creation of the new page. + */ + create: async () => { + try { + const poolResource = { + id: uuid(), + // Try to distribute the initial work count + workCount: Math.round(Math.random() * (poolOptions.workLimit / 2)) + }; + + return await newPage(poolResource); + } catch (error) { + throw new ExportError( + 'Error encountered when creating a new page.', + 400 + ).setError(error); + } + }, + + /** + * Validates a worker page in the export pool, checking if it has exceeded + * the work limit. + * + * @async + * @function validate + * + * @param {Object} poolResource - The handle to the worker, containing the + * worker's ID, a reference to the browser page, and work count. + * + * @returns {boolean} - Returns true if the worker is valid and within + * the work limit; otherwise, returns false. + */ + validate: async (poolResource) => { + // NOTE: + // In certain cases acquiring throws a TargetCloseError, which may + // be caused by two things: + // - The page is closed and attempted to be reused. + // - Lost contact with the browser. + // + // What we're seeing in logs is that successive exports typically + // succeeds, and the server recovers, indicating that it's likely + // the first case. This is an attempt at allievating the issue by + // simply not validating the worker if the page is null or closed. + // + // The actual result from when this happened, was that a worker would + // be completely locked, stopping it from being acquired until + // its work count reached the limit. + if (!poolResource.page || poolResource.page?.isClosed()) { + return false; + } + + // Check if the `workLimit` is exceeded + if ( + poolOptions.workLimit && + ++poolResource.workCount > poolOptions.workLimit + ) { + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works limit).` + ); + return false; + } + + // Check if the `page` is not valid + if (poolResource.page) { + // Check if the `page` is closed + if (poolResource.page.isClosed()) { + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).` + ); + } + + // Check if the `mainFrame` is detached + if (poolResource.page.mainFrame().detached) { + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).` + ); + } + return false; + } + + return true; + }, + + /** + * Destroys a worker entry in the export pool, closing its associated page. + * + * @async + * @function destroy + * + * @param {Object} poolResource - The handle to the worker, containing + * the worker's ID and a reference to the browser page. + */ + destroy: async (poolResource) => { + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Destroying a worker.` + ); + + if (poolResource.page && !poolResource.page.isClosed()) { + try { + // Remove all attached event listeners from the resource + poolResource.page.removeAllListeners('pageerror'); + poolResource.page.removeAllListeners('console'); + poolResource.page.removeAllListeners('framedetached'); + + // We need to wait around for this + await poolResource.page.close(); + } catch (error) { + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.` + ); + } + } + } + }; } export default { @@ -445,7 +580,7 @@ export default { killPool, postWork, getPool, + getPoolStats, getPoolInfo, - getPoolInfoJSON, - getStats: () => stats + getPoolInfoJSON }; From d96247ef9ab13f62eca60f60ebcef4e01b7a3605 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:27:28 +0100 Subject: [PATCH 015/102] Moved the logic of the prompt functionality into a separated module. --- lib/prompt.js | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 lib/prompt.js diff --git a/lib/prompt.js b/lib/prompt.js new file mode 100644 index 00000000..9b645acd --- /dev/null +++ b/lib/prompt.js @@ -0,0 +1,298 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +/** + * @overview This module provides a streamlined manual configuration feature + * that prompts users to customize and save their desired settings into + * a configuration file. + */ + +import { existsSync, promises as fsPromises, readFileSync } from 'fs'; + +import prompts from 'prompts'; + +import { logWithStack } from './logger.js'; +import { defaultConfig } from './schemas/config.js'; + +/** + * Initiates a manual configuration process, prompting the user to configure + * settings based on the specified configuration file and saving the results + * to the file. + * + * @async + * @function manualConfig + * + * @param {string} configFileName - The name of the configuration file to save + * to. + * + * @returns {Promise} A Promise that resolves to true once the manual + * configuration process is completed and the updated configuration is saved. + */ +export async function manualConfig(configFileName) { + // Initialize an empty object to hold the config data + let configFile = {}; + + // Check if the specified configuration file already exists + if (existsSync(configFileName)) { + // If the file exists, read and parse its contents + configFile = JSON.parse(readFileSync(configFileName, 'utf8')); + } + + /** + * Handles submitting of answers during the prompt process for each + * configuration category. + * + * @async + * @function onSubmit + * + * @param {Object} _ - Unused, automatically passed argument. + * @param {Array} categories - The selected categories to configure. + * + * @returns {Promise} Resolves to `true` once the configuration + * process is completed and saved. + */ + const onSubmit = async (_, categories) => { + // Track how many questions have been answered + let questionsCounter = 0; + + // Collect all prompt questions for selected categories + const allQuestions = []; + + // Object to store the prompt responses for configuration + const promptsConfig = {}; + + // Iterate through each selected category + for (const section of categories) { + Object.entries(defaultConfig[section]).forEach(([category, options]) => { + // Init an array to store prompts for this section if not already done + if (!promptsConfig[section]) { + promptsConfig[section] = []; + } + + // Add prompts for any option with prompt configuration + if (options.promptOptions) { + promptsConfig[section].push( + _preparePrompt([category, options], section) + ); + } + + // Handle subsections such as `proxy`, `rateLimiting` and `ssl` + if (['proxy', 'rateLimiting', 'ssl'].includes(category)) { + Object.entries(defaultConfig[section][category]).forEach( + (element) => { + // Add prompts for any option with prompt configuration + if (element[1].promptOptions) { + promptsConfig[section].push( + _preparePrompt( + [`${category}.${element[0]}`, element[1]], + section + ) + ); + } + } + ); + } + }); + + // Append all prompts for the section to the full list of questions + allQuestions.push(...promptsConfig[section]); + } + + // Prompt the user with the collected questions + await prompts(allQuestions, { + /** + * Handles submission of individual prompt answers. + * + * @async + * @function onSubmit + * + * @param {Object} prompt - The current prompt being answered. + * @param {any} answer - The user's response to the prompt. + * + * @returns {Promise} Resolves once the answer is processed. + */ + onSubmit: async (prompt, answer) => { + // Handle specific script configurations + if ( + ['coreScripts', 'moduleScripts', 'indicatorScripts'].includes( + prompt.name + ) + ) { + // If the answer is provided, use the selected values or the defaults + answer = answer.length + ? answer.map((module) => prompt.choices[module]) + : prompt.choices; + + // Store the answer in the config file under the appropriate section + configFile[prompt.section][prompt.name] = answer; + } else { + // Update the config file with an answer, handling nested properties + configFile[prompt.section] = _recursiveProps( + Object.assign({}, configFile[prompt.section] || {}), + prompt.name.split('.'), + prompt.choices ? prompt.choices[answer] : answer + ); + } + + // If all questions have been answered, save the updated config + if (++questionsCounter === allQuestions.length) { + try { + await fsPromises.writeFile( + configFileName, + JSON.stringify(configFile, null, 2), + 'utf8' + ); + } catch (error) { + logWithStack( + 1, + error, + `[config] An error occurred while creating the ${configFileName} file.` + ); + } + return true; + } + } + }); + return true; + }; + + // Generate a list of categories available for configuration + const choices = Object.keys(defaultConfig).map((choice) => ({ + title: `${choice} options`, + value: choice + })); + + // Prompt the user to select one or more categories to configure + return prompts( + { + type: 'multiselect', + name: 'category', + message: 'Which category do you want to configure?', + hint: 'Space: Select specific, A: Select all, Enter: Confirm.', + instructions: '', + choices + }, + { onSubmit } + ); +} + +/** + * Prepares a prompt configuration object based on the provided entry + * and section. The prompt configuration includes the prompt type, initial + * value, format, and additional options for rendering and validation + * in interactive prompts. + * + * @function _preparePrompt + * + * @param {Array} entry - An array where the first element is the name + * of the option, and the second element is an object containing the option + * details. + * @param {string} section - The section name for the prompt, used to categorize + * the prompt message. + * + * @returns {Object} The prepared prompt object containing configuration + * for the interactive prompt, including the type, initial value, + * and any formatting or choices required. + */ +function _preparePrompt(entry, section) { + // Retrieve the option name + const name = entry[0]; + + // Retrieve the option configuration + const option = entry[1]; + + // Collect common data for the prompt + const prompt = { + name, + message: `${section}.${name}`.blue + ` - ${option.description}`, + section, + ...option.promptOptions + }; + + // Update the prompt configuration with type-specific data + switch (prompt.type) { + case 'text': + prompt.initial = option.value; + prompt.format = (value) => (typeof value === 'string' ? value : null); + break; + case 'number': + prompt.initial = option.value; + prompt.format = (value) => (typeof value === 'number' ? value : null); + break; + case 'toggle': + prompt.initial = option.value; + prompt.format = (value) => (typeof value === 'boolean' ? value : null); + break; + case 'list': + prompt.initial = option.value.join(';'); + break; + case 'select': + prompt.initial = 0; + break; + case 'multiselect': + prompt.choices = option.value; + break; + } + + // Return the prompt configuration + return prompt; +} + +/** + * Recursively updates or creates nested properties within an object and assigns + * the final value to the deepest property. + * + * @function _recursiveProps + * + * @param {Object} objectToUpdate - The object in which nested properties + * will be updated or created. + * @param {Array.} nestedNames - An array of property names representing + * the nesting hierarchy. + * @param {unknown} value - The final value to be assigned to the deepest nested + * property. + * + * @returns {Object} The updated object with the specified value assigned + * to the nested property. + */ +function _recursiveProps(objectToUpdate, nestedNames, value) { + while (nestedNames.length > 1) { + // Retrieve and remove the next property name from the nested hierarchy + const propName = nestedNames.shift(); + + // Create an empty object if the property doesn't exist + if (!Object.prototype.hasOwnProperty.call(objectToUpdate, propName)) { + objectToUpdate[propName] = {}; + } + + // Recur to the next level, cloning the current property object + objectToUpdate[propName] = _recursiveProps( + Object.assign({}, objectToUpdate[propName]), + nestedNames, + value + ); + + // Return after each recursive call + return objectToUpdate; + } + + // Assign the final value to the last property in the chain + objectToUpdate[nestedNames[0]] = value; + + // Return the fully updated object + return objectToUpdate; +} + +export default { + manualConfig +}; From 2e300839728a595ab5aa458b0d05f5f8431839a5 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:27:42 +0100 Subject: [PATCH 016/102] Optimization of the resourceRelease module. --- lib/resourceRelease.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/resourceRelease.js b/lib/resourceRelease.js index 1d8e85ce..bea0305c 100644 --- a/lib/resourceRelease.js +++ b/lib/resourceRelease.js @@ -12,20 +12,27 @@ See LICENSE file in root for details. *******************************************************************************/ -import { clearAllIntervals } from './intervals.js'; +/** + * @overview Handles graceful shutdown of the Highcharts Export Server, ensuring + * proper cleanup of resources such as browsers, pages, servers, and timers. + */ + import { killPool } from './pool.js'; +import { clearAllTimers } from './timer.js'; import { closeServers } from './server/server.js'; /** * Clean up function to trigger before ending process for the graceful shutdown. * - * @param {number} exitCode - An exit code for the process.exit() function. + * @function shutdownCleanUp + * + * @param {number} exitCode - An exit code for the `process.exit()` function. */ -export const shutdownCleanUp = async (exitCode) => { +export async function shutdownCleanUp(exitCode) { // Await freeing all resources await Promise.allSettled([ // Clear all ongoing intervals - clearAllIntervals(), + clearAllTimers(), // Get available server instances (HTTP/HTTPS) and close them closeServers(), @@ -36,7 +43,7 @@ export const shutdownCleanUp = async (exitCode) => { // Exit process with a correct code process.exit(exitCode); -}; +} export default { shutdownCleanUp From 973306e7f6145fde89ccec4653ca71175449bb44 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:27:50 +0100 Subject: [PATCH 017/102] Optimization of the sanitize module. --- lib/sanitize.js | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/sanitize.js b/lib/sanitize.js index 42ffa5bc..5e0b45b9 100644 --- a/lib/sanitize.js +++ b/lib/sanitize.js @@ -15,23 +15,33 @@ See LICENSE file in root for details. /** * @overview Used to sanitize the strings coming from the exporting module * to prevent XSS attacks (with the DOMPurify library). - **/ + */ -import { JSDOM } from 'jsdom'; import DOMPurify from 'dompurify'; +import { JSDOM } from 'jsdom'; /** - * Sanitizes a given HTML string by removing tags and any content within them. + * Sanitizes a given HTML string by removing + * tags and any content within them. + * + * @function sanitize + * + * @param {string} input - The HTML string to be sanitized. * - * @param {string} input The HTML string to be sanitized. * @returns {string} The sanitized HTML string. */ export function sanitize(input) { + // Get the virtual DOM const window = new JSDOM('').window; + + // Create a purifying instance const purify = DOMPurify(window); + + // Return sanitized input return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] }); } -export default sanitize; +export default { + sanitize +}; From 6cb2e6fa4543cf3c2888a11b8b9a21665d0d7902 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:28:06 +0100 Subject: [PATCH 018/102] Renaming and optimization of the timer module. --- lib/intervals.js | 42 ------------------------------------ lib/timer.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 42 deletions(-) delete mode 100644 lib/intervals.js create mode 100644 lib/timer.js diff --git a/lib/intervals.js b/lib/intervals.js deleted file mode 100644 index f2563c6c..00000000 --- a/lib/intervals.js +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* - -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. - -*******************************************************************************/ - -import { log } from './logger.js'; - -// Array that contains ids of all ongoing intervals -const intervalIds = []; - -/** - * Adds id of a setInterval to the intervalIds array. - * - * @param {NodeJS.Timeout} id - Id of an interval. - */ -export const addInterval = (id) => { - intervalIds.push(id); -}; - -/** - * Clears all of ongoing intervals by ids gathered in the intervalIds array. - */ -export const clearAllIntervals = () => { - log(4, `[server] Clearing all registered intervals.`); - for (const id of intervalIds) { - clearInterval(id); - } -}; - -export default { - addInterval, - clearAllIntervals -}; diff --git a/lib/timer.js b/lib/timer.js new file mode 100644 index 00000000..525f21ac --- /dev/null +++ b/lib/timer.js @@ -0,0 +1,56 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +/** + * @overview This module provides utility functions for managing intervals + * and timeouts in a centralized manner. It maintains a registry of all active + * timers and allows for their efficient cleanup when needed. This can be useful + * in applications where proper resource management and clean shutdown of timers + * are critical to avoid memory leaks or unintended behavior. + */ + +import { log } from './logger.js'; + +// Array that contains ids of all ongoing intervals and timeouts +const timerIds = []; + +/** + * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array. + * + * @function addTimer + * + * @param {NodeJS.Timeout} id - Id of an interval/timeout. + */ +export function addTimer(id) { + timerIds.push(id); +} + +/** + * Clears all of ongoing intervals and timeouts by ids gathered + * in the `timerIds` array. + * + * @function clearAllTimers + */ +export function clearAllTimers() { + log(4, `[server] Clearing all registered intervals and timeouts.`); + for (const id of timerIds) { + clearInterval(id); + clearTimeout(id); + } +} + +export default { + addTimer, + clearAllTimers +}; From 5d522390c0b209d7030911f43e0cef33fafefd99 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:28:20 +0100 Subject: [PATCH 019/102] Extended, corrected, moved existing and introduced a few new utility functions. --- lib/utils.js | 525 +++++++++++++++++++++++++++++---------------------- 1 file changed, 300 insertions(+), 225 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 6823c003..e28fd7df 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -12,15 +12,19 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * @overview The Highcharts Export Server utility module provides + * a comprehensive set of helper functions and constants designed to streamline + * and enhance various operations required for Highcharts export tasks. + */ + import { readFileSync } from 'fs'; import { join } from 'path'; import { fileURLToPath } from 'url'; -import { defaultConfig } from '../lib/schemas/config.js'; -import { log, logWithStack } from './logger.js'; - const MAX_BACKOFF_ATTEMPTS = 6; +// The directory path export const __dirname = fileURLToPath(new URL('../.', import.meta.url)); /** @@ -28,32 +32,67 @@ export const __dirname = fileURLToPath(new URL('../.', import.meta.url)); * characters with a single space and trimming any leading or trailing * whitespace. * + * @function clearText + * * @param {string} text - The input text to be cleared. * @param {RegExp} [rule=/\s\s+/g] - The regular expression rule to match * multiple consecutive whitespace characters. * @param {string} [replacer=' '] - The string used to replace multiple * consecutive whitespace characters. * - * @returns {string} - The cleared and standardized text. + * @returns {string} The cleared and standardized text. */ -export const clearText = (text, rule = /\s\s+/g, replacer = ' ') => - text.replaceAll(rule, replacer).trim(); +export function clearText(text, rule = /\s\s+/g, replacer = ' ') { + return text.replaceAll(rule, replacer).trim(); +} + +/** + * Creates a deep copy of the given object or array. + * + * @function deepCopy + * + * @param {(Object|Array)} obj - The object or array to be deeply copied. + * + * @returns {(Object|Array)} The deep copy of the provided object or array. + */ +export function deepCopy(obj) { + // If the `obj` is null or not of the `object` type, return it + if (obj === null || typeof obj !== 'object') { + return obj; + } + + // Prepare either a new array or a new object + const copy = Array.isArray(obj) ? [] : {}; + + // Recursively copy each property + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + copy[key] = deepCopy(obj[key]); + } + } + + // Return the copied object + return copy; +} /** * Implements an exponential backoff strategy for retrying a function until * a certain number of attempts are reached. * + * @async + * @function expBackoff + * * @param {Function} fn - The function to be retried. * @param {number} [attempt=0] - The current attempt number. - * @param {...any} args - Arguments to be passed to the function. + * @param {...unknown} args - Arguments to be passed to the function. * - * @returns {Promise} - A promise that resolves to the result of the function + * @returns {Promise} A promise that resolves to the result of the function * if successful. * - * @throws {Error} - Throws an error if the maximum number of attempts + * @throws {Error} Throws an `Error` if the maximum number of attempts * is reached. */ -export const expBackoff = async (fn, attempt = 0, ...args) => { +export async function expBackoff(fn, attempt = 0, ...args) { try { // Try to call the function return await fn(...args); @@ -68,25 +107,63 @@ export const expBackoff = async (fn, attempt = 0, ...args) => { // Wait given amount of time await new Promise((response) => setTimeout(response, delayInMs)); - log( - 3, - `[pool] Waited ${delayInMs}ms until next call for the resource id: ${args[0]}.` - ); + + //// TO DO: Correct + // // Information about the resource timeout + // log( + // 3, + // `[utils] Waited ${delayInMs}ms until next call for the resource of ID: ${args[0]}.` + // ); // Try again return expBackoff(fn, attempt, ...args); } -}; +} + +/** + * Adjusts the constructor name by transforming and normalizing it based + * on common chart types. + * + * @function fixConstr + * + * @param {string} constr - The original constructor name to be fixed. + * + * @returns {string} The corrected constructor name, or 'chart' if the input + * is not recognized. + */ +export function fixConstr(constr) { + try { + // Fix the constructor by lowering casing + const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`; + + // Handle the case where the result is just 'Chart' + if (fixedConstr === 'Chart') { + fixedConstr.toLowerCase(); + } + + // Return the corrected constructor, otherwise default to 'chart' + return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes( + fixedConstr + ) + ? fixedConstr + : 'chart'; + } catch { + // Default to 'chart' in case of any error + return 'chart'; + } +} /** * Fixes the export type based on MIME types and file extensions. * + * @function fixType + * * @param {string} type - The original export type. * @param {string} outfile - The file path or name. * - * @returns {string} - The corrected export type. + * @returns {string} The corrected export type. */ -export const fixType = (type, outfile) => { +export function fixType(type, outfile) { // MIME types const mimeTypes = { 'image/png': 'png', @@ -111,130 +188,121 @@ export const fixType = (type, outfile) => { // Return a correct type return mimeTypes[type] || formats.find((t) => t === type) || 'png'; -}; +} /** - * Handles and validates resources for export. + * Returns stringified date without the GMT text information. * - * @param {Object|string} resources - The resources to be handled. Can be either - * a JSON object, stringified JSON or a path to a JSON file. - * @param {boolean} allowFileResources - Whether to allow loading resources from - * files. - * - * @returns {Object|undefined} - The handled resources or undefined if no valid - * resources are found. + * @function getNewDate */ -export const handleResources = (resources = false, allowFileResources) => { - const allowedProps = ['js', 'css', 'files']; - - let handledResources = resources; - let correctResources = false; - - // Try to load resources from a file - if (allowFileResources && resources.endsWith('.json')) { - try { - handledResources = isCorrectJSON(readFileSync(resources, 'utf8')); - } catch (error) { - return logWithStack(2, error, `[cli] No resources found.`); - } - } else { - // Try to get JSON - handledResources = isCorrectJSON(resources); - - // Get rid of the files section - if (handledResources && !allowFileResources) { - delete handledResources.files; - } - } - - // Filter from unnecessary properties - for (const propName in handledResources) { - if (!allowedProps.includes(propName)) { - delete handledResources[propName]; - } else if (!correctResources) { - correctResources = true; - } - } - - // Check if at least one of allowed properties is present - if (!correctResources) { - return log(3, `[cli] No resources found.`); - } - - // Handle files section - if (handledResources.files) { - handledResources.files = handledResources.files.map((item) => item.trim()); - if (!handledResources.files || handledResources.files.length <= 0) { - delete handledResources.files; - } - } +export function getNewDate() { + // Get rid of the GMT text information + return new Date().toString().split('(')[0].trim(); +} - // Return resources - return handledResources; -}; +/** + * Returns the stored time value in milliseconds. + * + * @function getNewDateTime + */ +export function getNewDateTime() { + return new Date().getTime(); +} /** - * Validates and parses JSON data. Checks if provided data is or can - * be a correct JSON. If a primitive is provided, it is stringified and returned. + * Validates, parses, and checks if the provided data is valid JSON. + * + * @function isCorrectJSON * - * @param {Object|string} data - The JSON data to be validated and parsed. - * @param {boolean} toString - Whether to return a stringified representation - * of the parsed JSON. + * @param {unknown} data - The data to be validated and parsed as JSON. + * Must be either an object or a string. + * @param {boolean} [toString=false] - Whether to return a stringified JSON + * version of the parsed data. + * @param {boolean} [allowFunctions=false] - Whether to allow functions + * in the parsed JSON. If true, functions are preserved. * - * @returns {Object|string|boolean} - The parsed JSON object, stringified JSON, - * or false if validation fails. + * @returns {(Object|string|null)} Returns the parsed JSON object, + * a stringified JSON object if `toString` is true, or null if the data + * is not valid JSON or parsing fails. */ -export function isCorrectJSON(data, toString) { +export function isCorrectJSON(data, toString = false, allowFunctions = false) { try { - // Get the string representation if not already before parsing - const parsedData = JSON.parse( - typeof data !== 'string' ? JSON.stringify(data) : data - ); - - // Return a stringified representation of a JSON if required - if (typeof parsedData !== 'string' && toString) { - return JSON.stringify(parsedData); + // Accept only objects and strings + if (!isObject(data) && typeof data !== 'string') { + // Return null if any other type + return null; } - // Return a JSON - return parsedData; - } catch { - return false; + // Get the JSON representation of original data + const jsonData = typeof data === 'string' ? JSON.parse(data) : data; + + // Preserve or remove potential functions based on the allowFunctions flag + const stringifiedOptions = optionsStringify( + jsonData, + allowFunctions, + false + ); + + // Parse the data to check if it is valid JSON + const parsedOptions = allowFunctions + ? JSON.parse( + optionsStringify(jsonData, allowFunctions, true), + (_, value) => + typeof value === 'string' && value.startsWith('function') + ? eval(`(${value})`) + : value + ) + : JSON.parse(stringifiedOptions); + + // Return stringified or parsed JSON object based on the toString flag + return toString ? stringifiedOptions : parsedOptions; + } catch (error) { + // Return null if parse fails + return null; } } /** * Checks if the given item is an object. * - * @param {any} item - The item to be checked. + * @function isObject * - * @returns {boolean} - True if the item is an object, false otherwise. + * @param {unknown} item - The item to be checked. + * + * @returns {boolean} True if the item is an object, false otherwise. */ -export const isObject = (item) => - typeof item === 'object' && !Array.isArray(item) && item !== null; +export function isObject(item) { + return Object.prototype.toString.call(item) === '[object Object]'; +} /** * Checks if the given object is empty. * + * @function isObjectEmpty + * * @param {Object} item - The object to be checked. * - * @returns {boolean} - True if the object is empty, false otherwise. + * @returns {boolean} True if the object is empty, false otherwise. */ -export const isObjectEmpty = (item) => - typeof item === 'object' && - !Array.isArray(item) && - item !== null && - Object.keys(item).length === 0; +export function isObjectEmpty(item) { + return ( + typeof item === 'object' && + !Array.isArray(item) && + item !== null && + Object.keys(item).length === 0 + ); +} /** * Checks if a private IP range URL is found in the given string. * + * @function isPrivateRangeUrlFound + * * @param {string} item - The string to be checked for a private IP range URL. * - * @returns {boolean} - True if a private IP range URL is found, false - * otherwise. + * @returns {boolean} True if a private IP range URL is found, false otherwise. */ -export const isPrivateRangeUrlFound = (item) => { +export function isPrivateRangeUrlFound(item) { const regexPatterns = [ /xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/, /xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/, @@ -244,187 +312,201 @@ export const isPrivateRangeUrlFound = (item) => { ]; return regexPatterns.some((pattern) => pattern.test(item)); -}; +} /** - * Creates a deep copy of the given object or array. + * Utility to measure elapsed time using the Node.js `process.hrtime()` method. * - * @param {Object|Array} obj - The object or array to be deeply copied. + * @function measureTime * - * @returns {Object|Array} - The deep copy of the provided object or array. + * @returns {Function} A function to calculate the elapsed time in milliseconds. */ -export const deepCopy = (obj) => { - if (obj === null || typeof obj !== 'object') { - return obj; - } - - const copy = Array.isArray(obj) ? [] : {}; +export function measureTime() { + const start = process.hrtime.bigint(); + return () => Number(process.hrtime.bigint() - start) / 1000000; +} - for (const key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - copy[key] = deepCopy(obj[key]); - } +/** + * Merges two sets of configuration options, considering absolute properties. + * + * @function mergeConfigOptions + * + * @param {Object} options - Original configuration options. + * @param {Object} newOptions - New configuration options to be merged. + * @param {Array.} [absoluteProps=[]] - List of properties that should + * not be recursively merged. + * + * @returns {Object} Merged configuration options. + */ +export function mergeConfigOptions(options, newOptions, absoluteProps = []) { + const mergedOptions = deepCopy(options); + for (const [key, value] of Object.entries(newOptions)) { + mergedOptions[key] = + isObject(value) && + !absoluteProps.includes(key) && + mergedOptions[key] !== undefined + ? mergeConfigOptions(mergedOptions[key], value, absoluteProps) + : value !== undefined + ? value + : mergedOptions[key]; } - - return copy; -}; + return mergedOptions; +} /** - * Converts the provided options object to a JSON-formatted string with the - * option to preserve functions. + * Converts the provided options object to a JSON-formatted string + * with the option to preserve functions. In order for a function + * to be preserved, it needs to follow the format `function (...) {...}`. + * It can also be stringified. + * + * @function optionsStringify * * @param {Object} options - The options object to be converted to a string. * @param {boolean} allowFunctions - If set to true, functions are preserved * in the output. + * @param {boolean} stringifyFunctions - If set to true, functions are saved + * as strings. * - * @returns {string} - The JSON-formatted string representing the options. + * @returns {string} The JSON-formatted string representing the options. */ -export const optionsStringify = (options, allowFunctions) => { - const replacerCallback = (name, value) => { +export function optionsStringify(options, allowFunctions, stringifyFunctions) { + const replacerCallback = (_, value) => { + // Trim string values if (typeof value === 'string') { value = value.trim(); + } + // If value is a function or stringified function + if ( + typeof value === 'function' || + (typeof value === 'string' && + value.startsWith('function') && + value.endsWith('}')) + ) { // If allowFunctions is set to true, preserve functions - if ( - (value.startsWith('function(') || value.startsWith('function (')) && - value.endsWith('}') - ) { - value = allowFunctions - ? `EXP_FUN${(value + '').replaceAll(/\n|\t|\r/g, ' ')}EXP_FUN` - : undefined; + if (allowFunctions) { + // Based on the stringifyFunctions options, set function values + return stringifyFunctions + ? // As stringified functions + `"EXP_FUN${(value + '').replaceAll(/\n|\t|\r|\s+/g, ' ')}EXP_FUN"` + : // As functions + `EXP_FUN${(value + '').replaceAll(/\n|\t|\r|\s+/g, ' ')}EXP_FUN`; + } else { + // Get rid of the function values otherwise + return undefined; } } - return typeof value === 'function' - ? `EXP_FUN${(value + '').replaceAll(/\n|\t|\r/g, ' ')}EXP_FUN` - : value; + // In all other cases, simply return the value + return value; }; // Stringify options and if required, replace special functions marks return JSON.stringify(options, replacerCallback).replaceAll( - /"EXP_FUN|EXP_FUN"/g, + stringifyFunctions ? /\\"EXP_FUN|EXP_FUN\\"/g : /"EXP_FUN|EXP_FUN"/g, '' ); -}; +} + +/** + * Prints the Highcharts Export Server logo, version, and license information. + * + * @function printLicense + */ +export function printLicense() { + // Print the logo and version information + printVersion(); + + // Print the license information + console.log( + 'This software requires a valid Highcharts license for commercial use.\n' + .yellow, + '\nFor a full list of CLI options, type:', + '\nhighcharts-export-server --help\n'.green, + '\nIf you do not have a license, one can be obtained here:', + '\nhttps://shop.highsoft.com/\n'.green, + '\nTo customize your installation, please refer to the README file at:', + '\nhttps://github.com/highcharts/node-export-server#readme\n'.green + ); +} /** - * Prints the Highcharts Export Server logo and version information. + * Prints the Highcharts Export Server logo or text with the version + * information. + * + * @function printVersion * - * @param {boolean} noLogo - If true, only prints version information without - * the logo. + * @param {boolean} noLogo - If true, only prints text with the version + * information, without the logo. */ -export const printLogo = (noLogo) => { - // Get package version either from env or from package.json +export function printVersion(noLogo) { + // Get package version either from `.env` or from `package.json` const packageVersion = JSON.parse( readFileSync(join(__dirname, 'package.json')) ).version; // Print text only if (noLogo) { - console.log(`Starting Highcharts Export Server v${packageVersion}...`); + console.log(`Highcharts Export Server v${packageVersion}`); return; } // Print the logo console.log( - readFileSync(__dirname + '/msg/startup.msg').toString().bold.yellow, + readFileSync(join(__dirname, 'msg', 'startup.msg')).toString().bold.yellow, `v${packageVersion}\n`.bold ); -}; - -/** - * Prints the usage information for CLI arguments. If required, it can list - * properties recursively - */ -export function printUsage() { - const pad = 48; - const readme = 'https://github.com/highcharts/node-export-server#readme'; - - // Display readme information - console.log( - '\nUsage of CLI arguments:'.bold, - '\n------', - `\nFor more detailed information, visit the readme at: ${readme.bold.yellow}.` - ); - - const cycleCategories = (options) => { - for (const [name, option] of Object.entries(options)) { - // If category has more levels, go further - if (!Object.prototype.hasOwnProperty.call(option, 'value')) { - cycleCategories(option); - } else { - let descName = ` --${option.cliName || name} ${ - ('<' + option.type + '>').green - } `; - if (descName.length < pad) { - for (let i = descName.length; i < pad; i++) { - descName += '.'; - } - } - - // Display correctly aligned messages - console.log( - descName, - option.description, - `[Default: ${option.value.toString().bold}]`.blue - ); - } - } - }; - - // Cycle through options of each categories and display the usage info - Object.keys(defaultConfig).forEach((category) => { - // Only puppeteer and highcharts categories cannot be configured through CLI - if (!['puppeteer', 'highcharts'].includes(category)) { - console.log(`\n${category.toUpperCase()}`.red); - cycleCategories(defaultConfig[category]); - } - }); - console.log('\n'); } /** * Rounds a number to the specified precision. * + * @function roundNumber + * * @param {number} value - The number to be rounded. * @param {number} precision - The number of decimal places to round to. * - * @returns {number} - The rounded number. + * @returns {number} The rounded number. */ -export const roundNumber = (value, precision = 1) => { +export function roundNumber(value, precision = 1) { const multiplier = Math.pow(10, precision || 0); return Math.round(+value * multiplier) / multiplier; -}; +} /** * Converts a value to a boolean. * - * @param {any} item - The value to be converted to a boolean. + * @function toBoolean + * + * @param {unknown} item - The value to be converted to a boolean. * - * @returns {boolean} - The boolean representation of the input value. + * @returns {boolean} The boolean representation of the input value. */ -export const toBoolean = (item) => - ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item) +export function toBoolean(item) { + return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item) ? false : !!item; +} /** * Wraps custom code to execute it safely. * + * @function wrapAround + * * @param {string} customCode - The custom code to be wrapped. * @param {boolean} allowFileResources - Flag to allow loading code from a file. * - * @returns {string|boolean} - The wrapped custom code or false if wrapping + * @returns {(string|boolean)} The wrapped custom code or false if wrapping * fails. */ -export const wrapAround = (customCode, allowFileResources) => { +export function wrapAround(customCode, allowFileResources) { if (customCode && typeof customCode === 'string') { customCode = customCode.trim(); if (customCode.endsWith('.js')) { return allowFileResources ? wrapAround(readFileSync(customCode, 'utf8')) - : false; + : null; } else if ( customCode.startsWith('function()') || customCode.startsWith('function ()') || @@ -435,34 +517,27 @@ export const wrapAround = (customCode, allowFileResources) => { } return customCode.replace(/;$/, ''); } -}; - -/** - * Utility to measure elapsed time using the Node.js process.hrtime() method. - * - * @returns {function(): number} - A function to calculate the elapsed time - * in milliseconds. - */ -export const measureTime = () => { - const start = process.hrtime.bigint(); - return () => Number(process.hrtime.bigint() - start) / 1000000; -}; +} export default { __dirname, clearText, + deepCopy, expBackoff, + fixConstr, fixType, - handleResources, + getNewDate, + getNewDateTime, isCorrectJSON, isObject, isObjectEmpty, isPrivateRangeUrlFound, + measureTime, + mergeConfigOptions, optionsStringify, - printLogo, - printUsage, + printLicense, + printVersion, roundNumber, toBoolean, - wrapAround, - measureTime + wrapAround }; From 557f65de585316173935003e49146fe9ae3e5e52 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:28:34 +0100 Subject: [PATCH 020/102] Corrected the HTTP error handler and moved it to the middlewares folder. --- lib/server/error.js | 48 -------------------- lib/server/middlewares/error.js | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 48 deletions(-) delete mode 100644 lib/server/error.js create mode 100644 lib/server/middlewares/error.js diff --git a/lib/server/error.js b/lib/server/error.js deleted file mode 100644 index eaeef9f9..00000000 --- a/lib/server/error.js +++ /dev/null @@ -1,48 +0,0 @@ -import { envs } from '../envs.js'; -import { logWithStack } from '../logger.js'; - -/** - * Middleware for logging errors with stack trace and handling error response. - * - * @param {Error} error - The error object. - * @param {Express.Request} req - The Express request object. - * @param {Express.Response} res - The Express response object. - * @param {Function} next - The next middleware function. - */ -const logErrorMiddleware = (error, req, res, next) => { - // Display the error with stack in a correct format - logWithStack(1, error); - - // Delete the stack for the environment other than the development - if (envs.OTHER_NODE_ENV !== 'development') { - delete error.stack; - } - - // Call the returnErrorMiddleware - next(error); -}; - -/** - * Middleware for returning error response. - * - * @param {Error} error - The error object. - * @param {Express.Request} req - The Express request object. - * @param {Express.Response} res - The Express response object. - * @param {Function} next - The next middleware function. - */ -const returnErrorMiddleware = (error, req, res, next) => { - // Gather all requied information for the response - const { statusCode: stCode, status, message, stack } = error; - const statusCode = stCode || status || 400; - - // Set and return response - res.status(statusCode).json({ statusCode, message, stack }); -}; - -export default (app) => { - // Add log error middleware - app.use(logErrorMiddleware); - - // Add set status and return error middleware - app.use(returnErrorMiddleware); -}; diff --git a/lib/server/middlewares/error.js b/lib/server/middlewares/error.js new file mode 100644 index 00000000..b0f23665 --- /dev/null +++ b/lib/server/middlewares/error.js @@ -0,0 +1,78 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +/** + * @overview Provides middlewares for logging errors with stack traces + * and handling error responses in an Express application. + */ + +import { getOptions } from '../../config.js'; +import { logWithStack } from '../../logger.js'; + +/** + * Middleware for logging errors with stack trace and handling error response. + * + * @function logErrorMiddleware + * + * @param {Error} error - The error object. + * @param {Express.Request} _request - The Express request object. + * @param {Express.Response} _response - The Express response object. + * @param {Function} next - The next middleware function. + */ +function logErrorMiddleware(error, _request, _response, next) { + // Display the error with stack in a correct format + logWithStack(1, error); + + // Delete the stack for the environment other than the development + if (getOptions().other.nodeEnv !== 'development') { + delete error.stack; + } + + // Call the `returnErrorMiddleware` + return next(error); +} + +/** + * Middleware for returning error response. + * + * @function returnErrorMiddleware + * + * @param {Error} error - The error object. + * @param {Express.Request} _request - The Express request object. + * @param {Express.Response} response - The Express response object. + * @param {Function} _next - The next middleware function. + */ +function returnErrorMiddleware(error, _request, response, _next) { + // Gather all requied information for the response + const { message, stack } = error; + + // Use the error's status code or the default 400 + const statusCode = error.statusCode || 400; + + // Set and return response + response.status(statusCode).json({ statusCode, message, stack }); +} + +/** + * Adds the two error middlewares to the passed express app instance. + * + * @param {Express} app - The Express app instance. + */ +export default function errorMiddleware(app) { + // Add log error middleware + app.use(logErrorMiddleware); + + // Add set status and return error middleware + app.use(returnErrorMiddleware); +} From c767c04da82f754be59fc27306fab321098cfd91 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:28:47 +0100 Subject: [PATCH 021/102] Separated and moved some of the export handler logic into a new validation middleware. --- lib/server/middlewares/validation.js | 186 +++++++++++++++++++ lib/server/routes/export.js | 268 +++++++-------------------- 2 files changed, 253 insertions(+), 201 deletions(-) create mode 100644 lib/server/middlewares/validation.js diff --git a/lib/server/middlewares/validation.js b/lib/server/middlewares/validation.js new file mode 100644 index 00000000..04dce82e --- /dev/null +++ b/lib/server/middlewares/validation.js @@ -0,0 +1,186 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +/** + * @overview Defines middleware functions for validating incoming HTTP requests + * in an Express application. This module ensures that requests contain + * appropriate content types and valid request bodies, including proper JSON + * structures and chart data for exports. It checks for potential issues such + * as missing or malformed data, private range URLs in SVG payloads, and allows + * for flexible options validation. The middleware logs detailed information + * and handles errors related to incorrect payloads, chart data, and private URL + * usage. + */ + +import { v4 as uuid } from 'uuid'; + +import { getAllowCodeExecution } from '../../chart.js'; +import { log } from '../../logger.js'; +import { + fixConstr, + fixType, + isCorrectJSON, + isObjectEmpty, + isPrivateRangeUrlFound +} from '../../utils.js'; + +import HttpError from '../../errors/HttpError.js'; +import NoCorrectBodyError from '../../errors/NoCorrectBodyError.js'; +import NoCorrectChartDataError from '../../errors/NoCorrectChartDataError.js'; +import PrivateRangeUrlError from '../../errors/PrivateRangeUrlError.js'; + +/** + * Middleware for validating the content-type header. + * + * @function contentTypeMiddleware + * + * @param {Express.Request} request - The Express request object. + * @param {Express.Response} _response - The Express response object. + * @param {Function} next - The next middleware function. + * + * @throws {HttpError} Throws an `HttpError` if the content-type + * is not correct. + */ +function contentTypeMiddleware(request, _response, next) { + // Get the content type header + const contentType = request.headers['content-type'] || ''; + + // Allow only JSON, URL-encoded and form data without files types of data + if ( + !contentType.includes('application/json') && + !contentType.includes('application/x-www-form-urlencoded') && + !contentType.includes('multipart/form-data') + ) { + throw new HttpError( + 'Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.', + 415 + ); + } + return next(); +} + +/** + * Middleware for validating the request body. + * + * @function requestBodyMiddleware + * + * @param {Express.Request} request - The Express request object. + * @param {Express.Response} _response - The Express response object. + * @param {Function} next - The next middleware function. + * + * @throws {NoCorrectBodyError} Throws an `NoCorrectBodyError` if the body + * is not correct. + * @throws {NoCorrectChartDataError} Throws an `NoCorrectChartDataError` + * if the chart data from the body is not correct. + * @throws {PrivateRangeUrlError} Throws an `PrivateRangeUrlError` in case + * of the private range url error. + * @throws {ValidationError} Throws an `ValidationError` in case of the body + * validation error. + */ +function requestBodyMiddleware(request, _response, next) { + // Get the request body + const body = request.body; + + // Create a unique ID for a request + const requestId = uuid().replace(/-/g, ''); + + // Throw 'NoCorrectBodyError' if there is no correct body + if (!body || isObjectEmpty(body)) { + log( + 2, + `The request with ID ${requestId} from ${ + request.headers['x-forwarded-for'] || request.connection.remoteAddress + } was incorrect. Received payload is empty.` + ); + throw new NoCorrectBodyError(); + } + + // Get the allowCodeExecution option for the server + const allowCodeExecution = getAllowCodeExecution(); + + // Find a correct chart options + const instr = isCorrectJSON( + // Use one of the below + body.infile || body.options || body.data, + // Stringify options + true, + // Allow or disallow functions + allowCodeExecution + ); + + // Throw 'NoCorrectChartDataError' if there is no correct chart data + if (instr === null && !body.svg) { + log( + 2, + `The request with ID ${requestId} from ${ + request.headers['x-forwarded-for'] || request.connection.remoteAddress + } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.` + ); + throw new NoCorrectChartDataError(); + } + + // Test xlink:href elements from payload's SVG + if (body.svg && isPrivateRangeUrlFound(body.svg)) { + throw PrivateRangeUrlError(); + } + + // Get options from the body and store parsed structure in the request + request.validatedOptions = { + export: { + instr, + svg: body.svg, + outfile: + body.outfile || + `${request.params.filename || 'chart'}.${fixType(body.type)}`, + type: fixType(body.type), + constr: fixConstr(body.constr), + b64: body.b64, + noDownload: body.noDownload, + height: body.height, + width: body.width, + scale: body.scale, + globalOptions: isCorrectJSON( + body.globalOptions, + true, + allowCodeExecution + ), + themeOptions: isCorrectJSON(body.themeOptions, true, allowCodeExecution) + }, + customLogic: { + allowCodeExecution, + allowFileResources: false, + customCode: body.customCode, + callback: body.callback, + resources: isCorrectJSON(body.resources, true, allowCodeExecution) + }, + payload: { + requestId + } + }; + + return next(); +} + +/** + * Adds the two validation middlewares to the passed express app instance. + * + * @param {Express} app - The Express app instance. + */ +export default function validationMiddleware(app) { + // Add content type validation middleware + app.post(['/', '/:filename'], contentTypeMiddleware); + + // Add request body request validation middleware + app.post(['/', '/:filename'], requestBodyMiddleware); +} diff --git a/lib/server/routes/export.js b/lib/server/routes/export.js index 4a3c656c..c9acd72a 100644 --- a/lib/server/routes/export.js +++ b/lib/server/routes/export.js @@ -12,21 +12,21 @@ See LICENSE file in root for details. *******************************************************************************/ -import { v4 as uuid } from 'uuid'; +/** + * @overview Defines the export routes and logic for handling chart export + * requests in an Express server. This module processes incoming requests + * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates + * with Highcharts' core functionalities and supports both immediate download + * responses and base64-encoded content returns. The code also features + * benchmarking for performance monitoring. + */ -import { getAllowCodeExecution, startExport } from '../../chart.js'; -import { getOptions, mergeConfigOptions } from '../../config.js'; +import { startExport } from '../../chart.js'; +import { getOptions } from '../../config.js'; import { log } from '../../logger.js'; -import { - fixType, - isCorrectJSON, - isObjectEmpty, - isPrivateRangeUrlFound, - optionsStringify, - measureTime -} from '../../utils.js'; +import { measureTime, mergeConfigOptions } from '../../utils.js'; -import HttpError from '../../errors/HttpError.js'; +import NoCorrectResultError from '../../errors/NoCorrectResultError.js'; // Reversed MIME types const reversedMime = { @@ -37,192 +37,52 @@ const reversedMime = { svg: 'image/svg+xml' }; -// The requests counter -let requestsCounter = 0; - -// The array of callbacks to call before a request -const beforeRequest = []; - -// The array of callbacks to call after a request -const afterRequest = []; - -/** - * Invokes an array of callback functions with specified parameters, allowing - * customization of request handling. - * - * @param {Function[]} callbacks - An array of callback functions - * to be executed. - * @param {Express.Request} request - The Express request object. - * @param {Express.Response} response - The Express response object. - * @param {Object} data - An object containing parameters like id, uniqueId, - * type, and body. - * - * @returns {boolean} - Returns a boolean indicating the overall result - * of the callback invocations. - */ -const doCallbacks = (callbacks, request, response, data) => { - let result = true; - const { id, uniqueId, type, body } = data; - - callbacks.some((callback) => { - if (callback) { - let callResponse = callback(request, response, id, uniqueId, type, body); - - if (callResponse !== undefined && callResponse !== true) { - result = callResponse; - } - - return true; - } - }); - - return result; -}; - /** * Handles the export requests from the client. * + * @async + * @function requestExport + * * @param {Express.Request} request - The Express request object. * @param {Express.Response} response - The Express response object. * @param {Function} next - The next middleware function. * - * @returns {Promise} - A promise that resolves once the export process + * @returns {Promise} A promise that resolves once the export process * is complete. */ -const exportHandler = async (request, response, next) => { +async function requestExport(request, response, next) { try { - // Start counting time - const stopCounter = measureTime(); - - // Create a unique ID for a request - const uniqueId = uuid().replace(/-/g, ''); - - // Get the current server's general options - const defaultOptions = getOptions(); - - const body = request.body; - const id = ++requestsCounter; - - let type = fixType(body.type); - - // Throw 'Bad Request' if there's no body - if (!body || isObjectEmpty(body)) { - throw new HttpError( - 'The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).', - 400 - ); - } - - // All of the below can be used - let instr = isCorrectJSON(body.infile || body.options || body.data); - - // Throw 'Bad Request' if there's no JSON or SVG to export - if (!instr && !body.svg) { - log( - 2, - `The request with ID ${uniqueId} from ${ - request.headers['x-forwarded-for'] || request.connection.remoteAddress - } was incorrect: - Content-Type: ${request.headers['content-type']}. - Chart constructor: ${body.constr}. - Dimensions: ${body.width}x${body.height} @ ${body.scale} scale. - Type: ${type}. - Is SVG set? ${typeof body.svg !== 'undefined'}. - B64? ${typeof body.b64 !== 'undefined'}. - No download? ${typeof body.noDownload !== 'undefined'}. - - Payload received: ${JSON.stringify(body.infile || body.options || body.data || body.svg)} - - ` - ); - - throw new HttpError( - "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 callResponse = false; - - // Call the before request functions - callResponse = doCallbacks(beforeRequest, request, response, { - id, - uniqueId, - type, - body - }); - - // Block the request if one of a callbacks failed - if (callResponse !== true) { - return response.send(callResponse); - } - - let connectionAborted = false; + // Start counting time for a request + const requestCounter = measureTime(); // In case the connection is closed, force to abort further actions + let connectionAborted = false; request.socket.on('close', (hadErrors) => { if (hadErrors) { connectionAborted = true; } }); - log(4, `[export] Got an incoming HTTP request with ID ${uniqueId}.`); + // Get the options previously validated in the middleware + const requestOptions = request.validatedOptions; - body.constr = (typeof body.constr === 'string' && body.constr) || 'chart'; + // Get the request id + const requestId = requestOptions.payload.requestId; - // Gather and organize options from the payload - const requestOptions = { - export: { - instr, - type, - constr: body.constr[0].toLowerCase() + body.constr.substr(1), - height: body.height, - width: body.width, - scale: body.scale || defaultOptions.export.scale, - globalOptions: isCorrectJSON(body.globalOptions, true), - themeOptions: isCorrectJSON(body.themeOptions, true) - }, - customLogic: { - allowCodeExecution: getAllowCodeExecution(), - allowFileResources: false, - resources: isCorrectJSON(body.resources, true), - callback: body.callback, - customCode: body.customCode - } - }; + // Log info about an incoming request with correct data + log(4, `[export] Got an incoming HTTP request with ID ${requestId}.`); - if (instr) { - // Stringify JSON with options - requestOptions.export.instr = optionsStringify( - instr, - requestOptions.customLogic.allowCodeExecution - ); - } + // Get the current server's global options + const defaultOptions = getOptions(); // Merge the request options into default ones const options = mergeConfigOptions(defaultOptions, requestOptions); - // Save the JSON if exists - options.export.options = instr; - - // Lastly, add the server specific arguments into options as payload - options.payload = { - svg: body.svg || false, - b64: body.b64 || false, - noDownload: body.noDownload || false, - requestId: uniqueId - }; - - // Test xlink:href elements from payload's SVG - if (body.svg && isPrivateRangeUrlFound(options.payload.svg)) { - throw new HttpError( - '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 - ); - } + // Save the instr in the options + options.export.options = requestOptions.export.instr; // Start the export process - await startExport(options, (error, info) => { + await startExport(options, (error, data) => { // Remove the close event from the socket request.socket.removeAllListeners('close'); @@ -230,7 +90,7 @@ const exportHandler = async (request, response, next) => { if (defaultOptions.server.benchmarking) { log( 5, - `[benchmark] Request with ID ${uniqueId} - After the whole exporting process: ${stopCounter()}ms.` + `[benchmark] Request: ${requestId} - After the whole exporting process: ${requestCounter()}ms.` ); } @@ -248,64 +108,70 @@ const exportHandler = async (request, response, next) => { } // If data is missing, log the message and send it to the error middleware - if (!info || !info.result) { - throw new HttpError( - `Unexpected return from chart generation. Please check your request data. For the request with ID ${uniqueId}, the result is ${info.result}.`, - 400 + if (!data || !data.result) { + log( + 2, + `The request with ID ${requestId} from ${ + request.headers['x-forwarded-for'] || + request.connection.remoteAddress + } was incorrect. Received result is ${data.result}.` ); + throw NoCorrectResultError(); } - // Get the type from options - type = info.options.export.type; - - // The after request callbacks - doCallbacks(afterRequest, request, response, { id, body: info.result }); + // Return the result in an appropriate format + if (data.result) { + // Get the type from options + const type = data.options.export.type; - if (info.result) { // If only base64 is required, return it - if (body.b64) { + if (options.export.b64) { // SVG Exception for the Highcharts 11.3.0 version if (type === 'pdf' || type == 'svg') { return response.send( - Buffer.from(info.result, 'utf8').toString('base64') + Buffer.from(data.result, 'utf8').toString('base64') ); } - return response.send(info.result); + // If b64, return base64 content + return response.send(data.result); } // Set correct content type response.header('Content-Type', reversedMime[type] || 'image/png'); // Decide whether to download or not chart file - if (!body.noDownload) { - response.attachment( - `${request.params.filename || request.body.filename || 'chart'}.${ - type || 'png' - }` - ); + if (!options.export.noDownload) { + response.attachment(options.export.outfile); } // If SVG, return plain content return type === 'svg' - ? response.send(info.result) - : response.send(Buffer.from(info.result, 'base64')); + ? response.send(data.result) + : response.send(Buffer.from(data.result, 'base64')); } }); } catch (error) { - next(error); + return next(error); } -}; +} -export default (app) => { +/** + * Adds the POST / and /:filename routes for the chart exporting. + * + * @function exportRoute + * + * @param {Express} app - The Express app instance. + */ +export default function exportRoute(app) { /** - * Adds the POST / a route for handling POST requests at the root endpoint. + * Adds the POST / - a route for handling POST requests at the root endpoint. */ - app.post('/', exportHandler); + app.post('/', requestExport); /** - * Adds the POST /:filename a route for handling POST requests with + * Adds the POST /:filename - a route for handling POST requests with * a specified filename parameter. */ - app.post('/:filename', exportHandler); -}; + app.post('/:filename', requestExport); +} From 4260c1a48a61308ce75885c8e3f730e55ac99307 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:29:02 +0100 Subject: [PATCH 022/102] Optimization of the health route. --- lib/server/routes/health.js | 76 ++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/lib/server/routes/health.js b/lib/server/routes/health.js index 5925cc68..8918a7e0 100644 --- a/lib/server/routes/health.js +++ b/lib/server/routes/health.js @@ -12,17 +12,21 @@ See LICENSE file in root for details. *******************************************************************************/ -import { readFileSync } from 'fs'; -import { join as pather } from 'path'; -import { log } from '../../logger.js'; +/** + * @overview Defines an Express route for server health monitoring, including + * uptime, success rates, and other server statistics. + */ -import { version } from '../../cache.js'; -import { addInterval } from '../../intervals.js'; -import pool from '../../pool.js'; -import { __dirname } from '../../utils.js'; +import { readFileSync } from 'fs'; +import { join } from 'path'; -const pkgFile = JSON.parse(readFileSync(pather(__dirname, 'package.json'))); +import { getHighchartsVersion } from '../../cache.js'; +import { log } from '../../logger.js'; +import { getPoolStats, getPoolInfoJSON } from '../../pool.js'; +import { addTimer } from '../../timer.js'; +import { __dirname, getNewDateTime } from '../../utils.js'; +const packageFile = JSON.parse(readFileSync(join(__dirname, 'package.json'))); const serverStartTime = new Date(); const successRates = []; @@ -33,22 +37,25 @@ const windowSize = 30; // 30 minutes * Calculates moving average indicator based on the data from the successRates * array. * - * @returns {number} - A moving average for success ratio of the server exports. + * @function _calculateMovingAverage + * + * @returns {number} A moving average for success ratio of the server exports. */ -function calculateMovingAverage() { - const sum = successRates.reduce((a, b) => a + b, 0); - return sum / successRates.length; +function _calculateMovingAverage() { + return successRates.reduce((a, b) => a + b, 0) / successRates.length; } /** * Starts the interval responsible for calculating current success rate ratio * and gathers * - * @returns {NodeJS.Timeout} id - Id of an interval. + * @function _startSuccessRate + * + * @returns {NodeJS.Timeout} Id of an interval. */ -export const startSuccessRate = () => - setInterval(() => { - const stats = pool.getStats(); +function _startSuccessRate() { + return setInterval(() => { + const stats = getPoolStats(); const successRatio = stats.exportAttempts === 0 ? 1 @@ -59,43 +66,44 @@ export const startSuccessRate = () => successRates.shift(); } }, recordInterval); +} /** - * Adds the /health and /success-moving-average routes - * which output basic stats for the server. + * Adds the GET /health route which output basic stats for the server. + * + * @function healthRoute + * + * @param {Express} app - The Express app instance. */ -export default function addHealthRoutes(app) { +export default function healthRoute(app) { if (!app) { return false; } // Start processing success rate ratio interval and save its id to the array - // for the graceful clearing on shutdown with injected addInterval funtion - addInterval(startSuccessRate()); + // for the graceful clearing on shutdown with injected `addTimer` funtion + addTimer(_startSuccessRate()); - app.get('/health', (_, res) => { - const stats = pool.getStats(); + app.get('/health', (_request, response) => { + const stats = getPoolStats(); const period = successRates.length; - const movingAverage = calculateMovingAverage(); - - log(4, '[health.js] GET /health [200] - returning server health.'); + const movingAverage = _calculateMovingAverage(); - res.send({ + log(4, '[health] Returning server health.'); + response.send({ status: 'OK', bootTime: serverStartTime, uptime: - Math.floor( - (new Date().getTime() - serverStartTime.getTime()) / 1000 / 60 - ) + ' minutes', - version: pkgFile.version, - highchartsVersion: version(), + Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60) + + ' minutes', + version: packageFile.version, + highchartsVersion: getHighchartsVersion(), averageProcessingTime: stats.spentAverage, performedExports: stats.performedExports, failedExports: stats.droppedExports, exportAttempts: stats.exportAttempts, sucessRatio: (stats.performedExports / stats.exportAttempts) * 100, - // eslint-disable-next-line import/no-named-as-default-member - pool: pool.getPoolInfoJSON(), + pool: getPoolInfoJSON(), // Moving average period, From 62e537dfb9212e5c726d68410cd279454164587f Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:29:12 +0100 Subject: [PATCH 023/102] Corrected missing route setting functionality with the optimization of the ui route. --- lib/server/routes/ui.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/server/routes/ui.js b/lib/server/routes/ui.js index 5255a9b9..efc120a0 100644 --- a/lib/server/routes/ui.js +++ b/lib/server/routes/ui.js @@ -12,18 +12,29 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * @overview Defines an Express route for serving the UI for the export server + * when enabled. + */ + import { join } from 'path'; +import { getOptions } from '../../config.js'; import { __dirname } from '../../utils.js'; /** * Adds the GET / route for a UI when enabled on the export server. + * + * @function uiRoute + * + * @param {Express} app - The Express app instance. */ -export default (app) => - !app +export default function uiRoute(app) { + return !app ? false - : app.get('/', (_request, response) => { + : app.get(getOptions().ui.route || '/', (_request, response) => { response.sendFile(join(__dirname, 'public', 'index.html'), { acceptRanges: false }); }); +} From f99e077205a96d4489e4907635d78ff048d88eaf Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:29:20 +0100 Subject: [PATCH 024/102] Corrected the route to the /version_change/ with the optimization of the versionChange route. --- lib/server/routes/versionChange.js | 37 +++++++++++++++++++----------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/lib/server/routes/versionChange.js b/lib/server/routes/versionChange.js index df72049e..a8d9a411 100644 --- a/lib/server/routes/versionChange.js +++ b/lib/server/routes/versionChange.js @@ -12,24 +12,32 @@ See LICENSE file in root for details. *******************************************************************************/ -import { updateVersion, version } from '../../cache.js'; +/** + * @overview Defines an Express route for updating the Highcharts version + * on the server, with authentication and validation. + */ + +import { updateHighchartsVersion, getHighchartsVersion } from '../../cache.js'; import { envs } from '../../envs.js'; import HttpError from '../../errors/HttpError.js'; /** - * Adds the POST /change_hc_version/:newVersion route that can be utilized to modify - * the Highcharts version on the server. + * Adds the POST /version_change/:newVersion route that can be utilized + * to modify the Highcharts version on the server. + * + * @function versionChangeRoute * - * TODO: Add auth token and connect to API + * @param {Express} app - The Express app instance. */ -export default (app) => - !app +export default function versionChangeRoute(app) { + return !app ? false : app.post( - '/version/change/:newVersion', + '/version_change/:newVersion', async (request, response, next) => { try { + // Get the token directly from envs const adminToken = envs.HIGHCHARTS_ADMIN_TOKEN; // Check the existence of the token @@ -40,8 +48,10 @@ export default (app) => ); } - // Check if the hc-auth header contain a correct token + // Get the token from the hc-auth header const token = request.get('hc-auth'); + + // Check if the hc-auth header contain a correct token if (!token || token !== adminToken) { throw new HttpError( 'Invalid or missing token: Set the token in the hc-auth header.', @@ -53,19 +63,19 @@ export default (app) => const newVersion = request.params.newVersion; if (newVersion) { try { - // eslint-disable-next-line import/no-named-as-default-member - await updateVersion(newVersion); + // Update version + await updateHighchartsVersion(newVersion); } catch (error) { throw new HttpError( `Version change: ${error.message}`, - error.statusCode + 400 ).setError(error); } // Success response.status(200).send({ statusCode: 200, - version: version(), + highchartsVersion: getHighchartsVersion(), message: `Successfully updated Highcharts to version: ${newVersion}.` }); } else { @@ -73,7 +83,8 @@ export default (app) => throw new HttpError('No new version supplied.', 400); } } catch (error) { - next(error); + return next(error); } } ); +} From e35f62e51e5d905d60c923bf5d1420195112ae62 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:29:29 +0100 Subject: [PATCH 025/102] Optimization of the rateLimiting module. --- lib/server/rateLimiting.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/server/rateLimiting.js b/lib/server/rateLimiting.js index d4686203..4d8a9833 100644 --- a/lib/server/rateLimiting.js +++ b/lib/server/rateLimiting.js @@ -20,20 +20,22 @@ import { log } from '../logger.js'; * Middleware for enabling rate limiting on the specified Express app. * * @param {Express} app - The Express app instance. - * @param {Object} limitConfig - Configuration options for rate limiting. + * + * @param {Object} rateLimitingOptions - Object containing rate limiting + * options. */ -export default (app, limitConfig) => { +export function rateLimiting(app, rateLimitingOptions) { const msg = 'Too many requests, you have been rate limited. Please try again later.'; // Options for the rate limiter const rateOptions = { - max: limitConfig.maxRequests || 30, - window: limitConfig.window || 1, - delay: limitConfig.delay || 0, - trustProxy: limitConfig.trustProxy || false, - skipKey: limitConfig.skipKey || false, - skipToken: limitConfig.skipToken || false + max: rateLimitingOptions.maxRequests || 30, + window: rateLimitingOptions.window || 1, + delay: rateLimitingOptions.delay || 0, + trustProxy: rateLimitingOptions.trustProxy || false, + skipKey: rateLimitingOptions.skipKey || false, + skipToken: rateLimitingOptions.skipToken || false }; // Set if behind a proxy @@ -48,7 +50,7 @@ export default (app, limitConfig) => { max: rateOptions.max, // Disable delaying, full speed until the max limit is reached delayMs: rateOptions.delay, - handler: (request, response) => { + handler: (_request, response) => { response.format({ json: () => { response.status(429).send({ message: msg }); @@ -80,4 +82,8 @@ export default (app, limitConfig) => { 3, `[rate limiting] Enabled rate limiting with ${rateOptions.max} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.` ); +} + +export default { + rateLimiting }; From 5b6ce797ceb3735771b635bdfded5503afd4fcf9 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:29:39 +0100 Subject: [PATCH 026/102] Corrections and updates of the server module logic. --- lib/server/server.js | 309 ++++++++++++++++++++++++++----------------- 1 file changed, 185 insertions(+), 124 deletions(-) diff --git a/lib/server/server.js b/lib/server/server.js index c4176fec..770ab146 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -12,8 +12,17 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * @overview A module that sets up and manages HTTP and HTTPS servers + * for the Highcharts Export Server. It handles server initialization, + * configuration, error handling, middleware setup, route definition, and rate + * limiting.The module exports functions to start, stop, and manage server + * instances, as well as utility functions for defining routes and attaching + * middleware. + */ + import { promises as fsPromises } from 'fs'; -import { posix } from 'path'; +import { join } from 'path'; import cors from 'cors'; import express from 'express'; @@ -21,15 +30,17 @@ import http from 'http'; import https from 'https'; import multer from 'multer'; -import errorHandler from './error.js'; -import rateLimit from './rateLimiting.js'; +import { getOptions } from '../config.js'; import { log, logWithStack } from '../logger.js'; +import { rateLimiting } from './rateLimiting.js'; import { __dirname } from '../utils.js'; -import vSwitchRoute from './routes/versionChange.js'; -import exportRoutes from './routes/export.js'; +import errorMiddleware from './middlewares/error.js'; +import validationMiddleware from './middlewares/validation.js'; +import exportRoute from './routes/export.js'; import healthRoute from './routes/health.js'; import uiRoute from './routes/ui.js'; +import versionChangeRoute from './routes/versionChange.js'; import ExportError from '../errors/ExportError.js'; @@ -39,126 +50,122 @@ const activeServers = new Map(); // Create express app const app = express(); -// Disable the X-Powered-By header -app.disable('x-powered-by'); - -// Enable CORS support -app.use(cors()); - -// Getting a lot of RangeNotSatisfiableError exception. -// Even though this is a deprecated options, let's try to set it to false. -app.use((_req, res, next) => { - res.set('Accept-Ranges', 'none'); - next(); -}); - -// TODO: Read from config/env -// NOTE: -// Too big limits lead to timeouts in the export process when the -// rasterization timeout is set too low. -const uploadLimitMiB = 3; -const uploadLimitBytes = uploadLimitMiB * 1024 * 1024; - -// Enable parsing of form data (files) with Multer package -const storage = multer.memoryStorage(); -const upload = multer({ - storage, - limits: { - fieldSize: uploadLimitBytes - } -}); - -// Enable body parser -app.use(express.json({ limit: uploadLimitBytes })); -app.use(express.urlencoded({ extended: true, limit: uploadLimitBytes })); - -// Use only non-file multipart form fields -app.use(upload.none()); - /** - * Attach error handlers to the server. + * Starts HTTP or/and HTTPS server based on the provided configuration. + * The `serverOptions` object contains all server related properties (see + * the `server` section in the `lib/schemas/config.js` file for a reference). * - * @param {http.Server} server - The HTTP/HTTPS server instance. - */ -const attachServerErrorHandlers = (server) => { - server.on('clientError', (error, socket) => { - logWithStack( - 1, - error, - `[server] Client error: ${error.message}, destroying socket.` - ); - socket.destroy(); - }); - - server.on('error', (error) => { - logWithStack(1, error, `[server] Server error: ${error.message}`); - }); - - server.on('connection', (socket) => { - socket.on('error', (error) => { - logWithStack(1, error, `[server] Socket error: ${error.message}`); - }); - }); -}; - -/** - * Starts an HTTP server based on the provided configuration. The `serverConfig` - * object contains all server related properties (see the `server` section - * in the `lib/schemas/config.js` file for a reference). + * @async + * @function startServer * - * @param {Object} serverConfig - The server configuration object. + * @param {Object} [serverOptions=getOptions().server] - Object containing + * server options. The default value is the global server options of the export + * server instance. * - * @throws {ExportError} - Throws an error if the server cannot be configured - * and started. + * @throws {ExportError} Throws an `ExportError` if the server cannot + * be configured and started. */ -export const startServer = async (serverConfig) => { +export async function startServer(serverOptions = getOptions().server) { try { // Stop if not enabled - if (!serverConfig.enable) { + if (!serverOptions.enable) { return false; } + // Too big limits lead to timeouts in the export process when + // the rasterization timeout is set too low + const uploadLimitBytes = 3 * 1024 * 1024; + + // Memory storage for multer package + const storage = multer.memoryStorage(); + + // Enable parsing of form data (files) with multer package + const upload = multer({ + storage, + limits: { + fieldSize: uploadLimitBytes + } + }); + + // Disable the X-Powered-By header + app.disable('x-powered-by'); + + // Enable CORS support + app.use( + cors({ + methods: ['POST', 'GET', 'OPTIONS'] + }) + ); + + // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this + // is a deprecated options, let's try to set it to false) + app.use((_request, response, next) => { + response.set('Accept-Ranges', 'none'); + next(); + }); + + // Enable body parser for JSON data + app.use( + express.json({ + limit: uploadLimitBytes + }) + ); + + // Enable body parser for URL-encoded form data + app.use( + express.urlencoded({ + extended: true, + limit: uploadLimitBytes + }) + ); + + // Use only non-file multipart form fields + app.use(upload.none()); + + // Set up static folder's route + app.use(express.static(join(__dirname, 'public'))); + // Listen HTTP server - if (!serverConfig.ssl.force) { + if (!serverOptions.ssl.force) { // Main server instance (HTTP) const httpServer = http.createServer(app); // Attach error handlers and listen to the server - attachServerErrorHandlers(httpServer); + _attachServerErrorHandlers(httpServer); // Listen - httpServer.listen(serverConfig.port, serverConfig.host); - - // Save the reference to HTTP server - activeServers.set(serverConfig.port, httpServer); + httpServer.listen(serverOptions.port, serverOptions.host, () => { + // Save the reference to HTTP server + activeServers.set(serverOptions.port, httpServer); - log( - 3, - `[server] Started HTTP server on ${serverConfig.host}:${serverConfig.port}.` - ); + log( + 3, + `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.` + ); + }); } // Listen HTTPS server - if (serverConfig.ssl.enable) { + if (serverOptions.ssl.enable) { // Set up an SSL server also let key, cert; try { // Get the SSL key key = await fsPromises.readFile( - posix.join(serverConfig.ssl.certPath, 'server.key'), + join(serverOptions.ssl.certPath, 'server.key'), 'utf8' ); // Get the SSL certificate cert = await fsPromises.readFile( - posix.join(serverConfig.ssl.certPath, 'server.crt'), + join(serverOptions.ssl.certPath, 'server.crt'), 'utf8' ); } catch (error) { log( 2, - `[server] Unable to load key/certificate from the '${serverConfig.ssl.certPath}' path. Could not run secured layer server.` + `[server] Unable to load key/certificate from the '${serverOptions.ssl.certPath}' path. Could not run secured layer server.` ); } @@ -167,52 +174,54 @@ export const startServer = async (serverConfig) => { const httpsServer = https.createServer({ key, cert }, app); // Attach error handlers and listen to the server - attachServerErrorHandlers(httpsServer); + _attachServerErrorHandlers(httpsServer); // Listen - httpsServer.listen(serverConfig.ssl.port, serverConfig.host); - - // Save the reference to HTTPS server - activeServers.set(serverConfig.ssl.port, httpsServer); - - log( - 3, - `[server] Started HTTPS server on ${serverConfig.host}:${serverConfig.ssl.port}.` - ); + httpsServer.listen(serverOptions.ssl.port, serverOptions.host, () => { + // Save the reference to HTTPS server + activeServers.set(serverOptions.ssl.port, httpsServer); + + log( + 3, + `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.` + ); + }); } } // Enable the rate limiter if config says so if ( - serverConfig.rateLimiting && - serverConfig.rateLimiting.enable && - ![0, NaN].includes(serverConfig.rateLimiting.maxRequests) + serverOptions.rateLimiting.enable && + ![0, NaN].includes(serverOptions.rateLimiting.maxRequests) ) { - rateLimit(app, serverConfig.rateLimiting); + rateLimiting(app, serverOptions.rateLimiting); } - // Set up static folder's route - app.use(express.static(posix.join(__dirname, 'public'))); + // Set up validation handler + validationMiddleware(app); // Set up routes healthRoute(app); - exportRoutes(app); + exportRoute(app); uiRoute(app); - vSwitchRoute(app); + versionChangeRoute(app); // Set up centralized error handler - errorHandler(app); + errorMiddleware(app); } catch (error) { throw new ExportError( - '[server] Could not configure and start the server.' + '[server] Could not configure and start the server.', + 500 ).setError(error); } -}; +} /** * Closes all servers associated with Express app instance. + * + * @function closeServers */ -export const closeServers = () => { +export function closeServers() { log(4, `[server] Closing all servers.`); for (const [port, server] of activeServers) { server.close(() => { @@ -220,65 +229,117 @@ export const closeServers = () => { log(4, `[server] Closed server on port: ${port}.`); }); } -}; +} /** * Get all servers associated with Express app instance. * - * @returns {Array} - Servers associated with Express app instance. + * @function getServers + * + * @returns {Array.} Servers associated with Express app instance. */ -export const getServers = () => activeServers; +export function getServers() { + return activeServers; +} /** * Enable rate limiting for the server. * + * @function enableRateLimiting + * * @param {Object} limitConfig - Configuration object for rate limiting. + * + * @returns {Object} Middleware for enabling rate limiting. */ -export const enableRateLimiting = (limitConfig) => rateLimit(app, limitConfig); +export function enableRateLimiting(limitConfig) { + return rateLimiting(app, limitConfig); +} /** * Get the Express instance. * - * @returns {Object} - The Express instance. + * @function getExpress + * + * @returns {Express} The Express instance. */ -export const getExpress = () => express; +export function getExpress() { + return express; +} /** * Get the Express app instance. * - * @returns {Object} - The Express app instance. + * @function getApp + * + * @returns {Express} The Express app instance. */ -export const getApp = () => app; +export function getApp() { + return app; +} /** * Apply middleware(s) to a specific path. * + * @function use + * * @param {string} path - The path to which the middleware(s) should be applied. * @param {...Function} middlewares - The middleware functions to be applied. */ -export const use = (path, ...middlewares) => { +export function use(path, ...middlewares) { app.use(path, ...middlewares); -}; +} /** * Set up a route with GET method and apply middleware(s). * + * @function get + * * @param {string} path - The route path. * @param {...Function} middlewares - The middleware functions to be applied. */ -export const get = (path, ...middlewares) => { +export function get(path, ...middlewares) { app.get(path, ...middlewares); -}; +} /** * Set up a route with POST method and apply middleware(s). * + * @function post + * * @param {string} path - The route path. * @param {...Function} middlewares - The middleware functions to be applied. */ -export const post = (path, ...middlewares) => { +export function post(path, ...middlewares) { app.post(path, ...middlewares); -}; +} + +/** + * Attach error handlers to the server. + * + * @function _attachServerErrorHandlers + * + * @param {http.Server} server - The HTTP/HTTPS server instance. + */ +function _attachServerErrorHandlers(server) { + server.on('clientError', (error, socket) => { + logWithStack( + 1, + error, + `[server] Client error: ${error.message}, destroying socket.` + ); + socket.destroy(); + }); + + server.on('error', (error) => { + logWithStack(1, error, `[server] Server error: ${error.message}`); + }); + + server.on('connection', (socket) => { + socket.on('error', (error) => { + logWithStack(1, error, `[server] Socket error: ${error.message}`); + }); + }); +} export default { startServer, From 8982d90b18fab70aeaaa472ec754deb1b2dec8c7 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:29:53 +0100 Subject: [PATCH 027/102] Updated .env.sample and README files. --- .env.sample | 34 +++- README.md | 435 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 309 insertions(+), 160 deletions(-) diff --git a/.env.sample b/.env.sample index 7283f259..29e87bcd 100644 --- a/.env.sample +++ b/.env.sample @@ -1,24 +1,46 @@ +# PUPPETEER CONFIG +PUPPETEER_ARGS = + # HIGHCHARTS CONFIG HIGHCHARTS_VERSION = latest HIGHCHARTS_CDN_URL = https://code.highcharts.com/ +HIGHCHARTS_FORCE_FETCH = false +HIGHCHARTS_CACHE_PATH = .cache +HIGHCHARTS_ADMIN_TOKEN = HIGHCHARTS_CORE_SCRIPTS = HIGHCHARTS_MODULE_SCRIPTS = HIGHCHARTS_INDICATOR_SCRIPTS = -HIGHCHARTS_FORCE_FETCH = false -HIGHCHARTS_CACHE_PATH = -HIGHCHARTS_ADMIN_TOKEN = +HIGHCHARTS_CUSTOM_SCRIPTS = # EXPORT CONFIG +EXPORT_INFILE = +EXPORT_INSTR = +EXPORT_OPTIONS = +EXPORT_SVG = +EXPORT_OUTFILE = EXPORT_TYPE = png EXPORT_CONSTR = chart +EXPORT_B64 = false +EXPORT_NO_DOWNLOAD = false +EXPORT_HEIGHT = +EXPORT_WIDTH = +EXPORT_SCALE = EXPORT_DEFAULT_HEIGHT = 400 EXPORT_DEFAULT_WIDTH = 600 EXPORT_DEFAULT_SCALE = 1 +EXPORT_GLOBAL_OPTIONS = +EXPORT_THEME_OPTIONS = +EXPORT_BATCH = EXPORT_RASTERIZATION_TIMEOUT = 1500 # CUSTOM LOGIC CONFIG CUSTOM_LOGIC_ALLOW_CODE_EXECUTION = false CUSTOM_LOGIC_ALLOW_FILE_RESOURCES = false +CUSTOM_LOGIC_CUSTOM_CODE = +CUSTOM_LOGIC_CALLBACK = +CUSTOM_LOGIC_RESOURCES = +CUSTOM_LOGIC_LOAD_CONFIG = +CUSTOM_LOGIC_CREATE_CONFIG = # SERVER CONFIG SERVER_ENABLE = false @@ -61,12 +83,12 @@ POOL_BENCHMARKING = false # LOGGING CONFIG LOGGING_LEVEL = 4 LOGGING_FILE = highcharts-export-server.log -LOGGING_DEST = log/ +LOGGING_DEST = log LOGGING_TO_CONSOLE = true LOGGING_TO_FILE = true # UI CONFIG -UI_ENABLE = true +UI_ENABLE = false UI_ROUTE = / # OTHER CONFIG @@ -78,7 +100,7 @@ OTHER_BROWSER_SHELL_MODE = true # DEBUG CONFIG DEBUG_ENABLE = false -DEBUG_HEADLESS = true +DEBUG_HEADLESS = false DEBUG_DEVTOOLS = false DEBUG_LISTEN_TO_CONSOLE = false DEBUG_DUMPIO = false diff --git a/README.md b/README.md index 92b1154d..07686bbe 100644 --- a/README.md +++ b/README.md @@ -93,16 +93,20 @@ There are four main ways of loading configurations: The JSON below represents the default configuration stored in the `lib/schemas/config.js` file. If no `.env` file is found (more details on the file and environment variables below), these options will be used. -The format, along with its default values, is as follows (using the recommended ordering of core and module scripts below): +The format, along with its default values, is as follows (using the recommended ordering of core and module scripts below). + +_Available default JSON config:_ ``` { "puppeteer": { - "args": [] + "args": [/* See the `./lib/schemas/config.js` file */] }, "highcharts": { "version": "latest", - "cdnURL": "https://code.highcharts.com/", + "cdnUrl": "https://code.highcharts.com", + "forceFetch": false, + "cachePath": ".cache" "coreScripts": [ "highcharts", "highcharts-more", @@ -182,33 +186,37 @@ The format, along with its default values, is as follows (using the recommended "customScripts": [ "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" - ], - "forceFetch": false, - "cachePath": ".cache" + ] }, "export": { - "infile": false, - "instr": false, - "options": false, - "outfile": false, + "infile": null, + "instr": null, + "options": null, + "svg": null, + "outfile": null, "type": "png", "constr": "chart", - "height": 400, - "width": 600, - "scale": 1, - "globalOptions": false, - "themeOptions": false, - "batch": false, + "b64": false, + "noDownload": false, + "defaultHeight": 400, + "defaultWidth": 600, + "defaultScale": 1, + "height": null, + "width": null, + "scale": null, + "globalOptions": null, + "themeOptions": null, + "batch": null, "rasterizationTimeout": 1500 }, "customLogic": { "allowCodeExecution": false, "allowFileResources": false, - "customCode": false, - "callback": false, - "resources": false, - "loadConfig": false, - "createConfig": false + "customCode": null, + "callback": null, + "resources": null, + "loadConfig": null, + "createConfig": null }, "server": { "enable": false, @@ -216,8 +224,8 @@ The format, along with its default values, is as follows (using the recommended "port": 7801, "benchmarking": false, "proxy": { - "host": "", - "port": 8080, + "host": null, + "port": null, "timeout": 5000 }, "rateLimiting": { @@ -226,14 +234,14 @@ The format, along with its default values, is as follows (using the recommended "window": 1, "delay": 0, "trustProxy": false, - "skipKey": "", - "skipToken": "" + "skipKey": null, + "skipToken": null }, "ssl": { "enable": false, "force": false, "port": 443, - "certPath": "" + "certPath": null } }, "pool": { @@ -251,7 +259,7 @@ The format, along with its default values, is as follows (using the recommended "logging": { "level": 4, "file": "highcharts-export-server.log", - "dest": "log/", + "dest": "log", "toConsole": true, "toFile": true }, @@ -268,7 +276,7 @@ The format, along with its default values, is as follows (using the recommended }, "debug": { "enable": false, - "headless": true, + "headless": false, "devtools": false, "listenToConsole": false, "dumpio": false, @@ -280,36 +288,61 @@ The format, along with its default values, is as follows (using the recommended ## Custom JSON Config -To load an additional JSON configuration file, use the `--loadConfig ` option. This JSON file can either be manually created or generated through a prompt triggered by the `--createConfig` option. +To load an additional JSON configuration file, use the `--loadConfig ` option. This JSON file can either be manually created or generated through a prompt triggered by the `--createConfig ` option. The value of the `` must be set with the _.json_ extension. ## Environment Variables These variables are set in your environment and take precedence over options from the `lib/schemas/config.js` file. They can be set in the `.env` file (refer to the `.env.sample` file). If you prefer setting these variables through the `package.json`, use `export` command on Linux/Mac OS X and `set` command on Windows. +_Available environment variables:_ + +### Puppeteer Config + +- `PUPPETEER_ARGS`: A stringified version of additional Puppeteer arguments sent during browser initialization. The string can be enclosed in _[_ and _]_, and the arguments must be separated by the _;_ character (defaults to ``). + ### Highcharts Config - `HIGHCHARTS_VERSION`: Highcharts version to use (defaults to `latest`). -- `HIGHCHARTS_CDN_URL`: Highcharts CDN URL of scripts to be used (defaults to `https://code.highcharts.com/`). +- `HIGHCHARTS_CDN_URL`: Highcharts CDN URL of scripts to be used (defaults to `https://code.highcharts.com`). +- `HIGHCHARTS_FORCE_FETCH`: The flag that determines whether to refetch all scripts after each server rerun (defaults to `false`). +- `HIGHCHARTS_CACHE_PATH`: A directory path where the fetched Highcharts scripts should be placed (defaults to `.cache`). Since v4.0.3 can be either absolute or relative path. +- `HIGHCHARTS_ADMIN_TOKEN`: An authentication token that is required to switch the Highcharts version on the server at runtime (defaults to ``). - `HIGHCHARTS_CORE_SCRIPTS`: Highcharts core scripts to fetch (defaults to ``). - `HIGHCHARTS_MODULE_SCRIPTS`: Highcharts module scripts to fetch (defaults to ``). - `HIGHCHARTS_INDICATOR_SCRIPTS`: Highcharts indicator scripts to fetch (defaults to ``). -- `HIGHCHARTS_FORCE_FETCH`: The flag that determines whether to refetch all scripts after each server rerun (defaults to `false`). -- `HIGHCHARTS_CACHE_PATH`: In which directory should the fetched Highcharts scripts be placed (defaults to `.cache`). -- `HIGHCHARTS_ADMIN_TOKEN`: An authentication token that is required to switch the Highcharts version on the server at runtime (defaults to ``). +- `HIGHCHARTS_CUSTOM_SCRIPTS`: Additional custom scripts or dependencies to fetch (defaults to ``). ### Export Config -- `EXPORT_TYPE`: The format of the file to export to. Can be **jpeg**, **png**, **pdf** or **svg** (defaults to `png`). -- `EXPORT_CONSTR`: The constructor to use. Can be **chart**, **stockChart**, **mapChart** or **ganttChart** (defaults to `chart`). -- `EXPORT_DEFAULT_HEIGHT`: The default height of the exported chart. Used when not found any value set (defaults to `400`). -- `EXPORT_DEFAULT_WIDTH`: The default width of the exported chart. Used when not found any value set (defaults to `600`). -- `EXPORT_DEFAULT_SCALE`: The default scale of the exported chart. Ranges between **0.1** and **5.0** (defaults to `1`). +- `EXPORT_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 ``). +- `EXPORT_INSTR`: An input in a form of a stringified JSON or SVG file. Overrides the `infile` option (defaults to ``). +- `EXPORT_OPTIONS`: An alias for the `instr` option (defaults to ``). +- `EXPORT_SVG`: A string containing SVG representation to render as a chart (defaults to ``). +- `EXPORT_OUTFILE`: The output filename, accompanied by a type (**jpeg**, **png**, **pdf**, or **svg**). Ignores the `type` option (defaults to ``). +- `EXPORT_TYPE`: The format of the file to export to. Can be **jpeg**, **png**, **pdf**, or **svg** (defaults to `png`). +- `EXPORT_CONSTR`: The constructor to use. Can be **chart**, **stockChart**, **mapChart**, or **ganttChart** (defaults to `chart`). +- `EXPORT_B64`: Boolean flag, set to **true** to receive the chart in the _base64_ format instead of the _binary_ (defaults to `false`). +- `EXPORT_NO_DOWNLOAD`: Boolean flag, set to **true** to exclude attachment headers from the response (defaults to `false`). +- `EXPORT_HEIGHT`: The height of the exported chart. Overrides the option in the chart settings (defaults to ``). +- `EXPORT_WIDTH`: The width of the exported chart. Overrides the option in the chart settings (defaults to ``). +- `EXPORT_SCALE`: The scale of the exported chart. Ranges between **0.1** and **5.0** (defaults to ``). +- `EXPORT_DEFAULT_HEIGHT`: The default height for exported charts if not set explicitly (defaults to `400`). +- `EXPORT_DEFAULT_WIDTH`: The default width for exported charts if not set explicitly (defaults to `600`). +- `EXPORT_DEFAULT_SCALE`: The default scale for exported charts if not set explicitly. Ranges between **0.1** and **5.0** (defaults to `1`). +- `EXPORT_GLOBAL_OPTIONS`: Either a stringified JSON or a filename containing global options to be passed into the `Highcharts.setOptions` (defaults to ``). +- `EXPORT_THEME_OPTIONS`: Either a stringified JSON or a filename containing theme options to be passed into the `Highcharts.setOptions` (defaults to ``). +- `EXPORT_BATCH`: Initiates a batch job with a string containing input/output pairs: **"in=out;in=out;.."** (defaults to ``). - `EXPORT_RASTERIZATION_TIMEOUT`: The specified duration, in milliseconds, to wait for rendering a webpage (defaults to `1500`). ### Custom Logic Config - `CUSTOM_LOGIC_ALLOW_CODE_EXECUTION`: Controls whether the execution of arbitrary code is allowed during the exporting process (defaults to `false`). - `CUSTOM_LOGIC_ALLOW_FILE_RESOURCES`: Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server (defaults to `false`). +- `CUSTOM_LOGIC_CUSTOM_CODE`: Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the _.js_ extension (defaults to ``). +- `CUSTOM_LOGIC_CALLBACK`: JavaScript code to run during construction. It can be a function or a filename with the _.js_ extension (defaults to ``). +- `CUSTOM_LOGIC_RESOURCES`: Additional resources in the form of a stringified JSON. It may contain `files` (array of JS filenames), `js` (stringified JS), and `css` (stringified CSS) sections (defaults to ``). +- `CUSTOM_LOGIC_LOAD_CONFIG`: A file containing a pre-defined configuration to use (defaults to ``). +- `CUSTOM_LOGIC_CREATE_CONFIG`: Enables setting options through a prompt and saving them in a provided config file (defaults to ``). ### Server Config @@ -322,7 +355,7 @@ These variables are set in your environment and take precedence over options fro - `SERVER_PROXY_HOST`: The host of the proxy server to use, if it exists (defaults to ``). - `SERVER_PROXY_PORT`: The port of the proxy server to use, if it exists (defaults to ``). -- `SERVER_PROXY_TIMEOUT`: The timeout for the proxy server to use, if it exists (defaults to ``). +- `SERVER_PROXY_TIMEOUT`: The timeout, in milliseconds, for the proxy server to use, if it exists (defaults to `5000`). ### Server Rate Limiting Config @@ -356,15 +389,15 @@ These variables are set in your environment and take precedence over options fro ### Logging Config -- `LOGGING_LEVEL`: The logging level to be used. Can be **0** - silent, **1** - error, **2** - warning, **3** - notice, **4** - verbose or **5** benchmark (defaults to `4`). +- `LOGGING_LEVEL`: The logging level to be used. Can be **0** - silent, **1** - error, **2** - warning, **3** - notice, **4** - verbose or **5** - benchmark (defaults to `4`). - `LOGGING_FILE`: The name of a log file. The `logToFile` and `logDest` options also need to be set to enable file logging (defaults to `highcharts-export-server.log`). -- `LOGGING_DEST`: The path to store log files. The `logToFile` option also needs to be set to enable file logging (defaults to `log/`). +- `LOGGING_DEST`: The path to store log files. The `logToFile` option also needs to be set to enable file logging (defaults to `log`). - `LOGGING_TO_CONSOLE`: Enables or disables showing logs in the console (defaults to `true`). - `LOGGING_TO_FILE`: Enables or disables creation of the log directory and saving the log into a .log file (defaults to `true`). ### UI Config -- `UI_ENABLE`: Enables or disables the user interface (UI) for the Export Server (defaults to `true`). +- `UI_ENABLE`: Enables or disables the user interface (UI) for the Export Server (defaults to `false`). - `UI_ROUTE`: The endpoint route to which the user interface (UI) should be attached (defaults to `/`). ### Other Config @@ -378,7 +411,7 @@ These variables are set in your environment and take precedence over options fro ### Debugging Config - `DEBUG_ENABLE`: Enables or disables debug mode for the underlying browser (defaults to `false`). -- `DEBUG_HEADLESS`: Controls the mode in which the browser is launched when in the debug mode (defaults to `true`). +- `DEBUG_HEADLESS`: Controls the mode in which the browser is launched when in the debug mode (defaults to `false`). - `DEBUG_DEVTOOLS`: Decides whether to enable DevTools when the browser is in a headful state (defaults to `false`). - `DEBUG_LISTEN_TO_CONSOLE`: Decides whether to enable a listener for console messages sent from the browser (defaults to `false`). - `DEBUG_DUMPIO`: Redirects browser process stdout and stderr to process.stdout and process.stderr (defaults to `false`). @@ -390,46 +423,89 @@ These variables are set in your environment and take precedence over options fro To supply command line arguments, add them as flags when running the application: `highcharts-export-server --flag1 value --flag2 value ...` -_Available options:_ +For readability reasons, many options passed as CLI arguments have slightly different names, which combine the option and the section it comes from. For example, to set `server.enable`, use `--enableServer`. This way, it is clear which option is being set. The full listing of CLI options can be seen by typing the `highcharts-export-server --help` command in the console. + +_Available CLI arguments:_ + +### Puppeteer Config + +- `--puppeteerArgs`: A stringified version of additional Puppeteer arguments sent during browser initialization. The string can be enclosed in _[_ and _]_, and the arguments must be separated by the _;_ character (defaults to [Default JSON Config](#default-json-config)). + +### Highcharts Config -- `--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`). -- `--options`: An alias for the `--instr` option (defaults to `false`). -- `--outfile`: The output filename, accompanied by a type (**jpeg**, **png**, **pdf**, or **svg**). Ignores the `--type` flag (defaults to `false`). +- `--version`: Highcharts version to use (defaults to `latest`). +- `--cdnUrl`: Highcharts CDN URL of scripts to be used (defaults to `https://code.highcharts.com`). +- `--forceFetch`: The flag that determines whether to refetch all scripts after each server rerun (defaults to `false`). +- `--cachePath`: A directory path where the fetched Highcharts scripts should be placed (defaults to `.cache`). Since v4.0.3 can be either absolute or relative path. +- `--coreScripts`: Highcharts core scripts to fetch (defaults to [Default JSON Config](#default-json-config)). +- `--moduleScripts`: Highcharts module scripts to fetch (defaults to [Default JSON Config](#default-json-config)). +- `--indicatorScripts`: Highcharts indicator scripts to fetch (defaults to [Default JSON Config](#default-json-config)). +- `--customScripts`: Additional custom scripts or dependencies to fetch (defaults to [Default JSON Config](#default-json-config)). + +### Export Config + +- `--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 `null`). +- `--instr`: An input in a form of a stringified JSON or SVG file. Overrides the `infile` option (defaults to `null`). +- `--options`: An alias for the `instr` option (defaults to `null`). +- `--svg`: A string containing SVG representation to render as a chart (defaults to `null`). +- `--outfile`: The output filename, accompanied by a type (**jpeg**, **png**, **pdf**, or **svg**). Ignores the `type` option (defaults to `null`). - `--type`: The format of the file to export to. Can be **jpeg**, **png**, **pdf**, or **svg** (defaults to `png`). -- `--constr`: The constructor to use. Can be **chart**, **stockChart**, **mapChart** or **ganttChart** (defaults to `chart`). -- `--height`: The height of the exported chart. Overrides the option in the chart settings (defaults to `400`). -- `--width`: The width of the exported chart. Overrides the option in the chart settings (defaults to `600`). -- `--scale`: The scale of the exported chart. Ranges between **0.1** and **5.0** (defaults to `1`). -- `--globalOptions`: Either a stringified JSON or a filename containing global options to be passed into the `Highcharts.setOptions` (defaults to `false`). -- `--themeOptions`: Either a stringified JSON or a filename containing theme options to be passed into the `Highcharts.setOptions` (defaults to `false`). -- `--batch`: Initiates a batch job with a string containing input/output pairs: **"in=out;in=out;.."** (defaults to `false`). +- `--constr`: The constructor to use. Can be **chart**, **stockChart**, **mapChart**, or **ganttChart** (defaults to `chart`). +- `--b64`: Boolean flag, set to **true** to receive the chart in the _base64_ format instead of the _binary_ (defaults to `false`). +- `--noDownload`: Boolean flag, set to **true** to exclude attachment headers from the response (defaults to `false`). +- `--height`: The height of the exported chart. Overrides the option in the chart settings (defaults to `null`). +- `--width`: The width of the exported chart. Overrides the option in the chart settings (defaults to `null`). +- `--scale`: The scale of the exported chart. Ranges between **0.1** and **5.0** (defaults to `null`). +- `--defaultHeight`: The default height for exported charts if not set explicitly (defaults to `400`). +- `--defaultWidth`: The default width for exported charts if not set explicitly (defaults to `600`). +- `--defaultScale`: The default scale for exported charts if not set explicitly. Ranges between **0.1** and **5.0** (defaults to `1`). +- `--globalOptions`: Either a stringified JSON or a filename containing global options to be passed into the `Highcharts.setOptions` (defaults to `null`). +- `--themeOptions`: Either a stringified JSON or a filename containing theme options to be passed into the `Highcharts.setOptions` (defaults to `null`). +- `--batch`: Initiates a batch job with a string containing input/output pairs: **"in=out;in=out;.."** (defaults to `null`). - `--rasterizationTimeout`: The specified duration, in milliseconds, to wait for rendering a webpage (defaults to `1500`). + +### Custom Logic Config + - `--allowCodeExecution`: Controls whether the execution of arbitrary code is allowed during the exporting process (defaults to `false`). - `--allowFileResources`: Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server (defaults to `false`). -- `--customCode`: Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the _.js_ extension (defaults to `false`). -- `--callback`: JavaScript code to run during construction. It can be a function or a filename with the _.js_ extension (defaults to `false`). -- `--resources`: Additional resources in the form of a stringified JSON. It may contain `files` (array of JS filenames), `js` (stringified JS), and `css` (stringified CSS) sections (defaults to `false`). -- `--loadConfig`: A file containing a pre-defined configuration to use (defaults to `false`). -- `--createConfig`: Enables setting options through a prompt and saving them in a provided config file (defaults to `false`). +- `--customCode`: Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the _.js_ extension (defaults to `null`). +- `--callback`: JavaScript code to run during construction. It can be a function or a filename with the _.js_ extension (defaults to `null`). +- `--resources`: Additional resources in the form of a stringified JSON. It may contain `files` (array of JS filenames), `js` (stringified JS), and `css` (stringified CSS) sections (defaults to `null`). +- `--loadConfig`: A file containing a pre-defined configuration to use (defaults to `null`). +- `--createConfig`: Enables setting options through a prompt and saving them in a provided config file (defaults to `null`). + +### Server Config + - `--enableServer`: If set to **true**, the server starts on 0.0.0.0 (defaults to `false`). - `--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`). -- `--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`). -- `--proxyTimeout`: The timeout for the proxy server to use, if it exists (defaults to `5000`). +- `--serverBenchmarking`: 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 Proxy Config + +- `--proxyHost`: The host of the proxy server to use, if it exists (defaults to `null`). +- `--proxyPort`: The port of the proxy server to use, if it exists (defaults to `null`). +- `--proxyTimeout`: The timeout, in milliseconds, for the proxy server to use, if it exists (defaults to `5000`). + +### Server Rate Limiting Config + - `--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 ``). +- `--skipKey`: Allows bypassing the rate limiter and should be provided with the `skipToken` argument (defaults to `null`). +- `--skipToken`: Allows bypassing the rate limiter and should be provided with the `skipKey` argument (defaults to `null`). + +### Server SSL Config + - `--enableSsl`: Enables or disables the SSL protocol (defaults to `false`). - `--sslForce`: If set to **true**, the server is forced to serve only over HTTPS (defaults to `false`). - `--sslPort`: The port on which to run the SSL server (defaults to `443`). -- `--certPath`: The path to the SSL certificate/key file (defaults to ``). +- `--sslCertPath`: The path to the SSL certificate/key file (defaults to `null`). + +### Pool Config + - `--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`). @@ -439,21 +515,33 @@ _Available options:_ - `--idleTimeout`: The duration, in milliseconds, after which an idle resource is destroyed (defaults to `30000`). - `--createRetryInterval`: The duration, in milliseconds, to wait before retrying the create process in case of a failure (defaults to `200`). - `--reaperInterval`: The duration, in milliseconds, after which the check for idle resources to destroy is triggered (defaults to `1000`). -- `--poolBenchmarking`: Indicate whether to show statistics for the pool of resources or not (defaults to `false`). +- `--poolBenchmarking`: Indicates whether to show statistics for the pool of resources or not (defaults to `false`). + +### Logging Config + - `--logLevel`: The logging level to be used. Can be **0** - silent, **1** - error, **2** - warning, **3** - notice, **4** - verbose or **5** - benchmark (defaults to `4`). - `--logFile`: The name of a log file. The `logToFile` and `logDest` options also need to be set to enable file logging (defaults to `highcharts-export-server.log`). -- `--logDest`: The path to store log files. The `logToFile` option also needs to be set to enable file logging (defaults to `log/`). +- `--logDest`: The path to store log files. The `logToFile` option also needs to be set to enable file logging (defaults to `log`). - `--logToConsole`: Enables or disables showing logs in the console (defaults to `true`). - `--logToFile`: Enables or disables creation of the log directory and saving the log into a .log file (defaults to `true`). + +### UI Config + - `--enableUi`: Enables or disables the user interface (UI) for the Export Server (defaults to `false`). - `--uiRoute`: The endpoint route to which the user interface (UI) should be attached (defaults to `/`). -- `--nodeEnv`: The type of Node.js environment (defaults to `production`). + +### Other Config + +- `--nodeEnv`: The type of Node.js environment. The value controls whether to include the error's stack in a response or not. Can be development or production (defaults to `production`). - `--listenToProcessExits`: Decides whether or not to attach _process.exit_ handlers (defaults to `true`). - `--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`). + +### Debugging Config + - `--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`). +- `--headless`: Controls the mode in which the browser is launched when in the debug mode (defaults to `false`). - `--devtools`: Decides whether to enable DevTools when the browser is in a headful state (defaults to `false`). - `--listenToConsole`: Decides whether to enable a listener for console messages sent from the browser (defaults to `false`). - `--dumpio`: Redirects browser process stdout and stderr to process.stdout and process.stderr (defaults to `false`). @@ -464,7 +552,7 @@ _Available options:_ Apart from using as a CLI tool, which allows you to run one command at a time, it is also possible to configure the server to accept POST requests. The simplest way to enable the server is to run the command below: -`highcharts-export-server --enableServer 1` +`highcharts-export-server --enableServer true` ## Server Test @@ -485,7 +573,9 @@ To enable SSL support, add `--certPath ` when running the serve ## HTTP Server POST Arguments -The server accepts the following arguments in a POST request body: +The server accepts the following arguments in a POST request body. + +_Available request arguments:_ - `infile`: Chart options in the form of JSON or stringified JSON. - `options`: An alias for the `infile` option. @@ -516,15 +606,16 @@ It is recommended to run the server using [pm2](https://www.npmjs.com/package/pm - `/`: An endpoint for exporting charts. - `/:filename` - An endpoint for exporting charts with a specified filename parameter to save the chart to. The file will be downloaded with the _{filename}.{type}_ name (the `noDownload` must be set to **false**). - - `/change_hc_version/:newVersion`: An authenticated endpoint allowing the modification of the Highcharts version on the server through the use of a token. + - `/version_change/:newVersion`: An authenticated endpoint allowing the modification of the Highcharts version on the server through the use of a token. - GET + - `/`: An endpoint to perform exports through the user interface the server allows it. - `/health`: An endpoint for outputting basic statistics for the server. -## Switching Highcharts Version at Runtime +## Switching Highcharts Version At Runtime -If the `HIGHCHARTS_ADMIN_TOKEN` is set, you can use the `POST /change_hc_version/:newVersion` route to switch the Highcharts version on the server at runtime, ie. without restarting or redeploying the application. +If the `HIGHCHARTS_ADMIN_TOKEN` is set, you can use the `POST /version_change/:newVersion` route to switch the Highcharts version on the server at runtime, ie. without restarting or redeploying the application. A sample request to change the version to 10.3.3 is as follows: @@ -548,8 +639,8 @@ Finally, the Export Server can also be used as a Node.js module to simplify inte // Import the Highcharts Export Server module const exporter = require('highcharts-export-server'); -// Export options correspond to the available CLI/HTTP arguments described above -const options = { +// Options correspond to the available CLI/HTTP arguments described above +const customOptions = { export: { type: 'png', options: { @@ -573,23 +664,28 @@ const options = { } }; -// Initialize export settings with your chart's config -const exportSettings = exporter.setOptions(options); +// Logic must be triggered in an asynchronous function +(async () => { + // Set options with user configuration + const options = exporter.setOptions(customOptions); -// Must initialize exporting before being able to export charts -await exporter.initExport(exportSettings); + // Must initialize exporting before being able to export charts + await exporter.initExport(options); -// Perform an export -await exporter.startExport(exportSettings, async (error, info) => { - // The export result is now in info - // It will be base64 encoded (info.data) + // Perform an export + await exporter.startExport(options, async (error, data) => { + // The export result is now in the `data` (it will be base64 encoded) + console.log(data.result); - // Kill the pool when we are done with it - await exporter.killPool(); -}); + // Kill the pool when we are done with it + await exporter.killPool(); + }); +})(); ``` -## CommonJS support +In order for everything to work as it is supposed to, the `setOptions` function must be called before running the `initExport` and any export-related function (`startExport`, `singleExport`, or `batchExport`) to correctly initialize all option values. + +## CommonJS Support This package supports both CommonJS and ES modules. @@ -599,104 +695,131 @@ This package supports both CommonJS and ES modules. - `server`: The server instance which offers the following functions: - - `async startServer(serverConfig)`: The same as `startServer` described below. +- `async function startServer(serverOptions = getOptions().server)`: Starts HTTP or/and HTTPS server based on the provided configuration. The `serverOptions` object contains all server related properties (see the `server` section in the `lib/schemas/config.js` file for a reference). + + - `@param {Object} [serverOptions=getOptions().server]` - Object containing server options. The default value is the global server options of the export server instance. + + - `@throws {ExportError}` Throws an `ExportError` if the server cannot be configured and started. + +- `function closeServers()`: Closes all servers associated with Express app instance. + +- `function getServers()`: Get all servers associated with Express app instance. + + - `@returns {Array.}` Servers associated with Express app instance. + +- `function enableRateLimiting(limitConfig)`: Enable rate limiting for the server. + + - `@param {Object} limitConfig` - Configuration object for rate limiting. - - `{Object} serverConfig`: The server configuration object. + - `@returns {Object}` Middleware for enabling rate limiting. - - `closeServers()`: Closes all servers associated with Express app instance. +- `function getExpress()`: Get the Express instance. - - `getServers()`: Get all servers associated with Express app instance. + - `@returns {Object}` The Express instance. - - `enableRateLimiting(limitConfig)`: Enable rate limiting for the server. +- `function getApp()`: Get the Express app instance. - - `{Object} limitConfig`: Configuration object for rate limiting. + - `@returns {Object}` The Express app instance. - - `getExpress()`: Get the Express instance. +- `function use(path, ...middlewares)`: Apply middleware(s) to a specific path. - - `getApp()`: Get the Express app instance. + - `@param {string} path` - The path to which the middleware(s) should be applied. + - `@param {...Function} middlewares` - The middleware functions to be applied. - - `use(path, ...middlewares)`: Apply middleware(s) to a specific path. +- `function get(path, ...middlewares)`: Set up a route with GET method and apply middleware(s). - - `{string} path`: The path to which the middleware(s) should be applied. - - `{...Function} middlewares`: The middleware functions to be applied. + - `@param {string} path` - The route path. + - `@param {...Function} middlewares` - The middleware functions to be applied. - - `get(path, ...middlewares)`: Set up a route with GET method and apply middleware(s). +- `function post(path, ...middlewares)`: Set up a route with POST method and apply middleware(s). - - `{string} path`: The route path. - - `{...Function} middlewares`: The middleware functions to be applied. + - `@param {string} path` - The route path. + - `@param {...Function} middlewares` - The middleware functions to be applied. - - `post(path, ...middlewares)`: Set up a route with POST method and apply middleware(s). - - `{string} path`: The route path. - - `{...Function} middlewares`: The middleware functions to be applied. +- `async function startServer(serverOptions = getOptions().server)`: Starts HTTP or/and HTTPS server based on the provided configuration. The `serverOptions` object contains all server related properties (see the `server` section in the `lib/schemas/config.js` file for a reference). -- `async startServer(serverConfig)`: Starts an HTTP server based on the provided configuration. The `serverConfig` object contains all server related properties (see the `server` section in the `lib/schemas/config.js` file for a reference). + - `@param {Object} [serverOptions=getOptions().server]` - Object containing server options. The default value is the global server options of the export server instance. - - `{Object} serverConfig`: The server configuration object. + - `@throws {ExportError}` Throws an `ExportError` if the server cannot be configured and started. -- `async initExport(options)`: Initializes the export process. Tasks such as configuring logging, checking cache and sources, and initializing the pool of resources happen during 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. +- `function getOptions(getGlobal = false)`: Gets the global options of the export server instance. - - `{Object} options`: All export options. + - `@param {boolean} [getGlobal = false]` - Optional parameter to decide whether to return the reference to the global options of the server instance object or return a copy of it. -- `async singleExport(options)`: Starts a single export process based on the specified options. Runs the `startExport` underneath. + - `@returns {Object}` The global options object of the server instance. - - `{Object} options`: The options object containing configuration for a single export. +- `function setOptions(customOptions = {}, cliArgs = [], modifyGlobal = false)`: Sets the general options of the export server instance, keeping the principle of the options load priority from all available sources. It accepts optional `customOptions` object and `cliArgs` array with arguments from the CLI. These options will be validated and applied if provided. -- `async batchExport(options)`: Starts a batch export process for multiple charts based on the information in the batch option. The batch is a string in the following format: `"infile1.json=outfile1.png;infile2.json=outfile2.png;..."`. Runs the `startExport` underneath. + - `@param {Object} [customOptions={}]` - Optional custom options for additional configuration. + - `@param {Array.} [cliArgs=[]]` - Optional command line arguments for additional configuration. + - `@param {boolean} [modifyGlobal = false]` - Optional parameter to decide whether to update and return the reference to the global options of the server instance object or return a copy of it. - - `{Object} options`: The options object containing configuration for a batch export. + - `@returns {Object}` The updated general options object, reflecting the merged configuration from all available sources. -- `async startExport(settings, endCallback)`: Starts an export process. The `settings` contains final options gathered from all possible sources (config, env, cli, json). The `endCallback` is called when the export is completed, with an error object as the first argument and the second containing the base64 respresentation of a chart. +- `async function initExport(options = getOptions())`: Initializes the export process. Tasks such as configuring logging, checking the cache and sources, and initializing the pool of resources happen during this stage. This function must be called before attempting to export charts or set up a server. The `options` parameter is an object that contains all possible options. If the object is not provided, the default general options will be retrieved using the `getOptions` function. - - `{Object} settings`: The settings object containing export configuration. - - `{function} endCallback`: The callback function to be invoked upon finalizing work or upon error occurance of the exporting process. + - `@param {Object} [options=getOptions()]` - The `options` object containing configuration for a custom export. The default value is the global options of the export server instance. -- `async initPool(config)`: Initializes the export pool with the provided configuration, creating a browser instance and setting up worker resources. +- `async function startExport(options = getOptions(), endCallback)`: Starts an export process. The `options` object contains final options gathered from all possible sources (config, custom json, env, cli). The `endCallback` is called when the export is completed, with the `error` object as the first argument and the `data` object as the second, which contains the base64 respresentation of a chart. - - `{Object} config`: Configuration options for the export pool along with custom puppeteer arguments for the puppeteer.launch function. + - `@param {Object} [options=getOptions()]` - The `options` object containing configuration for a custom export. The default value is the global options of the export server instance. + - `@param {Function} endCallback` - The callback function to be invoked upon finalizing work or upon error occurance of the exporting process. -- `async killPool()`: Kills all workers in the pool, destroys the pool, and closes the browser instance. + - `@returns {void}` This function does not return a value directly. Instead, it communicates results via the `endCallback`. -- `setOptions(userOptions, args)`: Initializes and sets the general options for the server instace, keeping the principle of the options load priority. It accepts optional userOptions and args from the CLI. +- `async function singleExport(options = getOptions())`: Starts a single export process based on the specified options. - - `{Object} userOptions`: User-provided options for customization. - - `{Array} args`: Command-line arguments for additional configuration (CLI usage). + - `@param {Object} [options=getOptions()]` - The `options` object containing configuration for a custom export. The default value is the global options of the export server instance. -- `async shutdownCleanUp(exitCode)`: Clean up function to trigger before ending process for the graceful shutdown. + - `@returns {Promise}` A Promise that resolves once the single export process is completed. - - `{number} exitCode`: An exit code for the process.exit() function. + - `@throws {ExportError}` Throws an `ExportError` if an error occurs during the single export process. -- `log(...args)`: Logs a message. Accepts a variable amount of arguments. Arguments after `level` will be passed directly to console.log, and/or will be joined and appended to the log file. +- `async function batchExport(options = getOptions())`: Starts a batch export process for multiple charts based on the information in the `batch` option. The `batch` is a string in the following format: "infile1.json=outfile1.png;infile2.json=outfile2.png;...". - - `{any} args`: An array of arguments where the first is the log level and the rest are strings to build a message with. + - `@param {Object} [options=getOptions()]` - The `options` object containing configuration for a custom export. The default value is the global options of the export server instance. -- `logWithStack(newLevel, error, customMessage)`: Logs an error message with its stack trace. Optionally, a custom message can be provided. + - `@returns {Promise}` A Promise that resolves once the batch export process is completed. - - `{number} newLevel`: The log level. - - `{Error} error`: The error object. - - `{string} customMessage`: An optional custom message to be logged along with the error. + - `@throws {ExportError}` Throws an `ExportError` if an error occurs during any of the batch export process. -- `setLogLevel(newLevel)`: Sets the log level to the specified value. Log levels are (0 = no logging, 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark). +- `async function initPool(poolOptions = getOptions().pool, puppeteerArgs)`: Initializes the export pool with the provided configuration, creating a browser instance and setting up worker resources. - - `{number} newLevel`: The new log level to be set. + - `@param {Object} poolOptions` - Object containing pool options. + - `@param {Array.} puppeteerArgs` - Array of custom puppeteer arguments for the puppeteer.launch function. -- `enableFileLogging(logDest, logFile)`: Enables file logging with the specified destination and log file. +- `async function killPool()`: Kills all workers in the pool, destroys the pool, and closes the browser instance. - - `{string} logDest`: The destination path for log files. - - `{string} logFile`: The log file name. + - `@returns {Promise}` A promise that resolves after the workers are killed, the pool is destroyed, and the browser is closed. -- `mapToNewConfig(oldOptions)`: Maps old-structured (PhantomJS) options to a new configuration format (Puppeteer). +- `function log(...args)`: Logs a message. Accepts a variable amount of arguments. Arguments after the `level` will be passed directly to `console.log`, and/or will be joined and appended to the log file. - - `{Object} oldOptions`: Old-structured options to be mapped. + - `@param {...unknown} args` - An array of arguments where the first is the log level and the rest are strings to build a message with. -- `async manualConfig(configFileName)`: Allows manual configuration based on specified prompts and saves the configuration to a file. +- `function logWithStack(newLevel, error, customMessage)`: Logs an error message with its stack trace. Optionally, a custom message can be provided. - - `{string} configFileName`: The name of the configuration file. + - `@param {number} newLevel` - The log level. + - `@param {Error} error` - The error object. + - `@param {string} customMessage` - An optional custom message to be logged along with the error. -- `printLogo(noLogo)`: Prints the Highcharts Export Server logo and version information. +- `function setLogLevel(newLevel)`: Sets the log level to the specified value. Log levels are (0 = no logging, 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark). - - `{boolean} noLogo`: If **true**, only prints version information without the logo. + - `@param {number} newLevel` - The new log level to be set. -- `printUsage()`: Prints the usage information for CLI arguments. If required, it can list properties recursively. +- `function enableFileLogging(dest, file)`: Enables file logging with the specified destination and log file. + + - `@param {string} logDest` - The destination path for log files. + - `@param {string} logFile` - The log file name. + +- `async function shutdownCleanUp(exitCode)`: Clean up function to trigger before ending process for the graceful shutdown. + + - `@param {number} exitCode` - An exit code for the `process.exit()` function. + +- `function mapToNewConfig(oldOptions)`: Maps old-structured configuration options (PhantomJS) to a new format (Puppeteer). This function converts flat, old-structured options into a new, nested configuration format based on a predefined mapping (`nestedArgs`). The new format is used for Puppeteer, while the old format was used for PhantomJS. + + - `@param {Object} oldOptions` - The old, flat configuration options to be converted. + + - `@returns {Object}` A new object containing options structured according to the mapping defined in `nestedArgs`. # Examples @@ -704,7 +827,11 @@ Samples and tests for every mentioned export method can be found in the `./sampl # Tips, Tricks & Notes -## Note about Deprecated Options +## Note About Version And Help Information + +Typing `highcharts-export-server --v` will display information about the current version of the Export Server, and `highcharts-export-server --h` will display information about available CLI options. + +## Note About Deprecated Options At some point during the transition process from the `PhantomJS` solution, certain options were deprecated. Here is a list of options that no longer work with the server based on `Puppeteer`: @@ -718,13 +845,13 @@ Additionally, some options are now named differently due to the new structure an - `fromFile` -> `loadConfig` - `sslOnly` -> `force` or `sslForce` -- `sslPath` -> `certPath` +- `sslPath` -> `sslCertPath` - `rateLimit` -> `maxRequests` - `workers` -> `maxWorkers` If you depend on any of the above options, the optimal approach is to directly change the old names to the new ones in the options. However, you don't have to do it manually, as there is a utility function called `mapToNewConfig` that can easily transfer the old-structured options to the new format. For an example, refer to the `./samples/module/optionsPhantom.js` file. -## Note about Chart Size +## Note About Chart Size If you need to set the `height` or `width` of the chart, it can be done in two ways: @@ -749,7 +876,7 @@ Like previously mentioned, there are multiple ways to set and prioritize options 5. The `height` and `width` from the `chart` section of chart's Highcharts global options, if provided. 6. If no options are found to this point, the default values will be used (`height = 400`, `width = 600` and `scale = 1`). -## Note about Event Listeners +## Note About Event Listeners The Export Server attaches event listeners to `process.exit`, `uncaughtException` and signals such as `SIGINT`, `SIGTERM` and `SIGHUP`. This is to make sure that there are no memory leaks or zombie processes if the application is unexpectedly terminated. @@ -759,11 +886,11 @@ If you do not want this behavior, start the server with `--listenToProcessExits Be aware though, that if you disable this and you do not take great care to manually kill the pool of resources along with a browser instance, your server will bleed memory when the app is terminated. -## Note about Resources +## Note About Resources If `--resources` argument is not set and a file named `resources.json` exists in the folder from which the CLI tool was ran, it will use the `resources.json` file. -## Note about Worker Count & Work Limit +## Note About Worker Count & Work Limit 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. @@ -773,13 +900,13 @@ Each of the workers has a maximum number of requests it can handle before it res # Usage -## Injecting the Highcharts Dependency +## Injecting The Highcharts Dependency In order to use the Export Server, Highcharts needs to be injected into the export template (see the `./templates` folder for reference). Since version 3.0.0, Highcharts is fetched in a Just-In-Time manner, making it easy to switch configurations. It is no longer required to explicitly accept the license, as in older versions. **However, the Export Server still requires a valid Highcharts license to be used**. -## Using in Automated Deployments +## Using In Automated Deployments Since version 3.0.0, when using in automated deployments, the configuration can be loaded either using environment variables or a JSON configuration file. @@ -809,7 +936,7 @@ On Windows: ## Library Fetches -When fetching the built Highcharts library, the default behaviour is to fetch them from `code.highcharts.com`. +When fetching the built Highcharts library, the default behaviour is to fetch them from `https://code.highcharts.com`. ## Installing Fonts @@ -841,7 +968,7 @@ Copy or move the TTF file to `C:\Windows\Fonts\`: copy yourFont.ttf C:\Windows\Fonts\yourFont.ttf ``` -### Google fonts +### Google Fonts If you need Google Fonts in your custom installation, they can be had here: https://github.com/google/fonts. From b5cedf78a741c1aacc51e0ed97ad19d2fd3385be Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:30:12 +0100 Subject: [PATCH 028/102] Updated versions of packages. --- package-lock.json | 922 +++++++++++++++++++++++++++------------------- package.json | 26 +- 2 files changed, 547 insertions(+), 401 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2d8b3ca5..17c471cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,23 +11,25 @@ "dependencies": { "colors": "1.4.0", "cors": "^2.8.5", - "dompurify": "^3.1.6", - "dotenv": "^16.4.5", - "express": "^4.19.2", - "express-rate-limit": "^7.3.1", - "https-proxy-agent": "^7.0.5", - "jsdom": "^24.1.0", + "dompurify": "^3.2.3", + "dotenv": "^16.4.7", + "express": "^4.21.2", + "express-rate-limit": "^7.4.1", + "https-proxy-agent": "^7.0.6", + "jsdom": "^25.0.1", + "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^22.12.1", + "puppeteer": "^23.10.3", "tarn": "^3.0.2", - "uuid": "^10.0.0", - "zod": "^3.23.8" + "uuid": "^11.0.3", + "zod": "^3.24.0" }, "bin": { "highcharts-export-server": "bin/cli.js" }, "devDependencies": { + "@jest/globals": "^29.7.0", "@rollup/plugin-terser": "^0.4.4", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", @@ -35,10 +37,10 @@ "eslint-plugin-prettier": "^5.2.1", "husky": "^9.1.7", "jest": "^29.7.0", - "lint-staged": "^15.2.10", + "lint-staged": "^15.2.11", "nodemon": "^3.1.7", - "prettier": "^3.4.1", - "rollup": "^4.27.4" + "prettier": "^3.4.2", + "rollup": "^4.28.1" }, "engines": { "node": ">=18.12.0" @@ -73,9 +75,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", "dev": true, "license": "MIT", "engines": { @@ -114,14 +116,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -233,13 +235,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -503,17 +505,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -532,9 +534,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "dev": true, "license": "MIT", "dependencies": { @@ -1185,15 +1187,15 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.0.tgz", - "integrity": "sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz", + "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==", "license": "Apache-2.0", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.4.0", "extract-zip": "^2.0.1", "progress": "^2.0.3", - "proxy-agent": "^6.4.0", + "proxy-agent": "^6.5.0", "semver": "^7.6.3", "tar-fs": "^3.0.6", "unbzip2-stream": "^1.4.3", @@ -1242,9 +1244,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz", - "integrity": "sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", + "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==", "cpu": [ "arm" ], @@ -1256,9 +1258,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz", - "integrity": "sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz", + "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==", "cpu": [ "arm64" ], @@ -1270,9 +1272,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz", - "integrity": "sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz", + "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==", "cpu": [ "arm64" ], @@ -1284,9 +1286,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz", - "integrity": "sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz", + "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==", "cpu": [ "x64" ], @@ -1298,9 +1300,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz", - "integrity": "sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz", + "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==", "cpu": [ "arm64" ], @@ -1312,9 +1314,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz", - "integrity": "sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz", + "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==", "cpu": [ "x64" ], @@ -1326,9 +1328,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz", - "integrity": "sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz", + "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==", "cpu": [ "arm" ], @@ -1340,9 +1342,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz", - "integrity": "sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz", + "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==", "cpu": [ "arm" ], @@ -1354,9 +1356,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz", - "integrity": "sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz", + "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==", "cpu": [ "arm64" ], @@ -1368,9 +1370,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz", - "integrity": "sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz", + "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==", "cpu": [ "arm64" ], @@ -1381,10 +1383,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz", + "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz", - "integrity": "sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz", + "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==", "cpu": [ "ppc64" ], @@ -1396,9 +1412,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz", - "integrity": "sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz", + "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==", "cpu": [ "riscv64" ], @@ -1410,9 +1426,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz", - "integrity": "sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz", + "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==", "cpu": [ "s390x" ], @@ -1424,9 +1440,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz", - "integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz", + "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==", "cpu": [ "x64" ], @@ -1438,9 +1454,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz", - "integrity": "sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz", + "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==", "cpu": [ "x64" ], @@ -1452,9 +1468,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz", - "integrity": "sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz", + "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==", "cpu": [ "arm64" ], @@ -1466,9 +1482,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz", - "integrity": "sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz", + "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==", "cpu": [ "ia32" ], @@ -1480,9 +1496,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz", - "integrity": "sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz", + "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==", "cpu": [ "x64" ], @@ -1646,6 +1662,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -1674,9 +1697,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", "dev": true, "license": "ISC" }, @@ -1717,13 +1740,10 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -2340,6 +2360,12 @@ "node": "*" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2367,16 +2393,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -2385,6 +2410,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2405,9 +2443,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001684", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001684.tgz", - "integrity": "sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==", + "version": "1.0.30001687", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", + "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", "dev": true, "funding": [ { @@ -2491,9 +2529,9 @@ } }, "node_modules/chromium-bidi": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.3.tgz", - "integrity": "sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.8.0.tgz", + "integrity": "sha512-uJydbGdTw0DEUjhoogGveneJVWX/9YuqkWePzMmkBYwtdAqo5d3J/ovNKFr+/2hWXYmYCr6it8mSSTIj6SS6Ug==", "license": "Apache-2.0", "dependencies": { "mitt": "3.0.1", @@ -2504,6 +2542,15 @@ "devtools-protocol": "*" } }, + "node_modules/chromium-bidi/node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -2930,9 +2977,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3072,9 +3119,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1312386", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz", - "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==", + "version": "0.0.1367902", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", + "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", "license": "BSD-3-Clause" }, "node_modules/diff-sequences": { @@ -3101,15 +3148,18 @@ } }, "node_modules/dompurify": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", - "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==", - "license": "(MPL-2.0 OR Apache-2.0)" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz", + "integrity": "sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } }, "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -3118,6 +3168,29 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", + "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3125,9 +3198,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.66", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.66.tgz", - "integrity": "sha512-pI2QF6+i+zjPbqRzJwkMvtvkdI7MjVbSh2g8dlMguDJIXEPw+kwasS1Jl+YGPEBfGVxsVgGUratAKymPdPo2vQ==", + "version": "1.5.72", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.72.tgz", + "integrity": "sha512-ZpSAUOZ2Izby7qnZluSrAlGgGQzucmFbN0n64dYzocYxnxV5ufurpj3VgEe4cUp7ir9LmeLxNYo8bVnlM8bQHw==", "dev": true, "license": "ISC" }, @@ -3274,13 +3347,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -3779,9 +3849,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -3803,7 +3873,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -3818,6 +3888,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express-rate-limit": { @@ -4088,20 +4162,6 @@ "node": ">= 0.6" } }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4195,16 +4255,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz", + "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "dunder-proto": "^1.0.0", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4255,15 +4318,14 @@ } }, "node_modules/get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", "license": "MIT", "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" + "debug": "^4.3.4" }, "engines": { "node": ">= 14" @@ -4338,12 +4400,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4353,6 +4415,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -4395,10 +4458,14 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -4407,9 +4474,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -4495,12 +4562,12 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -4723,13 +4790,16 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4749,14 +4819,14 @@ } }, "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.0.tgz", + "integrity": "sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4941,13 +5011,14 @@ } }, "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.0.tgz", + "integrity": "sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4973,14 +5044,16 @@ "license": "MIT" }, "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.0.tgz", + "integrity": "sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "gopd": "^1.1.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -5032,13 +5105,14 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.0.tgz", + "integrity": "sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.7", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5048,13 +5122,15 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.0.tgz", + "integrity": "sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==", "dev": true, "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bind": "^1.0.7", + "has-symbols": "^1.0.3", + "safe-regex-test": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -5840,12 +5916,12 @@ "license": "MIT" }, "node_modules/jsdom": { - "version": "24.1.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.3.tgz", - "integrity": "sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==", + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", "license": "MIT", "dependencies": { - "cssstyle": "^4.0.1", + "cssstyle": "^4.1.0", "data-urls": "^5.0.0", "decimal.js": "^10.4.3", "form-data": "^4.0.0", @@ -5858,7 +5934,7 @@ "rrweb-cssom": "^0.7.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.4", + "tough-cookie": "^5.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", @@ -5932,16 +6008,59 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" } }, "node_modules/keyv": { @@ -5988,9 +6107,9 @@ } }, "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "license": "MIT", "engines": { @@ -6007,22 +6126,22 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", - "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.11.tgz", + "integrity": "sha512-Ev6ivCTYRTGs9ychvpVw35m/bcNDuBN+mnTeObCL5h+boS5WzBEC6LHI4I9F/++sZm1m+J2LEiy0gxL/R9TBqQ==", "dev": true, "license": "MIT", "dependencies": { "chalk": "~5.3.0", "commander": "~12.1.0", - "debug": "~4.3.6", + "debug": "~4.4.0", "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", + "lilconfig": "~3.1.3", + "listr2": "~8.2.5", "micromatch": "~4.0.8", "pidtree": "~0.6.0", "string-argv": "~0.3.2", - "yaml": "~2.5.0" + "yaml": "~2.6.1" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -6225,6 +6344,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6232,6 +6387,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/log-update": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", @@ -6593,9 +6754,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, @@ -6688,9 +6849,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.13", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", - "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", "license": "MIT" }, "node_modules/object-assign": { @@ -6893,19 +7054,19 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", - "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz", + "integrity": "sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==", "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.5", + "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.4" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" @@ -7013,9 +7174,9 @@ "license": "MIT" }, "node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, "node_modules/pend": { @@ -7156,9 +7317,9 @@ } }, "node_modules/prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", - "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, "license": "MIT", "bin": { @@ -7254,19 +7415,19 @@ } }, "node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", + "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", + "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" @@ -7287,15 +7448,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, - "node_modules/psl": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.13.0.tgz", - "integrity": "sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw==", - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - } - }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -7323,34 +7475,37 @@ } }, "node_modules/puppeteer": { - "version": "22.15.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.15.0.tgz", - "integrity": "sha512-XjCY1SiSEi1T7iSYuxS82ft85kwDJUS7wj1Z0eGVXKdtr5g4xnVcbjwxhq5xBnpK/E7x1VZZoJDxpjAOasHT4Q==", + "version": "23.10.3", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.10.3.tgz", + "integrity": "sha512-ODG+L9vCSPkQ1j+yDtNDdkSsWt2NXNrQO5C8MlwkYgE2hYnXdqVRbBpsHnoP7+EULJJKbWyR2Q4BdfohjQor3A==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.3.0", + "@puppeteer/browsers": "2.6.1", + "chromium-bidi": "0.8.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1312386", - "puppeteer-core": "22.15.0" + "devtools-protocol": "0.0.1367902", + "puppeteer-core": "23.10.3", + "typed-query-selector": "^2.12.0" }, "bin": { - "puppeteer": "lib/esm/puppeteer/node/cli.js" + "puppeteer": "lib/cjs/puppeteer/node/cli.js" }, "engines": { "node": ">=18" } }, "node_modules/puppeteer-core": { - "version": "22.15.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.15.0.tgz", - "integrity": "sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==", + "version": "23.10.3", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.10.3.tgz", + "integrity": "sha512-7JG8klL2qHLyH8t2pOmM9zgykhaulUf7cxnmmqupjdwGfNMiGaYehQka20iUB9R/fwVyG8mFMZcsmw1FHrgKVw==", "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.3.0", - "chromium-bidi": "0.6.3", - "debug": "^4.3.6", - "devtools-protocol": "0.0.1312386", + "@puppeteer/browsers": "2.6.1", + "chromium-bidi": "0.8.0", + "debug": "^4.4.0", + "devtools-protocol": "0.0.1367902", + "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, "engines": { @@ -7389,12 +7544,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "license": "MIT" - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7498,19 +7647,20 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz", - "integrity": "sha512-bMvFGIUKlc/eSfXNX+aZ+EL95/EgZzuwA0OBPTbZZDEJw/0AkentjMuM1oiRfwHrshqk4RzdgiTg5CcDalXN5g==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", + "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", + "dunder-proto": "^1.0.0", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "which-builtin-type": "^1.1.4" + "gopd": "^1.2.0", + "which-builtin-type": "^1.2.0" }, "engines": { "node": ">= 0.4" @@ -7547,12 +7697,6 @@ "node": ">=0.10.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "license": "MIT" - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -7604,9 +7748,9 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "license": "MIT", "engines": { @@ -7695,9 +7839,9 @@ } }, "node_modules/rollup": { - "version": "4.27.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz", - "integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz", + "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==", "dev": true, "license": "MIT", "dependencies": { @@ -7711,24 +7855,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.27.4", - "@rollup/rollup-android-arm64": "4.27.4", - "@rollup/rollup-darwin-arm64": "4.27.4", - "@rollup/rollup-darwin-x64": "4.27.4", - "@rollup/rollup-freebsd-arm64": "4.27.4", - "@rollup/rollup-freebsd-x64": "4.27.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.27.4", - "@rollup/rollup-linux-arm-musleabihf": "4.27.4", - "@rollup/rollup-linux-arm64-gnu": "4.27.4", - "@rollup/rollup-linux-arm64-musl": "4.27.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4", - "@rollup/rollup-linux-riscv64-gnu": "4.27.4", - "@rollup/rollup-linux-s390x-gnu": "4.27.4", - "@rollup/rollup-linux-x64-gnu": "4.27.4", - "@rollup/rollup-linux-x64-musl": "4.27.4", - "@rollup/rollup-win32-arm64-msvc": "4.27.4", - "@rollup/rollup-win32-ia32-msvc": "4.27.4", - "@rollup/rollup-win32-x64-msvc": "4.27.4", + "@rollup/rollup-android-arm-eabi": "4.28.1", + "@rollup/rollup-android-arm64": "4.28.1", + "@rollup/rollup-darwin-arm64": "4.28.1", + "@rollup/rollup-darwin-x64": "4.28.1", + "@rollup/rollup-freebsd-arm64": "4.28.1", + "@rollup/rollup-freebsd-x64": "4.28.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.28.1", + "@rollup/rollup-linux-arm-musleabihf": "4.28.1", + "@rollup/rollup-linux-arm64-gnu": "4.28.1", + "@rollup/rollup-linux-arm64-musl": "4.28.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.28.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1", + "@rollup/rollup-linux-riscv64-gnu": "4.28.1", + "@rollup/rollup-linux-s390x-gnu": "4.28.1", + "@rollup/rollup-linux-x64-gnu": "4.28.1", + "@rollup/rollup-linux-x64-musl": "4.28.1", + "@rollup/rollup-win32-arm64-msvc": "4.28.1", + "@rollup/rollup-win32-ia32-msvc": "4.28.1", + "@rollup/rollup-win32-x64-msvc": "4.28.1", "fsevents": "~2.3.2" } }, @@ -8118,12 +8263,12 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "license": "MIT", "dependencies": { - "agent-base": "^7.1.1", + "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, @@ -8199,9 +8344,9 @@ } }, "node_modules/streamx": { - "version": "2.20.2", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.2.tgz", - "integrity": "sha512-aDGDLU+j9tJcUdPGOaHmVF1u/hhI+CsGkT02V3OKlHDV7IukOI+nTWAGkiZEKCO35rWN1wIr4tS7YFr1f4qSvA==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.0.tgz", + "integrity": "sha512-Qz6MsDZXJ6ur9u+b+4xCG18TluU7PGlRfXVAAjNiGsFrBUt/ioyLkxbFaKJygoPs+/kW4VyBj0bSj89Qu0IGyg==", "license": "MIT", "dependencies": { "fast-fifo": "^1.3.2", @@ -8479,9 +8624,9 @@ } }, "node_modules/terser": { - "version": "5.36.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", - "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -8531,10 +8676,13 @@ } }, "node_modules/text-decoder": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.1.tgz", - "integrity": "sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==", - "license": "Apache-2.0" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.2.tgz", + "integrity": "sha512-/MDslo7ZyWTA2vnk1j7XoDVfXsGk3tp+zFEJHJGm0UjIlQifonVFwlVbQDFh8KJzTBnT8ie115TYqir6bclddA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } }, "node_modules/text-table": { "version": "0.2.0", @@ -8549,6 +8697,24 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, + "node_modules/tldts": { + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.66.tgz", + "integrity": "sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.66" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.66", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.66.tgz", + "integrity": "sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g==", + "license": "MIT" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -8589,27 +8755,15 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", "license": "BSD-3-Clause", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts": "^6.1.32" }, "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "license": "MIT", - "engines": { - "node": ">= 4.0.0" + "node": ">=16" } }, "node_modules/tr46": { @@ -8793,6 +8947,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "license": "MIT" + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -8839,15 +8999,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -8898,16 +9049,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/urlpattern-polyfill": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", @@ -8930,16 +9071,16 @@ } }, "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/v8-to-istanbul": { @@ -9031,9 +9172,9 @@ } }, "node_modules/whatwg-url": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", - "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", "license": "MIT", "dependencies": { "tr46": "^5.0.0", @@ -9060,17 +9201,20 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz", + "integrity": "sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==", "dev": true, "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.0", + "is-number-object": "^1.1.0", + "is-string": "^1.1.0", + "is-symbol": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9302,9 +9446,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", - "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", "dev": true, "license": "ISC", "bin": { @@ -9394,9 +9538,9 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.0.tgz", + "integrity": "sha512-Hz+wiY8yD0VLA2k/+nsg2Abez674dDGTai33SwNvMPuf9uIrBC9eFgIMQxBBbHFxVXi8W+5nX9DcAh9YNSQm/w==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index f88e1090..08746625 100644 --- a/package.json +++ b/package.json @@ -51,20 +51,22 @@ "dependencies": { "colors": "1.4.0", "cors": "^2.8.5", - "dompurify": "^3.1.6", - "dotenv": "^16.4.5", - "express": "^4.19.2", - "express-rate-limit": "^7.3.1", - "https-proxy-agent": "^7.0.5", - "jsdom": "^24.1.0", + "dompurify": "^3.2.3", + "dotenv": "^16.4.7", + "express": "^4.21.2", + "express-rate-limit": "^7.4.1", + "https-proxy-agent": "^7.0.6", + "jsdom": "^25.0.1", + "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^22.12.1", + "puppeteer": "^23.10.3", "tarn": "^3.0.2", - "uuid": "^10.0.0", - "zod": "^3.23.8" + "uuid": "^11.0.3", + "zod": "^3.24.0" }, "devDependencies": { + "@jest/globals": "^29.7.0", "@rollup/plugin-terser": "^0.4.4", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", @@ -72,9 +74,9 @@ "eslint-plugin-prettier": "^5.2.1", "husky": "^9.1.7", "jest": "^29.7.0", - "lint-staged": "^15.2.10", + "lint-staged": "^15.2.11", "nodemon": "^3.1.7", - "prettier": "^3.4.1", - "rollup": "^4.27.4" + "prettier": "^3.4.2", + "rollup": "^4.28.1" } } From 8542ba07b07e1e288c2028f0dc96d32b282fa4e3 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 21:32:13 +0100 Subject: [PATCH 029/102] Samples, scenarios, tests, and other smaller fixes and additions. --- public/js/main.js | 14 +++++ samples/module/optionsPhantom.js | 26 ++++++-- samples/module/optionsPuppeteer.js | 24 ++++++-- samples/module/promises.js | 22 +++++-- samples/module/svg.js | 30 +++++++--- tests/cli/cliTestRunner.js | 6 +- tests/cli/cliTestRunnerSingle.js | 6 +- tests/http/httpTestRunner.js | 6 +- tests/http/httpTestRunnerSingle.js | 6 +- tests/node/nodeTestRunner.js | 30 +++++----- tests/node/nodeTestRunnerSingle.js | 60 ++++++++++--------- tests/node/scenarios/svgBasic.json | 6 +- tests/node/scenarios/svgBasicWithScale.json | 6 +- .../scenarios/svgBasicWithScaleToPdf.json | 8 +-- tests/node/scenarios/svgForeignObject.json | 6 +- tests/other/sideBySide.js | 6 +- tests/other/stressTest.js | 12 ++-- tests/unit/cache.test.js | 17 +++++- tests/unit/envs.test.js | 16 ++++- tests/unit/index.test.js | 16 +++++ tests/unit/sanitize.test.js | 16 +++++ tests/unit/utils.test.js | 32 +++++++--- 22 files changed, 262 insertions(+), 109 deletions(-) diff --git a/public/js/main.js b/public/js/main.js index 374b9875..4c613d81 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,3 +1,17 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + /* eslint-disable no-undef */ const highexp = {}; diff --git a/samples/module/optionsPhantom.js b/samples/module/optionsPhantom.js index a76a71a2..fa9618b4 100644 --- a/samples/module/optionsPhantom.js +++ b/samples/module/optionsPhantom.js @@ -1,9 +1,23 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + import { writeFileSync } from 'fs'; -import exporter from '../../lib/index.js'; +import exporter, { initExport } from '../../lib/index.js'; // Export settings with the old options structure (PhantomJS) -// Will be mapped appropriately to the new structure with the mapToNewConfig utility +// Will be mapped appropriately to the new structure with the `mapToNewConfig` const exportSettings = { type: 'png', constr: 'chart', @@ -56,20 +70,20 @@ const start = async () => { const options = exporter.setOptions(mappedOptions); // Init a pool for one export - await exporter.initExport(options); + await initExport(options); // Perform an export - await exporter.startExport(options, async (error, info) => { + await exporter.startExport(options, async (error, data) => { // Exit process and display error if (error) { throw error; } - const { outfile, type } = info.options.export; + const { outfile, type } = data.options.export; // Save the base64 from a buffer to a correct image file writeFileSync( outfile, - type !== 'svg' ? Buffer.from(info.result, 'base64') : info.result + type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result ); // Kill the pool diff --git a/samples/module/optionsPuppeteer.js b/samples/module/optionsPuppeteer.js index a1783a0f..b9a5b674 100644 --- a/samples/module/optionsPuppeteer.js +++ b/samples/module/optionsPuppeteer.js @@ -1,6 +1,20 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + import { writeFileSync } from 'fs'; -import exporter from '../../lib/index.js'; +import exporter, { initExport } from '../../lib/index.js'; // Export settings with new options structure (Puppeteer) const exportSettings = { @@ -127,20 +141,20 @@ const start = async () => { const options = exporter.setOptions(exportSettings); // Init a pool for one export - await exporter.initExport(options); + await initExport(options); // Perform an export - await exporter.startExport(options, async (error, info) => { + await exporter.startExport(options, async (error, data) => { // Exit process and display error if (error) { throw error; } - const { outfile, type } = info.options.export; + const { outfile, type } = data.options.export; // Save the base64 from a buffer to a correct image file writeFileSync( outfile, - type !== 'svg' ? Buffer.from(info.result, 'base64') : info.result + type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result ); // Kill the pool diff --git a/samples/module/promises.js b/samples/module/promises.js index c5cb7ab4..1790c1b2 100644 --- a/samples/module/promises.js +++ b/samples/module/promises.js @@ -1,13 +1,27 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + import { writeFileSync } from 'fs'; -import exporter from '../../lib/index.js'; +import exporter, { initExport } from '../../lib/index.js'; const exportCharts = async (charts, exportOptions = {}) => { // Set the new options const options = exporter.setOptions(exportOptions); // Init the pool - await exporter.initExport(options); + await initExport(options); const promises = []; const chartResults = []; @@ -19,13 +33,13 @@ const exportCharts = async (charts, exportOptions = {}) => { const settings = { ...options }; settings.export.options = chart; - exporter.startExport(settings, (error, info) => { + exporter.startExport(settings, (error, data) => { if (error) { return reject(error); } // Add the data to the chartResults - chartResults.push(info.result); + chartResults.push(data.result); resolve(); }); }) diff --git a/samples/module/svg.js b/samples/module/svg.js index 81a68263..6028efca 100644 --- a/samples/module/svg.js +++ b/samples/module/svg.js @@ -1,20 +1,32 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + import { writeFileSync } from 'fs'; -import exporter from '../../lib/index.js'; +import exporter, { initExport } from '../../lib/index.js'; // Export settings with new options structure (Puppeteer) const exportSettings = { export: { type: 'png', outfile: './samples/module/svg.png', - scale: 2 + scale: 2, + svg: 'Highcharts.com' }, pool: { minWorkers: 1, maxWorkers: 1 - }, - payload: { - svg: 'Highcharts.com' } }; @@ -24,20 +36,20 @@ const start = async () => { const options = exporter.setOptions(exportSettings); // Init a pool for one export - await exporter.initExport(options); + await initExport(options); // Perform an export - await exporter.startExport(options, async (error, info) => { + await exporter.startExport(options, async (error, data) => { // Exit process and display error if (error) { throw error; } - const { outfile, type } = info.options.export; + const { outfile, type } = data.options.export; // Save the base64 from a buffer to a correct image file writeFileSync( outfile, - type !== 'svg' ? Buffer.from(info.result, 'base64') : info.result + type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result ); // Kill the pool diff --git a/tests/cli/cliTestRunner.js b/tests/cli/cliTestRunner.js index 5bd02a4c..6a27a589 100644 --- a/tests/cli/cliTestRunner.js +++ b/tests/cli/cliTestRunner.js @@ -19,7 +19,7 @@ import { promisify } from 'util'; import 'colors'; -import { __dirname } from '../../lib/utils.js'; +import { __dirname, getNewDateTime } from '../../lib/utils.js'; // Convert from callback to promise const spawn = promisify(exec); @@ -81,7 +81,7 @@ for (const file of files.filter((file) => file.endsWith('.json'))) { cliCommand = cliCommand.join(' '); // The start date of a CLI command - const startDate = new Date().getTime(); + const startDate = getNewDateTime(); let didFail = false; try { @@ -94,7 +94,7 @@ for (const file of files.filter((file) => file.endsWith('.json'))) { testCounter++; const endMessage = `CLI command from file: ${file}, took ${ - new Date().getTime() - startDate + getNewDateTime() - startDate }ms.`; console.log( diff --git a/tests/cli/cliTestRunnerSingle.js b/tests/cli/cliTestRunnerSingle.js index 985f6ea8..4a06d863 100644 --- a/tests/cli/cliTestRunnerSingle.js +++ b/tests/cli/cliTestRunnerSingle.js @@ -18,7 +18,7 @@ import { basename, join } from 'path'; import 'colors'; -import { __dirname } from '../../lib/utils.js'; +import { __dirname, getNewDateTime } from '../../lib/utils.js'; // Test runner message console.log( @@ -70,7 +70,7 @@ if (existsSync(file) && file.endsWith('.json')) { cliCommand = cliCommand.join(' '); // The start date of a CLI command - const startDate = new Date().getTime(); + const startDate = getNewDateTime(); // Launch command in a new process spawn(cliCommand); @@ -78,7 +78,7 @@ if (existsSync(file) && file.endsWith('.json')) { // Close event for a process process.on('exit', (code) => { const endMessage = `CLI command from file: ${file}, took ${ - new Date().getTime() - startDate + getNewDateTime() - startDate }ms.`; // If code is 1, it means that export server thrown an error diff --git a/tests/http/httpTestRunner.js b/tests/http/httpTestRunner.js index fbb0cec0..ff11389a 100644 --- a/tests/http/httpTestRunner.js +++ b/tests/http/httpTestRunner.js @@ -25,7 +25,7 @@ import { join } from 'path'; import 'colors'; import { fetch } from '../../lib/fetch.js'; -import { __dirname, clearText } from '../../lib/utils.js'; +import { __dirname, clearText, getNewDateTime } from '../../lib/utils.js'; // Test runner message console.log( @@ -87,7 +87,7 @@ fetch(`${url}/health`) return new Promise((resolve) => { // The start date of a POST request - const startDate = new Date().getTime(); + const startDate = getNewDateTime(); const request = http.request( url, { @@ -110,7 +110,7 @@ fetch(`${url}/health`) fileStream.end(); const endMessage = `HTTP request with a payload from file: ${file}, took ${ - new Date().getTime() - startDate + getNewDateTime() - startDate }ms.`; // Based on received status code check if requests failed diff --git a/tests/http/httpTestRunnerSingle.js b/tests/http/httpTestRunnerSingle.js index 12241029..8e6fc4e9 100644 --- a/tests/http/httpTestRunnerSingle.js +++ b/tests/http/httpTestRunnerSingle.js @@ -19,7 +19,7 @@ import { basename, join } from 'path'; import 'colors'; import { fetch } from '../../lib/fetch.js'; -import { __dirname, clearText } from '../../lib/utils.js'; +import { __dirname, clearText, getNewDateTime } from '../../lib/utils.js'; // Test runner message console.log( @@ -64,7 +64,7 @@ fetch(`${url}/health`) ); // The start date of a POST request - const startDate = new Date().getTime(); + const startDate = getNewDateTime(); const request = http.request( url, { @@ -87,7 +87,7 @@ fetch(`${url}/health`) fileStream.end(); const endMessage = `HTTP request with a payload from file: ${file}, took ${ - new Date().getTime() - startDate + getNewDateTime() - startDate }ms.`; // Based on received status code check if requests failed diff --git a/tests/node/nodeTestRunner.js b/tests/node/nodeTestRunner.js index 9119aa70..36797a5b 100644 --- a/tests/node/nodeTestRunner.js +++ b/tests/node/nodeTestRunner.js @@ -23,8 +23,8 @@ import { basename, join } from 'path'; import 'colors'; -import exporter from '../../lib/index.js'; -import { __dirname } from '../../lib/utils.js'; +import exporter, { initExport } from '../../lib/index.js'; +import { __dirname, getNewDateTime } from '../../lib/utils.js'; console.log( 'Highcharts Export Server Node Test Runner'.yellow.bold.underline, @@ -46,12 +46,9 @@ console.log( // Get files names const files = readdirSync(scenariosPath); - // Set options - const options = exporter.setOptions(); - try { - // Initialize pool with disabled logging - await exporter.initExport(options); + // Initialize pool + await initExport(); } catch (error) { await exporter.killPool(); throw error; @@ -88,11 +85,14 @@ console.log( ); // The start date of a startExport function run - const startTime = new Date().getTime(); + const startTime = getNewDateTime(); + + // Init options + const options = exporter.setOptions(fileOptions); // Start the export process exporter - .startExport(fileOptions, (error, info) => { + .startExport(options, (error, data) => { // Throw an error if (error) { throw error; @@ -100,16 +100,16 @@ console.log( // Save returned data to a correct image file if no error occured writeFileSync( - info.options.export.outfile, - info.options?.export?.type !== 'svg' - ? Buffer.from(info.result, 'base64') - : info.result + data.options.export.outfile, + data.options?.export?.type !== 'svg' + ? Buffer.from(data.result, 'base64') + : data.result ); // Information about the results and the time it took console.log( `[Success] Node module from file: ${file}, took: ${ - new Date().getTime() - startTime + getNewDateTime() - startTime }ms.`.green ); }) @@ -117,7 +117,7 @@ console.log( // Information about the error and the time it took console.log( `[Fail] Node module from file: ${file}, took: ${ - new Date().getTime() - startTime + getNewDateTime() - startTime }ms.`.red ); exporter.setLogLevel(1); diff --git a/tests/node/nodeTestRunnerSingle.js b/tests/node/nodeTestRunnerSingle.js index af7b1ae2..bffdccf3 100644 --- a/tests/node/nodeTestRunnerSingle.js +++ b/tests/node/nodeTestRunnerSingle.js @@ -17,8 +17,12 @@ import { basename, join } from 'path'; import 'colors'; -import exporter from '../../lib/index.js'; -import { __dirname } from '../../lib/utils.js'; +import exporter, { initExport } from '../../lib/index.js'; +import { + __dirname, + getNewDateTime, + mergeConfigOptions +} from '../../lib/utils.js'; console.log( 'Highcharts Export Server Node Test Runner'.yellow.bold.underline, @@ -42,23 +46,6 @@ console.log( // Check if file even exists and if it is a JSON if (existsSync(file) && file.endsWith('.json')) { - // Set options - const options = exporter.setOptions({ - pool: { - minWorkers: 1, - maxWorkers: 1 - }, - logging: { - level: 0 - } - }); - - // Initialize pool with disabled logging - await exporter.initExport(options); - - // Start the export - console.log('[Test runner]'.blue, `Processing test ${file}.`); - // Options from a file const fileOptions = JSON.parse(readFileSync(file)); @@ -72,12 +59,31 @@ console.log( ) ); + // Set options + const options = exporter.setOptions( + mergeConfigOptions(fileOptions, { + pool: { + minWorkers: 1, + maxWorkers: 1 + }, + logging: { + level: 0 + } + }) + ); + + // Initialize pool with disabled logging + await initExport(options); + + // Start the export + console.log('[Test runner]'.blue, `Processing test ${file}.`); + // The start date of a startExport function run - const startTime = new Date().getTime(); + const startTime = getNewDateTime(); try { // Start the export process - await exporter.startExport(fileOptions, async (error, info) => { + await exporter.startExport(options, async (error, data) => { // Throw an error if (error) { throw error; @@ -85,16 +91,16 @@ console.log( // Save returned data to a correct image file if no error occured writeFileSync( - info.options.export.outfile, - info.options?.export?.type !== 'svg' - ? Buffer.from(info.result, 'base64') - : info.result + data.options.export.outfile, + data.options?.export?.type !== 'svg' + ? Buffer.from(data.result, 'base64') + : data.result ); // Information about the results and the time it took console.log( `[Success] Node module from file: ${file}, took: ${ - new Date().getTime() - startTime + getNewDateTime() - startTime }ms.`.green ); }); @@ -102,7 +108,7 @@ console.log( // Information about the error and the time it took console.log( `[Fail] Node module from file: ${file}, took: ${ - new Date().getTime() - startTime + getNewDateTime() - startTime }ms.`.red ); } diff --git a/tests/node/scenarios/svgBasic.json b/tests/node/scenarios/svgBasic.json index 44d36aaf..e267dba7 100644 --- a/tests/node/scenarios/svgBasic.json +++ b/tests/node/scenarios/svgBasic.json @@ -1,7 +1,7 @@ { - "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com", - "scale": 2, "export": { - "outfile": "svgBasic.png" + "outfile": "svgBasic.png", + "scale": 2, + "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com" } } diff --git a/tests/node/scenarios/svgBasicWithScale.json b/tests/node/scenarios/svgBasicWithScale.json index 1132fd6a..b4f82271 100644 --- a/tests/node/scenarios/svgBasicWithScale.json +++ b/tests/node/scenarios/svgBasicWithScale.json @@ -1,7 +1,7 @@ { - "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com", - "scale": 3, "export": { - "outfile": "svgBasicWithScale.png" + "outfile": "svgBasicWithScale.png", + "scale": 3, + "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com" } } diff --git a/tests/node/scenarios/svgBasicWithScaleToPdf.json b/tests/node/scenarios/svgBasicWithScaleToPdf.json index 8e2fb279..77bdba94 100644 --- a/tests/node/scenarios/svgBasicWithScaleToPdf.json +++ b/tests/node/scenarios/svgBasicWithScaleToPdf.json @@ -1,8 +1,8 @@ { - "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com", - "type": "pdf", - "scale": 2, "export": { - "outfile": "svgBasicWithScaleToPdf.pdf" + "type": "pdf", + "outfile": "svgBasicWithScaleToPdf.pdf", + "scale": 2, + "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com" } } diff --git a/tests/node/scenarios/svgForeignObject.json b/tests/node/scenarios/svgForeignObject.json index 74f9112f..e7f3ace7 100644 --- a/tests/node/scenarios/svgForeignObject.json +++ b/tests/node/scenarios/svgForeignObject.json @@ -1,7 +1,7 @@ { - "svg": "Created with Highcharts 10.2.1ValuesSVG with a foreign objectSeries 1Series 2JanFebMarAprMayJunJulAugSepOctNovDec-1001020304050Highcharts.comThis subtitle is HTML", - "scale": 2, "export": { - "outfile": "svgForeignObject.png" + "outfile": "svgForeignObject.png", + "scale": 2, + "svg": "Created with Highcharts 10.2.1ValuesSVG with a foreign objectSeries 1Series 2JanFebMarAprMayJunJulAugSepOctNovDec-1001020304050Highcharts.comThis subtitle is HTML" } } diff --git a/tests/other/sideBySide.js b/tests/other/sideBySide.js index 2c797134..43c9a9a2 100644 --- a/tests/other/sideBySide.js +++ b/tests/other/sideBySide.js @@ -19,7 +19,7 @@ import { join } from 'path'; import 'colors'; -import { __dirname } from '../../lib/utils.js'; +import { __dirname, getNewDateTime } from '../../lib/utils.js'; // Results paths const resultsPath = join(__dirname, 'tests', 'other', '_results'); @@ -93,7 +93,7 @@ try { ].join(' '); // The start date of a POST request - const startDate = new Date().getTime(); + const startDate = getNewDateTime(); // Launch command in a new process // eslint-disable-next-line no-global-assign @@ -103,7 +103,7 @@ try { process.on('close', () => { const message = `Done with ${ index ? '[PhantomJS]' : '[Puppeteer]' - } ${type} export, took ${new Date().getTime() - startDate}ms.`; + } ${type} export, took ${getNewDateTime() - startDate}ms.`; console.log(index ? message.blue : message.green); }); diff --git a/tests/other/stressTest.js b/tests/other/stressTest.js index 26bd1841..2f90ea89 100644 --- a/tests/other/stressTest.js +++ b/tests/other/stressTest.js @@ -12,9 +12,11 @@ See LICENSE file in root for details. *******************************************************************************/ -import { fetch, post } from '../../lib/fetch.js'; import 'colors'; +import { fetch, post } from '../../lib/fetch.js'; +import { getNewDateTime } from '../../lib/utils.js'; + // Test message console.log( 'Highcharts Export Server stress test'.yellow, @@ -45,14 +47,14 @@ const interval = 150; const stressTest = () => { for (let i = 1; i <= requestsNumber; i++) { - const startTime = new Date().getTime(); + const startTime = getNewDateTime(); // Perform a request post(url, requestBody) - .then(async (res) => { - const postTime = new Date().getTime() - startTime; + .then(async (response) => { + const postTime = getNewDateTime() - startTime; console.log(`${i} request is done, took ${postTime}ms`); - console.log(`---\n${res.text}\n---`); + console.log(`---\n${response.text}\n---`); }) .catch((error) => { return console.log(`[${i}] request returned error: ${error}`); diff --git a/tests/unit/cache.test.js b/tests/unit/cache.test.js index 8086706c..af01d01e 100644 --- a/tests/unit/cache.test.js +++ b/tests/unit/cache.test.js @@ -1,11 +1,24 @@ -// cacheManager.test.js +/******************************************************************************* + +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. + +*******************************************************************************/ + import { extractVersion, extractModuleName } from '../../lib/cache'; describe('extractVersion', () => { it('should extract the Highcharts version correctly', () => { const cache = { sources: '/* Highcharts 9.3.2 */' }; - const version = extractVersion(cache); + const version = extractVersion(cache.sources); expect(version).toBe('Highcharts 9.3.2'); }); }); diff --git a/tests/unit/envs.test.js b/tests/unit/envs.test.js index fe31e8b9..bf13f9a8 100644 --- a/tests/unit/envs.test.js +++ b/tests/unit/envs.test.js @@ -1,4 +1,18 @@ -import { Config } from '../../lib/envs'; +/******************************************************************************* + +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. + +*******************************************************************************/ + +import { Config } from '../../lib/envs.js'; describe('Environment variables should be correctly parsed', () => { test('HIGHCHARTS_VERSION accepts latests and not unrelated strings', () => { diff --git a/tests/unit/index.test.js b/tests/unit/index.test.js index 85e102db..b8a28276 100644 --- a/tests/unit/index.test.js +++ b/tests/unit/index.test.js @@ -1,3 +1,19 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +import { describe, expect, it } from '@jest/globals'; + describe('Simple Variable Comparison', () => { it('should compare two variables for equality', () => { const variable1 = 42; diff --git a/tests/unit/sanitize.test.js b/tests/unit/sanitize.test.js index 060ff643..48dfb1de 100644 --- a/tests/unit/sanitize.test.js +++ b/tests/unit/sanitize.test.js @@ -1,3 +1,19 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +import { describe, expect, it } from '@jest/globals'; + import { sanitize } from '../../lib/sanitize.js'; describe('sanitize', () => { diff --git a/tests/unit/utils.test.js b/tests/unit/utils.test.js index 7c89f52d..3ee11bf2 100644 --- a/tests/unit/utils.test.js +++ b/tests/unit/utils.test.js @@ -1,3 +1,20 @@ +/******************************************************************************* + +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. + +*******************************************************************************/ + +//// VALIDATION +import { describe, expect, it } from '@jest/globals'; + import { clearText, fixType, @@ -7,7 +24,7 @@ import { isObject, isObjectEmpty, isPrivateRangeUrlFound -} from '../../lib/utils'; +} from '../../lib/utils.js'; describe('clearText', () => { it('replaces multiple spaces with a single space and trims the text', () => { @@ -50,9 +67,9 @@ describe('isCorrectJSON', () => { expect(isCorrectJSON(json)).toEqual({ key: 'value' }); }); - it('returns false for invalid JSON strings', () => { + it('returns null for invalid JSON strings', () => { const json = '{"key":value}'; - expect(isCorrectJSON(json)).toBe(false); + expect(isCorrectJSON(json)).toBe(null); }); it('parses JavaScript objects', () => { @@ -69,17 +86,17 @@ describe('isCorrectJSON', () => { it('handles non-JSON strings', () => { const str = 'Just a string'; - expect(isCorrectJSON(str)).toBe(false); + expect(isCorrectJSON(str)).toBe(null); }); it('handles non-object types (e.g., numbers, booleans)', () => { - expect(isCorrectJSON(123)).toBe(123); - expect(isCorrectJSON(true)).toBe(true); + expect(isCorrectJSON(123)).toBe(null); + expect(isCorrectJSON(true)).toBe(null); }); it('correctly parses and stringifies an array when toString is true', () => { const arr = [1, 2, 3]; - expect(isCorrectJSON(arr, true)).toBe('[1,2,3]'); + expect(isCorrectJSON(arr, true)).toBe(null); }); }); @@ -166,3 +183,4 @@ describe('isPrivateRangeUrlFound', () => { }); }); }); +//// From 2f3616136907659ecc1ea0ca275f538a6e5abcc7 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 10 Dec 2024 22:07:54 +0100 Subject: [PATCH 030/102] Small corrections. --- lib/errors/ValidationError.js | 33 --------------------------------- tests/unit/utils.test.js | 2 -- 2 files changed, 35 deletions(-) delete mode 100644 lib/errors/ValidationError.js diff --git a/lib/errors/ValidationError.js b/lib/errors/ValidationError.js deleted file mode 100644 index 7418a4f3..00000000 --- a/lib/errors/ValidationError.js +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - -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. - -*******************************************************************************/ - -import HttpError from './HttpError.js'; - -/** - * The `ValidationError` error class that extends `HttpError`. Used to handle - * errors related to validation of provided options. - */ -class ValidationError extends HttpError { - /** - * Creates an instance of `ValidationError`. - */ - constructor() { - super( - 'The provided options are not correct. Please check if your data is of the correct types.', - 400 - ); - } -} - -export default ValidationError; diff --git a/tests/unit/utils.test.js b/tests/unit/utils.test.js index 3ee11bf2..db693072 100644 --- a/tests/unit/utils.test.js +++ b/tests/unit/utils.test.js @@ -12,7 +12,6 @@ See LICENSE file in root for details. *******************************************************************************/ -//// VALIDATION import { describe, expect, it } from '@jest/globals'; import { @@ -183,4 +182,3 @@ describe('isPrivateRangeUrlFound', () => { }); }); }); -//// From 015af527a532414a509762054ab3613e9e6fb83a Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:01:02 +0100 Subject: [PATCH 031/102] Optimization of the cli module. --- bin/cli.js | 61 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index b50f40ab..88c649be 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -3,7 +3,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -22,69 +22,76 @@ See LICENSE file in root for details. */ import { singleExport, batchExport } from '../lib/chart.js'; -import { setOptions } from '../lib/config.js'; +import { + printLicense, + printUsage, + printVersion, + setOptions +} from '../lib/config.js'; import { initExport } from '../lib/index.js'; import { log, logWithStack } from '../lib/logger.js'; import { manualConfig } from '../lib/prompt.js'; import { shutdownCleanUp } from '../lib/resourceRelease.js'; -import { printLicense, printVersion } from '../lib/utils.js'; -import { printUsage } from '../lib/schemas/config.js'; import { startServer } from '../lib/server/server.js'; import ExportError from '../lib/errors/ExportError.js'; /** * The primary function to initiate the server or perform the direct export. - * Logs an error if it occurs during the execution and gracefully shut down + * Logs an error if it occurs during the execution and gracefully shuts down * the process. * * @async * @function start * - * @throws {ExportError} Throws an `ExportError` if no valid options are - * provided. + * @returns {Promise} A Promise that resolves to ending the function + * execution when displaying license, version, or usage information, or when + * triggering manual configuration using the prompts functionality. + * + * @throws {ExportError} Throws an `ExportError` if no valid options + * are provided. */ async function start() { try { // Get the CLI arguments const args = process.argv; - // Display license information if requested - if (['-v', '--v'].includes(args[args.length - 1])) { - // Print logo with the version and license information - return printLicense(); + // Display version and license information if requested + if (['-v', '--v'].some((a) => args.includes(a))) { + // Print logo with version and license information + printLicense(); + return; } // Display help information if requested - if (['-h', '--h', '-help', '--help'].includes(args[args.length - 1])) { + if (['-h', '--h', '-help', '--help'].some((a) => args.includes(a))) { // Print CLI usage information - return printUsage(); + printUsage(); + return; } - // Print CLI usage information if no arguments supplied + // Print CLI usage information if no arguments are supplied if (args.length <= 2) { - printUsage(); - return log( + printVersion(); + log( 2, - '[cli] The number of provided arguments is too small. Please refer to the help section above.' + '[cli] The number of provided arguments is too small. Please refer to the help section (use --h or --help command).' ); + return; } - // Set the options, keeping the priority order of setting values: - // 1. Options from the `lib/schemas/config.js` file - // 2. Options from a custom JSON file (loaded by the `loadConfig` argument) - // 3. Options from the environment variables (the `.env` file) - // 4. Options from the CLI - const options = setOptions(null, args, true); + // Set the options, keeping the priority order of setting values + const options = setOptions({}, args, true); - // If all options correctly parsed + // If all options are correctly parsed if (options) { // Print initial logo or text with the version printVersion(options.other.noLogo); // In this case we want to prepare config manually if (options.customLogic.createConfig) { - return manualConfig(options.customLogic.createConfig); + manualConfig(options.customLogic.createConfig); + return; } // Start server @@ -97,7 +104,7 @@ async function start() { } else { // Perform batch exports if (options.export.batch) { - // Init a pool for the batch exports + // Init the export mechanism for batch exports await initExport(options); // Start batch exports @@ -107,7 +114,7 @@ async function start() { options.pool.minWorkers = 1; options.pool.maxWorkers = 1; - // Init a pool for one export + // Init the export mechanism for a single export await initExport(options); // Start a single export From 9c8035f45495ac4fe8ff2e0b47015f73d63303d9 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:01:30 +0100 Subject: [PATCH 032/102] Optimization of the browser module, with the newPage and _setPageEvents corrections. --- lib/browser.js | 163 ++++++++++++++++++++++--------------------------- 1 file changed, 74 insertions(+), 89 deletions(-) diff --git a/lib/browser.js b/lib/browser.js index c05a1474..e2b5f892 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -14,10 +14,10 @@ See LICENSE file in root for details. /** * @overview This module provides functions for managing Puppeteer browser - * instances, creating and clearing pages, injecting custom resources, - * and setting up Highcharts for server-side rendering. The module ensures that - * resources are correctly managed and can handle failures during operations - * like launching the browser or creating new pages. + * instance, creating and clearing pages, injecting custom resources, + * and setting up Highcharts for server-side rendering. The module ensures + * that resources are correctly managed and can handle failures during + * operations like launching the browser or creating new pages. */ import { readFileSync } from 'fs'; @@ -29,23 +29,25 @@ import { getCachePath } from './cache.js'; import { getOptions } from './config.js'; import { setupHighcharts } from './highcharts.js'; import { log, logWithStack } from './logger.js'; -import { __dirname } from './utils.js'; +import { __dirname, getAbsolutePath } from './utils.js'; import ExportError from './errors/ExportError.js'; -// Get the template for the page -const template = readFileSync(__dirname + '/templates/template.html', 'utf8'); +// Get the template for pages +const template = readFileSync( + join(__dirname, 'templates', 'template.html'), + 'utf8' +); // To save the browser -let browser; +let browser = null; /** * Retrieves the existing Puppeteer browser instance. * * @function getBrowser * - * @returns {Promise} A Promise resolving to the Puppeteer browser - * instance. + * @returns {Object} The Puppeteer browser instance. * * @throws {ExportError} Throws an `ExportError` if no valid browser * has been created. @@ -63,28 +65,28 @@ export function getBrowser() { * @async * @function createBrowser * - * @param {Array.} [puppeteerArg=[]] - Additional arguments - * for Puppeteer launch. The default value is an empty array. + * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer + * launch. * - * @returns {Promise} A Promise resolving to the Puppeteer browser - * instance. + * @returns {Promise} A Promise that resolves to the created 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. */ -export async function createBrowser(puppeteerArgs = []) { +export async function createBrowser(puppeteerArgs) { // Get `debug` and `other` options const { debug, other } = getOptions(); - // Get the debug options + // Get the `debug` options const { enable: enabledDebug, ...debugOptions } = debug; // Launch options for the browser instance const launchOptions = { headless: other.browserShellMode ? 'shell' : true, userDataDir: 'tmp', - args: puppeteerArgs, + args: puppeteerArgs || [], handleSIGINT: false, handleSIGTERM: false, handleSIGHUP: false, @@ -95,6 +97,7 @@ export async function createBrowser(puppeteerArgs = []) { // Create a browser if (!browser) { + // A counter for the browser's launch retries let tryCount = 0; const open = async () => { @@ -116,6 +119,8 @@ export async function createBrowser(puppeteerArgs = []) { // Retry to launch browser until reaching max attempts if (tryCount < 25) { log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`); + + // Wait for a 4 seconds before trying again await new Promise((response) => setTimeout(response, 4000)); await open(); } else { @@ -148,7 +153,7 @@ export async function createBrowser(puppeteerArgs = []) { } } - // Return a browser promise + // Return a browser instance return browser; } @@ -157,9 +162,6 @@ export async function createBrowser(puppeteerArgs = []) { * * @async * @function closeBrowser - * - * @returns {Promise} A Promise resolving to true after the browser - * is closed. */ export async function closeBrowser() { // Close the browser when connected @@ -172,26 +174,20 @@ export async function closeBrowser() { /** * Creates a new Puppeteer page within an existing browser instance. - * If the browser instance is not available, returns false. The function creates - * a new page, disables caching, sets content using `_setPageContent()`, - * and returns the created Puppeteer page. + * The function creates a new page, disables caching, sets content using + * the `_setPageContent()`, and returns the created Puppeteer page. * * @async * @function newPage * - * @param {Object} poolResource - The pool resource that contians page and id. - * - * @returns {(boolean|object)} Returns false if the browser instance - * is not available, or a Puppeteer Page object representing the newly created - * page. + * @param {Object} poolResource - The pool resource that contains `id`, + * `workCount`, and `page`. * * @throws {ExportError} Throws an `ExportError` if no valid browser * has been connected or if a page is invalid or closed. */ export async function newPage(poolResource) { - const startDate = new Date().getTime(); - - // Throw an error in case of no connected browser + // Error in case of no connected browser if (!browser || !browser.connected) { throw new ExportError(`[browser] Browser is not yet connected.`, 500); } @@ -206,42 +202,35 @@ export async function newPage(poolResource) { await _setPageContent(poolResource.page); // Set page events - _setPageEvents(poolResource); + _setPageEvents(poolResource.page); // Check if the page is correctly created if (!poolResource.page || poolResource.page.isClosed()) { - throw new ExportError('The page is invalid or closed.', 400); + throw new ExportError('[browser] The page is invalid or closed.', 400); } - - log( - 3, - `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${ - new Date().getTime() - startDate - }ms.` - ); - - // Return the resource with a ready to use page - return poolResource; } /** * Clears the content of a Puppeteer Page based on the specified mode. Logs - * thrown error if clearing the page content fails. + * thrown error if clearing of a page's content fails. * * @async * @function clearPage * - * @param {Object} poolResource - The pool resource that contians page and id. + * @param {Object} poolResource - The pool resource that contains page and id. * @param {boolean} [hardReset=false] - A flag indicating the type of clearing - * to be performed. If true, navigates to 'about:blank' and resets content + * to be performed. If true, navigates to `about:blank` and resets content * and scripts. If false, clears the body content by setting a predefined HTML - * structure. + * structure. The default value is false. + * + * @returns {Promise} A Promise that resolves to true when page + * is correctly cleared and false when it is not. */ export async function clearPage(poolResource, hardReset = false) { try { if (poolResource.page && !poolResource.page.isClosed()) { if (hardReset) { - // Navigate to about:blank + // Navigate to `about:blank` await poolResource.page.goto('about:blank', { waitUntil: 'domcontentloaded' }); @@ -263,6 +252,7 @@ export async function clearPage(poolResource, hardReset = false) { error, `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.` ); + // Set the `workLimit` to exceeded in order to recreate the resource poolResource.workCount = getOptions().pool.workLimit + 1; } @@ -276,12 +266,13 @@ export async function clearPage(poolResource, hardReset = false) { * @async * @function addPageResources * - * @param {Object} page - The Puppeteer Page object to which resources will + * @param {Object} page - The Puppeteer page object to which resources will * be added. - * @param {Object} customLogicOptions - The custom logic options. + * @param {Object} customLogicOptions - The object containing `customLogic` + * options. * - * @returns {Promise>} Promise resolving to an array of injected - * resources. + * @returns {Promise>} A Promise that resolves to an array + * of injected resources. */ export async function addPageResources(page, customLogicOptions) { // Injected resources array @@ -308,7 +299,7 @@ export async function addPageResources(page, customLogicOptions) { injectedJs.push( isLocal ? { - content: readFileSync(file, 'utf8') + content: readFileSync(getAbsolutePath(file), 'utf8') } : { url: file @@ -321,7 +312,7 @@ export async function addPageResources(page, customLogicOptions) { try { injectedResources.push(await page.addScriptTag(jsResource)); } catch (error) { - logWithStack(2, error, `[export] The JS resource cannot be loaded.`); + logWithStack(2, error, `[browser] The JS resource cannot be loaded.`); } } injectedJs.length = 0; @@ -366,7 +357,11 @@ export async function addPageResources(page, customLogicOptions) { try { injectedResources.push(await page.addStyleTag(cssResource)); } catch (error) { - logWithStack(2, error, `[export] The CSS resource cannot be loaded.`); + logWithStack( + 2, + error, + `[browser] The CSS resource cannot be loaded.` + ); } } injectedCss.length = 0; @@ -376,14 +371,14 @@ export async function addPageResources(page, customLogicOptions) { } /** - * Clears out all state set on the page with addScriptTag/addStyleTag. Removes - * injected resources and resets CSS and script tags on the page. Additionally, - * it destroys previously existing charts. + * Clears out all state set on the page with `addScriptTag` and `addStyleTag`. + * Removes injected resources and resets CSS and script tags on the page. + * Additionally, it destroys previously existing charts. * * @async * @function clearPageResources * - * @param {Object} page - The Puppeteer Page object from which resources will + * @param {Object} page - The Puppeteer page object from which resources will * be cleared. * @param {Array.} injectedResources - Array of injected resources * to be cleared. @@ -396,8 +391,7 @@ export async function clearPageResources(page, injectedResources) { // Destroy old charts after export is done and reset all CSS and script tags await page.evaluate(() => { - // We are not guaranteed that Highcharts is loaded, e,g, when doing SVG - // exports + // We are not guaranteed that Highcharts is loaded, when doing SVG exports if (typeof Highcharts !== 'undefined') { // eslint-disable-next-line no-undef const oldCharts = Highcharts.charts; @@ -430,65 +424,56 @@ export async function clearPageResources(page, injectedResources) { } }); } catch (error) { - logWithStack(1, error, `[browser] Could not clear page's resources.`); + logWithStack(2, error, `[browser] Could not clear page's resources.`); } } /** * Sets the content for a Puppeteer Page using a predefined template - * and additional scripts. Also, sets the pageerror in order to catch - * and display errors from the window context. + * and additional scripts. * * @async * @function _setPageContent * - * @param {Object} page - The Puppeteer Page object for which the content + * @param {Object} page - The Puppeteer page object to which the content * is being set. */ async function _setPageContent(page) { + // Set the initial page content await page.setContent(template, { waitUntil: 'domcontentloaded' }); // Add all registered Higcharts scripts, quite demanding await page.addScriptTag({ path: join(getCachePath(), 'sources.js') }); - // Set the initial animObject + // Set the initial `animObject` for Highcharts await page.evaluate(setupHighcharts); } /** - * Set events for a Puppeteer Page. + * Set events (like `pageerror` and `console`) for a Puppeteer Page in order + * to catch and display errors and console logs from the window context. * * @function _setPageEvents * - * @param {Object} poolResource - The pool resource that contians page and id. + * @param {Object} page - The Puppeteer page object to which the listeners + * are being set. */ -function _setPageEvents(poolResource) { - // Get `debug` and `pool` options - const { debug, pool } = getOptions(); +function _setPageEvents(page) { + // Get `debug` options + const { debug } = getOptions(); - // Set the pageerror listener - poolResource.page.on('pageerror', async (error) => { + // Set the `pageerror` listener + page.on('pageerror', async () => { // It would seem like this may fire at the same time or shortly before // a page is closed. - if (poolResource.page.isClosed()) { + if (page.isClosed()) { return; } - - await poolResource.page.$eval( - '#container', - (element, errorMessage) => { - // eslint-disable-next-line no-undef - if (window._displayErrors) { - element.innerHTML = errorMessage; - } - }, - `

Chart input data error:

${error.toString()}` - ); }); - // Set the console listener, if needed + // Set the `console` listener, if needed if (debug.enable && debug.listenToConsole) { - poolResource.page.on('console', (message) => { + page.on('console', (message) => { console.log(`[debug] ${message.text()}`); }); } From f859e59e94953156fe24fbd1d4c52881514e95d6 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:02:10 +0100 Subject: [PATCH 033/102] Optimization of the cache module. --- lib/cache.js | 107 ++++++++++++++++++++++++++------------------------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/lib/cache.js b/lib/cache.js index bf83a913..c40eab91 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -17,18 +17,19 @@ See LICENSE file in root for details. * the Highcharts library along with its dependencies. It ensures that these * resources are stored and retrieved efficiently to optimize performance * and reduce redundant network requests. The cache is stored in the `.cache` - * directory, which serves as a dedicated folder for keeping cached files. + * directory by default, which serves as a dedicated folder for keeping cached + * files. */ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'; -import { isAbsolute, join } from 'path'; +import { join } from 'path'; import { HttpsProxyAgent } from 'https-proxy-agent'; import { getOptions } from './config.js'; import { fetch } from './fetch.js'; import { log } from './logger.js'; -import { __dirname } from './utils.js'; +import { __dirname, getAbsolutePath } from './utils.js'; import ExportError from './errors/ExportError.js'; @@ -47,11 +48,9 @@ const cache = { * @async * @function checkAndUpdateCache * - * @param {Object} highchartsOptions - Object containing highcharts options. - * @param {Object} serverProxyOptions - Object containing server options. - * - * @returns {Promise} A Promise that resolves once the cache is checked - * and updated. + * @param {Object} highchartsOptions - Object containing `highcharts` options. + * @param {Object} serverProxyOptions - Object containing `server.proxy` + * options. */ export async function checkAndUpdateCache( highchartsOptions, @@ -62,15 +61,15 @@ export async function checkAndUpdateCache( // Get the cache path const cachePath = getCachePath(); - // Prepare paths to manifest and sources from the .cache folder + // Prepare paths to manifest and sources from the cache folder const manifestPath = join(cachePath, 'manifest.json'); const sourcePath = join(cachePath, 'sources.js'); // Create the cache destination if it doesn't exist already !existsSync(cachePath) && mkdirSync(cachePath, { recursive: true }); - // Fetch all the scripts either if manifest.json does not exist - // or if the forceFetch option is enabled + // Fetch all the scripts either if the `manifest.json` does not exist + // or if the `forceFetch` option is enabled if (!existsSync(manifestPath) || highchartsOptions.forceFetch) { log(3, '[cache] Fetching and caching Highcharts dependencies.'); fetchedModules = await _updateCache( @@ -92,6 +91,7 @@ export async function checkAndUpdateCache( manifest.modules = moduleMap; } + // Get the actual number of scripts to be fetched const { coreScripts, moduleScripts, indicatorScripts } = highchartsOptions; const numberOfModules = coreScripts.length + moduleScripts.length + indicatorScripts.length; @@ -124,6 +124,7 @@ export async function checkAndUpdateCache( }); } + // Update cache if needed if (requestUpdate) { fetchedModules = await _updateCache( highchartsOptions, @@ -168,14 +169,10 @@ export function getHighchartsVersion() { * @function updateHighchartsVersion * * @param {string} newVersion - The new Highcharts version to be applied. - * - * @returns {Promise<(Object|boolean)>} A Promise resolving to the updated - * configuration with the new version, or false if no applied configuration - * exists. */ export async function updateHighchartsVersion(newVersion) { // Get the reference to the global options to update to the new version - const options = getOptions(true); + const options = getOptions(); // Set to the new version options.highcharts.version = newVersion; @@ -235,14 +232,10 @@ export function getCache() { * * @function getCachePath * - * @returns {string} The path to the cache directory for Highcharts. + * @returns {string} The absolute path to the cache directory for Highcharts. */ export function getCachePath() { - const cachePathOption = getOptions().highcharts.cachePath; - - return isAbsolute(cachePathOption) // #562 - ? cachePathOption - : join(__dirname, cachePathOption); + return getAbsolutePath(getOptions().highcharts.cachePath); // #562 } /** @@ -257,9 +250,10 @@ export function getCachePath() { * @param {Object} fetchedModules - An object which tracks which Highcharts * modules have been fetched. * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error - * should be thrown. This should be used only for the core scripts. + * should be thrown. This should be used only for the core scripts. The default + * value is false. * - * @returns {Promise} A Promise resolving to the text representation + * @returns {Promise} A Promise that resolves to the text representation * of the fetched script. * * @throws {ExportError} Throws an `ExportError` if there is a problem @@ -289,9 +283,10 @@ async function _fetchAndProcessScript( return response.text; } + // Based on the `shouldThrowError` flag, decide how to serve error message if (shouldThrowError) { throw new ExportError( - `Could not fetch the ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`, + `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`, 404 ).setError(response); } else { @@ -300,8 +295,6 @@ async function _fetchAndProcessScript( `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.` ); } - - return ''; } /** @@ -311,9 +304,9 @@ async function _fetchAndProcessScript( * @async * @function _saveConfigToManifest * - * @param {Object} highchartsOptions - Highcharts-related configuration object. - * @param {Object} [fetchedModules={}] - An object that contains mapped names - * of fetched Highcharts modules to use. + * @param {Object} highchartsOptions - Object containing `highcharts` options. + * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts + * modules have been fetched. The default value is an empty object. * * @throws {ExportError} Throws an `ExportError` if an error occurs while * writing the cache manifest. @@ -343,21 +336,22 @@ async function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) { } /** - * Fetches Highcharts scripts and customScripts from the given CDNs. + * Fetches Highcharts `scripts` and `customScripts` from the given CDNs. * * @async * @function _fetchScripts * - * @param {string} coreScripts - Array of Highcharts core scripts to fetch. - * @param {string} moduleScripts - Array of Highcharts modules to fetch. - * @param {string} customScripts - Array of custom script paths to fetch - * (full URLs). - * @param {Object} proxyOptions - Options for the proxy agent to use - * for a request. + * @param {Array.} coreScripts - Highcharts core scripts to fetch. + * @param {Array.} moduleScripts - Highcharts modules to fetch. + * @param {Array.} customScripts - Custom script paths to fetch (full + * URLs). + * @param {Object} serverProxyOptions - Object containing `server.proxy` + * options. * @param {Object} fetchedModules - An object which tracks which Highcharts * modules have been fetched. * - * @returns {Promise} The fetched scripts content joined. + * @returns {Promise} A Promise that resolves to the fetched scripts + * content joined. * * @throws {ExportError} Throws an `ExportError` if an error occurs while * setting an HTTP Agent for proxy. @@ -366,13 +360,13 @@ async function _fetchScripts( coreScripts, moduleScripts, customScripts, - proxyOptions, + serverProxyOptions, fetchedModules ) { // Configure proxy if exists let proxyAgent; - const proxyHost = proxyOptions.host; - const proxyPort = proxyOptions.port; + const proxyHost = serverProxyOptions.host; + const proxyPort = serverProxyOptions.port; // Try to create a Proxy Agent if (proxyHost && proxyPort) { @@ -393,7 +387,7 @@ async function _fetchScripts( const requestOptions = proxyAgent ? { agent: proxyAgent, - timeout: proxyOptions.timeout + timeout: serverProxyOptions.timeout } : {}; @@ -419,11 +413,12 @@ async function _fetchScripts( * @async * @function _updateCache * - * @param {Object} highchartsOptions - Object containing Highcharts options. - * @param {Object} serverProxyOptions - Object containing server proxy options. + * @param {Object} highchartsOptions - Object containing `highcharts` options. + * @param {Object} serverProxyOptions - Object containing `server.proxy` + * options. * @param {string} sourcePath - The path to the source file in the cache. * - * @returns {Promise} A Promise resolving to an object representing + * @returns {Promise} A Promise that resolves to an object representing * the fetched modules. * * @throws {ExportError} Throws an `ExportError` if there is an issue updating @@ -433,7 +428,7 @@ async function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) { // Get Highcharts version for scripts const hcVersion = highchartsOptions.version === 'latest' - ? '' + ? null : `${highchartsOptions.version}`; // Get the CDN url for scripts @@ -449,18 +444,24 @@ async function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) { cache.sources = await _fetchScripts( [ - ...highchartsOptions.coreScripts.map( - (c) => `${cdnUrl}/${hcVersion}/${c}` + ...highchartsOptions.coreScripts.map((c) => + hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}` ) ], [ ...highchartsOptions.moduleScripts.map((m) => m === 'map' - ? `${cdnUrl}/maps/${hcVersion}/modules/${m}` - : `${cdnUrl}/${hcVersion}/modules/${m}` + ? hcVersion + ? `${cdnUrl}/maps/${hcVersion}/modules/${m}` + : `${cdnUrl}/maps/modules/${m}` + : hcVersion + ? `${cdnUrl}/${hcVersion}/modules/${m}` + : `${cdnUrl}/modules/${m}` ), - ...highchartsOptions.indicatorScripts.map( - (i) => `${cdnUrl}/stock/${hcVersion}/indicators/${i}` + ...highchartsOptions.indicatorScripts.map((i) => + hcVersion + ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}` + : `${cdnUrl}/stock/indicators/${i}` ) ], highchartsOptions.customScripts, From 8690d3cb95ff83b0cb7086bbab283ed6a7112d66 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:02:19 +0100 Subject: [PATCH 034/102] Main refactorization of the way how the export is prepared. --- lib/chart.js | 847 ++++++++++++++++++++++++++------------------------- 1 file changed, 432 insertions(+), 415 deletions(-) diff --git a/lib/chart.js b/lib/chart.js index 8c1bfe61..74197540 100644 --- a/lib/chart.js +++ b/lib/chart.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -13,251 +13,320 @@ See LICENSE file in root for details. *******************************************************************************/ /** - * @overview This module provides functions to manage the exporting - * of Highcharts configurations into various output formats such as images - * and SVGs. It supports single and batch export operations and allows extensive - * customization through options passed from configurations or APIs. + * @overview This module provides functions to prepare for the exporting charts + * into various image output formats such as JPEG, PNG, PDF, and SVGs. + * It supports single and batch export operations and allows customization + * through options passed from configurations or APIs. */ import { readFileSync, writeFileSync } from 'fs'; -import { getOptions } from './config.js'; +import { getOptions, mergeOptions, isAllowedConfig } from './config.js'; import { log, logWithStack } from './logger.js'; import { killPool, postWork, getPoolStats } from './pool.js'; import { sanitize } from './sanitize.js'; import { + fixOutfile, fixType, - isCorrectJSON, - optionsStringify, + getAbsolutePath, + getBase64, roundNumber, wrapAround } from './utils.js'; import ExportError from './errors/ExportError.js'; +// The global flag for the code execution permission let allowCodeExecution = false; /** - * Starts an export process. The `options` object contains final options - * gathered from all possible sources (config, custom json, env, cli). - * The `endCallback` is called when the export is completed, with the `error` - * object as the first argument and the `data` object as the second, - * which contains the base64 respresentation of a chart. + * Starts a single export process based on the specified options and saves + * the image in the provided outfile. * * @async - * @function startExport + * @function singleExport * - * @param {Object} [options=getOptions()] - The `options` object containing - * configuration for a custom export. The default value is the global options - * of the export server instance. - * @param {Function} endCallback - The callback function to be invoked upon - * finalizing work or upon error occurance of the exporting process. + * @param {Object} options - The `options` object, which may be a partial + * or complete set of options. It must contain at least one of the following + * properties: `infile`, `instr`, `options`, or `svg` to generate a valid image. * - * @returns {void} This function does not return a value directly. Instead, - * it communicates results via the `endCallback`. + * @returns {Promise} A Promise that resolves once the single export + * process is completed. * - * @throws {ExportError} Throws an `ExportError` if there is a problem with - * processing input of any type. + * @throws {ExportError} Throws an `ExportError` if an error occurs during + * the single export process. */ -export async function startExport(options = getOptions(), endCallback) { - // Starting exporting process message - log(4, '[chart] Starting the exporting process.'); - - // Get the export options - const exportOptions = options.export; - - // Export using SVG as an input - if (exportOptions.svg !== null) { - try { - log(4, '[chart] Attempting to export from an SVG input.'); - - // Export from an SVG string - const result = _exportAsString( - sanitize(exportOptions.svg), // #209 - options, - endCallback - ); - - // SVG export attempt counter - ++getPoolStats().exportFromSvgAttempts; - - // Return SVG export result - return result; - } catch (error) { - return endCallback( - new ExportError('[chart] Error loading an SVG input.', 400).setError( - error - ) - ); - } - } - - // Export using options from the file as an input - if (exportOptions.infile !== null) { - try { - log(4, '[chart] Attempting to export from a file input.'); - - // Try to read the file to get the string representation - exportOptions.instr = readFileSync(exportOptions.infile, 'utf8'); - - // Export from a file options - return _exportAsString(exportOptions.instr, options, endCallback); - } catch (error) { - return endCallback( - new ExportError('[chart] Error loading a file input.', 400).setError( - error - ) - ); - } - } - - // Export using options from the raw representation as an input - if (exportOptions.instr !== null || exportOptions.options !== null) { - // If not found, make sure that the instr gets the value from the options - if (!exportOptions.instr && exportOptions.options) { - exportOptions.instr = exportOptions.options; - } - - try { - log(4, '[chart] Attempting to export from a raw input.'); - - // Perform a direct inject when forced - if (options.customLogic.allowCodeExecution) { - return _doStraightInject(options, endCallback); +export async function singleExport(options) { + // Check if the export makes sense + if (options && options.export) { + // Perform an export + await startExport(options, async (error, data) => { + // Exit process when error exists + if (error) { + throw error; } - // Export from stringified options - if (typeof exportOptions.instr === 'string') { - return _exportAsString(exportOptions.instr, options, endCallback); - } + // Get the `b64`, `outfile`, and `type` for a chart + const { b64, outfile, type } = data.options.export; - // Export from object options - return _prepareExport(options, endCallback, exportOptions.instr, null); - } catch (error) { - return endCallback( - new ExportError('[chart] Error loading a raw input.', 400).setError( - error - ) - ); - } - } + // Save the result + try { + if (b64) { + // As a Base64 string to a txt file + writeFileSync( + `${outfile.split('.').shift() || 'chart'}.txt`, + getBase64(data.result, type) + ); + } else { + // As a correct image format + writeFileSync( + outfile || `chart.${type}`, + type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result + ); + } + } catch (error) { + throw new ExportError( + '[chart] Error while saving a chart.', + 500 + ).setError(error); + } - // No input specified, pass an error message to the callback - return endCallback( - new ExportError( - `[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.`, + // Kill pool and close browser after finishing single export + await killPool(); + }); + } else { + throw new ExportError( + '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.', 400 - ) - ); + ); + } } /** - * Starts a single export process based on the specified options. + * Starts a batch export process for multiple charts based on the information + * in the `batch` option. The `batch` is a string in the following format: + * "infile1.json=outfile1.png;infile2.json=outfile2.png;...". Results are saved + * in provided outfiles. * * @async - * @function singleExport + * @function batchExport * - * @param {Object} [options=getOptions()] - The `options` object containing - * configuration for a custom export. The default value is the global options - * of the export server instance. + * @param {Object} options - The `options` object, which may be a partial + * or complete set of options. It must contain the `batch` option to generate + * valid images. * - * @returns {Promise} A Promise that resolves once the single export - * process is completed. + * @returns {Promise} A Promise that resolves once the batch export + * processes are completed. * * @throws {ExportError} Throws an `ExportError` if an error occurs during - * the single export process. + * any of the batch export process. */ -export async function singleExport(options = getOptions()) { - // Use `instr` or its alias, `options` - options.export.instr = options.export.instr || options.export.options; - - // Perform an export - await startExport(options, async (error, data) => { - // Exit process when error exists - if (error) { - throw error; +export async function batchExport(options) { + // Check if the export makes sense + if (options && options.export && options.export.batch) { + // An array for collecting batch exports + const batchFunctions = []; + + // Split and pair the `batch` arguments + for (let pair of options.export.batch.split(';') || []) { + pair = pair.split('='); + if (pair.length === 2) { + batchFunctions.push( + startExport( + { + ...options, + export: { + ...options.export, + infile: pair[0], + outfile: pair[1] + } + }, + (error, data) => { + // Exit process when error exists + if (error) { + throw error; + } + + // Get the `b64`, `outfile`, and `type` for a chart + const { b64, outfile, type } = data.options.export; + + // Save the result + try { + if (b64) { + // As a Base64 string to a txt file + writeFileSync( + `${outfile.split('.').shift() || 'chart'}.txt`, + getBase64(data.result, type) + ); + } else { + // As a correct image format + writeFileSync( + outfile, + type !== 'svg' + ? Buffer.from(data.result, 'base64') + : data.result + ); + } + } catch (error) { + throw new ExportError( + '[chart] Error while saving a chart.', + 500 + ).setError(error); + } + } + ) + ); + } else { + log(2, '[chart] No correct pair found for the batch export.'); + } } - // Get the `outfile` and `type` for a chart - const { outfile, type } = data.options.export; - - // Save the base64 from a buffer to a correct image format - writeFileSync( - outfile || `chart.${type}`, - type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result - ); + // Await all exports are done + const batchResults = await Promise.allSettled(batchFunctions); - // Kill pool and close browser after finishing single export + // Kill pool and close browser after finishing batch export await killPool(); - }); + + // Log errors if found + batchResults.forEach((result, index) => { + // Log the error with stack about the specific batch export + if (result.reason) { + logWithStack( + 1, + result.reason, + `[chart] Batch export number ${index + 1} could not be correctly completed.` + ); + } + }); + } else { + throw new ExportError( + '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.', + 400 + ); + } } /** - * Starts a batch export process for multiple charts based on the information - * in the `batch` option. The `batch` is a string in the following format: - * "infile1.json=outfile1.png;infile2.json=outfile2.png;...". + * Starts an export process. The `customOptions` parameter is an object that + * may be partial or complete set of options. The `endCallback` is called when + * the export is completed, with the `error` object as the first argument + * and the `data` object as the second, which contains the Base64 representation + * of the chart in the `result` property and the full set of export options + * in the `options` property. * * @async - * @function batchExport + * @function startExport * - * @param {Object} [options=getOptions()] - The `options` object containing - * configuration for a custom export. The default value is the global options - * of the export server instance. + * @param {Object} customOptions - The `customOptions` object, which may + * be a partial or complete set of options. If the provided options are partial, + * missing values will be merged with the default general options, retrieved + * using the `getOptions` function. + * @param {Function} endCallback - The callback function to be invoked upon + * finalizing work or upon error occurance of the exporting process. The first + * argument is `error` object and the `data` object is the second, that contains + * the Base64 representation of the chart in the `result` property and the full + * set of export options in the `options` property. * - * @returns {Promise} A Promise that resolves once the batch export - * process is completed. + * @returns {Promise} This function does not return a value directly. + * Instead, it communicates results via the `endCallback`. * - * @throws {ExportError} Throws an `ExportError` if an error occurs during - * any of the batch export process. + * @throws {ExportError} Throws an `ExportError` if there is a problem with + * processing input of any type. The error is passed into the `endCallback` + * function and processed there. */ -export async function batchExport(options = getOptions()) { - const batchFunctions = []; - - // Split and pair the `batch` arguments - for (let pair of options.export.batch.split(';') || []) { - pair = pair.split('='); - if (pair.length === 2) { - batchFunctions.push( - startExport( - { - ...options, - export: { - ...options.export, - infile: pair[0], - outfile: pair[1] - } - }, - (error, data) => { - // Throw an error - if (error) { - throw error; - } +export async function startExport(customOptions, endCallback) { + try { + // Starting exporting process message + log(4, '[chart] Starting the exporting process.'); + + // Merge the custom options into default ones + const options = mergeOptions(getOptions(false), customOptions); + + // Get the export options + const exportOptions = options.export; + + // Export using options from the file as an input + if (exportOptions.infile !== null) { + log(4, '[chart] Attempting to export from a file input.'); + + let fileContent; + try { + // Try to read the file to get the string representation + fileContent = readFileSync( + getAbsolutePath(exportOptions.infile), + 'utf8' + ); + } catch (error) { + throw new ExportError( + '[chart] Error loading content from a file input.', + 400 + ).setError(error); + } + + // Check the file's extension + if (exportOptions.infile.endsWith('.svg')) { + // Set to the `svg` option + exportOptions.svg = fileContent; + } else if (exportOptions.infile.endsWith('.json')) { + // Set to the `instr` option + exportOptions.instr = fileContent; + } else { + throw new ExportError( + '[chart] Incorrect value of the `infile` option.', + 400 + ); + } + } + + // Export using SVG as an input + if (exportOptions.svg !== null) { + log(4, '[chart] Attempting to export from an SVG input.'); - // Get the `outfile` and `type` for a chart - const { outfile, type } = data.options.export; + // SVG exports attempts counter + ++getPoolStats().exportsFromSvgAttempts; - // Save the base64 from a buffer to a correct image file - writeFileSync( - outfile, - type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result - ); - } - ) + // Export from an SVG string + const result = await _exportFromSvg( + sanitize(exportOptions.svg), // #209 + options ); + + // SVG exports counter + ++getPoolStats().exportsFromSvg; + + // Pass SVG export result to the end callback + return endCallback(null, result); } - } - try { - // Await all exports are done - await Promise.all(batchFunctions); + // Export using options as an input + if (exportOptions.instr !== null || exportOptions.options !== null) { + log(4, '[chart] Attempting to export from options input.'); - // Kill pool and close browser after finishing batch export - await killPool(); + // Options exports attempts counter + ++getPoolStats().exportsFromOptionsAttempts; + + // Export from options + const result = await _exportFromOptions( + exportOptions.instr || exportOptions.options, + options + ); + + // Options exports counter + ++getPoolStats().exportsFromOptions; + + // Pass options export result to the end callback + return endCallback(null, result); + } + + // No input specified, pass an error message to the callback + return endCallback( + new ExportError( + `[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.`, + 400 + ) + ); } catch (error) { - throw new ExportError( - '[chart] Error encountered during batch export.', - 400 - ).setError(error); + return endCallback(error); } } @@ -285,185 +354,137 @@ export function setAllowCodeExecution(value) { } /** - * Performs a direct inject of options before export. The function attempts - * to stringify the provided options and removes unnecessary characters, - * ensuring a clean and formatted input. The resulting string is saved - * as a 'stright inject' string in the export options. It then invokes - * the `_prepareExport` function with the updated options. + * Exports from an SVG based input with the provided options. * - * IMPORTANT: Dangerous and must be used deliberately by someone who sets - * up a server (see the `allowCodeExecution` option). + * @async + * @function _exportFromSvg * - * @function _doStraightInject + * @param {string} inputToExport - The SVG based input to be exported. + * @param {Object} options - The `options` object containing complete set + * of options. * - * @param {Object} options - The `options` object containing the input - * to be injected. - * @param {Function} endCallback - The callback function to be invoked - * at the end of the process. + * @returns {Promise} A Promise that resolves to a result of the export + * process. * - * @returns {Promise} A Promise that resolves with the result of the export - * operation or rejects with an error if any issues occur during the process. + * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG + * input. */ -function _doStraightInject(options, endCallback) { - try { - let strInj; - let instr = options.export.instr; - - if (typeof instr !== 'string') { - // Try to stringify options - strInj = instr = optionsStringify( - instr, - options.customLogic.allowCodeExecution, - false - ); - } - strInj = instr.replaceAll(/\t|\n|\r/g, '').trim(); +async function _exportFromSvg(inputToExport, options) { + // Check if it is SVG + if ( + typeof inputToExport === 'string' && + (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0) + ) { + log(4, '[chart] Parsing input as SVG.'); - // Get rid of the ; - if (strInj[strInj.length - 1] === ';') { - strInj = strInj.substring(0, strInj.length - 1); - } + // Set the export input as SVG + options.export.svg = inputToExport; - // Save as stright inject string - options.export.strInj = strInj; + // Reset the rest of the export input options + options.export.instr = null; + options.export.options = null; - // Call the `_prepareExport` function with the straight injected string - return _prepareExport(options, endCallback, null, null); - } catch (error) { - return endCallback( - new ExportError( - (options.export?.requestId - ? `[chart] Malformed input detected for the request: ${options.export?.requestId}. ` - : '[chart] Malformed input detected. ') + - 'Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you are using SVG, it is unescaped.', - 400 - ).setError(error) - ); + // Call the function with an SVG string as an export input + return _prepareExport(options); + } else { + throw new ExportError('[chart] Not a correct SVG input.', 400); } } /** - * Exports a string based on the provided options and invokes the end callback. + * Exports from an options based input with the provided options. * - * @function _exportAsString + * @async + * @function _exportFromOptions + * + * @param {string} inputToExport - The options based input to be exported. + * @param {Object} options - The `options` object containing complete set + * of options. * - * @param {string} stringToExport - The string content to be exported. - * @param {Object} options - The `options` object containing general options - * configuration. - * @param {Function} endCallback - Callback function to be invoked at the end - * of the export process. + * @returns {Promise} A Promise that resolves to a result of the export + * process. * - * @returns {unknown} Result of the export process or an error if encountered. + * @throws {ExportError} Throws an `ExportError` if there is not a correct + * chart options input. */ -function _exportAsString(stringToExport, options, endCallback) { - // Check if it is SVG +async function _exportFromOptions(inputToExport, options) { + log(4, '[chart] Parsing input from options.'); + + // Try to check, validate and parse to stringified options + const stringifiedOptions = isAllowedConfig( + inputToExport, + true, + options.customLogic.allowCodeExecution + ); + + // Check if a correct stringified options if ( - stringToExport.indexOf('= 0 || - stringToExport.indexOf('= 0 + stringifiedOptions === null || + typeof stringifiedOptions !== 'string' || + !stringifiedOptions.startsWith('{') || + !stringifiedOptions.endsWith('}') ) { - log(4, '[chart] Parsing input as SVG.'); - - // Call the `_prepareExport` function with an SVG string - return _prepareExport(options, endCallback, null, stringToExport); + throw new ExportError( + '[chart] Invalid configuration provided - Only options 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` options set to true.', + 403 + ); } - try { - log(4, '[chart] Parsing input from a file.'); - - // Try to parse to JSON - const chartJSON = JSON.parse(stringToExport.replaceAll(/\t|\n|\r/g, ' ')); + // Set the export input as a stringified chart options + options.export.instr = stringifiedOptions; - if (!chartJSON || typeof chartJSON !== 'object') { - return endCallback( - new ExportError( - '[chart] Invalid configuration provided - the options must be an object, not a string.', - 400 - ) - ); - } + // Reset the rest of the export input options + options.export.svg = null; - // Call the `_prepareExport` function with a chart JSON options - return _prepareExport(options, endCallback, chartJSON, null); - } catch (error) { - // If not a valid JSON, try to inject stingified version - if (options.customLogic.allowCodeExecution) { - return _doStraightInject(options, endCallback); - } else { - // Do not allow straight injection if the `allowCodeExecution` is false - return endCallback( - new ExportError( - '[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` options set to true.', - 403 - ).setError(error) - ); - } - } + // Call the function with a stringified chart options + return _prepareExport(options); } /** - * Function for finalizing options before export. + * Function for finalizing options and configurations before export. * * @async * @function _prepareExport * - * @param {Object} options - The `options` object containing configuration - * for the export process. - * @param {Function} endCallback - The callback function to be called upon - * completion or error. - * @param {Object} json - The JSON representation of the chart. - * @param {string} svg - The SVG representation of the chart. + * @param {Object} options - The `options` object containing complete set + * of options. * - * @returns {Promise} A Promise that resolves once the export process - * is completed. + * @returns {Promise} A Promise that resolves to a result of the export + * process. */ -async function _prepareExport(options, endCallback, json, svg) { +async function _prepareExport(options) { const { export: exportOptions, customLogic: customLogicOptions } = options; - const allowCodeExecutionScoped = - customLogicOptions.allowCodeExecution || allowCodeExecution; + // Prepare the `type` option + exportOptions.type = fixType(exportOptions.type, exportOptions.outfile); + + // Prepare the `outfile` option + exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile); - // Prepare the custom logic (`customCode`, `callback`, `resources`) options - try { - _handleCustomLogic(customLogicOptions, allowCodeExecutionScoped); - } catch (error) { - return endCallback(error); - } + // Notify about the custom logic usage status + log( + 3, + `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.` + ); + + // Prepare the custom logic options (`customCode`, `callback`, `resources`) + _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution); // Prepare the `globalOptions` and `themeOptions` options _handleGlobalAndTheme( exportOptions, customLogicOptions.allowFileResources, - allowCodeExecutionScoped + customLogicOptions.allowCodeExecution ); - // Prepare the `type` option - exportOptions.type = fixType(exportOptions.type, exportOptions.outfile); - - // Prepare the `chart` and `exporting` properties to keep it lean and mean - if (json) { - json.chart = json.chart || {}; - json.exporting = json.exporting || {}; - json.exporting.enabled = false; - } - - // Set the `width` option to null in case of exporting to an SVG - if (exportOptions.type === 'svg') { - exportOptions.width = null; - } - - // Prepare the `height`, `width` and `scale` options + // Prepare the `height`, `width`, and `scale` options options.export = { ...exportOptions, ..._findChartSize(exportOptions) }; // Post the work to the pool - try { - const result = await postWork(exportOptions.strInj || json || svg, options); - return endCallback(null, result); - } catch (error) { - return endCallback(error); - } + return postWork(options); } /** @@ -472,17 +493,14 @@ async function _prepareExport(options, endCallback, json, svg) { * * The function prioritizes values in the following order: * 1. The `height`, `width`, `scale` from the `exportOptions`. - * 2. Options from the chart configuration (`chart` and `exporting` sections). - * 3. Options from the global options (`chart` and `exporting` sections). - * 4. Options from the theme options (`chart` and `exporting` sections). - * 5. The `defaultHeight`, `defaultWidth`, `defaultScale` from the - * `exportOptions`. - * 6. Fallback values (`height` = 400, `width` = 600, `scale` = 1). + * 2. Options from the chart configuration (from `exporting` and `chart`). + * 3. Options from the global options (from `exporting` and `chart`). + * 4. Options from the theme options (from `exporting` and `chart` sections). + * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`). * * @function _findChartSize * - * @param {Object} exportOptions - The `exportOptions` object containing - * export configuration. + * @param {Object} exportOptions - The object containing `export` options. * * @returns {Object} An object containing the calculated `height`, `width` * and `scale` values for the chart export. @@ -490,15 +508,15 @@ async function _prepareExport(options, endCallback, json, svg) { function _findChartSize(exportOptions) { // Check the `options` and `instr` for chart and exporting sections const { chart: optionsChart, exporting: optionsExporting } = - exportOptions.options || isCorrectJSON(exportOptions.instr) || false; + exportOptions.options || isAllowedConfig(exportOptions.instr) || false; // Check the `globalOptions` for chart and exporting sections const { chart: globalOptionsChart, exporting: globalOptionsExporting } = - isCorrectJSON(exportOptions.globalOptions) || false; + isAllowedConfig(exportOptions.globalOptions) || false; // Check the `themeOptions` for chart and exporting sections const { chart: themeOptionsChart, exporting: themeOptionsExporting } = - isCorrectJSON(exportOptions.themeOptions) || false; + isAllowedConfig(exportOptions.themeOptions) || false; // Find the `scale` value: // - It cannot be lower than 0.1 @@ -544,10 +562,10 @@ function _findChartSize(exportOptions) { exportOptions.defaultWidth || 600; - // Gather `height`, `width` and `scale` information + // Gather `height`, `width` and `scale` information in one object const size = { height, width, scale }; - // Get rid of potential px and % + // Get rid of potential `px` and `%` for (let [param, value] of Object.entries(size)) { size[param] = typeof value === 'string' ? +value.replace(/px|%/gi, '') : value; @@ -565,8 +583,8 @@ function _findChartSize(exportOptions) { * * @function _handleCustomLogic * - * @param {Object} customLogicOptions - Options containing custom logic settings - * such as `resources`, `customCode` and `callback`. + * @param {Object} customLogicOptions - The object containing `customLogic` + * options. * @param {boolean} allowCodeExecution - A flag indicating whether code * execution is allowed. * @@ -576,7 +594,7 @@ function _findChartSize(exportOptions) { function _handleCustomLogic(customLogicOptions, allowCodeExecution) { // In case of allowing code execution if (allowCodeExecution) { - // Process the `resources` + // Process the `resources` option if (typeof customLogicOptions.resources === 'string') { // Custom stringified resources customLogicOptions.resources = _handleResources( @@ -587,18 +605,17 @@ function _handleCustomLogic(customLogicOptions, allowCodeExecution) { } else if (!customLogicOptions.resources) { try { // Load the default one - const resources = readFileSync('resources.json', 'utf8'); customLogicOptions.resources = _handleResources( - resources, + readFileSync(getAbsolutePath('resources.json'), 'utf8'), customLogicOptions.allowFileResources, true ); } catch (error) { - log(2, `[chart] Unable to load the default resources.json file.`); + log(2, '[chart] Unable to load the default `resources.json` file.'); } } - // Process the `customCode` + // Process the `customCode` option try { // Try to load custom code and wrap around it in a self invoking function customLogicOptions.customCode = wrapAround( @@ -606,80 +623,76 @@ function _handleCustomLogic(customLogicOptions, allowCodeExecution) { customLogicOptions.allowFileResources ); } catch (error) { - logWithStack(2, error, `[chart] The 'customCode' cannot be loaded.`); + logWithStack(2, error, '[chart] The `customCode` cannot be loaded.'); + + // In case of an error, set the option with null + customLogicOptions.customCode = null; } - // Process the `callback` - if ( - customLogicOptions.callback && - customLogicOptions.callback.endsWith('.js') - ) { - // The `allowFileResources` is always set to false for HTTP requests - // to avoid injecting arbitrary files from the fs - if (customLogicOptions.allowFileResources) { - try { - customLogicOptions.callback = readFileSync( - customLogicOptions.callback, - 'utf8' - ); - } catch (error) { - customLogicOptions.callback = null; - logWithStack(2, error, `[chart] The 'callback' cannot be loaded.`); - } - } else { - customLogicOptions.callback = null; - } + // Process the `callback` option + try { + // Try to load callback function + customLogicOptions.callback = wrapAround( + customLogicOptions.callback, + customLogicOptions.allowFileResources, + true + ); + } catch (error) { + logWithStack(2, error, '[chart] The `callback` cannot be loaded.'); + + // In case of an error, set the option with null + customLogicOptions.callback = null; } // Check if there is the `customCode` present if ([null, undefined].includes(customLogicOptions.customCode)) { - log(3, `[cli] No custom code found.`); + log(3, '[chart] No value for the `customCode` option found.'); } // Check if there is the `callback` present if ([null, undefined].includes(customLogicOptions.callback)) { - log(3, `[cli] No callback found.`); + log(3, '[chart] No value for the `callback` option found.'); } // Check if there is the `resources` present if ([null, undefined].includes(customLogicOptions.resources)) { - log(3, `[cli] No resources found.`); + log(3, '[chart] No value for the `resources` option found.'); } } else { - // If the `allowCodeExecution` flag is not set, we should refuse the usage - // of `callback`, `resources`, and `customCode`. Additionally, the worker - // will refuse to run arbitrary JavaScript. Prioritized should be the scoped - // option + // If the `allowCodeExecution` flag is set to false, we should refuse + // the usage of the `callback`, `resources`, and `customCode` options. + // Additionally, the worker will refuse to run arbitrary JavaScript. if ( customLogicOptions.callback || customLogicOptions.resources || customLogicOptions.customCode ) { + // Reset all custom code options + customLogicOptions.callback = null; + customLogicOptions.resources = null; + customLogicOptions.customCode = null; + // Send a message saying that the exporter does not support these settings throw new ExportError( `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`, 403 ); } - - // Reset all additional custom code - customLogicOptions.callback = null; - customLogicOptions.resources = null; - customLogicOptions.customCode = null; } } /** - * Handles and validates resources for export. + * Handles and validates resources from the `resources` option for export. * * @function _handleResources * * @param {(Object|string|null)} [resources=null] - The resources to be handled. - * Can be either a JSON object, stringified JSON, a path to a JSON file or null. - * @param {boolean} allowFileResources - Whether to allow loading resources - * from files. - * @param {boolean} allowCodeExecution - Whether to allow executing arbitrary - * JS code. + * Can be either a JSON object, stringified JSON, a path to a JSON file, + * or null. The default value is null. + * @param {boolean} allowFileResources - A flag indicating whether loading + * resources from files is allowed. + * @param {boolean} allowCodeExecution - A flag indicating whether code + * execution is allowed. * * @returns {(Object|null)} The handled resources or null if no valid resources * are found. @@ -698,8 +711,8 @@ function _handleResources( // Try to load resources from a file if (allowFileResources && resources.endsWith('.json')) { try { - handledResources = isCorrectJSON( - readFileSync(resources, 'utf8'), + handledResources = isAllowedConfig( + readFileSync(getAbsolutePath(resources), 'utf8'), false, allowCodeExecution ); @@ -708,7 +721,7 @@ function _handleResources( } } else { // Try to get JSON - handledResources = isCorrectJSON(resources, false, allowCodeExecution); + handledResources = isAllowedConfig(resources, false, allowCodeExecution); // Get rid of the files section if (handledResources && !allowFileResources) { @@ -745,20 +758,19 @@ function _handleResources( /** * Handles the loading and validation of the `globalOptions` and `themeOptions` * in the export options. If the option is a string and references a JSON file - * (when `allowFileResources` is true), it reads and parses the file. Otherwise, - * it attempts to parse the string or object as JSON. If any errors occur during - * this process, the option is set to null. If there is an error loading - * or parsing the `globalOptions` or `themeOptions`, the error is logged + * (when the `allowFileResources` is true), it reads and parses the file. + * Otherwise, it attempts to parse the string or object as JSON. If any errors + * occur during this process, the option is set to null. If there is an error + * loading or parsing the `globalOptions` or `themeOptions`, the error is logged * and the option is set to null. * * @function _handleGlobalAndTheme * - * @param {Object} exportOptions - The `exportOptions` object containing - * the `globalOptions` and `themeOptions`. - * @param {boolean} allowFileResources - A flag indicating whether file - * resources (e.g. the .json files) are allowed. - * @param {boolean} allowCodeExecution - A flag indicating whether executing - * code (e.g., within JSON or theme options) is allowed. + * @param {Object} exportOptions - The object containing `export` options. + * @param {boolean} allowFileResources - A flag indicating whether loading + * resources from files is allowed. + * @param {boolean} allowCodeExecution - A flag indicating whether code + * execution is allowed. */ function _handleGlobalAndTheme( exportOptions, @@ -770,30 +782,21 @@ function _handleGlobalAndTheme( try { // Check if the option exists if (exportOptions[optionsName]) { - // Check if it is a string - if (typeof exportOptions[optionsName] === 'string') { - // Check if it is a file name with the .json extension - if ( - allowFileResources && - exportOptions[optionsName].endsWith('.json') - ) { - // Check if the file content can be a JSON, and save it as a string - exportOptions[optionsName] = isCorrectJSON( - readFileSync(exportOptions[optionsName], 'utf8'), - true, - allowCodeExecution - ); - } else { - // Check if the value can be a JSON, and save it as a string - exportOptions[optionsName] = isCorrectJSON( - exportOptions[optionsName], - true, - allowCodeExecution - ); - } + // Check if it is a string and a file name with the `.json` extension + if ( + allowFileResources && + typeof exportOptions[optionsName] === 'string' && + exportOptions[optionsName].endsWith('.json') + ) { + // Check if the file content can be a config, and save it as a string + exportOptions[optionsName] = isAllowedConfig( + readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'), + true, + allowCodeExecution + ); } else { - // Check if the value can be a JSON, and save it as an object - exportOptions[optionsName] = isCorrectJSON( + // Check if the value can be a config, and save it as a string + exportOptions[optionsName] = isAllowedConfig( exportOptions[optionsName], true, allowCodeExecution @@ -801,12 +804,26 @@ function _handleGlobalAndTheme( } } } catch (error) { - logWithStack(2, error, `[chart] The '${optionsName}' cannot be loaded.`); + logWithStack( + 2, + error, + `[chart] The \`${optionsName}\` cannot be loaded.` + ); // In case of an error, set the option with null exportOptions[optionsName] = null; } }); + + // Check if there is the `globalOptions` present + if ([null, undefined].includes(exportOptions.globalOptions)) { + log(3, '[chart] No value for the `globalOptions` option found.'); + } + + // Check if there is the `themeOptions` present + if ([null, undefined].includes(exportOptions.themeOptions)) { + log(3, '[chart] No value for the `themeOptions` option found.'); + } } export default { From 7b8849a7be4d23c0733a552879d8f2738f45f91d Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:02:29 +0100 Subject: [PATCH 035/102] Optimization and refactorization of the config module, with some new and moved logic. --- lib/config.js | 405 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 369 insertions(+), 36 deletions(-) diff --git a/lib/config.js b/lib/config.js index 27c4ebd0..033a9009 100644 --- a/lib/config.js +++ b/lib/config.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -16,50 +16,62 @@ See LICENSE file in root for details. * @overview Manages configuration for the Highcharts Export Server by loading * and merging options from multiple sources, such as default settings, * environment variables, user-provided options, and command-line arguments. - * Ensures the export server's global options are up-to-date with the highest - * priority values. Provides functions for accessing and updating configuration. + * Ensures the global options are up-to-date with the highest priority values. + * Provides functions for accessing and updating configuration. */ import { readFileSync } from 'fs'; +import { join } from 'path'; import { log, logWithStack } from './logger.js'; import { envs } from './envs.js'; -import { deepCopy } from './utils.js'; -import { defaultConfig, nestedArgs } from './schemas/config.js'; +import { __dirname, isObject, deepCopy, getAbsolutePath } from './utils.js'; +import { defaultConfig, nestedProps, absoluteProps } from './schemas/config.js'; // Sets the global options with initial values from the default config const globalOptions = _initGlobalOptions(defaultConfig); /** - * Gets the global options of the export server instance. + * Gets the reference to the global options of the server instance object + * or its copy. * * @function getOptions * - * @param {boolean} [getGlobal = false] - Optional parameter to decide whether + * @param {boolean} [getReference=true] - Optional parameter to decide whether * to return the reference to the global options of the server instance object - * or return a copy of it. + * or return a copy of it. The default value is true. * - * @returns {Object} The global options object of the server instance. + * @returns {Object} The reference to the global options of the server instance + * object or its copy. */ -export function getOptions(getGlobal = false) { - return getGlobal ? globalOptions : deepCopy(globalOptions); +export function getOptions(getReference = true) { + return getReference ? globalOptions : deepCopy(globalOptions); } /** - * Sets the general options of the export server instance, keeping the principle + * Sets the global options of the export server instance, keeping the principle * of the options load priority from all available sources. It accepts optional * `customOptions` object and `cliArgs` array with arguments from the CLI. These * options will be validated and applied if provided. * + * The priority order of setting values is: + * + * 1. Options from the `lib/schemas/config.js` file (default values). + * 2. Options from a custom JSON file (loaded by the `loadConfig` option). + * 3. Options from the environment variables (the `.env` file). + * 4. Options from the first parameter (by default an empty object). + * 5. Options from the CLI. + * * @function setOptions * * @param {Object} [customOptions={}] - Optional custom options for additional - * configuration. + * configuration. The default value is an empty object. * @param {Array.} [cliArgs=[]] - Optional command line arguments - * for additional configuration. - * @param {boolean} [modifyGlobal = false] - Optional parameter to decide + * for additional configuration. The default value is an empty array. + * @param {boolean} [modifyGlobal=false] - Optional parameter to decide * whether to update and return the reference to the global options - * of the server instance object or return a copy of it. + * of the server instance object or return a copy of it. The default value + * is false. * * @returns {Object} The updated general options object, reflecting the merged * configuration from all available sources. @@ -79,16 +91,13 @@ export function setOptions( if (cliArgs.length) { // Get options from the custom JSON loaded via the `loadConfig` configOptions = _loadConfigFile(cliArgs); - } - // Only for the CLI usage - if (cliArgs.length) { // Get options from the CLI - cliOptions = _pairArgumentValue(nestedArgs, cliArgs); + cliOptions = _pairArgumentValue(nestedProps, cliArgs); } // Get the reference to the global options object or a copy of the object - const generalOptions = modifyGlobal ? globalOptions : deepCopy(globalOptions); + const generalOptions = getOptions(modifyGlobal); // Update values of the general options with values from each source possible _updateOptions( @@ -103,23 +112,238 @@ export function setOptions( return generalOptions; } +/** + * Merges two sets of configuration options, considering absolute properties. + * + * @function mergeOptions + * + * @param {Object} originalOptions - Original configuration options. + * @param {Object} newOptions - New configuration options to be merged. + * + * @returns {Object} Merged configuration options. + */ +export function mergeOptions(originalOptions, newOptions) { + // Check if the `newOptions` is a correct object + if (isObject(newOptions)) { + for (const [key, value] of Object.entries(newOptions)) { + originalOptions[key] = + isObject(value) && + !absoluteProps.includes(key) && + originalOptions[key] !== undefined + ? mergeOptions(originalOptions[key], value) + : value !== undefined + ? value + : originalOptions[key]; + } + } + + // Return the result options + return originalOptions; +} + +/** + * Maps old-structured configuration options (PhantomJS) to a new format + * (Puppeteer). This function converts flat, old-structured options into + * a new, nested configuration format based on a predefined mapping + * (`nestedProps`). The new format is used for Puppeteer, while the old format + * was used for PhantomJS. + * + * @function mapToNewOptions + * + * @param {Object} oldOptions - The old, flat configuration options + * to be converted. + * + * @returns {Object} A new object containing options structured according + * to the mapping defined in `nestedProps` or an empty object if the provided + * `oldOptions` is not a correct object. + */ +export function mapToNewOptions(oldOptions) { + // An object for the new structured options + const newOptions = {}; + + // Check if provided value is a correct object + if (Object.prototype.toString.call(oldOptions) === '[object Object]') { + // Iterate over each key-value pair in the old-structured options + for (const [key, value] of Object.entries(oldOptions)) { + // If there is a nested mapping, split it into a properties chain + const propertiesChain = nestedProps[key] + ? nestedProps[key].split('.') + : []; + + // If it is the last property in the chain, assign the value, otherwise, + // create or reuse the nested object + propertiesChain.reduce( + (obj, prop, index) => + (obj[prop] = + propertiesChain.length - 1 === index ? value : obj[prop] || {}), + newOptions + ); + } + } + + // Return the new, structured options object + return newOptions; +} + +/** + * Validates, parses, and checks if the provided config is allowed set + * of options. + * + * @function isAllowedConfig + * + * @param {unknown} config - The config to be validated and parsed as a set + * of options. Must be either an object or a string. + * @param {boolean} [toString=false] - Whether to return a stringified version + * of the parsed config. The default value is false. + * @param {boolean} [allowFunctions=false] - Whether to allow functions + * in the parsed config. If true, functions are preserved. Otherwise, when + * a function is found, null is returned. The default value is false. + * + * @returns {(Object|string|null)} Returns a parsed set of options object, + * a stringified set of options object if the `toString` is true, and null + * if the config is not a valid set of options or parsing fails. + */ +export function isAllowedConfig( + config, + toString = false, + allowFunctions = false +) { + try { + // Accept only objects and strings + if (!isObject(config) && typeof config !== 'string') { + // Return null if any other type + return null; + } + + // Get the object representation of the original config + const objectConfig = + typeof config === 'string' + ? allowFunctions + ? eval(`(${config})`) + : JSON.parse(config) + : config; + + // Preserve or remove potential functions based on the `allowFunctions` flag + const stringifiedOptions = _optionsStringify( + objectConfig, + allowFunctions, + false + ); + + // Parse the config to check if it is valid set of options + const parsedOptions = allowFunctions + ? JSON.parse( + _optionsStringify(objectConfig, allowFunctions, true), + (_, value) => + typeof value === 'string' && value.startsWith('function') + ? eval(`(${value})`) + : value + ) + : JSON.parse(stringifiedOptions); + + // Return stringified or object options based on the `toString` flag + return toString ? stringifiedOptions : parsedOptions; + } catch (error) { + // Return null if parsing fails + return null; + } +} + +/** + * Prints the Highcharts Export Server logo, version, and license information. + * + * @function printLicense + */ +export function printLicense() { + // Print the logo and version information + printVersion(); + + // Print the license information + console.log( + 'This software requires a valid Highcharts license for commercial use.\n' + .yellow, + '\nFor a full list of CLI options, type:', + '\nhighcharts-export-server --help\n'.green, + '\nIf you do not have a license, one can be obtained here:', + '\nhttps://shop.highsoft.com/\n'.green, + '\nTo customize your installation, please refer to the README file at:', + '\nhttps://github.com/highcharts/node-export-server#readme\n'.green + ); +} + +/** + * Prints usage information for CLI arguments, displaying available options + * and their descriptions. It can list properties recursively if categories + * contain nested options. + * + * @function printUsage + */ +export function printUsage() { + // Display README and general usage information + console.log( + '\nUsage of CLI arguments:'.bold, + '\n-----------------------', + `\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\n` + ); + + // Iterate through each category in the `defaultConfig` and display usage info + Object.keys(defaultConfig).forEach((category) => { + console.log(`${category.toUpperCase()}`.bold.red); + _cycleCategories(defaultConfig[category]); + console.log(''); + }); +} + +/** + * Prints the Highcharts Export Server logo or text with the version + * information. + * + * @function printVersion + * + * @param {boolean} [noLogo=false] - If true, only prints text with the version + * information, without the logo. The default value is false. + */ +export function printVersion(noLogo = false) { + // Get package version either from `.env` or from `package.json` + const packageVersion = JSON.parse( + readFileSync(join(__dirname, 'package.json')) + ).version; + + // Print text only + if (noLogo) { + console.log(`Highcharts Export Server v${packageVersion}`); + } else { + // Print the logo + console.log( + readFileSync(join(__dirname, 'msg', 'startup.msg')).toString().bold + .yellow, + `v${packageVersion}\n`.bold + ); + } +} + /** * Initializes and returns global options object based on provided * configuration, setting values from nested properties recursively. * * @function _initGlobalOptions * - * @param {Object} config - Configuration to be used for initializing options. + * @param {Object} config - The configuration object to be used for initializing + * options. * * @returns {Object} Initialized options object. */ function _initGlobalOptions(config) { const options = {}; + + // Start initializing the `options` object recursively for (const [name, item] of Object.entries(config)) { options[name] = Object.prototype.hasOwnProperty.call(item, 'value') ? item.value : _initGlobalOptions(item); } + + // Return the created `options` object return options; } @@ -136,11 +360,11 @@ function _initGlobalOptions(config) { * the structure and default values for the options. * @param {Object} options - The options object that will be updated with values * from other sources. - * @param {Object} configOpt - The configuration options object, which may - * provide values to override defaults. + * @param {Object} configOpt - The configuration options object, loaded with + * the `loadConfig` option, which may provide values to override defaults. * @param {Object} customOpt - The custom options object, typically containing - * user-defined values that may override configuration options. - * @param {Object} cliOpt - The CLI options object, which includes values + * additional and user-defined values, which may override configuration options. + * @param {Object} cliOpt - The CLI options object, which may include values * provided through command-line arguments and has the highest precedence among * options. */ @@ -182,14 +406,73 @@ function _updateOptions(config, options, configOpt, customOpt, cliOpt) { }); } +/** + * Converts the provided options object to a JSON-formatted string + * with the option to preserve functions. In order for a function + * to be preserved, it needs to follow the format `function (...) {...}`. + * Such a function can also be stringified. + * + * @function _optionsStringify + * + * @param {Object} options - The options object to be converted to a string. + * @param {boolean} allowFunctions - If set to true, functions are preserved + * in the output. Otherwise an error is thrown. + * @param {boolean} stringifyFunctions - If set to true, functions are saved + * as strings. The `allowFunctions` must be set to true as well for this to take + * an effect. + * + * @returns {string} The JSON-formatted string representing the options. + * + * @throws {Error} Throws an `Error` when functions are not allowed but are + * found in provided options object. + */ +export function _optionsStringify(options, allowFunctions, stringifyFunctions) { + const replacerCallback = (_, value) => { + // Trim string values + if (typeof value === 'string') { + value = value.trim(); + } + + // If value is a function or stringified function + if ( + typeof value === 'function' || + (typeof value === 'string' && + value.startsWith('function') && + value.endsWith('}')) + ) { + // If allowFunctions is set to true, preserve functions + if (allowFunctions) { + // Based on the `stringifyFunctions` options, set function values + return stringifyFunctions + ? // As stringified functions + `"EXP_FUN${(value + '').replaceAll(/\s+/g, ' ')}EXP_FUN"` + : // As functions + `EXP_FUN${(value + '').replaceAll(/\s+/g, ' ')}EXP_FUN`; + } else { + // Throw an error otherwise + throw new Error(); + } + } + + // In all other cases, simply return the value + return value; + }; + + // Stringify options and if required, replace special functions marks + return JSON.stringify(options, replacerCallback).replaceAll( + stringifyFunctions ? /\\"EXP_FUN|EXP_FUN\\"/g : /"EXP_FUN|EXP_FUN"/g, + '' + ); +} + /** * Loads additional configuration from a specified file provided via - * the `--loadConfig` option in the command-line arguments. + * the `loadConfig` option in the command-line arguments. * * @function _loadConfigFile * * @param {Array.} cliArgs - Command-line arguments to search - * for the `--loadConfig` option and the corresponding file path. + * for the `loadConfig` option and the corresponding file path. * * @returns {Object} The additional configuration loaded from the specified * file, or an empty object if the file is not found, invalid, or an error @@ -208,7 +491,7 @@ function _loadConfigFile(cliArgs) { if (configFileName) { try { // Load an optional custom JSON config file - return JSON.parse(readFileSync(configFileName)); + return JSON.parse(readFileSync(getAbsolutePath(configFileName))); } catch (error) { logWithStack( 2, @@ -217,6 +500,7 @@ function _loadConfigFile(cliArgs) { ); } } + // No additional options to return return {}; } @@ -228,8 +512,8 @@ function _loadConfigFile(cliArgs) { * * @function _pairArgumentValue * - * @param {Array.} nestedArgs - An array of nesting level for all - * general options. + * @param {Array.} nestedProps - An array of nesting level for all + * options. * @param {Array.} cliArgs - An array of command-line arguments * containing options and their associated values. * @@ -237,7 +521,7 @@ function _loadConfigFile(cliArgs) { * the command-line is paired with its value, structured into nested objects * as defined. */ -function _pairArgumentValue(nestedArgs, cliArgs) { +function _pairArgumentValue(nestedProps, cliArgs) { // An empty object to collect and structurize data from the args const cliOptions = {}; @@ -246,8 +530,8 @@ function _pairArgumentValue(nestedArgs, cliArgs) { const option = cliArgs[i].replace(/-/g, ''); // Find the right place for property's value - const propertiesChain = nestedArgs[option] - ? nestedArgs[option].split('.') + const propertiesChain = nestedProps[option] + ? nestedProps[option].split('.') : []; // Create options object with values from CLI for later parsing and merging @@ -257,7 +541,7 @@ function _pairArgumentValue(nestedArgs, cliArgs) { if (!value) { log( 2, - `[config] Missing value for the '${option}' argument. Using the default value.` + `[config] Missing value for the CLI '--${option}' argument. Using the default value.` ); } obj[prop] = value || null; @@ -272,7 +556,56 @@ function _pairArgumentValue(nestedArgs, cliArgs) { return cliOptions; } +/** + * Recursively traverses the options object to print the usage information + * for each option category and individual option. + * + * @function _cycleCategories + * + * @param {Object} options - The options object containing CLI options. + * It may include nested categories and individual options. + */ +function _cycleCategories(options) { + for (const [name, option] of Object.entries(options)) { + // If the current entry is a category and not a leaf option, recurse into it + if (!Object.prototype.hasOwnProperty.call(option, 'value')) { + _cycleCategories(option); + } else { + // Prepare description + const descName = ` --${option.cliName || name}`; + + // Get the value + let optionValue = option.value; + + // Prepare value for option that is not null and is array of strings + if (optionValue !== null && option.types.includes('string[]')) { + optionValue = + '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']'; + } + + // Prepare value for option that is not null and is a string + if (optionValue !== null && option.types.includes('string')) { + optionValue = `'${optionValue}'`; + } + + // Display correctly aligned messages + console.log( + descName.green, + `${('<' + option.types.join('|') + '>').yellow}`, + `${String(optionValue).bold}`.blue, + `- ${option.description}.` + ); + } + } +} + export default { getOptions, - setOptions + setOptions, + mergeOptions, + mapToNewOptions, + isAllowedConfig, + printLicense, + printUsage, + printVersion }; From b3fc896bae0e12d6d8df11607cbaf83d86011b5f Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:02:40 +0100 Subject: [PATCH 036/102] Optimization of the envs module. --- lib/envs.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/envs.js b/lib/envs.js index c3ab2f49..1939e9f2 100644 --- a/lib/envs.js +++ b/lib/envs.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -15,7 +15,7 @@ See LICENSE file in root for details. /** * @overview This file is responsible for parsing the environment variables * with the 'zod' library. The parsed environment variables are then exported - * to be used in the application as "envs". We should not use the `process.env` + * to be used in the application as `envs`. We should not use the `process.env` * directly in the application as these would not be parsed properly. * * The environment variables are parsed and validated only once when @@ -139,18 +139,23 @@ export const Config = z.object({ HIGHCHARTS_FORCE_FETCH: v.boolean(), HIGHCHARTS_CACHE_PATH: v.string(), HIGHCHARTS_ADMIN_TOKEN: v.string(), - HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts), - HIGHCHARTS_MODULE_SCRIPTS: v.array(defaultConfig.highcharts.moduleScripts), + HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value), + HIGHCHARTS_MODULE_SCRIPTS: v.array( + defaultConfig.highcharts.moduleScripts.value + ), HIGHCHARTS_INDICATOR_SCRIPTS: v.array( - defaultConfig.highcharts.indicatorScripts + defaultConfig.highcharts.indicatorScripts.value + ), + HIGHCHARTS_CUSTOM_SCRIPTS: v.array( + defaultConfig.highcharts.customScripts.value ), - HIGHCHARTS_CUSTOM_SCRIPTS: v.array(defaultConfig.highcharts.customScripts), // export EXPORT_INFILE: v.string(), EXPORT_INSTR: v.string(), EXPORT_OPTIONS: v.string(), EXPORT_SVG: v.string(), + EXPORT_BATCH: v.string(), EXPORT_OUTFILE: v.string(), EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']), EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']), @@ -164,7 +169,6 @@ export const Config = z.object({ EXPORT_DEFAULT_SCALE: v.positiveNum(), EXPORT_GLOBAL_OPTIONS: v.string(), EXPORT_THEME_OPTIONS: v.string(), - EXPORT_BATCH: v.string(), EXPORT_RASTERIZATION_TIMEOUT: v.nonNegativeNum(), // custom @@ -180,6 +184,7 @@ export const Config = z.object({ SERVER_ENABLE: v.boolean(), SERVER_HOST: v.string(), SERVER_PORT: v.positiveNum(), + SERVER_UPLOAD_LIMIT: v.positiveNum(), SERVER_BENCHMARKING: v.boolean(), // server proxy From 117e8403e9420824f89c8f73b07a6783325b1f82 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:02:49 +0100 Subject: [PATCH 037/102] Optimization of the export module, with better distinction between svg and option based exports. --- lib/export.js | 265 ++++++++++++++++++++++++-------------------------- 1 file changed, 129 insertions(+), 136 deletions(-) diff --git a/lib/export.js b/lib/export.js index 33a10eee..af7b927d 100644 --- a/lib/export.js +++ b/lib/export.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -15,14 +15,13 @@ See LICENSE file in root for details. /** * @overview This module handles chart export functionality using Puppeteer. * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module - * manages resources, sets up the export environment, and processes chart + * manages page resources, sets up the export environment, and processes chart * configurations or SVG inputs for rendering. Exports to a chart from a page * using Puppeteer. */ import { addPageResources, clearPageResources } from './browser.js'; -import { getCache } from './cache.js'; -import { triggerExport } from './highcharts.js'; +import { createChart } from './highcharts.js'; import { log } from './logger.js'; import svgTemplate from '../templates/svgExport/svgExport.js'; @@ -36,75 +35,50 @@ import ExportError from './errors/ExportError.js'; * @function puppeteerExport * * @param {Object} page - Puppeteer page object. - * @param {unknown} chart - The chart object or SVG configuration - * to be exported. - * @param {Object} options - Export options and configuration. + * @param {Object} options - The `options` object containing complete set + * of options. * - * @returns {Promise<(string|Buffer|ExportError)>} Promise resolving + * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves * to the exported data or rejecting with an `ExportError`. + * + * @throws {ExportError} Throws an `ExportError` if export to an unsupported + * output format occurs. */ -export async function puppeteerExport(page, chart, options) { +export async function puppeteerExport(page, options) { // Injected resources array (additional JS and CSS) - let injectedResources = []; + const injectedResources = []; try { - log(4, '[export] Determining export path.'); - + // Get the `export` options const exportOptions = options.export; - // Decide whether display error or debbuger wrapper around it - const displayErrors = - exportOptions?.options?.chart?.displayErrors && - getCache().activeManifest.modules.debugger; - - let isSVG; - if ( - chart.indexOf && - (chart.indexOf('= 0 || chart.indexOf('= 0) - ) { - // SVG input handling - log(4, '[export] Treating as SVG.'); + let isSVG = false; + if (exportOptions.svg) { + log(4, '[export] Treating as SVG input.'); - // If input is also SVG, just return it + // If the `type` is also SVG, return the input if (exportOptions.type === 'svg') { - return chart; + return exportOptions.svg; } + // Mark as SVG export for the later size corrections isSVG = true; - await page.setContent(svgTemplate(chart), { - waitUntil: 'domcontentloaded' - }); - } else { - // JSON config handling - log(4, '[export] Treating as config.'); - // Need to perform straight inject - if (exportOptions.strInj) { - // Injection based configuration export - await _setAsConfig( - page, - { - chart: { - height: exportOptions.height, - width: exportOptions.width - } - }, - options, - displayErrors - ); - } else { - // Basic configuration export - chart.chart.height = exportOptions.height; - chart.chart.width = exportOptions.width; + // SVG export + await _setAsSvg(page, exportOptions.svg); + } else { + log(4, '[export] Treating as JSON config.'); - await _setAsConfig(page, chart, options, displayErrors); - } + // Options export + await _setAsOptions(page, options); } // Keeps track of all resources added on the page with addXXXTag. etc // It's VITAL that all added resources ends up here so we can clear things // out when doing a new export in the same page! - injectedResources = await addPageResources(page, options.customLogic); + injectedResources.push( + ...(await addPageResources(page, options.customLogic)) + ); // Get the real chart size and set the zoom accordingly const size = isSVG @@ -117,8 +91,7 @@ export async function puppeteerExport(page, chart, options) { const chartHeight = svgElement.height.baseVal.value * scale; const chartWidth = svgElement.width.baseVal.value * scale; - // In case of SVG the zoom must be set directly for body - // Set the zoom as scale + // In case of SVG the zoom must be set directly for body as scale // eslint-disable-next-line no-undef document.body.style.zoom = scale; @@ -146,17 +119,19 @@ export async function puppeteerExport(page, chart, options) { }; }); - // Set final height and width for viewport + // Get the clip region for the page + const { x, y } = await _getClipRegion(page); + + // Set final `height` for viewport const viewportHeight = Math.abs( Math.ceil(size.chartHeight || exportOptions.height) ); + + // Set final `width` for viewport const viewportWidth = Math.abs( Math.ceil(size.chartWidth || exportOptions.width) ); - // Get the clip region for the page - const { x, y } = await _getClipRegion(page); - // Set the final viewport now that we have the real height await page.setViewport({ height: viewportHeight, @@ -164,44 +139,44 @@ export async function puppeteerExport(page, chart, options) { deviceScaleFactor: isSVG ? 1 : parseFloat(exportOptions.scale) }); - let data; + let result; // Rasterization process - if (exportOptions.type === 'svg') { - // SVG - data = await _createSVG(page); - } else if (['png', 'jpeg'].includes(exportOptions.type)) { - // PNG or JPEG - data = await _createImage( - page, - exportOptions.type, - 'base64', - { - width: viewportWidth, - height: viewportHeight, - x, - y - }, - exportOptions.rasterizationTimeout - ); - } else if (exportOptions.type === 'pdf') { - // PDF - data = await _createPDF( - page, - viewportHeight, - viewportWidth, - 'base64', - exportOptions.rasterizationTimeout - ); - } else { - throw new ExportError( - `[export] Unsupported output format ${exportOptions.type}.`, - 400 - ); + switch (exportOptions.type) { + case 'svg': + result = await _createSVG(page); + break; + case 'png': + case 'jpeg': + result = await _createImage( + page, + exportOptions.type, + { + width: viewportWidth, + height: viewportHeight, + x, + y + }, + exportOptions.rasterizationTimeout + ); + break; + case 'pdf': + result = await _createPDF( + page, + viewportHeight, + viewportWidth, + exportOptions.rasterizationTimeout + ); + break; + default: + throw new ExportError( + `[export] Unsupported output format: ${exportOptions.type}.`, + 400 + ); } // Clear previously injected JS and CSS resources await clearPageResources(page, injectedResources); - return data; + return result; } catch (error) { await clearPageResources(page, injectedResources); return error; @@ -209,16 +184,53 @@ export async function puppeteerExport(page, chart, options) { } /** - * Retrieves the clipping region coordinates of the specified page element with - * the id 'chart-container'. + * Sets the specified page's content with provided export input within + * the window context using the `page.setContent` function. + * + * @async + * @function _setAsSvg + * + * @param {Object} page - Puppeteer page object. + * @param {string} svg - The SVG input content to be exported. + * + * @returns {Promise} A Promise that resolves after the content is set. + */ +async function _setAsSvg(page, svg) { + await page.setContent(svgTemplate(svg), { + waitUntil: 'domcontentloaded' + }); +} + +/** + * Sets the options with specified export input and sizes as configuration into + * the `createChart` function within the window context using + * the `page.evaluate` function. + * + * @async + * @function _setAsOptions + * + * @param {Object} page - Puppeteer page object. + * @param {Object} options - The `options` object containing complete set + * of options. + * + * @returns {Promise} A Promise that resolves after the configuration + * is set. + */ +async function _setAsOptions(page, options) { + await page.evaluate(createChart, options); +} + +/** + * Retrieves the clipping region coordinates of the specified page element + * with the 'chart-container' id. * * @async * @function _getClipRegion * * @param {Object} page - Puppeteer page object. * - * @returns {Promise} Promise resolving to an object containing - * x, y, width, and height properties. + * @returns {Promise} A Promise that resolves to an object containing + * `x`, `y`, `width`, and `height` properties. */ async function _getClipRegion(page) { return page.$eval('#chart-container', (element) => { @@ -233,25 +245,25 @@ async function _getClipRegion(page) { } /** - * Sets the specified chart and options as configuration into the triggerExport - * function within the window context using page.evaluate. + * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element + * inside an element with the id 'container'. * * @async - * @function _setAsConfig + * @function _createSVG * * @param {Object} page - Puppeteer page object. - * @param {unknown} chart - The chart object to be configured. - * @param {Object} options - Configuration options for the chart. - * @param {boolean} displayErrors - A flag indicating whether to display errors. * - * @returns {Promise} Promise resolving after the configuration is set. + * @returns {Promise} A Promise that resolves to the SVG string. */ -async function _setAsConfig(page, chart, options, displayErrors) { - return page.evaluate(triggerExport, chart, options, displayErrors); +async function _createSVG(page) { + return page.$eval( + '#container svg:first-of-type', + (element) => element.outerHTML + ); } /** - * Creates an image using Puppeteer's page screenshot functionality with + * Creates an image using Puppeteer's page `screenshot` functionality with * specified options. * * @async @@ -259,25 +271,23 @@ async function _setAsConfig(page, chart, options, displayErrors) { * * @param {Object} page - Puppeteer page object. * @param {string} type - Image type. - * @param {string} encoding - Image encoding. * @param {Object} clip - Clipping region coordinates. * @param {number} rasterizationTimeout - Timeout for rasterization * in milliseconds. * - * @returns {Promise} Promise resolving to the image buffer or rejecting - * with an ExportError for timeout. + * @returns {Promise} A Promise that resolves to the image buffer + * or rejecting with an `ExportError` for timeout. */ -async function _createImage(page, type, encoding, clip, rasterizationTimeout) { +async function _createImage(page, type, clip, rasterizationTimeout) { return Promise.race([ page.screenshot({ type, - encoding, clip, - captureBeyondViewport: true, + encoding: 'base64', fullPage: false, optimizeForSpeed: true, + captureBeyondViewport: true, ...(type !== 'png' ? { quality: 80 } : {}), - // Always render on a transparent page if the expected type format is PNG omitBackground: type == 'png' // #447, #463 }), @@ -291,7 +301,7 @@ async function _createImage(page, type, encoding, clip, rasterizationTimeout) { } /** - * Creates a PDF using Puppeteer's page PDF functionality with specified + * Creates a PDF using Puppeteer's page `pdf` functionality with specified * options. * * @async @@ -300,39 +310,22 @@ async function _createImage(page, type, encoding, clip, rasterizationTimeout) { * @param {Object} page - Puppeteer page object. * @param {number} height - PDF height. * @param {number} width - PDF width. - * @param {string} encoding - PDF encoding. + * @param {number} rasterizationTimeout - Timeout for rasterization + * in milliseconds. * - * @returns {Promise} Promise resolving to the PDF buffer. + * @returns {Promise} A Promise that resolves to the PDF buffer. */ -async function _createPDF(page, height, width, encoding, rasterizationTimeout) { +async function _createPDF(page, height, width, rasterizationTimeout) { await page.emulateMediaType('screen'); return page.pdf({ // This will remove an extra empty page in PDF exports height: height + 1, width, - encoding, + encoding: 'base64', timeout: rasterizationTimeout || 1500 }); } -/** - * Creates an SVG string by evaluating the outerHTML of the first 'svg' element - * inside an element with the id 'container'. - * - * @async - * @function _createSVG - * - * @param {Object} page - Puppeteer page object. - * - * @returns {Promise} Promise resolving to the SVG string. - */ -async function _createSVG(page) { - return page.$eval( - '#container svg:first-of-type', - (element) => element.outerHTML - ); -} - export default { puppeteerExport }; From fa7beddd0ebb704902397b3fea014ea349b5f1bf Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:02:59 +0100 Subject: [PATCH 038/102] Optimization of the fetch module. --- lib/fetch.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/fetch.js b/lib/fetch.js index b5784e69..6d249b92 100644 --- a/lib/fetch.js +++ b/lib/fetch.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -30,27 +30,28 @@ import https from 'https'; * * @param {string} url - The URL to fetch data from. * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request. + * The default value is an empty object. * - * @returns {Promise} Promise resolving to the HTTP/HTTPS response + * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response * object with added 'text' property or rejecting with an error. */ export async function fetch(url, requestOptions = {}) { return new Promise((resolve, reject) => { _getProtocolModule(url) .get(url, requestOptions, (response) => { - let data = ''; + let responseData = ''; // A chunk of data has been received response.on('data', (chunk) => { - data += chunk; + responseData += chunk; }); // The whole response has been received response.on('end', () => { - if (!data) { + if (!responseData) { reject('Nothing was fetched from the URL.'); } - response.text = data; + response.text = responseData; resolve(response); }); }) @@ -69,9 +70,11 @@ export async function fetch(url, requestOptions = {}) { * * @param {string} url - The URL to send the POST request to. * @param {Object} [body={}] - The JSON body to include in the POST request. + * The default value is an empty object. * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request. + * The default value is an empty object. * - * @returns {Promise} Promise resolving to the HTTP/HTTPS response + * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response * object with added 'text' property or rejecting with an error. */ export async function post(url, body = {}, requestOptions = {}) { From f372cfee63c74d40209617fe2a0e284f28a571e8 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:03:08 +0100 Subject: [PATCH 039/102] Optimization of the highcharts module, with a simpler logic. --- lib/highcharts.js | 99 ++++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/lib/highcharts.js b/lib/highcharts.js index 7f5ab54a..e783937a 100644 --- a/lib/highcharts.js +++ b/lib/highcharts.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -12,16 +12,16 @@ See LICENSE file in root for details. *******************************************************************************/ -/* eslint-disable no-undef */ - /** * @overview Provides methods for initializing Highcharts with customized * animation settings and triggering the creation of Highcharts charts with - * export-specific configurations. Supports dynamic option merging, custom logic - * injection, and control over rendering behaviors. Designed to accommodate - * export workflows and custom user requirements. Used by the Puppeteer page. + * export-specific configurations in the page context. Supports dynamic option + * merging, custom logic injection, and control over rendering behaviors. Used + * by the Puppeteer page. */ +/* eslint-disable no-undef */ + /** * Setting the `Highcharts.animObject` function. Called when initing the page. * @@ -34,37 +34,22 @@ export function setupHighcharts() { } /** - * Creates the actual chart. + * Creates the actual Highcharts chart on a page. * * @async - * @function triggerExport + * @function createChart * - * @param {Object} chartOptions - The options for the Highcharts chart. - * @param {Object} options - The general export server options. - * @param {boolean} displayErrors - A flag indicating whether to display errors. + * @param {Object} options - The `options` object containing complete set + * of options. */ -export async function triggerExport(chartOptions, options, displayErrors) { - // Display errors flag taken from chart options nad debugger module - window._displayErrors = displayErrors; - +export async function createChart(options) { // Get required functions const { getOptions, merge, setOptions, wrap } = Highcharts; - // Create a separate object for a potential setOptions usages in order to - // prevent from polluting other exports that can happen on the same page + // Create a separate object for a potential `setOptions` usages in order + // to prevent from polluting other exports that can happen on the same page Highcharts.setOptionsObj = merge(false, {}, getOptions()); - // By default animation is disabled - const chart = { - animation: false - }; - - // When straight inject, the size is set through CSS only - if (options.export.strInj) { - chart.height = chartOptions.chart.height; - chart.width = chartOptions.chart.width; - } - // NOTE: Is this used for anything useful? window.isRenderComplete = false; wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, cb) { @@ -104,45 +89,63 @@ export async function triggerExport(chartOptions, options, displayErrors) { proceed.apply(this, [chart, options]); }); - // Get the user options - const userOptions = options.export.strInj - ? new Function(`return ${options.export.strInj}`)() - : chartOptions; + // Some mandatory additional `chart` and `exporting` options + const additionalOptions = { + chart: { + // By default animation is disabled + animation: false, + // Get the right size values + height: options.export.height, + width: options.export.width + }, + exporting: { + // No need for the exporting button + enabled: false + } + }; - // Trigger custom code - if (options.customLogic.customCode) { - new Function('options', options.customLogic.customCode)(userOptions); - } + // Get the input to export from the `instr` option + const userOptions = new Function(`return ${options.export.instr}`)(); + + // Get the `themeOptions` option + const themeOptions = new Function(`return ${options.export.themeOptions}`)(); + + // Get the `globalOptions` option + const globalOptions = new Function( + `return ${options.export.globalOptions}` + )(); - // Merge the globalOptions, themeOptions, options from the wrapped - // setOptions function and user options to create the final options object + // Merge the following options objects to create final options const finalOptions = merge( false, - JSON.parse(options.export.themeOptions), + themeOptions, userOptions, // Placed it here instead in the init because of the size issues - { chart } + additionalOptions ); + // Prepare the `callback` option const finalCallback = options.customLogic.callback ? new Function(`return ${options.customLogic.callback}`)() - : undefined; + : null; + + // Trigger the `customCode` option + if (options.customLogic.customCode) { + new Function('options', options.customLogic.customCode)(userOptions); + } // Set the global options if exist - const globalOptions = JSON.parse(options.export.globalOptions); if (globalOptions) { setOptions(globalOptions); } - let constr = options.export.constr || 'chart'; - constr = typeof Highcharts[constr] !== 'undefined' ? constr : 'chart'; - - Highcharts[constr]('container', finalOptions, finalCallback); + // Call the chart creation + Highcharts[options.export.constr]('container', finalOptions, finalCallback); // Get the current global options const defaultOptions = getOptions(); - // Clear it just in case (e.g. the setOptions was used in the customCode) + // Clear it just in case (e.g. the `setOptions` was used in the `customCode`) for (const prop in defaultOptions) { if (typeof defaultOptions[prop] !== 'function') { delete defaultOptions[prop]; @@ -158,5 +161,5 @@ export async function triggerExport(chartOptions, options, displayErrors) { export default { setupHighcharts, - triggerExport + createChart }; From 7b304f80f8600172a89a33184733ae3eaf61e511 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:03:17 +0100 Subject: [PATCH 040/102] Modified new options merging logic, new API functions, along with JSDocs and logs corrections. --- lib/index.js | 54 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/lib/index.js b/lib/index.js index 24daefc8..738cf54d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -23,40 +23,47 @@ import 'colors'; import { checkAndUpdateCache } from './cache.js'; import { - startExport, singleExport, batchExport, + startExport, setAllowCodeExecution } from './chart.js'; -import { getOptions, setOptions } from './config.js'; +import { + getOptions, + setOptions, + mergeOptions, + mapToNewOptions +} from './config.js'; import { log, logWithStack, initLogging, setLogLevel, + enableConsoleLogging, enableFileLogging } from './logger.js'; import { initPool, killPool } from './pool.js'; import { shutdownCleanUp } from './resourceRelease.js'; -import { mapToNewConfig } from './schemas/config.js'; import server, { startServer } from './server/server.js'; /** * Initializes the export process. Tasks such as configuring logging, checking - * the cache and sources and initializing the pool of resources happen during - * this stage. This function must be called before attempting to export charts - * or set up a server. The `options` parameter is an object that contains all - * possible options. If the object is not provided, the default general options - * will be retrieved using the `getOptions` function. + * the cache and sources, and initializing the resource pool occur during this + * stage. This function must be called before attempting to export charts or set + * up a server. * * @async * @function initExport * - * @param {Object} [options=getOptions()] - The `options` object containing - * configuration for a custom export. The default value is the global options - * of the export server instance. + * @param {Object} customOptions - The `customOptions` object, which may + * be a partial or complete set of options. If the provided options are partial, + * missing values will be merged with the default general options, retrieved + * using the `getOptions` function. */ -export async function initExport(options = getOptions()) { +export async function initExport(customOptions) { + // Get the global options object copy and extend it with the incoming options + const options = mergeOptions(getOptions(false), customOptions); + // Set the `allowCodeExecution` per export module scope setAllowCodeExecution(options.customLogic.allowCodeExecution); @@ -87,30 +94,30 @@ function _attachProcessExitListeners() { // Handler for the 'exit' process.on('exit', (code) => { - log(4, `Process exited with code ${code}.`); + log(4, `[process] Process exited with code ${code}.`); }); // Handler for the 'SIGINT' process.on('SIGINT', async (name, code) => { - log(4, `The ${name} event with code: ${code}.`); + log(4, `[process] The ${name} event with code: ${code}.`); await shutdownCleanUp(0); }); // Handler for the 'SIGTERM' process.on('SIGTERM', async (name, code) => { - log(4, `The ${name} event with code: ${code}.`); + log(4, `[process] The ${name} event with code: ${code}.`); await shutdownCleanUp(0); }); // Handler for the 'SIGHUP' process.on('SIGHUP', async (name, code) => { - log(4, `The ${name} event with code: ${code}.`); + log(4, `[process] The ${name} event with code: ${code}.`); await shutdownCleanUp(0); }); // Handler for the 'uncaughtException' process.on('uncaughtException', async (error, name) => { - logWithStack(1, error, `The ${name} error.`); + logWithStack(1, error, `[process] The ${name} error.`); await shutdownCleanUp(1); }); } @@ -123,12 +130,17 @@ export default { // Options getOptions, setOptions, + mergeOptions, + mapToNewOptions, // Exporting initExport, - startExport, singleExport, batchExport, + startExport, + + // Cache + checkAndUpdateCache, // Pool initPool, @@ -138,9 +150,9 @@ export default { log, logWithStack, setLogLevel, + enableConsoleLogging, enableFileLogging, // Utils - shutdownCleanUp, - mapToNewConfig + shutdownCleanUp }; From 56edd6e144bfbb6e1349b9da5357e47c806adaba Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:03:35 +0100 Subject: [PATCH 041/102] Optimization of the logger module. --- lib/logger.js | 132 ++++++++++++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 57 deletions(-) diff --git a/lib/logger.js b/lib/logger.js index 82f0be67..eda50167 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -22,13 +22,13 @@ See LICENSE file in root for details. import { appendFile, existsSync, mkdirSync } from 'fs'; import { join } from 'path'; -import { getNewDate } from './utils.js'; +import { getAbsolutePath, getNewDate } from './utils.js'; // The available colors const colors = ['red', 'yellow', 'blue', 'gray', 'green']; // The default logging config -let logging = { +const logging = { // Flags for logging status toConsole: true, toFile: false, @@ -60,28 +60,6 @@ let logging = { ] }; -/** - * Initializes logging with the specified logging configuration. - * - * @function initLogging - * - * @param {Object} loggingOptions - Object containing logging options. - */ -export function initLogging(loggingOptions) { - // Set all the logging options on our logging module object - for (const [key, value] of Object.entries(loggingOptions)) { - logging[key] = value; - } - - // Set the log level - setLogLevel(loggingOptions.level); - - // Set the log file path and name - if (loggingOptions.toFile) { - enableFileLogging(loggingOptions.dest, loggingOptions.file); - } -} - /** * Logs a message. Accepts a variable amount of arguments. Arguments after * the `level` will be passed directly to `console.log`, and/or will be joined @@ -91,6 +69,9 @@ export function initLogging(loggingOptions) { * * @param {...unknown} args - An array of arguments where the first is the log * level and the rest are strings to build a message with. + * + * @returns {void} Ends the function execution when attempting to log + * information at a higher level than what is allowed. */ export function log(...args) { const [newLevel, ...texts] = args; @@ -98,7 +79,7 @@ export function log(...args) { // Current logging options const { levelsDesc, level } = logging; - // Check if log level is within a correct range or is a benchmark log + // Check if the log level is within a correct range or is it a benchmark log if ( newLevel !== 5 && (newLevel === 0 || newLevel > level || level > levelsDesc.length) @@ -109,6 +90,11 @@ export function log(...args) { // Create a message's prefix const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`; + // Log to file + if (logging.toFile) { + _logToFile(texts, prefix); + } + // Log to console if (logging.toConsole) { console.log.apply( @@ -116,11 +102,6 @@ export function log(...args) { [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts) ); } - - // Log to file - if (logging.toFile) { - _logToFile(texts, prefix); - } } /** @@ -133,6 +114,9 @@ export function log(...args) { * @param {Error} error - The error object. * @param {string} customMessage - An optional custom message to be logged * along with the error. + * + * @returns {void} Ends the function execution when attempting to log + * information at a higher level than what is allowed. */ export function logWithStack(newLevel, error, customMessage) { // Get the main message @@ -141,7 +125,7 @@ export function logWithStack(newLevel, error, customMessage) { // Current logging options const { level, levelsDesc } = logging; - // Check if log level is within a correct range + // Check if the log level is within a correct range if (newLevel === 0 || newLevel > level || level > levelsDesc.length) { return; } @@ -149,12 +133,8 @@ export function logWithStack(newLevel, error, customMessage) { // Create a message's prefix const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`; - // If the `customMessage` exists, we want to display the whole stack message - const stackMessage = - error && - (error.message !== error.stackMessage || error.stackMessage === undefined - ? error.stack - : error.stack.split('\n').slice(1).join('\n')); + // Add the whole stack message + const stackMessage = error.stack; // Combine custom message or error message with error stack message, if exists const texts = [mainMessage]; @@ -179,36 +159,71 @@ export function logWithStack(newLevel, error, customMessage) { } } +/** + * Initializes logging with the specified logging configuration. + * + * @function initLogging + * + * @param {Object} loggingOptions - Object containing `logging` options. + */ +export function initLogging(loggingOptions) { + // Get options from the `loggingOptions` object + const { level, dest, file, toConsole, toFile } = loggingOptions; + + // Set the logging level + setLogLevel(level); + + // Set the console logging + enableConsoleLogging(toConsole); + + // Set the file logging + enableFileLogging(dest, file, toFile); +} + /** * Sets the log level to the specified value. Log levels are (0 = no logging, - * 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark). + * 1 = error, 2 = warning, 3 = notice, 4 = verbose, or 5 = benchmark). * * @function setLogLevel * - * @param {number} newLevel - The new log level to be set. + * @param {number} level - The log level to be set. */ -export function setLogLevel(newLevel) { - if (newLevel >= 0 && newLevel <= logging.levelsDesc.length) { - logging.level = newLevel; +export function setLogLevel(level) { + if (level >= 0 && level <= logging.levelsDesc.length) { + logging.level = level; } } +/** + * Enables console logging. + * + * @function enableConsoleLogging + * + * @param {boolean} toConsole - The flag for setting the logging to the console. + */ +export function enableConsoleLogging(toConsole) { + // Update options for the console logging + logging.toConsole = toConsole; +} + /** * Enables file logging with the specified destination and log file. * * @function enableFileLogging * - * @param {string} dest - The destination path for log files. + * @param {string} dest - The destination path for the log file. * @param {string} file - The log file name. + * @param {boolean} toFile - The flag for setting the logging to a file. */ -export function enableFileLogging(dest, file) { - // Update logging options - logging = { - ...logging, - dest, - file, - toFile: true - }; +export function enableFileLogging(dest, file, toFile) { + // Update options for the file logging + logging.toFile = toFile; + + // Set the `dest` and `file` only if the file logging is enabled + if (toFile) { + logging.dest = dest; + logging.file = file; + } } /** @@ -224,10 +239,11 @@ export function enableFileLogging(dest, file) { function _logToFile(texts, prefix) { if (!logging.pathCreated) { // Create if does not exist - !existsSync(logging.dest) && mkdirSync(logging.dest); + !existsSync(getAbsolutePath(logging.dest)) && + mkdirSync(getAbsolutePath(logging.dest)); // Create the full path - logging.pathToLog = join(logging.dest, logging.file); + logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file)); // We now assume the path is available, e.g. it's the responsibility // of the user to create the path with the correct access rights. @@ -239,18 +255,20 @@ function _logToFile(texts, prefix) { logging.pathToLog, [prefix].concat(texts).join(' ') + '\n', (error) => { - if (error) { - console.log(`[logger] Unable to write to log file: ${error}`); + if (error && logging.toFile && logging.pathCreated) { logging.toFile = false; + logging.pathCreated = false; + logWithStack(2, error, `[logger] Unable to write to log file.`); } } ); } export default { - initLogging, log, logWithStack, + initLogging, setLogLevel, + enableConsoleLogging, enableFileLogging }; From 40bc66bbc2f8b74fd9123256c1f748133a35f32a Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:03:44 +0100 Subject: [PATCH 042/102] Optimization of the pool module, with factory functions corrections. --- lib/pool.js | 260 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 152 insertions(+), 108 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index cd4fe30e..6a64c184 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -14,8 +14,8 @@ See LICENSE file in root for details. /** * @overview This module provides a worker pool implementation for managing - * browser instances and pages, specifically designed for use with - * the Highcharts Export Server. It optimizes resource usage and performance + * the browser instance and pages, specifically designed for use with + * the Highcharts Export Server. It optimizes resources usage and performance * by maintaining a pool of workers that can handle concurrent export tasks * using Puppeteer. */ @@ -32,16 +32,19 @@ import { getNewDateTime, measureTime } from './utils.js'; import ExportError from './errors/ExportError.js'; // The pool instance -let pool = false; +let pool = null; // Pool statistics const poolStats = { - performedExports: 0, - exportAttempts: 0, - exportFromSvgAttempts: 0, + exportsAttempted: 0, + exportsPerformed: 0, + exportsDropped: 0, + exportsFromSvg: 0, + exportsFromOptions: 0, + exportsFromSvgAttempts: 0, + exportsFromOptionsAttempts: 0, timeSpent: 0, - droppedExports: 0, - spentAverage: 0 + timeSpentAverage: 0 }; /** @@ -51,38 +54,47 @@ const poolStats = { * @async * @function initPool * - * @param {Object} poolOptions - Object containing pool options. - * @param {Array.} puppeteerArgs - Array of custom puppeteer arguments - * for the puppeteer.launch function. + * @param {Object} [poolOptions=getOptions().pool] - Object containing `pool` + * options. The default value is the global pool options of the export server + * instance. + * @param {Array.} [puppeteerArgs=[]] - Additional arguments + * for Puppeteer launch. The default value is an empty array. + * + * @returns {Promise} A Promise that resolves to ending the function + * execution when an already initialized pool of resources is found. * * @throws {ExportError} Throws an `ExportError` if could not create the pool * of workers. */ -export async function initPool(poolOptions = getOptions().pool, puppeteerArgs) { +export async function initPool( + poolOptions = getOptions().pool, + puppeteerArgs = [] +) { // Create a browser instance with the puppeteer arguments await createBrowser(puppeteerArgs); - log( - 3, - `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.` - ); - - if (pool) { - return log( - 4, - '[pool] Already initialized, please kill it before creating a new one.' + try { + log( + 3, + `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.` ); - } - // Keep an eye on a correct min and max workers number - if (poolOptions.minWorkers > poolOptions.maxWorkers) { - poolOptions.minWorkers = poolOptions.maxWorkers; - } + if (pool) { + log( + 4, + '[pool] Already initialized, please kill it before creating a new one.' + ); + return; + } + + // Keep an eye on a correct min and max workers number + if (poolOptions.minWorkers > poolOptions.maxWorkers) { + poolOptions.minWorkers = poolOptions.maxWorkers; + } - try { // Create a pool along with a minimal number of resources pool = new Pool({ - // Get the create/validate/destroy/log functions + // Get the `create`, `validate`, and `destroy` functions ..._factory(poolOptions), min: poolOptions.minWorkers, max: poolOptions.maxWorkers, @@ -135,7 +147,7 @@ export async function initPool(poolOptions = getOptions().pool, puppeteerArgs) { ); } catch (error) { throw new ExportError( - '[pool] Could not create the pool of workers.', + '[pool] Could not configure and create the pool of workers.', 500 ).setError(error); } @@ -148,7 +160,7 @@ export async function initPool(poolOptions = getOptions().pool, puppeteerArgs) { * @async * @function killPool * - * @returns {Promise} A promise that resolves after the workers are + * @returns {Promise} A Promise that resolves after the workers are * killed, the pool is destroyed, and the browser is closed. */ export async function killPool() { @@ -164,7 +176,7 @@ export async function killPool() { // Destroy the pool if it is still available if (!pool.destroyed) { await pool.destroy(); - log(4, '[browser] Destroyed the pool of resources.'); + log(4, '[pool] Destroyed the pool of resources.'); } pool = null; } @@ -181,54 +193,61 @@ export async function killPool() { * @async * @function postWork * - * @param {string} chart - The chart data or configuration to be exported. - * @param {Object} options - Export options and configuration. + * @param {Object} options - The `options` object containing complete set + * of options. * - * @returns {Promise} A promise that resolves with the export resultand - * options. + * @returns {Promise} A Promise that resolves to the export result + * and options. * * @throws {ExportError} Throws an `ExportError` if an error occurs during * the export process. */ -export async function postWork(chart, options) { +export async function postWork(options) { let workerHandle; try { log(4, '[pool] Work received, starting to process.'); - ++poolStats.exportAttempts; + // An export attempt counted + ++poolStats.exportsAttempted; + + // Display the pool information if needed if (getOptions().pool.benchmarking) { getPoolInfo(); } + // Throw an error in case of lacking the pool instance if (!pool) { throw new ExportError( - 'Work received, but pool has not been started.', + '[pool] Work received, but pool has not been started.', 500 ); } - // Acquire the worker along with the id of resource and work count + // The acquire counter const acquireCounter = measureTime(); + + // Try to acquire the worker along with the id, works count and page try { log(4, '[pool] Acquiring a worker handle.'); + + // Acquire a pool resource workerHandle = await pool.acquire().promise; // Check the page acquire time if (options.server.benchmarking) { log( 5, - options.payload?.requestId - ? `[benchmark] Request: ${options.payload?.requestId} -` - : '[benchmark]', - `Acquired a worker handle: ${acquireCounter()}ms.` + options._requestId + ? `[benchmark] Request [${options._requestId}] - ` + : '[benchmark] ', + `Acquiring a worker handle took ${acquireCounter()}ms.` ); } } catch (error) { throw new ExportError( - (options.payload?.requestId - ? `Request: ${options.payload?.requestId} - ` - : '') + + '[pool] ' + + (options._requestId ? `Request [${options._requestId}] - ` : '') + `Error encountered when acquiring an available entry: ${acquireCounter()}ms.`, 400 ).setError(error); @@ -239,13 +258,13 @@ export async function postWork(chart, options) { // Set the `workLimit` to exceeded in order to recreate the resource workerHandle.workCount = options.pool.workLimit + 1; throw new ExportError( - 'Resolved worker page is invalid: the pool setup is wonky.', + '[pool] Resolved worker page is invalid: the pool setup is wonky.', 400 ); } // Save the start time - let workStart = getNewDateTime(); + const workStart = getNewDateTime(); log( 4, @@ -254,7 +273,7 @@ export async function postWork(chart, options) { // Perform an export on a puppeteer level const exportCounter = measureTime(); - const result = await puppeteerExport(workerHandle.page, chart, options); + const result = await puppeteerExport(workerHandle.page, options); // Check if it's an error if (result instanceof Error) { @@ -281,13 +300,15 @@ export async function postWork(chart, options) { result.message === 'Rasterization timeout' ) { throw new ExportError( - 'Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.' + '[pool] ' + + (options._requestId ? `Request [${options._requestId}] - ` : '') + + 'Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.' ).setError(result); } else { throw new ExportError( - (options.payload?.requestId - ? `Request: ${options.payload?.requestId} - ` - : '') + `Error encountered during export: ${exportCounter()}ms.` + '[pool] ' + + (options._requestId ? `Request [${options._requestId}] - ` : '') + + `Error encountered during export: ${exportCounter()}ms.` ).setError(result); } } @@ -296,10 +317,10 @@ export async function postWork(chart, options) { if (options.server.benchmarking) { log( 5, - options.payload?.requestId - ? `[benchmark] Request: ${options.payload?.requestId} -` - : '[benchmark]', - `Exported a chart sucessfully: ${exportCounter()}ms.` + options._requestId + ? `[benchmark] Request [${options._requestId}] - ` + : '[benchmark] ', + `Exporting a chart sucessfully took ${exportCounter()}ms.` ); } @@ -312,9 +333,10 @@ export async function postWork(chart, options) { const exportTime = workEnd - workStart; poolStats.timeSpent += exportTime; - poolStats.spentAverage = poolStats.timeSpent / ++poolStats.performedExports; + poolStats.timeSpentAverage = + poolStats.timeSpent / ++poolStats.exportsPerformed; - log(4, `[pool] Work completed in ${exportTime} ms.`); + log(4, `[pool] Work completed in ${exportTime}ms.`); // Otherwise return the result return { @@ -322,15 +344,13 @@ export async function postWork(chart, options) { options }; } catch (error) { - ++poolStats.droppedExports; + ++poolStats.exportsDropped; if (workerHandle) { pool.release(workerHandle); } - throw new ExportError(`[pool] In pool.postWork: ${error.message}`).setError( - error - ); + throw error; } } @@ -435,12 +455,12 @@ export function getPoolInfo() { } /** - * Factory function that returns an object with create, validate and destroy - * functions for the pool instance. + * Factory function that returns an object with `create`, `validate`, + * and `destroy` functions for the pool instance. * * @function _factory * - * @param {Object} poolOptions - Object containing pool options. + * @param {Object} poolOptions - Object containing `pool` options. */ function _factory(poolOptions) { return { @@ -450,26 +470,44 @@ function _factory(poolOptions) { * @async * @function create * - * @returns {Object} An object containing the worker ID, a reference to the - * browser page, and initial work count. + * @returns {Promise} A Promise that resolves to an object + * containing the worker ID, a reference to the browser page, and initial + * work count. * * @throws {ExportError} Throws an `ExportError` if there is an error during * the creation of the new page. */ create: async () => { + // Init the resource with unique id and work count + const poolResource = { + id: uuid(), + // Try to distribute the initial work count + workCount: Math.round(Math.random() * (poolOptions.workLimit / 2)) + }; + try { - const poolResource = { - id: uuid(), - // Try to distribute the initial work count - workCount: Math.round(Math.random() * (poolOptions.workLimit / 2)) - }; + // Start measuring a page creation time + const startDate = getNewDateTime(); - return await newPage(poolResource); + // Create a new page + await newPage(poolResource); + + // Measure the time of full creation and configuration of a page + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${ + getNewDateTime() - startDate + }ms.` + ); + + // Return ready pool resource + return poolResource; } catch (error) { - throw new ExportError( - 'Error encountered when creating a new page.', - 400 - ).setError(error); + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.` + ); + throw error; } }, @@ -480,18 +518,18 @@ function _factory(poolOptions) { * @async * @function validate * - * @param {Object} poolResource - The handle to the worker, containing the - * worker's ID, a reference to the browser page, and work count. + * @param {Object} poolResource - The handle to the worker, containing + * the worker's ID, a reference to the browser page, and work count. * - * @returns {boolean} - Returns true if the worker is valid and within - * the work limit; otherwise, returns false. + * @returns {Promise} A Promise that resolves to true if the worker + * is valid and within the work limit; otherwise, to false. */ validate: async (poolResource) => { // NOTE: // In certain cases acquiring throws a TargetCloseError, which may // be caused by two things: - // - The page is closed and attempted to be reused. - // - Lost contact with the browser. + // - The page is closed and attempted to be reused. + // - Lost contact with the browser. // // What we're seeing in logs is that successive exports typically // succeeds, and the server recovers, indicating that it's likely @@ -501,7 +539,31 @@ function _factory(poolOptions) { // The actual result from when this happened, was that a worker would // be completely locked, stopping it from being acquired until // its work count reached the limit. - if (!poolResource.page || poolResource.page?.isClosed()) { + + // Check if the `page` is valid + if (!poolResource.page) { + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).` + ); + return false; + } + + // Check if the `page` is closed + if (poolResource.page.isClosed()) { + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).` + ); + return false; + } + + // Check if the `mainFrame` is detached + if (poolResource.page.mainFrame().detached) { + log( + 3, + `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).` + ); return false; } @@ -512,31 +574,12 @@ function _factory(poolOptions) { ) { log( 3, - `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works limit).` + `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).` ); return false; } - // Check if the `page` is not valid - if (poolResource.page) { - // Check if the `page` is closed - if (poolResource.page.isClosed()) { - log( - 3, - `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).` - ); - } - - // Check if the `mainFrame` is detached - if (poolResource.page.mainFrame().detached) { - log( - 3, - `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).` - ); - } - return false; - } - + // The `poolResource` is validated return true; }, @@ -547,7 +590,7 @@ function _factory(poolOptions) { * @function destroy * * @param {Object} poolResource - The handle to the worker, containing - * the worker's ID and a reference to the browser page. + * the worker's ID, a reference to the browser page, and work count. */ destroy: async (poolResource) => { log( @@ -569,6 +612,7 @@ function _factory(poolOptions) { 3, `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.` ); + throw error; } } } From 3ce9051b9a7406be369cdb042d58fa13cafd05c0 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:03:52 +0100 Subject: [PATCH 043/102] The prompt module file saving correction. --- lib/prompt.js | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/lib/prompt.js b/lib/prompt.js index 9b645acd..512e15f2 100644 --- a/lib/prompt.js +++ b/lib/prompt.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -18,11 +18,13 @@ See LICENSE file in root for details. * a configuration file. */ -import { existsSync, promises as fsPromises, readFileSync } from 'fs'; +import { existsSync, readFileSync } from 'fs'; +import { writeFile } from 'fs/promises'; import prompts from 'prompts'; -import { logWithStack } from './logger.js'; +import { log, logWithStack } from './logger.js'; +import { getAbsolutePath } from './utils.js'; import { defaultConfig } from './schemas/config.js'; /** @@ -44,9 +46,18 @@ export async function manualConfig(configFileName) { let configFile = {}; // Check if the specified configuration file already exists - if (existsSync(configFileName)) { - // If the file exists, read and parse its contents - configFile = JSON.parse(readFileSync(configFileName, 'utf8')); + if (existsSync(getAbsolutePath(configFileName))) { + try { + // If the file exists, read and parse its contents + configFile = JSON.parse( + readFileSync(getAbsolutePath(configFileName), 'utf8') + ); + } catch (error) { + log( + 2, + '[prompt] The existing file for the `createConfig` option is not valid JSON. Using an empty object for the config instead.' + ); + } } /** @@ -56,11 +67,11 @@ export async function manualConfig(configFileName) { * @async * @function onSubmit * - * @param {Object} _ - Unused, automatically passed argument. - * @param {Array} categories - The selected categories to configure. + * @param {unknown} _ - Unused, automatically passed argument. + * @param {Array.} categories - The selected categories to configure. * - * @returns {Promise} Resolves to `true` once the configuration - * process is completed and saved. + * @returns {Promise} A Promise that resolves to true once + * the configuration process is completed and saved. */ const onSubmit = async (_, categories) => { // Track how many questions have been answered @@ -118,9 +129,10 @@ export async function manualConfig(configFileName) { * @function onSubmit * * @param {Object} prompt - The current prompt being answered. - * @param {any} answer - The user's response to the prompt. + * @param {unknown} answer - The user's response to the prompt. * - * @returns {Promise} Resolves once the answer is processed. + * @returns {Promise} A Promise that resolves once the answer + * is processed. */ onSubmit: async (prompt, answer) => { // Handle specific script configurations @@ -148,8 +160,8 @@ export async function manualConfig(configFileName) { // If all questions have been answered, save the updated config if (++questionsCounter === allQuestions.length) { try { - await fsPromises.writeFile( - configFileName, + await writeFile( + getAbsolutePath(configFileName), JSON.stringify(configFile, null, 2), 'utf8' ); @@ -157,7 +169,7 @@ export async function manualConfig(configFileName) { logWithStack( 1, error, - `[config] An error occurred while creating the ${configFileName} file.` + `[prompt] An error occurred while creating the ${configFileName} file.` ); } return true; @@ -195,9 +207,9 @@ export async function manualConfig(configFileName) { * * @function _preparePrompt * - * @param {Array} entry - An array where the first element is the name - * of the option, and the second element is an object containing the option - * details. + * @param {Array.[string, Object]} entry - An array where the first element + * is the name of the option, and the second element is an object containing + * the option details. * @param {string} section - The section name for the prompt, used to categorize * the prompt message. * From da5233ac172043eae5be1248be312f8e7837c20b Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:04:01 +0100 Subject: [PATCH 044/102] Optimization of the resourceRelease module. --- lib/resourceRelease.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/resourceRelease.js b/lib/resourceRelease.js index bea0305c..dba0d026 100644 --- a/lib/resourceRelease.js +++ b/lib/resourceRelease.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -14,7 +14,7 @@ See LICENSE file in root for details. /** * @overview Handles graceful shutdown of the Highcharts Export Server, ensuring - * proper cleanup of resources such as browsers, pages, servers, and timers. + * proper cleanup of resources such as browser, pages, servers, and timers. */ import { killPool } from './pool.js'; @@ -22,7 +22,8 @@ import { clearAllTimers } from './timer.js'; import { closeServers } from './server/server.js'; /** - * Clean up function to trigger before ending process for the graceful shutdown. + * Cleans up function to trigger before ending process for the graceful + * shutdown. * * @function shutdownCleanUp * @@ -37,7 +38,7 @@ export async function shutdownCleanUp(exitCode) { // Get available server instances (HTTP/HTTPS) and close them closeServers(), - // Close pool along with its workers and the browser instance, if exists + // Close an active pool along with its workers and the browser instance killPool() ]); From 2fd4d7144934d227e13394b22e3df177e5bb366a Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:04:11 +0100 Subject: [PATCH 045/102] Optimization of the sanitize module. --- lib/sanitize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sanitize.js b/lib/sanitize.js index 5e0b45b9..e1e445e1 100644 --- a/lib/sanitize.js +++ b/lib/sanitize.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. From 27deb26826c660d51676474d59ea4ab4850d555f Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:04:20 +0100 Subject: [PATCH 046/102] Optimization of the timer module. --- lib/timer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/timer.js b/lib/timer.js index 525f21ac..8b74c825 100644 --- a/lib/timer.js +++ b/lib/timer.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -30,7 +30,7 @@ const timerIds = []; * * @function addTimer * - * @param {NodeJS.Timeout} id - Id of an interval/timeout. + * @param {NodeJS.Timeout} id - Id of an interval or a timeout. */ export function addTimer(id) { timerIds.push(id); @@ -43,7 +43,7 @@ export function addTimer(id) { * @function clearAllTimers */ export function clearAllTimers() { - log(4, `[server] Clearing all registered intervals and timeouts.`); + log(4, `[timer] Clearing all registered intervals and timeouts.`); for (const id of timerIds) { clearInterval(id); clearTimeout(id); From 1730105a8c721ab319de7b71f6d5d1ef6dc1b676 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:04:37 +0100 Subject: [PATCH 047/102] New fixOutfile, getAbsolutePath, getBase64 utils and moved out isCorrectJSON, mergeConfigOptions, optionsStringify, printLicense, printVersion. --- lib/utils.js | 323 ++++++++++++++++----------------------------------- 1 file changed, 102 insertions(+), 221 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index e28fd7df..8eb66292 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -19,7 +19,7 @@ See LICENSE file in root for details. */ import { readFileSync } from 'fs'; -import { join } from 'path'; +import { isAbsolute, join } from 'path'; import { fileURLToPath } from 'url'; const MAX_BACKOFF_ATTEMPTS = 6; @@ -36,9 +36,10 @@ export const __dirname = fileURLToPath(new URL('../.', import.meta.url)); * * @param {string} text - The input text to be cleared. * @param {RegExp} [rule=/\s\s+/g] - The regular expression rule to match - * multiple consecutive whitespace characters. + * multiple consecutive whitespace characters. The default value + * is the '/\s\s+/g' RegExp. * @param {string} [replacer=' '] - The string used to replace multiple - * consecutive whitespace characters. + * consecutive whitespace characters. The default value is the ' ' string. * * @returns {string} The cleared and standardized text. */ @@ -51,28 +52,28 @@ export function clearText(text, rule = /\s\s+/g, replacer = ' ') { * * @function deepCopy * - * @param {(Object|Array)} obj - The object or array to be deeply copied. + * @param {(Object|Array)} objArr - The object or array to be deeply copied. * * @returns {(Object|Array)} The deep copy of the provided object or array. */ -export function deepCopy(obj) { - // If the `obj` is null or not of the `object` type, return it - if (obj === null || typeof obj !== 'object') { - return obj; +export function deepCopy(objArr) { + // If the `objArr` is null or not of the `object` type, return it + if (objArr === null || typeof objArr !== 'object') { + return objArr; } // Prepare either a new array or a new object - const copy = Array.isArray(obj) ? [] : {}; + const objArrCopy = Array.isArray(objArr) ? [] : {}; // Recursively copy each property - for (const key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - copy[key] = deepCopy(obj[key]); + for (const key in objArr) { + if (Object.prototype.hasOwnProperty.call(objArr, key)) { + objArrCopy[key] = deepCopy(objArr[key]); } } // Return the copied object - return copy; + return objArrCopy; } /** @@ -83,11 +84,12 @@ export function deepCopy(obj) { * @function expBackoff * * @param {Function} fn - The function to be retried. - * @param {number} [attempt=0] - The current attempt number. + * @param {number} [attempt=0] - The current attempt number. The default value + * is 0. * @param {...unknown} args - Arguments to be passed to the function. * - * @returns {Promise} A promise that resolves to the result of the function - * if successful. + * @returns {Promise} A Promise that resolves to the result + * of the function if successful. * * @throws {Error} Throws an `Error` if the maximum number of attempts * is reached. @@ -100,7 +102,7 @@ export async function expBackoff(fn, attempt = 0, ...args) { // Calculate delay in ms const delayInMs = 2 ** attempt * 1000; - // If the attempt exceeds the maximum attempts of reapeat, throw an error + // If the attempt exceeds the maximum attempts of repeat, throw an error if (++attempt >= MAX_BACKOFF_ATTEMPTS) { throw error; } @@ -108,7 +110,7 @@ export async function expBackoff(fn, attempt = 0, ...args) { // Wait given amount of time await new Promise((response) => setTimeout(response, delayInMs)); - //// TO DO: Correct + /// TO DO: Correct // // Information about the resource timeout // log( // 3, @@ -153,17 +155,38 @@ export function fixConstr(constr) { } } +/** + * Fixes the outfile based on provided type. + * + * @function fixOutfile + * + * @param {string} type - The original export type. + * @param {string} outfile - The file path or name. + * + * @returns {string} The corrected outfile. + */ +export function fixOutfile(type, outfile) { + // Get the file name from the `outfile` option + const fileName = getAbsolutePath(outfile || 'chart') + .split('.') + .shift(); + + // Return a correct outfile + return `${fileName}.${type}`; +} + /** * Fixes the export type based on MIME types and file extensions. * * @function fixType * * @param {string} type - The original export type. - * @param {string} outfile - The file path or name. + * @param {string} [outfile=null] - The file path or name. The default value + * is null. * * @returns {string} The corrected export type. */ -export function fixType(type, outfile) { +export function fixType(type, outfile = null) { // MIME types const mimeTypes = { 'image/png': 'png', @@ -172,13 +195,14 @@ export function fixType(type, outfile) { 'image/svg+xml': 'svg' }; - // Formats - const formats = ['png', 'jpeg', 'pdf', 'svg']; + // Get formats + const formats = Object.values(mimeTypes); // Check if type and outfile's extensions are the same if (outfile) { const outType = outfile.split('.').pop(); + // Support the JPG type if (outType === 'jpg') { type = 'jpeg'; } else if (formats.includes(outType) && type !== outType) { @@ -190,6 +214,40 @@ export function fixType(type, outfile) { return mimeTypes[type] || formats.find((t) => t === type) || 'png'; } +/** + * Checks if the given path is relative or absolute and returns the corrected, + * absolute path. + * + * @function isAbsolutePath + * + * @param {string} path - The path to be checked on. + * + * @returns {string} The absolute path. + */ +export function getAbsolutePath(path) { + return isAbsolute(path) ? path : join(__dirname, path); +} + +/** + * Converts input data to a Base64 string based on the export type. + * + * @function getBase64 + * + * @param {string} input - The input to be transformed to Base64 format. + * @param {string} type - The original export type. + * + * @returns {string} The Base64 string representation of the input. + */ +export function getBase64(input, type) { + // For pdf and svg types the input must be transformed to Base64 from a buffer + if (type === 'pdf' || type == 'svg') { + return Buffer.from(input, 'utf8').toString('base64'); + } + + // For png and jpeg input is already a Base64 string + return input; +} + /** * Returns stringified date without the GMT text information. * @@ -209,59 +267,6 @@ export function getNewDateTime() { return new Date().getTime(); } -/** - * Validates, parses, and checks if the provided data is valid JSON. - * - * @function isCorrectJSON - * - * @param {unknown} data - The data to be validated and parsed as JSON. - * Must be either an object or a string. - * @param {boolean} [toString=false] - Whether to return a stringified JSON - * version of the parsed data. - * @param {boolean} [allowFunctions=false] - Whether to allow functions - * in the parsed JSON. If true, functions are preserved. - * - * @returns {(Object|string|null)} Returns the parsed JSON object, - * a stringified JSON object if `toString` is true, or null if the data - * is not valid JSON or parsing fails. - */ -export function isCorrectJSON(data, toString = false, allowFunctions = false) { - try { - // Accept only objects and strings - if (!isObject(data) && typeof data !== 'string') { - // Return null if any other type - return null; - } - - // Get the JSON representation of original data - const jsonData = typeof data === 'string' ? JSON.parse(data) : data; - - // Preserve or remove potential functions based on the allowFunctions flag - const stringifiedOptions = optionsStringify( - jsonData, - allowFunctions, - false - ); - - // Parse the data to check if it is valid JSON - const parsedOptions = allowFunctions - ? JSON.parse( - optionsStringify(jsonData, allowFunctions, true), - (_, value) => - typeof value === 'string' && value.startsWith('function') - ? eval(`(${value})`) - : value - ) - : JSON.parse(stringifiedOptions); - - // Return stringified or parsed JSON object based on the toString flag - return toString ? stringifiedOptions : parsedOptions; - } catch (error) { - // Return null if parse fails - return null; - } -} - /** * Checks if the given item is an object. * @@ -326,138 +331,6 @@ export function measureTime() { return () => Number(process.hrtime.bigint() - start) / 1000000; } -/** - * Merges two sets of configuration options, considering absolute properties. - * - * @function mergeConfigOptions - * - * @param {Object} options - Original configuration options. - * @param {Object} newOptions - New configuration options to be merged. - * @param {Array.} [absoluteProps=[]] - List of properties that should - * not be recursively merged. - * - * @returns {Object} Merged configuration options. - */ -export function mergeConfigOptions(options, newOptions, absoluteProps = []) { - const mergedOptions = deepCopy(options); - for (const [key, value] of Object.entries(newOptions)) { - mergedOptions[key] = - isObject(value) && - !absoluteProps.includes(key) && - mergedOptions[key] !== undefined - ? mergeConfigOptions(mergedOptions[key], value, absoluteProps) - : value !== undefined - ? value - : mergedOptions[key]; - } - return mergedOptions; -} - -/** - * Converts the provided options object to a JSON-formatted string - * with the option to preserve functions. In order for a function - * to be preserved, it needs to follow the format `function (...) {...}`. - * It can also be stringified. - * - * @function optionsStringify - * - * @param {Object} options - The options object to be converted to a string. - * @param {boolean} allowFunctions - If set to true, functions are preserved - * in the output. - * @param {boolean} stringifyFunctions - If set to true, functions are saved - * as strings. - * - * @returns {string} The JSON-formatted string representing the options. - */ -export function optionsStringify(options, allowFunctions, stringifyFunctions) { - const replacerCallback = (_, value) => { - // Trim string values - if (typeof value === 'string') { - value = value.trim(); - } - - // If value is a function or stringified function - if ( - typeof value === 'function' || - (typeof value === 'string' && - value.startsWith('function') && - value.endsWith('}')) - ) { - // If allowFunctions is set to true, preserve functions - if (allowFunctions) { - // Based on the stringifyFunctions options, set function values - return stringifyFunctions - ? // As stringified functions - `"EXP_FUN${(value + '').replaceAll(/\n|\t|\r|\s+/g, ' ')}EXP_FUN"` - : // As functions - `EXP_FUN${(value + '').replaceAll(/\n|\t|\r|\s+/g, ' ')}EXP_FUN`; - } else { - // Get rid of the function values otherwise - return undefined; - } - } - - // In all other cases, simply return the value - return value; - }; - - // Stringify options and if required, replace special functions marks - return JSON.stringify(options, replacerCallback).replaceAll( - stringifyFunctions ? /\\"EXP_FUN|EXP_FUN\\"/g : /"EXP_FUN|EXP_FUN"/g, - '' - ); -} - -/** - * Prints the Highcharts Export Server logo, version, and license information. - * - * @function printLicense - */ -export function printLicense() { - // Print the logo and version information - printVersion(); - - // Print the license information - console.log( - 'This software requires a valid Highcharts license for commercial use.\n' - .yellow, - '\nFor a full list of CLI options, type:', - '\nhighcharts-export-server --help\n'.green, - '\nIf you do not have a license, one can be obtained here:', - '\nhttps://shop.highsoft.com/\n'.green, - '\nTo customize your installation, please refer to the README file at:', - '\nhttps://github.com/highcharts/node-export-server#readme\n'.green - ); -} - -/** - * Prints the Highcharts Export Server logo or text with the version - * information. - * - * @function printVersion - * - * @param {boolean} noLogo - If true, only prints text with the version - * information, without the logo. - */ -export function printVersion(noLogo) { - // Get package version either from `.env` or from `package.json` - const packageVersion = JSON.parse( - readFileSync(join(__dirname, 'package.json')) - ).version; - - // Print text only - if (noLogo) { - console.log(`Highcharts Export Server v${packageVersion}`); - return; - } - - // Print the logo - console.log( - readFileSync(join(__dirname, 'msg', 'startup.msg')).toString().bold.yellow, - `v${packageVersion}\n`.bold - ); -} - /** * Rounds a number to the specified precision. * @@ -495,26 +368,36 @@ export function toBoolean(item) { * * @param {string} customCode - The custom code to be wrapped. * @param {boolean} allowFileResources - Flag to allow loading code from a file. + * @param {boolean} [isCallback=false] - Flag that indicates the returned code + * must be in a callback format. * - * @returns {(string|boolean)} The wrapped custom code or false if wrapping - * fails. + * @returns {(string|null)} The wrapped custom code or null if wrapping fails. */ -export function wrapAround(customCode, allowFileResources) { +export function wrapAround(customCode, allowFileResources, isCallback = false) { if (customCode && typeof customCode === 'string') { customCode = customCode.trim(); if (customCode.endsWith('.js')) { + // Load a file if the file resources are allowed return allowFileResources - ? wrapAround(readFileSync(customCode, 'utf8')) + ? wrapAround( + readFileSync(getAbsolutePath(customCode), 'utf8'), + allowFileResources, + isCallback + ) : null; } else if ( - customCode.startsWith('function()') || - customCode.startsWith('function ()') || - customCode.startsWith('()=>') || - customCode.startsWith('() =>') + !isCallback && + (customCode.startsWith('function()') || + customCode.startsWith('function ()') || + customCode.startsWith('()=>') || + customCode.startsWith('() =>')) ) { + // Treat a function as a self-invoking expression return `(${customCode})()`; } + + // Or return as a stringified code return customCode.replace(/;$/, ''); } } @@ -525,18 +408,16 @@ export default { deepCopy, expBackoff, fixConstr, + fixOutfile, fixType, + getAbsolutePath, + getBase64, getNewDate, getNewDateTime, - isCorrectJSON, isObject, isObjectEmpty, isPrivateRangeUrlFound, measureTime, - mergeConfigOptions, - optionsStringify, - printLicense, - printVersion, roundNumber, toBoolean, wrapAround From b54aa988a9f1b814f5e3776388e934413b919cb9 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:04:50 +0100 Subject: [PATCH 048/102] Template corrections. --- templates/svgExport/css.js | 7 ++++++- templates/svgExport/svgExport.js | 13 ++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/templates/svgExport/css.js b/templates/svgExport/css.js index 76d08a5a..cddd360a 100644 --- a/templates/svgExport/css.js +++ b/templates/svgExport/css.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -12,6 +12,11 @@ See LICENSE file in root for details. *******************************************************************************/ +/** + * The CSS to be used on the exported page. + * + * @returns {string} The CSS configuration. + */ export default () => ` html, body { diff --git a/templates/svgExport/svgExport.js b/templates/svgExport/svgExport.js index f3ae939c..98d6236b 100644 --- a/templates/svgExport/svgExport.js +++ b/templates/svgExport/svgExport.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -14,7 +14,14 @@ See LICENSE file in root for details. import cssTemplate from './css.js'; -export default (chart) => ` +/** + * The SVG template to use when loading SVG content to be exported. + * + * @param {string} svg - The SVG input content to be exported. + * + * @returns {string} The SVG template. + */ +export default (svg) => ` @@ -26,7 +33,7 @@ export default (chart) => `
- ${chart} + ${svg}
From ddf81a78fac24fd5e8c0e4f9c20d88a5d3bb1bf4 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:05:02 +0100 Subject: [PATCH 049/102] Moved mapToNewConfig, printUsage, _cycleCategories functions and optimized remaining ones. --- lib/schemas/config.js | 245 +++++++++++++++++------------------------- 1 file changed, 100 insertions(+), 145 deletions(-) diff --git a/lib/schemas/config.js b/lib/schemas/config.js index b3a90e35..2da50bb3 100644 --- a/lib/schemas/config.js +++ b/lib/schemas/config.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -15,7 +15,7 @@ See LICENSE file in root for details. /** * @overview Configuration management module for the Highcharts Export Server. * Provides default configurations that support environment variables, CLI - * arguments, and interactive prompts for customization and adaptability. + * arguments, and interactive prompts for customization of options and features. * Additionally, it maps legacy options to modern structures, generates nested * argument mappings, and displays CLI usage information. */ @@ -29,9 +29,9 @@ See LICENSE file in root for details. * - Data types for validation * - Names of corresponding environment variables * - Descriptions of each property + * - Information used for prompts in interactive configuration * - [Optional] Corresponding CLI argument names for CLI usage * - [Optional] Legacy names from the previous PhantomJS-based server - * - [Optional] Information used for prompts in interactive configuration */ export const defaultConfig = { puppeteer: { @@ -256,7 +256,7 @@ export const defaultConfig = { }, instr: { value: null, - types: ['object', 'string', 'null'], + types: ['string', 'null'], envLink: 'EXPORT_INSTR', description: 'Overrides the `infile` with JSON, stringified JSON, or SVG input', @@ -266,7 +266,7 @@ export const defaultConfig = { }, options: { value: null, - types: ['object', 'string', 'null'], + types: ['Object', 'null'], envLink: 'EXPORT_OPTIONS', description: 'Alias for the `instr` option', promptOptions: { @@ -282,6 +282,16 @@ export const defaultConfig = { type: 'text' } }, + batch: { + value: null, + types: ['string', 'null'], + envLink: 'EXPORT_BATCH', + description: + 'Batch job string with input/output pairs: "in=out;in=out;..."', + promptOptions: { + type: 'text' + } + }, outfile: { value: null, types: ['string', 'null'], @@ -320,7 +330,7 @@ export const defaultConfig = { types: ['boolean'], envLink: 'EXPORT_B64', description: - 'Whether or not to the chart should be received in base64 format instead of binary', + 'Whether or not to the chart should be received in Base64 format instead of binary', promptOptions: { type: 'toggle' } @@ -335,6 +345,34 @@ export const defaultConfig = { type: 'toggle' } }, + height: { + value: null, + types: ['number', 'null'], + envLink: 'EXPORT_HEIGHT', + description: 'Height of the exported chart, overrides chart settings', + promptOptions: { + type: 'number' + } + }, + width: { + value: null, + types: ['number', 'null'], + envLink: 'EXPORT_WIDTH', + description: 'Width of the exported chart, overrides chart settings', + promptOptions: { + type: 'number' + } + }, + scale: { + value: null, + types: ['number', 'null'], + envLink: 'EXPORT_SCALE', + description: + 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0', + promptOptions: { + type: 'number' + } + }, defaultHeight: { value: 400, types: ['number'], @@ -365,37 +403,9 @@ export const defaultConfig = { max: 5 } }, - height: { - value: null, - types: ['number', 'null'], - envLink: 'EXPORT_HEIGHT', - description: 'Height of the exported chart, overrides chart settings', - promptOptions: { - type: 'number' - } - }, - width: { - value: null, - types: ['number', 'null'], - envLink: 'EXPORT_WIDTH', - description: 'Width of the exported chart, overrides chart settings', - promptOptions: { - type: 'number' - } - }, - scale: { - value: null, - types: ['number', 'null'], - envLink: 'EXPORT_SCALE', - description: - 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0', - promptOptions: { - type: 'number' - } - }, globalOptions: { value: null, - types: ['object', 'string', 'null'], + types: ['Object', 'string', 'null'], envLink: 'EXPORT_GLOBAL_OPTIONS', description: 'JSON, stringified JSON or filename with global options for Highcharts.setOptions', @@ -405,7 +415,7 @@ export const defaultConfig = { }, themeOptions: { value: null, - types: ['object', 'string', 'null'], + types: ['Object', 'string', 'null'], envLink: 'EXPORT_THEME_OPTIONS', description: 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions', @@ -413,16 +423,6 @@ export const defaultConfig = { type: 'text' } }, - batch: { - value: null, - types: ['string', 'null'], - envLink: 'EXPORT_BATCH', - description: - 'Batch job string with input/output pairs: "in=out;in=out;..."', - promptOptions: { - type: 'text' - } - }, rasterizationTimeout: { value: 1500, types: ['number'], @@ -476,7 +476,7 @@ export const defaultConfig = { }, resources: { value: null, - types: ['object', 'string', 'null'], + types: ['Object', 'string', 'null'], envLink: 'CUSTOM_LOGIC_RESOURCES', description: 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections', @@ -534,6 +534,15 @@ export const defaultConfig = { type: 'number' } }, + uploadLimit: { + value: 3, + types: ['number'], + envLink: 'SERVER_UPLOAD_LIMIT', + description: 'Maximum request body size in MB', + promptOptions: { + type: 'number' + } + }, benchmarking: { value: false, types: ['boolean'], @@ -983,69 +992,11 @@ export const defaultConfig = { } }; -// Argument nesting level of all export server options -export const nestedArgs = _createNestedArgs(defaultConfig); +// Properties nesting level of all options +export const nestedProps = _createNestedProps(defaultConfig); -/** - * Maps old-structured configuration options (PhantomJS) to a new format - * (Puppeteer). This function converts flat, old-structured options into - * a new, nested configuration format based on a predefined mapping - * (`nestedArgs`). The new format is used for Puppeteer, while the old format - * was used for PhantomJS. - * - * @function mapToNewConfig - * - * @param {Object} oldOptions - The old, flat configuration options - * to be converted. - * - * @returns {Object} A new object containing options structured according - * to the mapping defined in `nestedArgs`. - */ -export function mapToNewConfig(oldOptions) { - // An object for the new structured options - const newOptions = {}; - - // Iterate over each key-value pair in the old-structured options - for (const [key, value] of Object.entries(oldOptions)) { - // If there is a nested mapping, split it into a properties chain - const propertiesChain = nestedArgs[key] ? nestedArgs[key].split('.') : []; - - // If it is the last property in the chain, assign the value, otherwise, - // create or reuse the nested object - propertiesChain.reduce( - (obj, prop, index) => - (obj[prop] = - propertiesChain.length - 1 === index ? value : obj[prop] || {}), - newOptions - ); - } - - // Return the new, structured options object - return newOptions; -} - -/** - * Prints usage information for CLI arguments, displaying available options - * and their descriptions. It can list properties recursively if categories - * contain nested options. - * - * @function printUsage - */ -export function printUsage() { - // Display README and general usage information - console.log( - '\nUsage of CLI arguments:'.bold, - '\n-----', - `\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}`, - '\n' - ); - - // Iterate through each category in the `defaultConfig` and display usage info - Object.keys(defaultConfig).forEach((category) => { - console.log(`${category.toUpperCase()}`.red); - _cycleCategories(defaultConfig[category]); - }); -} +// Properties names that should not be recursively merged +export const absoluteProps = _createAbsoluteProps(defaultConfig); /** * Recursively generates a mapping of nested argument chains from a nested @@ -1054,18 +1005,18 @@ export function printUsage() { * or the original key) and each value is a string representing the chain * of nested properties leading to that argument. * - * @function _createNestedArgs + * @function _createNestedProps * - * @param {Object} config - The nested configuration object containing argument. - * @param {Object} [nestedArgs={}] - The accumulator object for storing - * the resulting argument chains. + * @param {Object} config - The configuration object. + * @param {Object} [nestedProps={}] - The accumulator object for storing + * the resulting arguments chains. The default value is an empty object. * @param {string} [propChain=''] - The current chain of nested properties, - * used internally during recursion. + * used internally during recursion. The default value is an empty string. * * @returns {Object} An object mapping argument names to their corresponding * nested property chains. */ -function _createNestedArgs(config, nestedArgs = {}, propChain = '') { +function _createNestedProps(config, nestedProps = {}, propChain = '') { Object.keys(config).forEach((key) => { // Get the specific section const entry = config[key]; @@ -1073,56 +1024,60 @@ function _createNestedArgs(config, nestedArgs = {}, propChain = '') { // Check if there is still more depth to traverse if (typeof entry.value === 'undefined') { // Recurse into deeper levels of nested arguments - _createNestedArgs(entry, nestedArgs, `${propChain}.${key}`); + _createNestedProps(entry, nestedProps, `${propChain}.${key}`); } else { // Create the chain of nested arguments - nestedArgs[entry.cliName || key] = `${propChain}.${key}`.substring(1); + nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1); // Support for the legacy, PhantomJS properties names if (entry.legacyName !== undefined) { - nestedArgs[entry.legacyName] = `${propChain}.${key}`.substring(1); + nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1); } } }); // Return the object with nested argument chains - return nestedArgs; + return nestedProps; } /** - * Recursively traverses the options object to print the usage information - * for each option category and individual option. + * Recursively gathers the names of properties from a configuration object that + * can be treated as absolute properties. These properties have values that + * are objects and do not contain further nested depth when merging an object + * containing these options. * - * @function _cycleCategories + * @function _createAbsoluteProps * - * @param {Object} options - The options object containing CLI options. - * It may include nested categories and individual options. + * @param {Object} config - The configuration object. + * @param {Array.} [absoluteProps=[]] - An array to collect the names + * of absolute properties. The default value is an empty array. + * + * @returns {Array.} An array containing the names of absolute + * properties. */ -function _cycleCategories(options) { - for (const [name, option] of Object.entries(options)) { - // If the current entry is a category and not a leaf option, recurse into it - if (!Object.prototype.hasOwnProperty.call(option, 'value')) { - _cycleCategories(option); - } else { - const descName = ` --${option.cliName || name} ${ - ('<' + option.types.join('|') + '>').green - }\n`; +function _createAbsoluteProps(config, absoluteProps = []) { + Object.keys(config).forEach((key) => { + // Get the specific section + const entry = config[key]; - // Display correctly aligned messages - console.log( - descName, - `[Default: ${String(option.value).bold}]`.blue, - '-', - `${option.description}.`, - '\n' - ); + // Check if there is still more depth to traverse + if (typeof entry.types === 'undefined') { + // Recurse into deeper levels + _createAbsoluteProps(entry, absoluteProps); + } else { + // If the option can be an object, save its type in the array + if (entry.types.includes('Object')) { + absoluteProps.push(key); + } } - } + }); + + // Return the array with the names of absolute properties + return absoluteProps; } export default { defaultConfig, - nestedArgs, - mapToNewConfig, - printUsage + nestedProps, + absoluteProps }; From 036dfcb6a61215afec96d92ef6c7f2542e59f9ec Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:05:13 +0100 Subject: [PATCH 050/102] Removed unnecessary errors in separated files. --- lib/errors/ExportError.js | 4 ++-- lib/errors/HttpError.js | 8 +++---- lib/errors/NoCorrectBodyError.js | 33 --------------------------- lib/errors/NoCorrectChartDataError.js | 33 --------------------------- lib/errors/NoCorrectResultError.js | 33 --------------------------- lib/errors/PrivateRangeUrlError.js | 33 --------------------------- 6 files changed, 6 insertions(+), 138 deletions(-) delete mode 100644 lib/errors/NoCorrectBodyError.js delete mode 100644 lib/errors/NoCorrectChartDataError.js delete mode 100644 lib/errors/NoCorrectResultError.js delete mode 100644 lib/errors/PrivateRangeUrlError.js diff --git a/lib/errors/ExportError.js b/lib/errors/ExportError.js index 04683ab6..760554e3 100644 --- a/lib/errors/ExportError.js +++ b/lib/errors/ExportError.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -19,7 +19,7 @@ See LICENSE file in root for details. */ class ExportError extends Error { /** - * Creates an instance of `ExportError`. + * Creates an instance of the `ExportError`. * * @param {string} message - The error message to be displayed. * @param {number} statusCode - Optional HTTP status code associated diff --git a/lib/errors/HttpError.js b/lib/errors/HttpError.js index a14143ce..d63a922a 100644 --- a/lib/errors/HttpError.js +++ b/lib/errors/HttpError.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -15,12 +15,12 @@ See LICENSE file in root for details. import ExportError from './ExportError.js'; /** - * A custom HTTP error class that extends `ExportError`. Used to handle errors - * with HTTP status codes. + * A custom HTTP error class that extends the `ExportError`. Used to handle + * errors with HTTP status codes. */ class HttpError extends ExportError { /** - * Creates an instance of `HttpError`. + * Creates an instance of the `HttpError`. * * @param {string} message - The error message to be displayed. * @param {number} statusCode - Optional HTTP status code associated diff --git a/lib/errors/NoCorrectBodyError.js b/lib/errors/NoCorrectBodyError.js deleted file mode 100644 index 0a037b5f..00000000 --- a/lib/errors/NoCorrectBodyError.js +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - -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. - -*******************************************************************************/ - -import HttpError from './HttpError.js'; - -/** - * The `NoCorrectBodyError` error class that extends `HttpError`. Used to handle - * errors related to incorrect request's body. - */ -class NoCorrectBodyError extends HttpError { - /** - * Creates an instance of `NoCorrectBodyError`. - */ - constructor() { - super( - "The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.", - 400 - ); - } -} - -export default NoCorrectBodyError; diff --git a/lib/errors/NoCorrectChartDataError.js b/lib/errors/NoCorrectChartDataError.js deleted file mode 100644 index 9b64956a..00000000 --- a/lib/errors/NoCorrectChartDataError.js +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - -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. - -*******************************************************************************/ - -import HttpError from './HttpError.js'; - -/** - * The `NoCorrectChartDataError` error class that extends `HttpError`. Used - * to handle errors related to incorrect chart's data. - */ -class NoCorrectChartDataError extends HttpError { - /** - * Creates an instance of `NoCorrectChartDataError`. - */ - constructor() { - super( - "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 - ); - } -} - -export default NoCorrectChartDataError; diff --git a/lib/errors/NoCorrectResultError.js b/lib/errors/NoCorrectResultError.js deleted file mode 100644 index 6edfac76..00000000 --- a/lib/errors/NoCorrectResultError.js +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - -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. - -*******************************************************************************/ - -import HttpError from './HttpError.js'; - -/** - * The `NoCorrectResultError` error class that extends `HttpError`. Used - * to handle errors related to unexpected return of the export result. - */ -class NoCorrectResultError extends HttpError { - /** - * Creates an instance of `NoCorrectResultError`. - */ - constructor() { - super( - 'Unexpected return of the export result from the chart generation. Please check your request data.', - 400 - ); - } -} - -export default NoCorrectResultError; diff --git a/lib/errors/PrivateRangeUrlError.js b/lib/errors/PrivateRangeUrlError.js deleted file mode 100644 index c064f718..00000000 --- a/lib/errors/PrivateRangeUrlError.js +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - -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. - -*******************************************************************************/ - -import HttpError from './HttpError.js'; - -/** - * The `PrivateRangeUrlError` error class that extends `HttpError`. Used - * to handle errors related to private range url. - */ -class PrivateRangeUrlError extends HttpError { - /** - * Creates an instance of `PrivateRangeUrlError`. - */ - constructor() { - super( - "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 - ); - } -} - -export default PrivateRangeUrlError; From d061c60e499401d71049dfda1831eafe19d74238 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:05:38 +0100 Subject: [PATCH 051/102] Corrections in validator middleware and export route. --- lib/server/middlewares/validation.js | 239 +++++++++++++++------------ lib/server/routes/export.js | 91 +++++----- 2 files changed, 169 insertions(+), 161 deletions(-) diff --git a/lib/server/middlewares/validation.js b/lib/server/middlewares/validation.js index 04dce82e..3d281d12 100644 --- a/lib/server/middlewares/validation.js +++ b/lib/server/middlewares/validation.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -13,7 +13,7 @@ See LICENSE file in root for details. *******************************************************************************/ /** - * @overview Defines middleware functions for validating incoming HTTP requests + * @overview Provides middleware functions for validating incoming HTTP requests * in an Express application. This module ensures that requests contain * appropriate content types and valid request bodies, including proper JSON * structures and chart data for exports. It checks for potential issues such @@ -26,19 +26,16 @@ See LICENSE file in root for details. import { v4 as uuid } from 'uuid'; import { getAllowCodeExecution } from '../../chart.js'; +import { isAllowedConfig } from '../../config.js'; import { log } from '../../logger.js'; import { fixConstr, fixType, - isCorrectJSON, isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js'; import HttpError from '../../errors/HttpError.js'; -import NoCorrectBodyError from '../../errors/NoCorrectBodyError.js'; -import NoCorrectChartDataError from '../../errors/NoCorrectChartDataError.js'; -import PrivateRangeUrlError from '../../errors/PrivateRangeUrlError.js'; /** * Middleware for validating the content-type header. @@ -46,134 +43,160 @@ import PrivateRangeUrlError from '../../errors/PrivateRangeUrlError.js'; * @function contentTypeMiddleware * * @param {Express.Request} request - The Express request object. - * @param {Express.Response} _response - The Express response object. + * @param {Express.Response} response - The Express response object. * @param {Function} next - The next middleware function. * + * @returns {undefined} The call to the next middleware function. + * * @throws {HttpError} Throws an `HttpError` if the content-type * is not correct. */ -function contentTypeMiddleware(request, _response, next) { - // Get the content type header - const contentType = request.headers['content-type'] || ''; - - // Allow only JSON, URL-encoded and form data without files types of data - if ( - !contentType.includes('application/json') && - !contentType.includes('application/x-www-form-urlencoded') && - !contentType.includes('multipart/form-data') - ) { - throw new HttpError( - 'Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.', - 415 - ); +function contentTypeMiddleware(request, response, next) { + try { + // Get the content type header + const contentType = request.headers['content-type'] || ''; + + // Allow only JSON, URL-encoded and form data without files types of data + if ( + !contentType.includes('application/json') && + !contentType.includes('application/x-www-form-urlencoded') && + !contentType.includes('multipart/form-data') + ) { + throw new HttpError( + '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.', + 415 + ); + } + + // Call the `requestBodyMiddleware` middleware + return next(); + } catch (error) { + return next(error); } - return next(); } /** - * Middleware for validating the request body. + * Middleware for validating the request's body. * * @function requestBodyMiddleware * * @param {Express.Request} request - The Express request object. - * @param {Express.Response} _response - The Express response object. + * @param {Express.Response} response - The Express response object. * @param {Function} next - The next middleware function. * - * @throws {NoCorrectBodyError} Throws an `NoCorrectBodyError` if the body + * @returns {undefined} The call to the next middleware function. + * + * @throws {HttpError} Throws an `HttpError` if the body is not correct. + * @throws {HttpError} Throws an `HttpError` if the chart data from the body * is not correct. - * @throws {NoCorrectChartDataError} Throws an `NoCorrectChartDataError` - * if the chart data from the body is not correct. - * @throws {PrivateRangeUrlError} Throws an `PrivateRangeUrlError` in case - * of the private range url error. - * @throws {ValidationError} Throws an `ValidationError` in case of the body - * validation error. + * @throws {HttpError} Throws an `HttpError` in case of the private range url + * error. */ -function requestBodyMiddleware(request, _response, next) { - // Get the request body - const body = request.body; - - // Create a unique ID for a request - const requestId = uuid().replace(/-/g, ''); - - // Throw 'NoCorrectBodyError' if there is no correct body - if (!body || isObjectEmpty(body)) { - log( - 2, - `The request with ID ${requestId} from ${ - request.headers['x-forwarded-for'] || request.connection.remoteAddress - } was incorrect. Received payload is empty.` - ); - throw new NoCorrectBodyError(); - } +function requestBodyMiddleware(request, response, next) { + try { + // Get the request body + const body = request.body; + + // Create a unique ID for a request + const requestId = uuid().replace(/-/g, ''); + + // Throw an error if there is no correct body + if (!body || isObjectEmpty(body)) { + log( + 2, + `[validation] Request [${requestId}] - The request from ${ + request.headers['x-forwarded-for'] || request.connection.remoteAddress + } was incorrect. Received payload is empty.` + ); + + throw new HttpError( + "[validation] The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.", + 400 + ); + } - // Get the allowCodeExecution option for the server - const allowCodeExecution = getAllowCodeExecution(); - - // Find a correct chart options - const instr = isCorrectJSON( - // Use one of the below - body.infile || body.options || body.data, - // Stringify options - true, - // Allow or disallow functions - allowCodeExecution - ); - - // Throw 'NoCorrectChartDataError' if there is no correct chart data - if (instr === null && !body.svg) { - log( - 2, - `The request with ID ${requestId} from ${ - request.headers['x-forwarded-for'] || request.connection.remoteAddress - } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.` + // Get the allowCodeExecution option for the server + const allowCodeExecution = getAllowCodeExecution(); + + // Find a correct chart options + const instr = isAllowedConfig( + // Use one of the below + body.instr || body.options || body.infile || body.data, + // Stringify options + true, + // Allow or disallow functions + allowCodeExecution ); - throw new NoCorrectChartDataError(); - } - // Test xlink:href elements from payload's SVG - if (body.svg && isPrivateRangeUrlFound(body.svg)) { - throw PrivateRangeUrlError(); - } + // Throw an error if there is no correct chart data + if (instr === null && !body.svg) { + log( + 2, + `[validation] Request [${requestId}] - The request from ${ + request.headers['x-forwarded-for'] || request.connection.remoteAddress + } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.` + ); + + throw new HttpError( + "[validation] 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 + ); + } - // Get options from the body and store parsed structure in the request - request.validatedOptions = { - export: { - instr, - svg: body.svg, - outfile: - body.outfile || - `${request.params.filename || 'chart'}.${fixType(body.type)}`, - type: fixType(body.type), - constr: fixConstr(body.constr), - b64: body.b64, - noDownload: body.noDownload, - height: body.height, - width: body.width, - scale: body.scale, - globalOptions: isCorrectJSON( - body.globalOptions, - true, - allowCodeExecution - ), - themeOptions: isCorrectJSON(body.themeOptions, true, allowCodeExecution) - }, - customLogic: { - allowCodeExecution, - allowFileResources: false, - customCode: body.customCode, - callback: body.callback, - resources: isCorrectJSON(body.resources, true, allowCodeExecution) - }, - payload: { - requestId + // Throw an error if test of xlink:href elements from payload's SVG fails + if (body.svg && isPrivateRangeUrlFound(body.svg)) { + throw new HttpError( + "[validation] 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 + ); } - }; - return next(); + // Get options from the body and store parsed structure in the request + request.validatedOptions = { + // Set the created ID as a `_requestId` property in the validated options + _requestId: requestId, + export: { + instr, + svg: body.svg, + outfile: + body.outfile || + `${request.params.filename || 'chart'}.${fixType(body.type)}`, + type: fixType(body.type, body.outfile), + constr: fixConstr(body.constr), + b64: body.b64, + noDownload: body.noDownload, + height: body.height, + width: body.width, + scale: body.scale, + globalOptions: isAllowedConfig( + body.globalOptions, + true, + allowCodeExecution + ), + themeOptions: isAllowedConfig( + body.themeOptions, + true, + allowCodeExecution + ) + }, + customLogic: { + allowCodeExecution, + allowFileResources: false, + customCode: body.customCode, + callback: body.callback, + resources: isAllowedConfig(body.resources, true, allowCodeExecution) + } + }; + + // Call the next middleware + return next(); + } catch (error) { + return next(error); + } } /** - * Adds the two validation middlewares to the passed express app instance. + * Adds the validation middlewares to the passed express app instance. * * @param {Express} app - The Express app instance. */ diff --git a/lib/server/routes/export.js b/lib/server/routes/export.js index c9acd72a..e5e1d708 100644 --- a/lib/server/routes/export.js +++ b/lib/server/routes/export.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -17,16 +17,15 @@ See LICENSE file in root for details. * requests in an Express server. This module processes incoming requests * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates * with Highcharts' core functionalities and supports both immediate download - * responses and base64-encoded content returns. The code also features + * responses and Base64-encoded content returns. The code also features * benchmarking for performance monitoring. */ import { startExport } from '../../chart.js'; -import { getOptions } from '../../config.js'; import { log } from '../../logger.js'; -import { measureTime, mergeConfigOptions } from '../../utils.js'; +import { getBase64, measureTime } from '../../utils.js'; -import NoCorrectResultError from '../../errors/NoCorrectResultError.js'; +import HttpError from '../../errors/HttpError.js'; // Reversed MIME types const reversedMime = { @@ -47,7 +46,7 @@ const reversedMime = { * @param {Express.Response} response - The Express response object. * @param {Function} next - The next middleware function. * - * @returns {Promise} A promise that resolves once the export process + * @returns {Promise} A Promise that resolves once the export process * is complete. */ async function requestExport(request, response, next) { @@ -63,43 +62,27 @@ async function requestExport(request, response, next) { } }); - // Get the options previously validated in the middleware + // Get the options previously validated in the validation middleware const requestOptions = request.validatedOptions; // Get the request id - const requestId = requestOptions.payload.requestId; + const requestId = requestOptions._requestId; - // Log info about an incoming request with correct data + // Info about an incoming request with correct data log(4, `[export] Got an incoming HTTP request with ID ${requestId}.`); - // Get the current server's global options - const defaultOptions = getOptions(); - - // Merge the request options into default ones - const options = mergeConfigOptions(defaultOptions, requestOptions); - - // Save the instr in the options - options.export.options = requestOptions.export.instr; - // Start the export process - await startExport(options, (error, data) => { + await startExport(requestOptions, (error, data) => { // Remove the close event from the socket request.socket.removeAllListeners('close'); - // After the whole exporting process - if (defaultOptions.server.benchmarking) { - log( - 5, - `[benchmark] Request: ${requestId} - After the whole exporting process: ${requestCounter()}ms.` - ); - } - // If the connection was closed, do nothing if (connectionAborted) { - return log( + log( 3, - `[export] The client closed the connection before the chart finished processing.` + `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.` ); + return; } // If error, log it and send it to the error middleware @@ -111,41 +94,42 @@ async function requestExport(request, response, next) { if (!data || !data.result) { log( 2, - `The request with ID ${requestId} from ${ + `[export] Request [${requestId}] - Request from ${ request.headers['x-forwarded-for'] || request.connection.remoteAddress } was incorrect. Received result is ${data.result}.` ); - throw NoCorrectResultError(); + + throw new HttpError( + '[export] Unexpected return of the export result from the chart generation. Please check your request data.', + 400 + ); } // Return the result in an appropriate format if (data.result) { - // Get the type from options - const type = data.options.export.type; - - // If only base64 is required, return it - if (options.export.b64) { - // SVG Exception for the Highcharts 11.3.0 version - if (type === 'pdf' || type == 'svg') { - return response.send( - Buffer.from(data.result, 'utf8').toString('base64') - ); - } - - // If b64, return base64 content - return response.send(data.result); + log( + 3, + `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.` + ); + + // Get the `type`, `b64`, `noDownload`, and `outfile` from options + const { type, b64, noDownload, outfile } = data.options.export; + + // If only Base64 is required, return it + if (b64) { + return response.send(getBase64(data.result, type)); } // Set correct content type response.header('Content-Type', reversedMime[type] || 'image/png'); // Decide whether to download or not chart file - if (!options.export.noDownload) { - response.attachment(options.export.outfile); + if (!noDownload) { + response.attachment(outfile); } - // If SVG, return plain content + // If SVG, return plain content, otherwise a b64 string from a buffer return type === 'svg' ? response.send(data.result) : response.send(Buffer.from(data.result, 'base64')); @@ -157,20 +141,21 @@ async function requestExport(request, response, next) { } /** - * Adds the POST / and /:filename routes for the chart exporting. + * Adds the `export` routes. * - * @function exportRoute + * @function exportRoutes * * @param {Express} app - The Express app instance. */ -export default function exportRoute(app) { +export default function exportRoutes(app) { /** - * Adds the POST / - a route for handling POST requests at the root endpoint. + * Adds the POST '/' - A route for handling POST requests at the root + * endpoint. */ app.post('/', requestExport); /** - * Adds the POST /:filename - a route for handling POST requests with + * Adds the POST '/:filename' - A route for handling POST requests with * a specified filename parameter. */ app.post('/:filename', requestExport); From 97600079b2f62f6d597d75af7597d71ac40333bd Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:06:02 +0100 Subject: [PATCH 052/102] Optimized server related logic, middlewares and routes. --- lib/server/middlewares/error.js | 23 +++-- lib/server/middlewares/rateLimiting.js | 110 ++++++++++++++++++++++ lib/server/rateLimiting.js | 89 ------------------ lib/server/routes/health.js | 116 +++++++++++++---------- lib/server/routes/ui.js | 25 +++-- lib/server/routes/versionChange.js | 110 +++++++++++----------- lib/server/server.js | 125 +++++++++++++------------ 7 files changed, 326 insertions(+), 272 deletions(-) create mode 100644 lib/server/middlewares/rateLimiting.js delete mode 100644 lib/server/rateLimiting.js diff --git a/lib/server/middlewares/error.js b/lib/server/middlewares/error.js index b0f23665..f454c89a 100644 --- a/lib/server/middlewares/error.js +++ b/lib/server/middlewares/error.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -13,7 +13,7 @@ See LICENSE file in root for details. *******************************************************************************/ /** - * @overview Provides middlewares for logging errors with stack traces + * @overview Provides middleware functions for logging errors with stack traces * and handling error responses in an Express application. */ @@ -26,11 +26,14 @@ import { logWithStack } from '../../logger.js'; * @function logErrorMiddleware * * @param {Error} error - The error object. - * @param {Express.Request} _request - The Express request object. - * @param {Express.Response} _response - The Express response object. + * @param {Express.Request} request - The Express request object. + * @param {Express.Response} response - The Express response object. * @param {Function} next - The next middleware function. + * + * @returns {undefined} The call to the next middleware function with + * the passed error. */ -function logErrorMiddleware(error, _request, _response, next) { +function logErrorMiddleware(error, request, response, next) { // Display the error with stack in a correct format logWithStack(1, error); @@ -39,7 +42,7 @@ function logErrorMiddleware(error, _request, _response, next) { delete error.stack; } - // Call the `returnErrorMiddleware` + // Call the `returnErrorMiddleware` middleware return next(error); } @@ -49,11 +52,11 @@ function logErrorMiddleware(error, _request, _response, next) { * @function returnErrorMiddleware * * @param {Error} error - The error object. - * @param {Express.Request} _request - The Express request object. + * @param {Express.Request} request - The Express request object. * @param {Express.Response} response - The Express response object. - * @param {Function} _next - The next middleware function. + * @param {Function} next - The next middleware function. */ -function returnErrorMiddleware(error, _request, response, _next) { +function returnErrorMiddleware(error, request, response, next) { // Gather all requied information for the response const { message, stack } = error; @@ -65,7 +68,7 @@ function returnErrorMiddleware(error, _request, response, _next) { } /** - * Adds the two error middlewares to the passed express app instance. + * Adds the error middlewares to the passed express app instance. * * @param {Express} app - The Express app instance. */ diff --git a/lib/server/middlewares/rateLimiting.js b/lib/server/middlewares/rateLimiting.js new file mode 100644 index 00000000..9ebfb517 --- /dev/null +++ b/lib/server/middlewares/rateLimiting.js @@ -0,0 +1,110 @@ +/******************************************************************************* + +Highcharts Export Server + +Copyright (c) 2016-2025, Highsoft + +Licenced under the MIT licence. + +Additionally a valid Highcharts license is required for use. + +See LICENSE file in root for details. + +*******************************************************************************/ + +/** + * @overview Provides middleware functions for configuring and enabling rate + * limiting in an Express application. + */ + +import rateLimit from 'express-rate-limit'; + +import { getOptions } from '../../config.js'; +import { log } from '../../logger.js'; + +import ExportError from '../../errors/ExportError.js'; + +/** + * Middleware for enabling rate limiting on the specified Express app. + * + * @param {Express} app - The Express app instance. + * + * @param {Object} [rateLimitingOptions=getOptions().server.rateLimiting] - + * Object containing `rateLimiting` options. The default value is the global + * rate limiting options of the export server instance. + * + * @throws {ExportError} Throws an `ExportError` if could not configure and set + * the rate limiting options. + */ +export default function rateLimitingMiddleware( + app, + rateLimitingOptions = getOptions().server.rateLimiting +) { + try { + // Check if the rate limiting is enabled + if (rateLimitingOptions.enable) { + const msg = + 'Too many requests, you have been rate limited. Please try again later.'; + + // Options for the rate limiter + const rateOptions = { + max: rateLimitingOptions.maxRequests || 30, + window: rateLimitingOptions.window || 1, + delay: rateLimitingOptions.delay || 0, + trustProxy: rateLimitingOptions.trustProxy || false, + skipKey: rateLimitingOptions.skipKey || false, + skipToken: rateLimitingOptions.skipToken || false + }; + + // Set if behind a proxy + if (rateOptions.trustProxy) { + app.enable('trust proxy'); + } + + // Create a limiter + const limiter = rateLimit({ + windowMs: rateOptions.window * 60 * 1000, + // Limit each IP to 100 requests per windowMs + max: rateOptions.max, + // Disable delaying, full speed until the max limit is reached + delayMs: rateOptions.delay, + handler: (request, response) => { + response.format({ + json: () => { + response.status(429).send({ message: msg }); + }, + default: () => { + response.status(429).send(msg); + } + }); + }, + skip: (request) => { + // Allow bypassing the limiter if a valid key/token has been sent + if ( + rateOptions.skipKey !== false && + rateOptions.skipToken !== false && + request.query.key === rateOptions.skipKey && + request.query.access_token === rateOptions.skipToken + ) { + log(4, '[rate limiting] Skipping rate limiter.'); + return true; + } + return false; + } + }); + + // Use a limiter as a middleware + app.use(limiter); + + log( + 3, + `[rate limiting] Enabled rate limiting with ${rateOptions.max} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.` + ); + } + } catch (error) { + throw new ExportError( + '[rate limiting] Could not configure and set the rate limiting options.', + 500 + ).setError(error); + } +} diff --git a/lib/server/rateLimiting.js b/lib/server/rateLimiting.js deleted file mode 100644 index 4d8a9833..00000000 --- a/lib/server/rateLimiting.js +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - -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. - -*******************************************************************************/ - -import rateLimit from 'express-rate-limit'; - -import { log } from '../logger.js'; - -/** - * Middleware for enabling rate limiting on the specified Express app. - * - * @param {Express} app - The Express app instance. - * - * @param {Object} rateLimitingOptions - Object containing rate limiting - * options. - */ -export function rateLimiting(app, rateLimitingOptions) { - const msg = - 'Too many requests, you have been rate limited. Please try again later.'; - - // Options for the rate limiter - const rateOptions = { - max: rateLimitingOptions.maxRequests || 30, - window: rateLimitingOptions.window || 1, - delay: rateLimitingOptions.delay || 0, - trustProxy: rateLimitingOptions.trustProxy || false, - skipKey: rateLimitingOptions.skipKey || false, - skipToken: rateLimitingOptions.skipToken || false - }; - - // Set if behind a proxy - if (rateOptions.trustProxy) { - app.enable('trust proxy'); - } - - // Create a limiter - const limiter = rateLimit({ - windowMs: rateOptions.window * 60 * 1000, - // Limit each IP to 100 requests per windowMs - max: rateOptions.max, - // Disable delaying, full speed until the max limit is reached - delayMs: rateOptions.delay, - handler: (_request, response) => { - response.format({ - json: () => { - response.status(429).send({ message: msg }); - }, - default: () => { - response.status(429).send(msg); - } - }); - }, - skip: (request) => { - // Allow bypassing the limiter if a valid key/token has been sent - if ( - rateOptions.skipKey !== false && - rateOptions.skipToken !== false && - request.query.key === rateOptions.skipKey && - request.query.access_token === rateOptions.skipToken - ) { - log(4, '[rate limiting] Skipping rate limiter.'); - return true; - } - return false; - } - }); - - // Use a limiter as a middleware - app.use(limiter); - - log( - 3, - `[rate limiting] Enabled rate limiting with ${rateOptions.max} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.` - ); -} - -export default { - rateLimiting -}; diff --git a/lib/server/routes/health.js b/lib/server/routes/health.js index 8918a7e0..f1ba3d51 100644 --- a/lib/server/routes/health.js +++ b/lib/server/routes/health.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -26,15 +26,23 @@ import { getPoolStats, getPoolInfoJSON } from '../../pool.js'; import { addTimer } from '../../timer.js'; import { __dirname, getNewDateTime } from '../../utils.js'; -const packageFile = JSON.parse(readFileSync(join(__dirname, 'package.json'))); +// Set the start date of the server const serverStartTime = new Date(); +// Get the `package.json` content +const packageFile = JSON.parse(readFileSync(join(__dirname, 'package.json'))); + +// An array for success rate ratios const successRates = []; -const recordInterval = 60 * 1000; // record every minute -const windowSize = 30; // 30 minutes + +// Record every minute +const recordInterval = 60 * 1000; + +// 30 minutes +const windowSize = 30; /** - * Calculates moving average indicator based on the data from the successRates + * Calculates moving average indicator based on the data from the `successRates` * array. * * @function _calculateMovingAverage @@ -47,7 +55,7 @@ function _calculateMovingAverage() { /** * Starts the interval responsible for calculating current success rate ratio - * and gathers + * and collects records to the `successRates` array. * * @function _startSuccessRate * @@ -57,9 +65,9 @@ function _startSuccessRate() { return setInterval(() => { const stats = getPoolStats(); const successRatio = - stats.exportAttempts === 0 + stats.exportsAttempted === 0 ? 1 - : (stats.performedExports / stats.exportAttempts) * 100; + : (stats.exportsPerformed / stats.exportsAttempted) * 100; successRates.push(successRatio); if (successRates.length > windowSize) { @@ -69,53 +77,65 @@ function _startSuccessRate() { } /** - * Adds the GET /health route which output basic stats for the server. + * Adds the `health` routes. * - * @function healthRoute + * @function healthRoutes * * @param {Express} app - The Express app instance. */ -export default function healthRoute(app) { - if (!app) { - return false; - } - +export default function healthRoutes(app) { // Start processing success rate ratio interval and save its id to the array // for the graceful clearing on shutdown with injected `addTimer` funtion addTimer(_startSuccessRate()); - app.get('/health', (_request, response) => { - const stats = getPoolStats(); - const period = successRates.length; - const movingAverage = _calculateMovingAverage(); - - log(4, '[health] Returning server health.'); - response.send({ - status: 'OK', - bootTime: serverStartTime, - uptime: - Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60) + - ' minutes', - version: packageFile.version, - highchartsVersion: getHighchartsVersion(), - averageProcessingTime: stats.spentAverage, - performedExports: stats.performedExports, - failedExports: stats.droppedExports, - exportAttempts: stats.exportAttempts, - sucessRatio: (stats.performedExports / stats.exportAttempts) * 100, - pool: getPoolInfoJSON(), - - // Moving average - period, - movingAverage, - message: - isNaN(movingAverage) || !successRates.length - ? 'Too early to report. No exports made yet. Please check back soon.' - : `Last ${period} minutes had a success rate of ${movingAverage.toFixed(2)}%.`, - - // SVG/JSON attempts - svgExportAttempts: stats.exportFromSvgAttempts, - jsonExportAttempts: stats.performedExports - stats.exportFromSvgAttempts - }); + /** + * Adds the GET '/health' - A route for getting the basic stats of the server. + */ + app.get('/health', (request, response, next) => { + try { + log(4, '[health] Returning server health.'); + + const stats = getPoolStats(); + const period = successRates.length; + const movingAverage = _calculateMovingAverage(); + + // Send the server's statistics + response.send({ + // Status and times + status: 'OK', + bootTime: serverStartTime, + uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`, + + // Versions + serverVersion: packageFile.version, + highchartsVersion: getHighchartsVersion(), + + // Exports + averageExportTime: stats.timeSpentAverage, + attemptedExports: stats.exportsAttempted, + performedExports: stats.exportsPerformed, + failedExports: stats.exportsDropped, + sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100, + + // Pool + pool: getPoolInfoJSON(), + + // Moving average + period, + movingAverage, + message: + isNaN(movingAverage) || !successRates.length + ? 'Too early to report. No exports made yet. Please check back soon.' + : `Last ${period} minutes had a success rate of ${movingAverage.toFixed(2)}%.`, + + // SVG and JSON exports + svgExports: stats.exportsFromSvg, + jsonExports: stats.exportsFromOptions, + svgExportsAttempts: stats.exportsFromSvgAttempts, + jsonExportsAttempts: stats.exportsFromOptionsAttempts + }); + } catch (error) { + return next(error); + } }); } diff --git a/lib/server/routes/ui.js b/lib/server/routes/ui.js index efc120a0..6ce88d98 100644 --- a/lib/server/routes/ui.js +++ b/lib/server/routes/ui.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -23,18 +23,23 @@ import { getOptions } from '../../config.js'; import { __dirname } from '../../utils.js'; /** - * Adds the GET / route for a UI when enabled on the export server. + * Adds the `ui` routes. * - * @function uiRoute + * @function uiRoutes * * @param {Express} app - The Express app instance. */ -export default function uiRoute(app) { - return !app - ? false - : app.get(getOptions().ui.route || '/', (_request, response) => { - response.sendFile(join(__dirname, 'public', 'index.html'), { - acceptRanges: false - }); +export default function uiRoutes(app) { + /** + * Adds the GET '/' - A route for a UI when enabled on the export server. + */ + app.get(getOptions().ui.route || '/', (request, response, next) => { + try { + response.sendFile(join(__dirname, 'public', 'index.html'), { + acceptRanges: false }); + } catch (error) { + return next(error); + } + }); } diff --git a/lib/server/routes/versionChange.js b/lib/server/routes/versionChange.js index a8d9a411..edbe89d5 100644 --- a/lib/server/routes/versionChange.js +++ b/lib/server/routes/versionChange.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -23,68 +23,66 @@ import { envs } from '../../envs.js'; import HttpError from '../../errors/HttpError.js'; /** - * Adds the POST /version_change/:newVersion route that can be utilized - * to modify the Highcharts version on the server. + * Adds the `version_change` routes. * - * @function versionChangeRoute + * @function versionChangeRoutes * * @param {Express} app - The Express app instance. */ -export default function versionChangeRoute(app) { - return !app - ? false - : app.post( - '/version_change/:newVersion', - async (request, response, next) => { - try { - // Get the token directly from envs - const adminToken = envs.HIGHCHARTS_ADMIN_TOKEN; +export default function versionChangeRoutes(app) { + /** + * Adds the POST '/version_change/:newVersion' - A route for changing + * the Highcharts version on the server. + */ + app.post('/version_change/:newVersion', async (request, response, next) => { + try { + // Get the token directly from envs + const adminToken = envs.HIGHCHARTS_ADMIN_TOKEN; - // Check the existence of the token - if (!adminToken || !adminToken.length) { - throw new HttpError( - 'The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.', - 401 - ); - } + // Check the existence of the token + if (!adminToken || !adminToken.length) { + throw new HttpError( + '[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.', + 401 + ); + } - // Get the token from the hc-auth header - const token = request.get('hc-auth'); + // Get the token from the hc-auth header + const token = request.get('hc-auth'); - // Check if the hc-auth header contain a correct token - if (!token || token !== adminToken) { - throw new HttpError( - 'Invalid or missing token: Set the token in the hc-auth header.', - 401 - ); - } + // Check if the hc-auth header contain a correct token + if (!token || token !== adminToken) { + throw new HttpError( + '[version] Invalid or missing token: Set the token in the hc-auth header.', + 401 + ); + } - // Compare versions - const newVersion = request.params.newVersion; - if (newVersion) { - try { - // Update version - await updateHighchartsVersion(newVersion); - } catch (error) { - throw new HttpError( - `Version change: ${error.message}`, - 400 - ).setError(error); - } - - // Success - response.status(200).send({ - statusCode: 200, - highchartsVersion: getHighchartsVersion(), - message: `Successfully updated Highcharts to version: ${newVersion}.` - }); - } else { - // No version specified - throw new HttpError('No new version supplied.', 400); - } - } catch (error) { - return next(error); - } + // Compare versions + const newVersion = request.params.newVersion; + if (newVersion) { + try { + // Update version + await updateHighchartsVersion(newVersion); + } catch (error) { + throw new HttpError( + `[version] Version change: ${error.message}`, + 400 + ).setError(error); } - ); + + // Success + response.status(200).send({ + statusCode: 200, + highchartsVersion: getHighchartsVersion(), + message: `Successfully updated Highcharts to version: ${newVersion}.` + }); + } else { + // No version specified + throw new HttpError('[version] No new version supplied.', 400); + } + } catch (error) { + return next(error); + } + }); } diff --git a/lib/server/server.js b/lib/server/server.js index 770ab146..19e8a04e 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -16,12 +16,12 @@ See LICENSE file in root for details. * @overview A module that sets up and manages HTTP and HTTPS servers * for the Highcharts Export Server. It handles server initialization, * configuration, error handling, middleware setup, route definition, and rate - * limiting.The module exports functions to start, stop, and manage server + * limiting. The module exports functions to start, stop, and manage server * instances, as well as utility functions for defining routes and attaching - * middleware. + * middlewares. */ -import { promises as fsPromises } from 'fs'; +import { readFile } from 'fs/promises'; import { join } from 'path'; import cors from 'cors'; @@ -32,15 +32,16 @@ import multer from 'multer'; import { getOptions } from '../config.js'; import { log, logWithStack } from '../logger.js'; -import { rateLimiting } from './rateLimiting.js'; -import { __dirname } from '../utils.js'; +import { __dirname, getAbsolutePath } from '../utils.js'; import errorMiddleware from './middlewares/error.js'; +import rateLimitingMiddleware from './middlewares/rateLimiting.js'; import validationMiddleware from './middlewares/validation.js'; -import exportRoute from './routes/export.js'; -import healthRoute from './routes/health.js'; -import uiRoute from './routes/ui.js'; -import versionChangeRoute from './routes/versionChange.js'; + +import exportRoutes from './routes/export.js'; +import healthRoutes from './routes/health.js'; +import uiRoutes from './routes/ui.js'; +import versionChangeRoutes from './routes/versionChange.js'; import ExportError from '../errors/ExportError.js'; @@ -59,8 +60,12 @@ const app = express(); * @function startServer * * @param {Object} [serverOptions=getOptions().server] - Object containing - * server options. The default value is the global server options of the export - * server instance. + * `server` options. The default value is the global server options + * of the export server instance. + * + * @returns {Promise} A Promise that resolves to ending the function + * execution when the server should not be enabled or when no valid Express app + * is found. * * @throws {ExportError} Throws an `ExportError` if the server cannot * be configured and started. @@ -68,13 +73,16 @@ const app = express(); export async function startServer(serverOptions = getOptions().server) { try { // Stop if not enabled - if (!serverOptions.enable) { - return false; + if (!serverOptions.enable || !app) { + throw new ExportError( + '[server] Server cannot be started (not enabled or no correct Express app found).', + 500 + ); } // Too big limits lead to timeouts in the export process when // the rasterization timeout is set too low - const uploadLimitBytes = 3 * 1024 * 1024; + const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024; // Memory storage for multer package const storage = multer.memoryStorage(); @@ -99,7 +107,7 @@ export async function startServer(serverOptions = getOptions().server) { // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this // is a deprecated options, let's try to set it to false) - app.use((_request, response, next) => { + app.use((request, response, next) => { response.set('Accept-Ranges', 'none'); next(); }); @@ -152,14 +160,14 @@ export async function startServer(serverOptions = getOptions().server) { try { // Get the SSL key - key = await fsPromises.readFile( - join(serverOptions.ssl.certPath, 'server.key'), + key = await readFile( + join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'), 'utf8' ); // Get the SSL certificate - cert = await fsPromises.readFile( - join(serverOptions.ssl.certPath, 'server.crt'), + cert = await readFile( + join(getAbsolutePath(serverOptions.ssl.certPath), 'server.crt'), 'utf8' ); } catch (error) { @@ -189,24 +197,19 @@ export async function startServer(serverOptions = getOptions().server) { } } - // Enable the rate limiter if config says so - if ( - serverOptions.rateLimiting.enable && - ![0, NaN].includes(serverOptions.rateLimiting.maxRequests) - ) { - rateLimiting(app, serverOptions.rateLimiting); - } + // Set up the rate limiter + rateLimitingMiddleware(app, serverOptions.rateLimiting); - // Set up validation handler + // Set up the validation handler validationMiddleware(app); // Set up routes - healthRoute(app); - exportRoute(app); - uiRoute(app); - versionChangeRoute(app); + healthRoutes(app); + exportRoutes(app); + uiRoutes(app); + versionChangeRoutes(app); - // Set up centralized error handler + // Set up the centralized error handler errorMiddleware(app); } catch (error) { throw new ExportError( @@ -222,12 +225,17 @@ export async function startServer(serverOptions = getOptions().server) { * @function closeServers */ export function closeServers() { - log(4, `[server] Closing all servers.`); - for (const [port, server] of activeServers) { - server.close(() => { - activeServers.delete(port); - log(4, `[server] Closed server on port: ${port}.`); - }); + // Check if there are servers working + if (activeServers.size > 0) { + log(4, `[server] Closing all servers.`); + + // Close each one of servers + for (const [port, server] of activeServers) { + server.close(() => { + activeServers.delete(port); + log(4, `[server] Closed server on port: ${port}.`); + }); + } } } @@ -242,19 +250,6 @@ export function getServers() { return activeServers; } -/** - * Enable rate limiting for the server. - * - * @function enableRateLimiting - * - * @param {Object} limitConfig - Configuration object for rate limiting. - * - * @returns {Object} Middleware for enabling rate limiting. - */ -export function enableRateLimiting(limitConfig) { - return rateLimiting(app, limitConfig); -} - /** * Get the Express instance. * @@ -277,13 +272,25 @@ export function getApp() { return app; } +/** + * Enable rate limiting for the server. + * + * @function enableRateLimiting + * + * @param {Object} rateLimitingOptions - Object containing `rateLimiting` + * options. + */ +export function enableRateLimiting(rateLimitingOptions) { + rateLimitingMiddleware(app, rateLimitingOptions); +} + /** * Apply middleware(s) to a specific path. * * @function use * * @param {string} path - The path to which the middleware(s) should be applied. - * @param {...Function} middlewares - The middleware functions to be applied. + * @param {...Function} middlewares - The middleware function(s) to be applied. */ export function use(path, ...middlewares) { app.use(path, ...middlewares); @@ -294,8 +301,8 @@ export function use(path, ...middlewares) { * * @function get * - * @param {string} path - The route path. - * @param {...Function} middlewares - The middleware functions to be applied. + * @param {string} path - The path to which the middleware(s) should be applied. + * @param {...Function} middlewares - The middleware function(s) to be applied. */ export function get(path, ...middlewares) { app.get(path, ...middlewares); @@ -306,8 +313,8 @@ export function get(path, ...middlewares) { * * @function post * - * @param {string} path - The route path. - * @param {...Function} middlewares - The middleware functions to be applied. + * @param {string} path - The path to which the middleware(s) should be applied. + * @param {...Function} middlewares - The middleware function(s) to be applied. */ export function post(path, ...middlewares) { app.post(path, ...middlewares); @@ -318,7 +325,7 @@ export function post(path, ...middlewares) { * * @function _attachServerErrorHandlers * - * @param {http.Server} server - The HTTP/HTTPS server instance. + * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance. */ function _attachServerErrorHandlers(server) { server.on('clientError', (error, socket) => { @@ -345,9 +352,9 @@ export default { startServer, closeServers, getServers, - enableRateLimiting, getExpress, getApp, + enableRateLimiting, use, get, post From 11523749e22381d790b75edf92e60e309d5547be Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:07:01 +0100 Subject: [PATCH 053/102] Other, smaller corrections. --- .env.sample | 5 +++-- .github/ISSUE_TEMPLATE.md | 2 +- LICENSE | 2 +- dist/index.cjs | 4 ++-- dist/index.esm.js | 2 +- dist/index.esm.js.map | 2 +- msg/licenseagree.msg | 4 ++-- public/js/main.js | 5 +++-- 8 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.env.sample b/.env.sample index 29e87bcd..02b80816 100644 --- a/.env.sample +++ b/.env.sample @@ -3,7 +3,7 @@ PUPPETEER_ARGS = # HIGHCHARTS CONFIG HIGHCHARTS_VERSION = latest -HIGHCHARTS_CDN_URL = https://code.highcharts.com/ +HIGHCHARTS_CDN_URL = https://code.highcharts.com HIGHCHARTS_FORCE_FETCH = false HIGHCHARTS_CACHE_PATH = .cache HIGHCHARTS_ADMIN_TOKEN = @@ -17,6 +17,7 @@ EXPORT_INFILE = EXPORT_INSTR = EXPORT_OPTIONS = EXPORT_SVG = +EXPORT_BATCH = EXPORT_OUTFILE = EXPORT_TYPE = png EXPORT_CONSTR = chart @@ -30,7 +31,6 @@ EXPORT_DEFAULT_WIDTH = 600 EXPORT_DEFAULT_SCALE = 1 EXPORT_GLOBAL_OPTIONS = EXPORT_THEME_OPTIONS = -EXPORT_BATCH = EXPORT_RASTERIZATION_TIMEOUT = 1500 # CUSTOM LOGIC CONFIG @@ -46,6 +46,7 @@ CUSTOM_LOGIC_CREATE_CONFIG = SERVER_ENABLE = false SERVER_HOST = 0.0.0.0 SERVER_PORT = 7801 +SERVER_UPLOAD_LIMIT = 3 SERVER_BENCHMARKING = false # SERVER PROXY CONFIG diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6ce17cf8..5f7e4135 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -16,4 +16,4 @@ - + diff --git a/LICENSE b/LICENSE index f236833f..0ba67eaa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/dist/index.cjs b/dist/index.cjs index abb4c7cb..40d941ee 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("url"),a=require("http"),l=require("https"),c=require("tarn"),p=require("uuid"),h=require("puppeteer"),u=require("jsdom"),d=require("dompurify"),g=require("cors"),m=require("express"),f=require("multer"),v=require("express-rate-limit"),y="undefined"!=typeof document?document.currentScript:null;const b={core:["highcharts","highcharts-more","highcharts-3d"],modules:["stock","map","gantt","exporting","parallel-coordinates","accessibility","boost-canvas","boost","data","data-tools","draggable-points","static-scale","broken-axis","heatmap","tilemap","tiledwebmap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","geoheatmap","pyramid3d","networkgraph","overlapping-datalabels","pareto","pattern-fill","pictorial","price-indicator","sankey","arc-diagram","dependency-wheel","series-label","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"]},w={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{value:b.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:b.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:b.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{value:b.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:{enable:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},delay:{value:0,type:"number",envLink:"SERVER_RATE_LIMITING_DELAY",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The `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."}}},E={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:w.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:w.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:w.highcharts.cdnURL.value},{type:"multiselect",name:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:w.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:w.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:w.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:w.highcharts.customScripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:w.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:w.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${w.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${w.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:w.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:w.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:w.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:w.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:w.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:w.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:w.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:w.server.host.value},{type:"number",name:"port",message:"Server port",initial:w.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:w.server.benchmarking.value},{type:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:w.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:w.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:w.server.proxy.timeout.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:w.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:w.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:w.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:w.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:w.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:w.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:w.server.rateLimiting.skipToken.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:w.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:w.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:w.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:w.server.ssl.certPath.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:w.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:w.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:w.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:w.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:w.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:w.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:w.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:w.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:w.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:w.pool.benchmarking.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:w.logging.level.value,round:0,min:0,max:5},{type:"text",name:"file",message:"A log file name. Set with --toFile and --logDest to enable file logging",initial:w.logging.file.value},{type:"text",name:"dest",message:"The path to a log file when the file logging is enabled",initial:w.logging.dest.value},{type:"toggle",name:"toConsole",message:"Enable logging to the console",initial:w.logging.toConsole.value},{type:"toggle",name:"toFile",message:"Enables logging to a file",initial:w.logging.toFile.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:w.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:w.ui.route.value}],other:[{type:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:w.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:w.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:w.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:w.other.hardResetPage.value},{type:"toggle",name:"browserShellMode",message:"Decides if the browser runs in the shell mode",initial:w.other.browserShellMode.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:w.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:w.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:w.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:w.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:w.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:w.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:w.debug.debuggingPort.value}]},T=["options","globalOptions","themeOptions","resources","payload"],S={},x=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?x(o,`${t}.${r}`):(S[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(S[o.legacyName]=`${t}.${r}`.substring(1)))}}))};x(w),i.config();const R=e=>s.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),L=()=>s.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),O=e=>s.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),_=()=>s.z.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),(e=>({message:`The string contains forbidden values, received '${e}'`}))).transform((e=>""!==e?e:void 0)),k=()=>s.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),(e=>({message:`The value must be numeric and positive, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),I=()=>s.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),(e=>({message:`The value must be numeric and non-negative, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),C=s.z.object({HIGHCHARTS_VERSION:s.z.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),(e=>({message:`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:s.z.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),(e=>({message:`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CORE_SCRIPTS:R(b.core),HIGHCHARTS_MODULE_SCRIPTS:R(b.modules),HIGHCHARTS_INDICATOR_SCRIPTS:R(b.indicators),HIGHCHARTS_FORCE_FETCH:L(),HIGHCHARTS_CACHE_PATH:_(),HIGHCHARTS_ADMIN_TOKEN:_(),EXPORT_TYPE:O(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:O(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:k(),EXPORT_DEFAULT_WIDTH:k(),EXPORT_DEFAULT_SCALE:k(),EXPORT_RASTERIZATION_TIMEOUT:I(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:L(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:L(),SERVER_ENABLE:L(),SERVER_HOST:_(),SERVER_PORT:k(),SERVER_BENCHMARKING:L(),SERVER_PROXY_HOST:_(),SERVER_PROXY_PORT:k(),SERVER_PROXY_TIMEOUT:I(),SERVER_RATE_LIMITING_ENABLE:L(),SERVER_RATE_LIMITING_MAX_REQUESTS:I(),SERVER_RATE_LIMITING_WINDOW:I(),SERVER_RATE_LIMITING_DELAY:I(),SERVER_RATE_LIMITING_TRUST_PROXY:L(),SERVER_RATE_LIMITING_SKIP_KEY:_(),SERVER_RATE_LIMITING_SKIP_TOKEN:_(),SERVER_SSL_ENABLE:L(),SERVER_SSL_FORCE:L(),SERVER_SSL_PORT:k(),SERVER_SSL_CERT_PATH:_(),POOL_MIN_WORKERS:I(),POOL_MAX_WORKERS:I(),POOL_WORK_LIMIT:k(),POOL_ACQUIRE_TIMEOUT:I(),POOL_CREATE_TIMEOUT:I(),POOL_DESTROY_TIMEOUT:I(),POOL_IDLE_TIMEOUT:I(),POOL_CREATE_RETRY_INTERVAL:I(),POOL_REAPER_INTERVAL:I(),POOL_BENCHMARKING:L(),LOGGING_LEVEL:s.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),(e=>({message:`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:_(),LOGGING_DEST:_(),LOGGING_TO_CONSOLE:L(),LOGGING_TO_FILE:L(),UI_ENABLE:L(),UI_ROUTE:_(),OTHER_NODE_ENV:O(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:L(),OTHER_NO_LOGO:L(),OTHER_HARD_RESET_PAGE:L(),OTHER_BROWSER_SHELL_MODE:L(),DEBUG_ENABLE:L(),DEBUG_HEADLESS:L(),DEBUG_DEVTOOLS:L(),DEBUG_LISTEN_TO_CONSOLE:L(),DEBUG_DUMPIO:L(),DEBUG_SLOW_MO:I(),DEBUG_DEBUGGING_PORT:k()}).partial().parse(process.env),N=["red","yellow","blue","gray","green"];let A={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:N[0]},{title:"warning",color:N[1]},{title:"notice",color:N[2]},{title:"verbose",color:N[3]},{title:"benchmark",color:N[4]}],listeners:[]};const P=(t,r)=>{A.pathCreated||(!e.existsSync(A.dest)&&e.mkdirSync(A.dest),A.pathCreated=!0),e.appendFile(`${A.dest}${A.file}`,[r].concat(t).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),A.toFile=!1)}))},H=(...e)=>{const[t,...r]=e,{levelsDesc:o,level:i}=A;if(5!==t&&(0===t||t>i||i>o.length))return;const s=`${(new Date).toString().split("(")[0].trim()} [${o[t-1].title}] -`;A.listeners.forEach((e=>{e(s,r.join(" "))})),A.toConsole&&console.log.apply(void 0,[s.toString()[A.levelsDesc[t-1].color]].concat(r)),A.toFile&&P(r,s)},$=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:s}=A;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];A.toConsole&&console.log.apply(void 0,[n.toString()[A.levelsDesc[e-1].color]].concat([o[N[e-1]],"\n",a])),A.listeners.forEach((e=>{e(n,l.join(" "))})),A.toFile&&P(l,n)},G=e=>{e>=0&&e<=A.levelsDesc.length&&(A.level=e)},D=(e,t)=>{if(A={...A,dest:e||A.dest,file:t||A.file,toFile:!0},0===A.dest.length)return H(1,"[logger] File logging initialization: no path supplied.");A.dest.endsWith("/")||(A.dest+="/")},F=n.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:y&&y.src||new URL("index.cjs",document.baseURI).href)),U=(e,t)=>{const r=["png","jpeg","pdf","svg"];if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||r.find((t=>t===e))||"png"},j=(t=!1,r)=>{const o=["js","css","files"];let i=t,s=!1;if(r&&t.endsWith(".json"))try{i=M(e.readFileSync(t,"utf8"))}catch(e){return $(2,e,"[cli] No resources found.")}else i=M(t),i&&!r&&delete i.files;for(const e in i)o.includes(e)?s||(s=!0):delete i[e];return s?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):H(3,"[cli] No resources found.")};function M(e,t){try{const r=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof r&&t?JSON.stringify(r):r}catch{return!1}}const q=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=q(e[r]));return t},W=(e,t)=>JSON.stringify(e,((e,r)=>("string"==typeof r&&((r=r.trim()).startsWith("function(")||r.startsWith("function ("))&&r.endsWith("}")&&(r=t?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof r?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:r))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function V(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[r,o]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(o,"value")){let e=` --${o.cliName||r} ${("<"+o.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,o.description,`[Default: ${o.value.toString().bold}]`.blue)}else e(o)};Object.keys(w).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(w[t]))})),console.log("\n")}const B=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,X=(t,r)=>{if(t&&"string"==typeof t)return(t=t.trim()).endsWith(".js")?!!r&&X(e.readFileSync(t,"utf8")):t.startsWith("function()")||t.startsWith("function ()")||t.startsWith("()=>")||t.startsWith("() =>")?`(${t})()`:t.replace(/;$/,"")},z=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let K={};const J=()=>K,Y=(e,t,r=[])=>{const o=q(e);for(const[e,s]of Object.entries(t))o[e]="object"!=typeof(i=s)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==s?s:o[e]:Y(o[e],s,r);var i;return o};function Q(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],s=t&&t[o];void 0===i.value?Q(i,s,`${r}.${o}`):(void 0!==s&&(i.value=s),i.envLink in C&&void 0!==C[i.envLink]&&(i.value=C[i.envLink]))}))}function Z(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:Z(o);return t}function ee(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=ee(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}async function te(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?l:a)(e);i.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||o("Nothing was fetched from the URL."),e.text=t,r(e)}))})).on("error",(e=>{o(e)}))}))}class re extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}}const oe={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},ie=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),se=async(e,t,r,o=!1)=>{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),H(4,`[cache] Fetching script - ${e}.js`);const i=await te(`${e}.js`,t);if(200===i.statusCode&&"string"==typeof i.text){if(r){r[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return i.text}if(o)throw new re(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${i.statusCode}).`).setError(i);return H(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},ne=async(t,o,i)=>{const s=t.version,n="latest"!==s&&s?`${s}/`:"",a=t.cdnURL||oe.cdnURL;H(3,`[cache] Updating cache version to Highcharts: ${n||"latest"}.`);const l={};try{return oe.sources=await(async(e,t,o,i,s)=>{let n;const a=i.host,l=i.port;if(a&&l)try{n=new r.HttpsProxyAgent({host:a,port:l})}catch(e){throw new re("[cache] Could not create a Proxy Agent.").setError(e)}const c=n?{agent:n,timeout:C.SERVER_PROXY_TIMEOUT}:{},p=[...e.map((e=>se(`${e}`,c,s,!0))),...t.map((e=>se(`${e}`,c,s))),...o.map((e=>se(`${e}`,c)))];return(await Promise.all(p)).join(";\n")})([...t.coreScripts.map((e=>`${a}${n}${e}`))],[...t.moduleScripts.map((e=>"map"===e?`${a}maps/${n}modules/${e}`:`${a}${n}modules/${e}`)),...t.indicatorScripts.map((e=>`${a}stock/${n}indicators/${e}`))],t.customScripts,o,l),oe.hcVersion=ie(oe),e.writeFileSync(i,oe.sources),l}catch(e){throw new re("[cache] Unable to update the local Highcharts cache.").setError(e)}},ae=async r=>{const{highcharts:o,server:i}=r,s=t.join(F,o.cachePath);let n;const a=t.join(s,"manifest.json"),l=t.join(s,"sources.js");if(!e.existsSync(s)&&e.mkdirSync(s),!e.existsSync(a)||o.forceFetch)H(3,"[cache] Fetching and caching Highcharts dependencies."),n=await ne(o,i.proxy,l);else{let t=!1;const r=JSON.parse(e.readFileSync(a));if(r.modules&&Array.isArray(r.modules)){const e={};r.modules.forEach((t=>e[t]=1)),r.modules=e}const{coreScripts:s,moduleScripts:c,indicatorScripts:p}=o,h=s.length+c.length+p.length;r.version!==o.version?(H(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),t=!0):Object.keys(r.modules||{}).length!==h?(H(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),t=!0):t=(c||[]).some((e=>{if(!r.modules[e])return H(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),t?n=await ne(o,i.proxy,l):(H(3,"[cache] Dependency cache is up to date, proceeding."),oe.sources=e.readFileSync(l,"utf8"),n=r.modules,oe.hcVersion=ie(oe))}await(async(r,o)=>{const i={version:r.version,modules:o||{}};oe.activeManifest=i,H(3,"[cache] Writing a new manifest.");try{e.writeFileSync(t.join(F,r.cachePath,"manifest.json"),JSON.stringify(i),"utf8")}catch(e){throw new re("[cache] Error writing the cache manifest.").setError(e)}})(o,n)},le=()=>t.join(F,J().highcharts.cachePath),ce=()=>oe.hcVersion;function pe(){Highcharts.animObject=function(){return{duration:0}}}async function he(e,t,r){window._displayErrors=r;const{getOptions:o,merge:i,setOptions:s,wrap:n}=Highcharts;Highcharts.setOptionsObj=i(!1,{},o()),t.customLogic.customCode&&new Function(t.customLogic.customCode)();const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=i(t,{exporting:{enabled:!1},plotOptions:{series:{label:{enabled:!1}}},tooltip:{}})).series||[]).forEach((function(e){e.animation=!1})),window.onHighchartsRender||(window.onHighchartsRender=Highcharts.addEvent(this,"render",(()=>{window.isRenderComplete=!0}))),e.apply(this,[t,r])})),n(Highcharts.Series.prototype,"init",(function(e,t,r){e.apply(this,[t,r])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e,c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,h=JSON.parse(t.export.globalOptions);h&&s(h),Highcharts[t.export.constr||"chart"]("container",c,p);const u=o();for(const e in u)"function"!=typeof u[e]&&delete u[e];s(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const ue=e.readFileSync(F+"/templates/template.html","utf8");let de;async function ge(){if(!de)return!1;const e=await de.newPage();return await e.setCacheEnabled(!1),await fe(e),function(e){const{debug:t}=J();t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}));e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)}))}(e),e}async function me(e,t){for(const e of t)await e.dispose();await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))}async function fe(e){await e.setContent(ue,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${le()}/sources.js`}),await e.evaluate(pe)}const ve=async(e,t,r,o)=>e.evaluate(he,t,r,o);var ye=async(r,o,i)=>{let s=[];try{H(4,"[export] Determining export path.");const n=i.export,a=n?.options?.chart?.displayErrors&&oe.activeManifest.modules.debugger;let l;if(o.indexOf&&(o.indexOf("=0||o.indexOf("=0)){if(H(4,"[export] Treating as SVG."),"svg"===n.type)return o;l=!0,await r.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(o),{waitUntil:"domcontentloaded"})}else H(4,"[export] Treating as config."),n.strInj?await ve(r,{chart:{height:n.height,width:n.width}},i,a):(o.chart.height=n.height,o.chart.width=n.width,await ve(r,o,i,a));s=await async function(r,o){const i=[],s=o.customLogic.resources;if(s){const n=[];if(s.js&&n.push({content:s.js}),s.files)for(const t of s.files){const r=!t.startsWith("http");n.push(r?{content:e.readFileSync(t,"utf8")}:{url:t})}for(const e of n)try{i.push(await r.addScriptTag(e))}catch(e){$(2,e,"[export] The JS resource cannot be loaded.")}n.length=0;const a=[];if(s.css){let e=s.css.match(/@import\s*([^;]*);/g);if(e)for(let r of e)r&&(r=r.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),r.startsWith("http")?a.push({url:r}):o.customLogic.allowFileResources&&a.push({path:t.join(F,r)}));a.push({content:s.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const e of a)try{i.push(await r.addStyleTag(e))}catch(e){$(2,e,"[export] The CSS resource cannot be loaded.")}a.length=0}}return i}(r,i);const c=l?await r.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),r=t.height.baseVal.value*e,o=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:r,chartWidth:o}}),parseFloat(n.scale)):await r.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),p=Math.ceil(c.chartHeight||n.height),h=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 re("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 re(`[export] Unsupported output format ${n.type}.`);g=await(async(e,t,r,o,i)=>(await e.emulateMediaType("screen"),Promise.race([e.pdf({height:t+1,width:r,encoding:o}),new Promise(((e,t)=>setTimeout((()=>t(new re("Rasterization timeout"))),i||1500)))])))(r,p,h,"base64",n.rasterizationTimeout)}return await me(r,s),g}catch(e){return await me(r,s),e}};let be=!1;const we={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let Ee={};const Te={create:async()=>{let e=!1;const t=p.v4(),r=(new Date).getTime();try{if(e=await ge(),!e||e.isClosed())throw new re("The page is invalid or closed.");H(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new re("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(Ee.workLimit/2))}},validate:async e=>!(Ee.workLimit&&++e.workCount>Ee.workLimit)||(H(3,`[pool] Worker failed validation: exceeded work limit (limit is ${Ee.workLimit}).`),!1),destroy:async e=>{H(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&await e.page.close()}},Se=async e=>{if(Ee=e&&e.pool?{...e.pool}:{},await async function(e){const{debug:t,other:r}=J(),{enable:o,...i}=t,s={headless:!r.browserShellMode||"shell",userDataDir:"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...o&&i};if(!de){let e=0;const t=async()=>{try{H(3,`[browser] Attempting to get a browser instance (try ${++e}).`),de=await h.launch(s)}catch(r){if($(1,r,"[browser] Failed to launch a browser instance."),!(e<25))throw r;H(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===s.headless&&H(3,"[browser] Launched browser in shell mode."),o&&H(3,"[browser] Launched browser in debug mode.")}catch(e){throw new re("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!de)throw new re("[browser] Cannot find a browser to open.")}return de}(e.puppeteerArgs),H(3,`[pool] Initializing pool with workers: min ${Ee.minWorkers}, max ${Ee.maxWorkers}.`),be)return H(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(Ee.minWorkers)>parseInt(Ee.maxWorkers)&&(Ee.minWorkers=Ee.maxWorkers);try{be=new c.Pool({...Te,min:parseInt(Ee.minWorkers),max:parseInt(Ee.maxWorkers),acquireTimeoutMillis:Ee.acquireTimeout,createTimeoutMillis:Ee.createTimeout,destroyTimeoutMillis:Ee.destroyTimeout,idleTimeoutMillis:Ee.idleTimeout,createRetryIntervalMillis:Ee.createRetryInterval,reapIntervalMillis:Ee.reaperInterval,propagateCreateError:!1}),be.on("release",(async e=>{await async function(e,t=!1){try{e.isClosed()||(t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await fe(e)):await e.evaluate((()=>{document.body.innerHTML='
'})))}catch(e){$(2,e,"[browser] Could not clear the content of the page.")}}(e.page,!1),H(4,`[pool] Releasing a worker with ID ${e.id}.`)})),be.on("destroySuccess",((e,t)=>{H(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t{be.release(e)})),H(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new re("[pool] Could not create the pool of workers.").setError(e)}};async function xe(){if(H(3,"[pool] Killing pool with all workers and closing browser."),be){for(const e of be.used)be.release(e.resource);be.destroyed||(await be.destroy(),H(4,"[browser] Destroyed the pool of resources."))}await async function(){de?.connected&&await de.close(),H(4,"[browser] Closed the browser.")}()}const Re=async(e,t)=>{let r;try{if(H(4,"[pool] Work received, starting to process."),++we.exportAttempts,Ee.benchmarking&&Oe(),!be)throw new re("Work received, but pool has not been started.");const o=z();try{H(4,"[pool] Acquiring a worker handle."),r=await be.acquire().promise,t.server.benchmarking&&H(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${o()}ms.`)}catch(e){throw new re((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`).setError(e)}if(H(4,"[pool] Acquired a worker handle."),!r.page)throw new re("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();H(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const s=z(),n=await ye(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.page.close(),r.page=await ge()),new re((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${s()}ms.`).setError(n);t.server.benchmarking&&H(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${s()}ms.`),be.release(r);const a=(new Date).getTime()-i;return we.timeSpent+=a,we.spentAverage=we.timeSpent/++we.performedExports,H(4,`[pool] Work completed in ${a} ms.`),{result:n,options:t}}catch(e){throw++we.droppedExports,r&&be.release(r),new re(`[pool] In pool.postWork: ${e.message}`).setError(e)}},Le=()=>({min:be.min,max:be.max,all:be.numFree()+be.numUsed(),available:be.numFree(),used:be.numUsed(),pending:be.numPendingAcquires()});function Oe(){const{min:e,max:t,all:r,available:o,used:i,pending:s}=Le();H(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),H(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),H(5,`[pool] The number of all created resources: ${r}.`),H(5,`[pool] The number of available resources: ${o}.`),H(5,`[pool] The number of acquired resources: ${i}.`),H(5,`[pool] The number of resources waiting to be acquired: ${s}.`)}var _e=Le,ke=()=>we;let Ie=!1;const Ce=async(t,r)=>{H(4,"[chart] Starting the exporting process.");const o=((e,t={})=>{let r={};return e.svg?(r=q(t),r.export.type=e.type||e.export.type,r.export.scale=e.scale||e.export.scale,r.export.outfile=e.outfile||e.export.outfile,r.payload={svg:e.svg}):r=Y(t,e,T),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(t,J()),i=o.export;if(o.payload?.svg&&""!==o.payload.svg)try{H(4,"[chart] Attempting to export from a SVG input.");const e=He(function(e){const t=new u.JSDOM("").window;return d(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}(o.payload.svg),o,r);return++we.exportFromSvgAttempts,e}catch(e){return r(new re("[chart] Error loading SVG input.").setError(e))}if(i.infile&&i.infile.length)try{return H(4,"[chart] Attempting to export from an input file."),o.export.instr=e.readFileSync(i.infile,"utf8"),He(o.export.instr.trim(),o,r)}catch(e){return r(new re("[chart] Error loading input file.").setError(e))}if(i.instr&&""!==i.instr||i.options&&""!==i.options)try{return H(4,"[chart] Attempting to export from a raw input."),B(o.customLogic?.allowCodeExecution)?Pe(o,r):"string"==typeof i.instr?He(i.instr.trim(),o,r):Ae(o,i.instr||i.options,r)}catch(e){return r(new re("[chart] Error loading raw input.").setError(e))}return r(new re("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},Ne=e=>{const{chart:t,exporting:r}=e.export?.options||M(e.export?.instr),o=M(e.export?.globalOptions);let i=e.export?.scale||r?.scale||o?.exporting?.scale||e.export?.defaultScale||1;i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const r=Math.pow(10,t||0);return Math.round(+e*r)/r})(i,2);const s={height:e.export?.height||r?.sourceHeight||t?.height||o?.exporting?.sourceHeight||o?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||r?.sourceWidth||t?.width||o?.exporting?.sourceWidth||o?.chart?.width||e.export?.defaultWidth||600,scale:i};for(let[e,t]of Object.entries(s))s[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return s},Ae=async(t,r,o,i)=>{let{export:s,customLogic:n}=t;const a="boolean"==typeof n.allowCodeExecution?n.allowCodeExecution:Ie;if(n){if(a)if("string"==typeof t.customLogic.resources)t.customLogic.resources=j(t.customLogic.resources,B(t.customLogic.allowFileResources));else if(!t.customLogic.resources)try{const r=e.readFileSync("resources.json","utf8");t.customLogic.resources=j(r,B(t.customLogic.allowFileResources))}catch(e){$(2,e,"[chart] Unable to load the default resources.json file.")}}else n=t.customLogic={};if(!a&&n){if(n.callback||n.resources||n.customCode)return o(new re("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));n.callback=!1,n.resources=!1,n.customCode=!1}if(r&&(r.chart=r.chart||{},r.exporting=r.exporting||{},r.exporting.enabled=!1),s.constr=s.constr||"chart",s.type=U(s.type,s.outfile),"svg"===s.type&&(s.width=!1),["globalOptions","themeOptions"].forEach((t=>{try{s&&s[t]&&("string"==typeof s[t]&&s[t].endsWith(".json")?s[t]=M(e.readFileSync(s[t],"utf8"),!0):s[t]=M(s[t],!0))}catch(e){s[t]={},$(2,e,`[chart] The '${t}' cannot be loaded.`)}})),n.allowCodeExecution)try{n.customCode=X(n.customCode,n.allowFileResources)}catch(e){$(2,e,"[chart] The 'customCode' cannot be loaded.")}if(n&&n.callback&&n.callback?.indexOf("{")<0)if(n.allowFileResources)try{n.callback=e.readFileSync(n.callback,"utf8")}catch(e){n.callback=!1,$(2,e,"[chart] The 'callback' cannot be loaded.")}else n.callback=!1;t.export={...t.export,...Ne(t)};try{return o(!1,await Re(s.strInj||r||i,t))}catch(e){return o(e)}},Pe=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=W(o,e.customLogic?.allowCodeExecution)),r=o.replaceAll(/\t|\n|\r/g,"").trim(),";"===r[r.length-1]&&(r=r.substring(0,r.length-1)),e.export.strInj=r,Ae(e,!1,t)}catch(r){return t(new re(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(r))}},He=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return H(4,"[chart] Parsing input as SVG."),Ae(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return Ae(t,o,r)}catch(e){return B(o)?Pe(t,r):r(new re("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},$e=[],Ge=()=>{H(4,"[server] Clearing all registered intervals.");for(const e of $e)clearInterval(e)},De=(e,t,r,o)=>{$(1,e),"development"!==C.OTHER_NODE_ENV&&delete e.stack,o(e)},Fe=(e,t,r,o)=>{const{statusCode:i,status:s,message:n,stack:a}=e,l=i||s||500;r.status(l).json({statusCode:l,message:n,stack:a})};var Ue=(e,t)=>{const r="Too many requests, you have been rate limited. Please try again later.",o={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};o.trustProxy&&e.enable("trust proxy");const i=v({windowMs:60*o.window*1e3,max:o.max,delayMs:o.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:r})},default:()=>{t.status(429).send(r)}})},skip:e=>!1!==o.skipKey&&!1!==o.skipToken&&e.query.key===o.skipKey&&e.query.access_token===o.skipToken&&(H(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),H(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class je extends re{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}var Me=e=>!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=C.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new je("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 je("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i)throw new je("No new version supplied.",400);try{await(async e=>{const t=J();t?.highcharts&&(t.highcharts.version=e),await ae(t)})(i)}catch(e){throw new je(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:ce(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}));const qe={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let We=0;const Ve=[],Be=[],Xe=(e,t,r,o)=>{let i=!0;const{id:s,uniqueId:n,type:a,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,s,n,a,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},ze=async(e,t,r)=>{try{const r=z(),i=p.v4().replace(/-/g,""),s=J(),n=e.body,a=++We;let l=U(n.type);if(!n||"object"==typeof(o=n)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new je("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400);let c=M(n.infile||n.options||n.data);if(!c&&!n.svg)throw H(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Payload received: ${JSON.stringify(n)}.`),new je("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 h=!1;if(h=Xe(Ve,e,t,{id:a,uniqueId:i,type:l,body:n}),!0!==h)return t.send(h);let u=!1;e.socket.on("close",(()=>{u=!0})),H(4,`[export] Got an incoming HTTP request with ID ${i}.`),n.constr="string"==typeof n.constr&&n.constr||"chart";const d={export:{instr:c,type:l,constr:n.constr[0].toLowerCase()+n.constr.substr(1),height:n.height,width:n.width,scale:n.scale||s.export.scale,globalOptions:M(n.globalOptions,!0),themeOptions:M(n.themeOptions,!0)},customLogic:{allowCodeExecution:Ie,allowFileResources:!1,resources:M(n.resources,!0),callback:n.callback,customCode:n.customCode}};c&&(d.export.instr=W(c,d.customLogic.allowCodeExecution));const g=Y(s,d);if(g.export.options=c,g.payload={svg:n.svg||!1,b64:n.b64||!1,noDownload:n.noDownload||!1,requestId:i},n.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(g.payload.svg))throw new je("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400);await Ce(g,((o,c)=>{if(e.socket.removeAllListeners("close"),s.server.benchmarking&&H(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),u)return H(3,"[export] The client closed the connection before the chart finished processing.");if(o)throw o;if(!c||!c.result)throw new je(`Unexpected return from chart generation. Please check your request data. For the request with ID ${i}, the result is ${c.result}.`,400);return l=c.options.export.type,Xe(Be,e,t,{id:a,body:c.result}),c.result?n.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",qe[l]||"image/png"),n.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){r(e)}var o};const Ke=JSON.parse(e.readFileSync(t.join(F,"package.json"))),Je=new Date,Ye=[];function Qe(e){if(!e)return!1;var t;t=setInterval((()=>{const e=ke(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;Ye.push(t),Ye.length>30&&Ye.shift()}),6e4),$e.push(t),e.get("/health",((e,t)=>{const r=ke(),o=Ye.length,i=Ye.reduce(((e,t)=>e+t),0)/Ye.length;H(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:Je,uptime:Math.floor(((new Date).getTime()-Je.getTime())/1e3/60)+" minutes",version:Ke.version,highchartsVersion:ce(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,exportAttempts:r.exportAttempts,sucessRatio:r.performedExports/r.exportAttempts*100,pool:_e(),period:o,movingAverage:i,message:isNaN(i)||!Ye.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 Ze=new Map,et=m();et.disable("x-powered-by"),et.use(g());const tt=f.memoryStorage(),rt=f({storage:tt,limits:{fieldSize:52428800}});et.use(m.json({limit:52428800})),et.use(m.urlencoded({extended:!0,limit:52428800})),et.use(rt.none());const ot=e=>{e.on("clientError",(e=>{$(1,e,`[server] Client error: ${e.message}`)})),e.on("error",(e=>{$(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{$(1,e,`[server] Socket error: ${e.message}`)}))}))},it=async r=>{try{if(!r.enable)return!1;if(!r.ssl.force){const e=a.createServer(et);ot(e),e.listen(r.port,r.host),Ze.set(r.port,e),H(3,`[server] Started HTTP server on ${r.host}:${r.port}.`)}if(r.ssl.enable){let o,i;try{o=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.key"),"utf8"),i=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.crt"),"utf8")}catch(e){H(2,`[server] Unable to load key/certificate from the '${r.ssl.certPath}' path. Could not run secured layer server.`)}if(o&&i){const e=l.createServer({key:o,cert:i},et);ot(e),e.listen(r.ssl.port,r.host),Ze.set(r.ssl.port,e),H(3,`[server] Started HTTPS server on ${r.host}:${r.ssl.port}.`)}}r.rateLimiting&&r.rateLimiting.enable&&![0,NaN].includes(r.rateLimiting.maxRequests)&&Ue(et,r.rateLimiting),et.use(m.static(t.posix.join(F,"public"))),Qe(et),(e=>{e.post("/",ze),e.post("/:filename",ze)})(et),(e=>{!!e&&e.get("/",((e,r)=>{r.sendFile(t.join(F,"public","index.html"))}))})(et),Me(et),(e=>{e.use(De),e.use(Fe)})(et)}catch(e){throw new re("[server] Could not configure and start the server.").setError(e)}},st=()=>{H(4,"[server] Closing all servers.");for(const[e,t]of Ze)t.close((()=>{Ze.delete(e),H(4,`[server] Closed server on port: ${e}.`)}))};var nt={startServer:it,closeServers:st,getServers:()=>Ze,enableRateLimiting:e=>Ue(et,e),getExpress:()=>m,getApp:()=>et,use:(e,...t)=>{et.use(e,...t)},get:(e,...t)=>{et.get(e,...t)},post:(e,...t)=>{et.post(e,...t)}};const at=async e=>{await Promise.allSettled([Ge(),st(),xe()]),process.exit(e)};var lt={server:nt,startServer:it,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,Ie=B(t),(e=>{for(const[t,r]of Object.entries(e))A[t]=r;G(e&&parseInt(e.level)),e&&e.dest&&e.toFile&&D(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(H(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{H(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{H(4,`The ${e} event with code: ${t}.`),await at(0)})),process.on("SIGTERM",(async(e,t)=>{H(4,`The ${e} event with code: ${t}.`),await at(0)})),process.on("SIGHUP",(async(e,t)=>{H(4,`The ${e} event with code: ${t}.`),await at(0)})),process.on("uncaughtException",(async(e,t)=>{$(1,e,`The ${t} error.`),await at(1)}))),await ae(e),await Se({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer.args||[]}),e},singleExport:async t=>{t.export.instr=t.export.instr||t.export.options,await Ce(t,(async(t,r)=>{if(t)throw t;const{outfile:o,type:i}=r.options.export;e.writeFileSync(o||`chart.${i}`,"svg"!==i?Buffer.from(r.result,"base64"):r.result),await xe()}))},batchExport:async t=>{const r=[];for(let o of t.export.batch.split(";"))o=o.split("="),2===o.length&&r.push(Ce({...t,export:{...t.export,infile:o[0],outfile:o[1]}},((t,r)=>{if(t)throw t;e.writeFileSync(r.options.export.outfile,"svg"!==r.options.export.type?Buffer.from(r.result,"base64"):r.result)})));try{await Promise.all(r),await xe()}catch(e){throw new re("[chart] Error encountered during batch export.").setError(e)}},startExport:Ce,initPool:Se,killPool:xe,setOptions:(t,r)=>(r?.length&&(K=function(t){const r=t.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(r>-1&&t[r+1]){const o=t[r+1];try{if(o&&o.endsWith(".json"))return JSON.parse(e.readFileSync(o))}catch(e){$(2,e,`[config] Unable to load the configuration from the ${o} file.`)}}return{}}(r)),Q(w,K),K=Z(w),t&&(K=Y(K,t,T)),r?.length&&(K=function(e,t,r){let o=!1;for(let i=0;i(n.length-1===r&&(a=e[t].type),e[t])),r),n.reduce(((e,r,l)=>(n.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===a?e[r]=B(t[i]):"number"===a?e[r]=+t[i]:a.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:(H(2,`[config] Missing value for the '${s}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&V();return e}(K,r,w)),K),shutdownCleanUp:at,log:H,logWithStack:$,setLogLevel:G,enableFileLogging:D,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=S[r]?S[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async t=>{let r={};e.existsSync(t)&&(r=JSON.parse(e.readFileSync(t,"utf8")));const i=Object.keys(E).map((e=>({title:`${e} options`,value:e})));return o({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:i},{onSubmit:async(i,s)=>{let n=0,a=[];for(const e of s)E[e]=E[e].map((t=>({...t,section:e}))),a=[...a,...E[e]];return await o(a,{onSubmit:async(o,i)=>{if("moduleScripts"===o.name?(i=i.length?i.map((e=>o.choices[e])):o.choices,r[o.section][o.name]=i):r[o.section]=ee(Object.assign({},r[o.section]||{}),o.name.split("."),o.choices?o.choices[i]:i),++n===a.length){try{await e.promises.writeFile(t,JSON.stringify(r,null,2),"utf8")}catch(e){$(1,e,`[config] An error occurred while creating the ${t} file.`)}return!0}}}),!0}})},printLogo:r=>{const o=JSON.parse(e.readFileSync(t.join(F,"package.json"))).version;r?console.log(`Starting Highcharts Export Server v${o}...`):console.log(e.readFileSync(F+"/msg/startup.msg").toString().bold.yellow,`v${o}\n`.bold)},printUsage:V};module.exports=lt; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvc2NoZW1hcy9jb25maWcuanMiLCIuLi9saWIvZW52cy5qcyIsIi4uL2xpYi9sb2dnZXIuanMiLCIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi9saWIvZXhwb3J0LmpzIiwiLi4vdGVtcGxhdGVzL3N2Z19leHBvcnQvc3ZnX2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2ludGVydmFscy5qcyIsIi4uL2xpYi9zZXJ2ZXIvZXJyb3IuanMiLCIuLi9saWIvc2VydmVyL3JhdGVfbGltaXQuanMiLCIuLi9saWIvZXJyb3JzL0h0dHBFcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2NoYW5nZV9oY192ZXJzaW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy91aS5qcyIsIi4uL2xpYi9yZXNvdXJjZV9yZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG4vLyBQb3NzaWJsZSBuYW1lcyBmb3IgSGlnaGNoYXJ0cyBzY3JpcHRzXG5leHBvcnQgY29uc3Qgc2NyaXB0c05hbWVzID0ge1xuICBjb3JlOiBbJ2hpZ2hjaGFydHMnLCAnaGlnaGNoYXJ0cy1tb3JlJywgJ2hpZ2hjaGFydHMtM2QnXSxcbiAgbW9kdWxlczogW1xuICAgICdzdG9jaycsXG4gICAgJ21hcCcsXG4gICAgJ2dhbnR0JyxcbiAgICAnZXhwb3J0aW5nJyxcbiAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxuICAgICdhY2Nlc3NpYmlsaXR5JyxcbiAgICAvLyAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxuICAgICdib29zdC1jYW52YXMnLFxuICAgICdib29zdCcsXG4gICAgJ2RhdGEnLFxuICAgICdkYXRhLXRvb2xzJyxcbiAgICAnZHJhZ2dhYmxlLXBvaW50cycsXG4gICAgJ3N0YXRpYy1zY2FsZScsXG4gICAgJ2Jyb2tlbi1heGlzJyxcbiAgICAnaGVhdG1hcCcsXG4gICAgJ3RpbGVtYXAnLFxuICAgICd0aWxlZHdlYm1hcCcsXG4gICAgJ3RpbWVsaW5lJyxcbiAgICAndHJlZW1hcCcsXG4gICAgJ3RyZWVncmFwaCcsXG4gICAgJ2l0ZW0tc2VyaWVzJyxcbiAgICAnZHJpbGxkb3duJyxcbiAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXG4gICAgJ2J1bGxldCcsXG4gICAgJ2Z1bm5lbCcsXG4gICAgJ2Z1bm5lbDNkJyxcbiAgICAnZ2VvaGVhdG1hcCcsXG4gICAgJ3B5cmFtaWQzZCcsXG4gICAgJ25ldHdvcmtncmFwaCcsXG4gICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxuICAgICdwYXJldG8nLFxuICAgICdwYXR0ZXJuLWZpbGwnLFxuICAgICdwaWN0b3JpYWwnLFxuICAgICdwcmljZS1pbmRpY2F0b3InLFxuICAgICdzYW5rZXknLFxuICAgICdhcmMtZGlhZ3JhbScsXG4gICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxuICAgICdzZXJpZXMtbGFiZWwnLFxuICAgICdzZXJpZXMtb24tcG9pbnQnLFxuICAgICdzb2xpZC1nYXVnZScsXG4gICAgJ3NvbmlmaWNhdGlvbicsXG4gICAgLy8gJ3N0b2NrLXRvb2xzJyxcbiAgICAnc3RyZWFtZ3JhcGgnLFxuICAgICdzdW5idXJzdCcsXG4gICAgJ3ZhcmlhYmxlLXBpZScsXG4gICAgJ3Zhcml3aWRlJyxcbiAgICAndmVjdG9yJyxcbiAgICAndmVubicsXG4gICAgJ3dpbmRiYXJiJyxcbiAgICAnd29yZGNsb3VkJyxcbiAgICAneHJhbmdlJyxcbiAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcbiAgICAnZHJhZy1wYW5lcycsXG4gICAgJ2RlYnVnZ2VyJyxcbiAgICAnZHVtYmJlbGwnLFxuICAgICdsb2xsaXBvcCcsXG4gICAgJ2N5bGluZGVyJyxcbiAgICAnb3JnYW5pemF0aW9uJyxcbiAgICAnZG90cGxvdCcsXG4gICAgJ21hcmtlci1jbHVzdGVycycsXG4gICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcbiAgICAnaGVpa2luYXNoaScsXG4gICAgJ2Zsb3dtYXAnLFxuICAgICdleHBvcnQtZGF0YScsXG4gICAgJ25hdmlnYXRvcicsXG4gICAgJ3RleHRwYXRoJ1xuICBdLFxuICBpbmRpY2F0b3JzOiBbJ2luZGljYXRvcnMtYWxsJ10sXG4gIGN1c3RvbTogW1xuICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQuanMvMi4zMC4xL21vbWVudC5taW4uanMnLFxuICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQtdGltZXpvbmUvMC41LjQ1L21vbWVudC10aW1lem9uZS13aXRoLWRhdGEubWluLmpzJ1xuICBdXG59O1xuXG4vLyBUaGlzIGlzIHRoZSBjb25maWd1cmF0aW9uIG9iamVjdCB3aXRoIGFsbCBvcHRpb25zIGFuZCB0aGVpciBkZWZhdWx0IHZhbHVlcyxcbi8vIGFsc28gZnJvbSB0aGUgLmVudiBmaWxlIGlmIG9uZSBleGlzdHNcbmV4cG9ydCBjb25zdCBkZWZhdWx0Q29uZmlnID0ge1xuICBwdXBwZXRlZXI6IHtcbiAgICBhcmdzOiB7XG4gICAgICB2YWx1ZTogW1xuICAgICAgICAnLS1hbGxvdy1ydW5uaW5nLWluc2VjdXJlLWNvbnRlbnQnLFxuICAgICAgICAnLS1hc2gtbm8tbnVkZ2VzJyxcbiAgICAgICAgJy0tYXV0b3BsYXktcG9saWN5PXVzZXItZ2VzdHVyZS1yZXF1aXJlZCcsXG4gICAgICAgICctLWJsb2NrLW5ldy13ZWItY29udGVudHMnLFxuICAgICAgICAnLS1kaXNhYmxlLWFjY2VsZXJhdGVkLTJkLWNhbnZhcycsXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC1uZXR3b3JraW5nJyxcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLXRpbWVyLXRocm90dGxpbmcnLFxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXG4gICAgICAgICctLWRpc2FibGUtYnJlYWtwYWQnLFxuICAgICAgICAnLS1kaXNhYmxlLWNoZWNrZXItaW1hZ2luZycsXG4gICAgICAgICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtZXh0ZW5zaW9ucy13aXRoLWJhY2tncm91bmQtcGFnZXMnLFxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxuICAgICAgICAnLS1kaXNhYmxlLWRlZmF1bHQtYXBwcycsXG4gICAgICAgICctLWRpc2FibGUtZGV2LXNobS11c2FnZScsXG4gICAgICAgICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcbiAgICAgICAgJy0tZGlzYWJsZS1leHRlbnNpb25zJyxcbiAgICAgICAgJy0tZGlzYWJsZS1mZWF0dXJlcz1DYWxjdWxhdGVOYXRpdmVXaW5PY2NsdXNpb24sSW50ZXJlc3RGZWVkQ29udGVudFN1Z2dlc3Rpb25zLFdlYk9UUCcsXG4gICAgICAgICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcbiAgICAgICAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXG4gICAgICAgICctLWRpc2FibGUtbG9nZ2luZycsXG4gICAgICAgICctLWRpc2FibGUtbm90aWZpY2F0aW9ucycsXG4gICAgICAgICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcbiAgICAgICAgJy0tZGlzYWJsZS1wb3B1cC1ibG9ja2luZycsXG4gICAgICAgICctLWRpc2FibGUtcHJpbnQtcHJldmlldycsXG4gICAgICAgICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXG4gICAgICAgICctLWRpc2FibGUtcmVuZGVyZXItYmFja2dyb3VuZGluZycsXG4gICAgICAgICctLWRpc2FibGUtc2VhcmNoLWVuZ2luZS1jaG9pY2Utc2NyZWVuJyxcbiAgICAgICAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcbiAgICAgICAgJy0tZGlzYWJsZS1zZXR1aWQtc2FuZGJveCcsXG4gICAgICAgICctLWRpc2FibGUtc2l0ZS1pc29sYXRpb24tdHJpYWxzJyxcbiAgICAgICAgJy0tZGlzYWJsZS1zcGVlY2gtYXBpJyxcbiAgICAgICAgJy0tZGlzYWJsZS1zeW5jJyxcbiAgICAgICAgJy0tZW5hYmxlLXVuc2FmZS13ZWJncHUnLFxuICAgICAgICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcbiAgICAgICAgJy0taGlkZS1zY3JvbGxiYXJzJyxcbiAgICAgICAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXG4gICAgICAgICctLW11dGUtYXVkaW8nLFxuICAgICAgICAnLS1uby1kZWZhdWx0LWJyb3dzZXItY2hlY2snLFxuICAgICAgICAnLS1uby1maXJzdC1ydW4nLFxuICAgICAgICAnLS1uby1waW5ncycsXG4gICAgICAgICctLW5vLXNhbmRib3gnLFxuICAgICAgICAnLS1uby1zdGFydHVwLXdpbmRvdycsXG4gICAgICAgICctLW5vLXp5Z290ZScsXG4gICAgICAgICctLXBhc3N3b3JkLXN0b3JlPWJhc2ljJyxcbiAgICAgICAgJy0tcHJvY2Vzcy1wZXItdGFiJyxcbiAgICAgICAgJy0tdXNlLW1vY2sta2V5Y2hhaW4nXG4gICAgICBdLFxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnQXJndW1lbnRzIGFycmF5IHRvIHNlbmQgdG8gUHVwcGV0ZWVyLidcbiAgICB9XG4gIH0sXG4gIGhpZ2hjaGFydHM6IHtcbiAgICB2ZXJzaW9uOiB7XG4gICAgICB2YWx1ZTogJ2xhdGVzdCcsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIHVzZWQuJ1xuICAgIH0sXG4gICAgY2RuVVJMOiB7XG4gICAgICB2YWx1ZTogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbS8nLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIENETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cyB0byBiZSB1c2VkLidcbiAgICB9LFxuICAgIGNvcmVTY3JpcHRzOiB7XG4gICAgICB2YWx1ZTogc2NyaXB0c05hbWVzLmNvcmUsXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGNvcmUgSGlnaGNoYXJ0cyBzY3JpcHRzIHRvIGZldGNoLidcbiAgICB9LFxuICAgIG1vZHVsZVNjcmlwdHM6IHtcbiAgICAgIHZhbHVlOiBzY3JpcHRzTmFtZXMubW9kdWxlcyxcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBtb2R1bGVzIG9mIEhpZ2hjaGFydHMgdG8gZmV0Y2guJ1xuICAgIH0sXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xuICAgICAgdmFsdWU6IHNjcmlwdHNOYW1lcy5pbmRpY2F0b3JzLFxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGluZGljYXRvcnMgb2YgSGlnaGNoYXJ0cyB0byBmZXRjaC4nXG4gICAgfSxcbiAgICBjdXN0b21TY3JpcHRzOiB7XG4gICAgICB2YWx1ZTogc2NyaXB0c05hbWVzLmN1c3RvbSxcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXG4gICAgICBkZXNjcmlwdGlvbjogJ0FkZGl0aW9uYWwgY3VzdG9tIHNjcmlwdHMgb3IgZGVwZW5kZW5jaWVzIHRvIGZldGNoLidcbiAgICB9LFxuICAgIGZvcmNlRmV0Y2g6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGZsYWcgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdG8gcmVmZXRjaCBhbGwgc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bi4nXG4gICAgfSxcbiAgICBjYWNoZVBhdGg6IHtcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ0FDSEVfUEFUSCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnkuIEl0IGlzIHVzZWQgdG8gc3RvcmUgdGhlIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgY3VzdG9tIHNjcmlwdHMuJ1xuICAgIH1cbiAgfSxcbiAgZXhwb3J0OiB7XG4gICAgaW5maWxlOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGlucHV0IGZpbGUgc2hvdWxkIGluY2x1ZGUgYSBuYW1lIGFuZCBhIHR5cGUgKGpzb24gb3Igc3ZnKS4gSXQgbXVzdCBiZSBjb3JyZWN0bHkgZm9ybWF0dGVkIGFzIGEgSlNPTiBvciBTVkcgZmlsZS4nXG4gICAgfSxcbiAgICBpbnN0cjoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0lucHV0LCBwcm92aWRlZCBpbiB0aGUgZm9ybSBvZiBhIHN0cmluZ2lmaWVkIEpTT04gb3IgU1ZHIGZpbGUsIHdpbGwgb3ZlcnJpZGUgdGhlIC0taW5maWxlIG9wdGlvbi4nXG4gICAgfSxcbiAgICBvcHRpb25zOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnQW4gYWxpYXMgZm9yIHRoZSAtLWluc3RyIG9wdGlvbi4nXG4gICAgfSxcbiAgICBvdXRmaWxlOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIG91dHB1dCBmaWxlbmFtZSBhbG9uZyB3aXRoIGEgdHlwZSAoanBlZywgcG5nLCBwZGYsIG9yIHN2ZykuIFRoaXMgd2lsbCBpZ25vcmUgdGhlIC0tdHlwZSBmbGFnLidcbiAgICB9LFxuICAgIHR5cGU6IHtcbiAgICAgIHZhbHVlOiAncG5nJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGZpbGUgZXhwb3J0IGZvcm1hdC4gSXQgY2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcuJ1xuICAgIH0sXG4gICAgY29uc3RyOiB7XG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ0VYUE9SVF9DT05TVFInLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgY29uc3RydWN0b3IgdG8gdXNlLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0LidcbiAgICB9LFxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcbiAgICAgIHZhbHVlOiA0MDAsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICd0aGUgZGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LiBVc2VkIHdoZW4gbm8gdmFsdWUgaXMgc2V0LidcbiAgICB9LFxuICAgIGRlZmF1bHRXaWR0aDoge1xuICAgICAgdmFsdWU6IDYwMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1dJRFRIJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGRlZmF1bHQgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LiBVc2VkIHdoZW4gbm8gdmFsdWUgaXMgc2V0LidcbiAgICB9LFxuICAgIGRlZmF1bHRTY2FsZToge1xuICAgICAgdmFsdWU6IDEsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9TQ0FMRScsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBkZWZhdWx0IHNjYWxlIG9mIHRoZSBleHBvcnRlZCBjaGFydC4gVXNlZCB3aGVuIG5vIHZhbHVlIGlzIHNldC4nXG4gICAgfSxcbiAgICBoZWlnaHQ6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGluZyB0aGUgb3B0aW9uIGluIHRoZSBjaGFydCBzZXR0aW5ncy4nXG4gICAgfSxcbiAgICB3aWR0aDoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRpbmcgdGhlIG9wdGlvbiBpbiB0aGUgY2hhcnQgc2V0dGluZ3MuJ1xuICAgIH0sXG4gICAgc2NhbGU6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkaW5nIHRoZSBvcHRpb24gaW4gdGhlIGNoYXJ0IHNldHRpbmdzLiBSYW5nZXMgYmV0d2VlbiAwLjEgYW5kIDUuMC4nXG4gICAgfSxcbiAgICBnbG9iYWxPcHRpb25zOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnRWl0aGVyIGEgc3RyaW5naWZpZWQgSlNPTiBvciBhIGZpbGVuYW1lIGNvbnRhaW5pbmcgb3B0aW9ucyB0byBiZSBwYXNzZWQgaW50byB0aGUgSGlnaGNoYXJ0cy5zZXRPcHRpb25zLidcbiAgICB9LFxuICAgIHRoZW1lT3B0aW9uczoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0VpdGhlciBhIHN0cmluZ2lmaWVkIEpTT04gb3IgYSBmaWxlbmFtZSBjb250YWluaW5nIHRoZW1lIG9wdGlvbnMgdG8gYmUgcGFzc2VkIGludG8gdGhlIEhpZ2hjaGFydHMuc2V0T3B0aW9ucy4nXG4gICAgfSxcbiAgICBiYXRjaDoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0luaXRpYXRlcyBhIGJhdGNoIGpvYiB3aXRoIGEgc3RyaW5nIGNvbnRhaW5pbmcgaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCIuJ1xuICAgIH0sXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcbiAgICAgIHZhbHVlOiAxNTAwLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiBpbiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgcmVuZGVyaW5nIGEgd2VicGFnZS4nXG4gICAgfVxuICB9LFxuICBjdXN0b21Mb2dpYzoge1xuICAgIGFsbG93Q29kZUV4ZWN1dGlvbjoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0NvbnRyb2xzIHdoZXRoZXIgdGhlIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBpcyBhbGxvd2VkIGR1cmluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJ1xuICAgIH0sXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnQ29udHJvbHMgdGhlIGFiaWxpdHkgdG8gaW5qZWN0IHJlc291cmNlcyBmcm9tIHRoZSBmaWxlc3lzdGVtLiBUaGlzIHNldHRpbmcgaGFzIG5vIGVmZmVjdCB3aGVuIHJ1bm5pbmcgYXMgYSBzZXJ2ZXIuJ1xuICAgIH0sXG4gICAgY3VzdG9tQ29kZToge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0N1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgYmVmb3JlIGNoYXJ0IGluaXRpYWxpemF0aW9uLiBJdCBjYW4gYmUgYSBmdW5jdGlvbiwgY29kZSB3cmFwcGVkIHdpdGhpbiBhIGZ1bmN0aW9uLCBvciBhIGZpbGVuYW1lIHdpdGggdGhlIC5qcyBleHRlbnNpb24uJ1xuICAgIH0sXG4gICAgY2FsbGJhY2s6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIEl0IGNhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgZmlsZW5hbWUgd2l0aCB0aGUgLmpzIGV4dGVuc2lvbi4nXG4gICAgfSxcbiAgICByZXNvdXJjZXM6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdBZGRpdGlvbmFsIHJlc291cmNlIGluIHRoZSBmb3JtIG9mIGEgc3RyaW5naWZpZWQgSlNPTiwgd2hpY2ggbWF5IGNvbnRhaW4gZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zLidcbiAgICB9LFxuICAgIGxvYWRDb25maWc6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnQSBmaWxlIGNvbnRhaW5pbmcgYSBwcmUtZGVmaW5lZCBjb25maWd1cmF0aW9uIHRvIHVzZS4nXG4gICAgfSxcbiAgICBjcmVhdGVDb25maWc6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdFbmFibGVzIHNldHRpbmcgb3B0aW9ucyB0aHJvdWdoIGEgcHJvbXB0IGFuZCBzYXZpbmcgdGhlbSBpbiBhIHByb3ZpZGVkIGNvbmZpZyBmaWxlLidcbiAgICB9XG4gIH0sXG4gIHNlcnZlcjoge1xuICAgIGVuYWJsZToge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ1NFUlZFUl9FTkFCTEUnLFxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1doZW4gc2V0IHRvIHRydWUsIHRoZSBzZXJ2ZXIgc3RhcnRzIG9uIHRoZSBsb2NhbCBJUCBhZGRyZXNzIDAuMC4wLjAuJ1xuICAgIH0sXG4gICAgaG9zdDoge1xuICAgICAgdmFsdWU6ICcwLjAuMC4wJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGhvc3RuYW1lIG9mIHRoZSBzZXJ2ZXIuIEFkZGl0aW9uYWxseSwgaXQgc3RhcnRzIGEgc2VydmVyIG9uIHRoZSBwcm92aWRlZCBob3N0bmFtZS4nXG4gICAgfSxcbiAgICBwb3J0OiB7XG4gICAgICB2YWx1ZTogNzgwMSxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1NFUlZFUl9QT1JUJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHNlcnZlciBwb3J0IHdoZW4gZW5hYmxlZC4nXG4gICAgfSxcbiAgICBiZW5jaG1hcmtpbmc6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfQkVOQ0hNQVJLSU5HJyxcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdJbmRpY2F0ZXMgd2hldGhlciB0byBkaXNwbGF5IHRoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBvZiBzcGVjaWZpYyBhY3Rpb25zIHRoYXQgb2NjdXIgb24gdGhlIHNlcnZlciB3aGlsZSBzZXJ2aW5nIGEgcmVxdWVzdC4nXG4gICAgfSxcbiAgICBwcm94eToge1xuICAgICAgaG9zdDoge1xuICAgICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxuICAgICAgICBjbGlOYW1lOiAncHJveHlIb3N0JyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgaG9zdCBvZiB0aGUgcHJveHkgc2VydmVyIHRvIHVzZSwgaWYgaXQgZXhpc3RzLidcbiAgICAgIH0sXG4gICAgICBwb3J0OiB7XG4gICAgICAgIHZhbHVlOiA4MDgwLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHBvcnQgb2YgdGhlIHByb3h5IHNlcnZlciB0byB1c2UsIGlmIGl0IGV4aXN0cy4nXG4gICAgICB9LFxuICAgICAgdGltZW91dDoge1xuICAgICAgICB2YWx1ZTogNTAwMCxcbiAgICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfVElNRU9VVCcsXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVRpbWVvdXQnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSB0aW1lb3V0IGZvciB0aGUgcHJveHkgc2VydmVyIHRvIHVzZSwgaWYgaXQgZXhpc3RzLidcbiAgICAgIH1cbiAgICB9LFxuICAgIHJhdGVMaW1pdGluZzoge1xuICAgICAgZW5hYmxlOiB7XG4gICAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVJhdGVMaW1pdGluZycsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyByYXRlIGxpbWl0aW5nIGZvciB0aGUgc2VydmVyLidcbiAgICAgIH0sXG4gICAgICBtYXhSZXF1ZXN0czoge1xuICAgICAgICB2YWx1ZTogMTAsXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTJyxcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgaW4gb25lIG1pbnV0ZS4nXG4gICAgICB9LFxuICAgICAgd2luZG93OiB7XG4gICAgICAgIHZhbHVlOiAxLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVycsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHRpbWUgd2luZG93LCBpbiBtaW51dGVzLCBmb3IgdGhlIHJhdGUgbGltaXRpbmcuJ1xuICAgICAgfSxcbiAgICAgIGRlbGF5OiB7XG4gICAgICAgIHZhbHVlOiAwLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZJyxcbiAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgJ1RoZSBkZWxheSBkdXJhdGlvbiBmb3IgZWFjaCBzdWNjZXNzaXZlIHJlcXVlc3QgYmVmb3JlIHJlYWNoaW5nIHRoZSBtYXhpbXVtIGxpbWl0LidcbiAgICAgIH0sXG4gICAgICB0cnVzdFByb3h5OiB7XG4gICAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ1NldCB0aGlzIHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyLidcbiAgICAgIH0sXG4gICAgICBza2lwS2V5OiB7XG4gICAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWScsXG4gICAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgYW5kIHNob3VsZCBiZSBwcm92aWRlZCB3aXRoIHRoZSBza2lwVG9rZW4gYXJndW1lbnQuJ1xuICAgICAgfSxcbiAgICAgIHNraXBUb2tlbjoge1xuICAgICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTicsXG4gICAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgYW5kIHNob3VsZCBiZSBwcm92aWRlZCB3aXRoIHRoZSBza2lwS2V5IGFyZ3VtZW50LidcbiAgICAgIH1cbiAgICB9LFxuICAgIHNzbDoge1xuICAgICAgZW5hYmxlOiB7XG4gICAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9FTkFCTEUnLFxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBTU0wgcHJvdG9jb2wuJ1xuICAgICAgfSxcbiAgICAgIGZvcmNlOiB7XG4gICAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXG4gICAgICAgIGNsaU5hbWU6ICdzc2xGb3JjZScsXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdzc2xPbmx5JyxcbiAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgJ1doZW4gc2V0IHRvIHRydWUsIHRoZSBzZXJ2ZXIgaXMgZm9yY2VkIHRvIHNlcnZlIG9ubHkgb3ZlciBIVFRQUy4nXG4gICAgICB9LFxuICAgICAgcG9ydDoge1xuICAgICAgICB2YWx1ZTogNDQzLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfUE9SVCcsXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgcG9ydCBvbiB3aGljaCB0byBydW4gdGhlIFNTTCBzZXJ2ZXIuJ1xuICAgICAgfSxcbiAgICAgIGNlcnRQYXRoOiB7XG4gICAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0NFUlRfUEFUSCcsXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdzc2xQYXRoJyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgcGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlLidcbiAgICAgIH1cbiAgICB9XG4gIH0sXG4gIHBvb2w6IHtcbiAgICBtaW5Xb3JrZXJzOiB7XG4gICAgICB2YWx1ZTogNCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1BPT0xfTUlOX1dPUktFUlMnLFxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbnVtYmVyIG9mIG1pbmltdW0gYW5kIGluaXRpYWwgcG9vbCB3b3JrZXJzIHRvIHNwYXduLidcbiAgICB9LFxuICAgIG1heFdvcmtlcnM6IHtcbiAgICAgIHZhbHVlOiA4LFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NQVhfV09SS0VSUycsXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBudW1iZXIgb2YgbWF4aW11bSBwb29sIHdvcmtlcnMgdG8gc3Bhd24uJ1xuICAgIH0sXG4gICAgd29ya0xpbWl0OiB7XG4gICAgICB2YWx1ZTogNDAsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdQT09MX1dPUktfTElNSVQnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgbnVtYmVyIG9mIHdvcmsgcGllY2VzIHRoYXQgY2FuIGJlIHBlcmZvcm1lZCBiZWZvcmUgcmVzdGFydGluZyB0aGUgd29ya2VyIHByb2Nlc3MuJ1xuICAgIH0sXG4gICAgYWNxdWlyZVRpbWVvdXQ6IHtcbiAgICAgIHZhbHVlOiA1MDAwLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UuJ1xuICAgIH0sXG4gICAgY3JlYXRlVGltZW91dDoge1xuICAgICAgdmFsdWU6IDUwMDAsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9USU1FT1VUJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UuJ1xuICAgIH0sXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcbiAgICAgIHZhbHVlOiA1MDAwLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlLidcbiAgICB9LFxuICAgIGlkbGVUaW1lb3V0OiB7XG4gICAgICB2YWx1ZTogMzAwMDAsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdQT09MX0lETEVfVElNRU9VVCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCBhbiBpZGxlIHJlc291cmNlIGlzIGRlc3Ryb3llZC4nXG4gICAgfSxcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XG4gICAgICB2YWx1ZTogMjAwLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBiZWZvcmUgcmV0cnlpbmcgdGhlIGNyZWF0ZSBwcm9jZXNzIGluIGNhc2Ugb2YgYSBmYWlsdXJlLidcbiAgICB9LFxuICAgIHJlYXBlckludGVydmFsOiB7XG4gICAgICB2YWx1ZTogMTAwMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIGFmdGVyIHdoaWNoIHRoZSBjaGVjayBmb3IgaWRsZSByZXNvdXJjZXMgdG8gZGVzdHJveSBpcyB0cmlnZ2VyZWQuJ1xuICAgIH0sXG4gICAgYmVuY2htYXJraW5nOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnUE9PTF9CRU5DSE1BUktJTkcnLFxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdJbmRpY2F0ZSB3aGV0aGVyIHRvIHNob3cgc3RhdGlzdGljcyBmb3IgdGhlIHBvb2wgb2YgcmVzb3VyY2VzIG9yIG5vdC4nXG4gICAgfVxuICB9LFxuICBsb2dnaW5nOiB7XG4gICAgbGV2ZWw6IHtcbiAgICAgIHZhbHVlOiA0LFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19MRVZFTCcsXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbG9nZ2luZyBsZXZlbCB0byBiZSB1c2VkLidcbiAgICB9LFxuICAgIGZpbGU6IHtcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0ZJTEUnLFxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgbmFtZSBvZiBhIGxvZyBmaWxlLiBUaGUgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCBvcHRpb25zIGFsc28gbmVlZCB0byBiZSBzZXQgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZy4nXG4gICAgfSxcbiAgICBkZXN0OiB7XG4gICAgICB2YWx1ZTogJ2xvZy8nLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19ERVNUJyxcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIHBhdGggdG8gc3RvcmUgbG9nIGZpbGVzLiBUaGUgYGxvZ1RvRmlsZWAgb3B0aW9uIGFsc28gbmVlZHMgdG8gYmUgc2V0IHRvIGVuYWJsZSBmaWxlIGxvZ2dpbmcuJ1xuICAgIH0sXG4gICAgdG9Db25zb2xlOiB7XG4gICAgICB2YWx1ZTogdHJ1ZSxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0NPTlNPTEUnLFxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgc2hvd2luZyBsb2dzIGluIHRoZSBjb25zb2xlLidcbiAgICB9LFxuICAgIHRvRmlsZToge1xuICAgICAgdmFsdWU6IHRydWUsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcbiAgICAgIGNsaU5hbWU6ICdsb2dUb0ZpbGUnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGNyZWF0aW9uIG9mIHRoZSBsb2cgZGlyZWN0b3J5IGFuZCBzYXZpbmcgdGhlIGxvZyBpbnRvIGEgLmxvZyBmaWxlLidcbiAgICB9XG4gIH0sXG4gIHVpOiB7XG4gICAgZW5hYmxlOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVVaScsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIHVzZXIgaW50ZXJmYWNlIChVSSkgZm9yIHRoZSBleHBvcnQgc2VydmVyLidcbiAgICB9LFxuICAgIHJvdXRlOiB7XG4gICAgICB2YWx1ZTogJy8nLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxuICAgICAgY2xpTmFtZTogJ3VpUm91dGUnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgZW5kcG9pbnQgcm91dGUgdG8gd2hpY2ggdGhlIHVzZXIgaW50ZXJmYWNlIChVSSkgc2hvdWxkIGJlIGF0dGFjaGVkLidcbiAgICB9XG4gIH0sXG4gIG90aGVyOiB7XG4gICAgbm9kZUVudjoge1xuICAgICAgdmFsdWU6ICdwcm9kdWN0aW9uJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ09USEVSX05PREVfRU5WJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHR5cGUgb2YgTm9kZS5qcyBlbnZpcm9ubWVudC4nXG4gICAgfSxcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xuICAgICAgdmFsdWU6IHRydWUsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxuICAgICAgZGVzY3JpcHRpb246ICdEZWNpZGVzIHdoZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMuJ1xuICAgIH0sXG4gICAgbm9Mb2dvOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9fTE9HTycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gYSBzdGFydHVwLiBXaWxsIGJlIHJlcGxhY2VkIGJ5IGEgc2ltcGxlIHRleHQuJ1xuICAgIH0sXG4gICAgaGFyZFJlc2V0UGFnZToge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ09USEVSX0hBUkRfUkVTRVRfUEFHRScsXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlY2lkZXMgaWYgdGhlIHBhZ2UgY29udGVudCBzaG91bGQgYmUgcmVzZXQgZW50aXJlbHkuJ1xuICAgIH0sXG4gICAgYnJvd3NlclNoZWxsTW9kZToge1xuICAgICAgdmFsdWU6IHRydWUsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVjaWRlcyBpZiB0aGUgYnJvd3NlciBydW5zIGluIHRoZSBzaGVsbCBtb2RlLidcbiAgICB9XG4gIH0sXG4gIGRlYnVnOiB7XG4gICAgZW5hYmxlOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVEZWJ1ZycsXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIHVuZGVybHlpbmcgYnJvd3Nlci4nXG4gICAgfSxcbiAgICBoZWFkbGVzczoge1xuICAgICAgdmFsdWU6IHRydWUsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdDb250cm9scyB0aGUgbW9kZSBpbiB3aGljaCB0aGUgYnJvd3NlciBpcyBsYXVuY2hlZCB3aGVuIGluIHRoZSBkZWJ1ZyBtb2RlLidcbiAgICB9LFxuICAgIGRldnRvb2xzOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdEZWNpZGVzIHdoZXRoZXIgdG8gZW5hYmxlIERldlRvb2xzIHdoZW4gdGhlIGJyb3dzZXIgaXMgaW4gYSBoZWFkZnVsIHN0YXRlLidcbiAgICB9LFxuICAgIGxpc3RlblRvQ29uc29sZToge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ0RFQlVHX0xJU1RFTl9UT19DT05TT0xFJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnRGVjaWRlcyB3aGV0aGVyIHRvIGVuYWJsZSBhIGxpc3RlbmVyIGZvciBjb25zb2xlIG1lc3NhZ2VzIHNlbnQgZnJvbSB0aGUgYnJvd3Nlci4nXG4gICAgfSxcbiAgICBkdW1waW86IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGVudkxpbms6ICdERUJVR19EVU1QSU8nLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdSZWRpcmVjdHMgYnJvd3NlciBwcm9jZXNzIHN0ZG91dCBhbmQgc3RkZXJyIHRvIHByb2Nlc3Muc3Rkb3V0IGFuZCBwcm9jZXNzLnN0ZGVyci4nXG4gICAgfSxcbiAgICBzbG93TW86IHtcbiAgICAgIHZhbHVlOiAwLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1Nsb3dzIGRvd24gUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgbWlsbGlzZWNvbmRzLidcbiAgICB9LFxuICAgIGRlYnVnZ2luZ1BvcnQ6IHtcbiAgICAgIHZhbHVlOiA5MjIyLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVCVUdHSU5HX1BPUlQnLFxuICAgICAgZGVzY3JpcHRpb246ICdTcGVjaWZpZXMgdGhlIGRlYnVnZ2luZyBwb3J0LidcbiAgICB9XG4gIH1cbn07XG5cbi8vIFRoZSBjb25maWcgZGVzY3JpcHRpb25zIG9iamVjdCBmb3IgdGhlIHByb21wdHMgZnVuY3Rpb25hbGl0eS4gSXQgY29udGFpbnNcbi8vIGluZm9ybWF0aW9uIGxpa2U6XG4vLyAqIFR5cGUgb2YgYSBwcm9tcHRcbi8vICogTmFtZSBvZiBhbiBvcHRpb25cbi8vICogU2hvcnQgZGVzY3JpcHRpb24gb2YgYSBjaG9zZW4gb3B0aW9uXG4vLyAqIEluaXRpYWwgdmFsdWVcbmV4cG9ydCBjb25zdCBwcm9tcHRzQ29uZmlnID0ge1xuICBwdXBwZXRlZXI6IFtcbiAgICB7XG4gICAgICB0eXBlOiAnbGlzdCcsXG4gICAgICBuYW1lOiAnYXJncycsXG4gICAgICBtZXNzYWdlOiAnUHVwcGV0ZWVyIGFyZ3VtZW50cycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnB1cHBldGVlci5hcmdzLnZhbHVlLmpvaW4oJywnKSxcbiAgICAgIHNlcGFyYXRvcjogJywnXG4gICAgfVxuICBdLFxuICBoaWdoY2hhcnRzOiBbXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ3ZlcnNpb24nLFxuICAgICAgbWVzc2FnZTogJ0hpZ2hjaGFydHMgdmVyc2lvbicsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMudmVyc2lvbi52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ2NkblVSTCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIFVSTCBvZiBDRE4nLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNkblVSTC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ211bHRpc2VsZWN0JyxcbiAgICAgIG5hbWU6ICdjb3JlU2NyaXB0cycsXG4gICAgICBtZXNzYWdlOiAnQXZhaWxhYmxlIGNvcmUgc2NyaXB0cycsXG4gICAgICBpbnN0cnVjdGlvbnM6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxuICAgICAgY2hvaWNlczogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNvcmVTY3JpcHRzLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxuICAgICAgbmFtZTogJ21vZHVsZVNjcmlwdHMnLFxuICAgICAgbWVzc2FnZTogJ0F2YWlsYWJsZSBtb2R1bGUgc2NyaXB0cycsXG4gICAgICBpbnN0cnVjdGlvbnM6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxuICAgICAgY2hvaWNlczogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLm1vZHVsZVNjcmlwdHMudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXG4gICAgICBuYW1lOiAnaW5kaWNhdG9yU2NyaXB0cycsXG4gICAgICBtZXNzYWdlOiAnQXZhaWxhYmxlIGluZGljYXRvciBzY3JpcHRzJyxcbiAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtLicsXG4gICAgICBjaG9pY2VzOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuaW5kaWNhdG9yU2NyaXB0cy52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ2xpc3QnLFxuICAgICAgbmFtZTogJ2N1c3RvbVNjcmlwdHMnLFxuICAgICAgbWVzc2FnZTogJ0N1c3RvbSBzY3JpcHRzJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jdXN0b21TY3JpcHRzLnZhbHVlLmpvaW4oJywnKSxcbiAgICAgIHNlcGFyYXRvcjogJywnXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdmb3JjZUZldGNoJyxcbiAgICAgIG1lc3NhZ2U6ICdGb3JjZSByZS1mZXRjaCB0aGUgc2NyaXB0cycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuZm9yY2VGZXRjaC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ2NhY2hlUGF0aCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY2FjaGVQYXRoLnZhbHVlXG4gICAgfVxuICBdLFxuICBleHBvcnQ6IFtcbiAgICB7XG4gICAgICB0eXBlOiAnc2VsZWN0JyxcbiAgICAgIG5hbWU6ICd0eXBlJyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBleHBvcnQgZmlsZSB0eXBlJyxcbiAgICAgIGhpbnQ6IGBEZWZhdWx0OiAke2RlZmF1bHRDb25maWcuZXhwb3J0LnR5cGUudmFsdWV9YCxcbiAgICAgIGluaXRpYWw6IDAsXG4gICAgICBjaG9pY2VzOiBbJ3BuZycsICdqcGVnJywgJ3BkZicsICdzdmcnXVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3NlbGVjdCcsXG4gICAgICBuYW1lOiAnY29uc3RyJyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBjb25zdHJ1Y3RvciBmb3IgSGlnaGNoYXJ0cycsXG4gICAgICBoaW50OiBgRGVmYXVsdDogJHtkZWZhdWx0Q29uZmlnLmV4cG9ydC5jb25zdHIudmFsdWV9YCxcbiAgICAgIGluaXRpYWw6IDAsXG4gICAgICBjaG9pY2VzOiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdkZWZhdWx0SGVpZ2h0JyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZXhwb3J0LmRlZmF1bHRIZWlnaHQudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ2RlZmF1bHRXaWR0aCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZmFsbGJhY2sgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZXhwb3J0LmRlZmF1bHRXaWR0aC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnZGVmYXVsdFNjYWxlJyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdFNjYWxlLnZhbHVlLFxuICAgICAgbWluOiAwLjEsXG4gICAgICBtYXg6IDVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3Jhc3Rlcml6YXRpb25UaW1lb3V0JyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcmVuZGVyaW5nIHdlYnBhZ2UgdGltZW91dCBpbiBtaWxsaXNlY29uZHMnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQucmFzdGVyaXphdGlvblRpbWVvdXQudmFsdWVcbiAgICB9XG4gIF0sXG4gIGN1c3RvbUxvZ2ljOiBbXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnYWxsb3dDb2RlRXhlY3V0aW9uJyxcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgZXhlY3V0aW9uIG9mIGN1c3RvbSBjb2RlJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdhbGxvd0ZpbGVSZXNvdXJjZXMnLFxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBmaWxlIHJlc291cmNlcycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmN1c3RvbUxvZ2ljLmFsbG93RmlsZVJlc291cmNlcy52YWx1ZVxuICAgIH1cbiAgXSxcbiAgc2VydmVyOiBbXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnZW5hYmxlJyxcbiAgICAgIG1lc3NhZ2U6ICdTdGFydHMgdGhlIHNlcnZlciBvbiAwLjAuMC4wJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmVuYWJsZS52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ2hvc3QnLFxuICAgICAgbWVzc2FnZTogJ1NlcnZlciBob3N0bmFtZScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5ob3N0LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdwb3J0JyxcbiAgICAgIG1lc3NhZ2U6ICdTZXJ2ZXIgcG9ydCcsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5wb3J0LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdiZW5jaG1hcmtpbmcnLFxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBzZXJ2ZXIgYmVuY2htYXJraW5nJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmJlbmNobWFya2luZy52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ3Byb3h5Lmhvc3QnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBob3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnByb3h5Lmhvc3QudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3Byb3h5LnBvcnQnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBwb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnByb3h5LnBvcnQudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3Byb3h5LnRpbWVvdXQnLFxuICAgICAgbWVzc2FnZTogJ1RoZSB0aW1lb3V0IGZvciB0aGUgcHJveHkgc2VydmVyIHRvIHVzZScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5wcm94eS50aW1lb3V0LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuZW5hYmxlJyxcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgcmF0ZSBsaW1pdGluZycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuZW5hYmxlLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcubWF4UmVxdWVzdHMnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBtYXhpbXVtIHJlcXVlc3RzIGFsbG93ZWQgcGVyIG1pbnV0ZScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcubWF4UmVxdWVzdHMudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy53aW5kb3cnLFxuICAgICAgbWVzc2FnZTogJ1RoZSByYXRlLWxpbWl0aW5nIHRpbWUgd2luZG93IGluIG1pbnV0ZXMnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLndpbmRvdy52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLmRlbGF5JyxcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgICdUaGUgZGVsYXkgZm9yIGVhY2ggc3VjY2Vzc2l2ZSByZXF1ZXN0IGJlZm9yZSByZWFjaGluZyB0aGUgbWF4aW11bScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuZGVsYXkudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy50cnVzdFByb3h5JyxcbiAgICAgIG1lc3NhZ2U6ICdTZXQgdG8gdHJ1ZSBpZiBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy50cnVzdFByb3h5LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndGV4dCcsXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLnNraXBLZXknLFxuICAgICAgbWVzc2FnZTpcbiAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciB3aGVuIHByb3ZpZGVkIHdpdGggdGhlIHNraXBUb2tlbiBhcmd1bWVudCcsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuc2tpcEtleS52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5za2lwVG9rZW4nLFxuICAgICAgbWVzc2FnZTpcbiAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciB3aGVuIHByb3ZpZGVkIHdpdGggdGhlIHNraXBLZXkgYXJndW1lbnQnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLnNraXBUb2tlbi52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnc3NsLmVuYWJsZScsXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIFNTTCBwcm90b2NvbCcsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wuZW5hYmxlLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdzc2wuZm9yY2UnLFxuICAgICAgbWVzc2FnZTogJ0ZvcmNlIHNlcnZpbmcgb25seSBvdmVyIEhUVFBTJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5mb3JjZS52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnc3NsLnBvcnQnLFxuICAgICAgbWVzc2FnZTogJ1NTTCBzZXJ2ZXIgcG9ydCcsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wucG9ydC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ3NzbC5jZXJ0UGF0aCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gZmluZCB0aGUgU1NMIGNlcnRpZmljYXRlL2tleScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wuY2VydFBhdGgudmFsdWVcbiAgICB9XG4gIF0sXG4gIHBvb2w6IFtcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdtaW5Xb3JrZXJzJyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgaW5pdGlhbCBudW1iZXIgb2Ygd29ya2VycyB0byBzcGF3bicsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wubWluV29ya2Vycy52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnbWF4V29ya2VycycsXG4gICAgICBtZXNzYWdlOiAnVGhlIG1heGltdW0gbnVtYmVyIG9mIHdvcmtlcnMgdG8gc3Bhd24nLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLm1heFdvcmtlcnMudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3dvcmtMaW1pdCcsXG4gICAgICBtZXNzYWdlOlxuICAgICAgICAnVGhlIHBpZWNlcyBvZiB3b3JrIHRoYXQgY2FuIGJlIHBlcmZvcm1lZCBiZWZvcmUgcmVzdGFydGluZyBhIFB1cHBldGVlciBwcm9jZXNzJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC53b3JrTGltaXQudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ2FjcXVpcmVUaW1lb3V0JyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBhY3F1aXJpbmcgYSByZXNvdXJjZScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYWNxdWlyZVRpbWVvdXQudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ2NyZWF0ZVRpbWVvdXQnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmNyZWF0ZVRpbWVvdXQudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ2Rlc3Ryb3lUaW1lb3V0JyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmRlc3Ryb3lUaW1lb3V0LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdpZGxlVGltZW91dCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgYWZ0ZXIgYW4gaWRsZSByZXNvdXJjZSBpcyBkZXN0cm95ZWQnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmlkbGVUaW1lb3V0LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdjcmVhdGVSZXRyeUludGVydmFsJyxcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgICdUaGUgcmV0cnkgaW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGFmdGVyIGEgY3JlYXRlIHByb2Nlc3MgZmFpbHMnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmNyZWF0ZVJldHJ5SW50ZXJ2YWwudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3JlYXBlckludGVydmFsJyxcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgICdUaGUgcmVhcGVyIGludGVydmFsIGluIG1pbGxpc2Vjb25kcyBhZnRlciB0cmlnZ2VyaW5nIHRoZSBjaGVjayBmb3IgaWRsZSByZXNvdXJjZXMgdG8gZGVzdHJveScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wucmVhcGVySW50ZXJ2YWwudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2JlbmNobWFya2luZycsXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIGJlbmNobWFya2luZyBmb3IgYSByZXNvdXJjZSBwb29sJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5iZW5jaG1hcmtpbmcudmFsdWVcbiAgICB9XG4gIF0sXG4gIGxvZ2dpbmc6IFtcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdsZXZlbCcsXG4gICAgICBtZXNzYWdlOlxuICAgICAgICAnVGhlIGxvZyBsZXZlbCAoMDogc2lsZW50LCAxOiBlcnJvciwgMjogd2FybmluZywgMzogbm90aWNlLCA0OiB2ZXJib3NlLCA1OiBiZW5jaG1hcmspJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5sZXZlbC52YWx1ZSxcbiAgICAgIHJvdW5kOiAwLFxuICAgICAgbWluOiAwLFxuICAgICAgbWF4OiA1XG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndGV4dCcsXG4gICAgICBuYW1lOiAnZmlsZScsXG4gICAgICBtZXNzYWdlOlxuICAgICAgICAnQSBsb2cgZmlsZSBuYW1lLiBTZXQgd2l0aCAtLXRvRmlsZSBhbmQgLS1sb2dEZXN0IHRvIGVuYWJsZSBmaWxlIGxvZ2dpbmcnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5sb2dnaW5nLmZpbGUudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0ZXh0JyxcbiAgICAgIG5hbWU6ICdkZXN0JyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcGF0aCB0byBhIGxvZyBmaWxlIHdoZW4gdGhlIGZpbGUgbG9nZ2luZyBpcyBlbmFibGVkJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5kZXN0LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICd0b0NvbnNvbGUnLFxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBsb2dnaW5nIHRvIHRoZSBjb25zb2xlJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy50b0NvbnNvbGUudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ3RvRmlsZScsXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmxvZ2dpbmcudG9GaWxlLnZhbHVlXG4gICAgfVxuICBdLFxuICB1aTogW1xuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2VuYWJsZScsXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlcicsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnVpLmVuYWJsZS52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ3JvdXRlJyxcbiAgICAgIG1lc3NhZ2U6ICdBIHJvdXRlIHRvIGF0dGFjaCB0aGUgVUknLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy51aS5yb3V0ZS52YWx1ZVxuICAgIH1cbiAgXSxcbiAgb3RoZXI6IFtcbiAgICB7XG4gICAgICB0eXBlOiAndGV4dCcsXG4gICAgICBuYW1lOiAnbm9kZUVudicsXG4gICAgICBtZXNzYWdlOiAnVGhlIHR5cGUgb2YgTm9kZS5qcyBlbnZpcm9ubWVudCcsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLm5vZGVFbnYudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2xpc3RlblRvUHJvY2Vzc0V4aXRzJyxcbiAgICAgIG1lc3NhZ2U6ICdTZXQgdG8gZmFsc2UgdG8gc2tpcCBhdHRhY2hpbmcgcHJvY2Vzcy5leGl0IGhhbmRsZXJzJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ25vTG9nbycsXG4gICAgICBtZXNzYWdlOiAnU2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwLiBSZXBsYWNlZCBieSBzaW1wbGUgdGV4dCcsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLm5vTG9nby52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnaGFyZFJlc2V0UGFnZScsXG4gICAgICBtZXNzYWdlOiAnRGVjaWRlcyBpZiB0aGUgcGFnZSBjb250ZW50IHNob3VsZCBiZSByZXNldCBlbnRpcmVseScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLmhhcmRSZXNldFBhZ2UudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2Jyb3dzZXJTaGVsbE1vZGUnLFxuICAgICAgbWVzc2FnZTogJ0RlY2lkZXMgaWYgdGhlIGJyb3dzZXIgcnVucyBpbiB0aGUgc2hlbGwgbW9kZScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLmJyb3dzZXJTaGVsbE1vZGUudmFsdWVcbiAgICB9XG4gIF0sXG4gIGRlYnVnOiBbXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnZW5hYmxlJyxcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZGVidWcuZW5hYmxlLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdoZWFkbGVzcycsXG4gICAgICBtZXNzYWdlOiAnVGhlIG1vZGUgc2V0dGluZyBmb3IgdGhlIGJyb3dzZXInLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5oZWFkbGVzcy52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnZGV2dG9vbHMnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBEZXZUb29scyBmb3IgdGhlIGhlYWRmdWwgYnJvd3NlcicsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmRldnRvb2xzLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdsaXN0ZW5Ub0NvbnNvbGUnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBldmVudCBsaXN0ZW5lciBmb3IgY29uc29sZSBtZXNzYWdlcyBmcm9tIHRoZSBicm93c2VyJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZGVidWcubGlzdGVuVG9Db25zb2xlLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdkdW1waW8nLFxuICAgICAgbWVzc2FnZTogJ1JlZGlyZWN0cyB0aGUgYnJvd3NlciBzdGRvdXQgYW5kIHN0ZGVyciB0byBOb2RlSlMgcHJvY2VzcycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmR1bXBpby52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnc2xvd01vJyxcbiAgICAgIG1lc3NhZ2U6ICdQdXBwZXRlZXIgb3BlcmF0aW9ucyBzbG93IGRvd24gaW4gbWlsbGlzZWNvbmRzJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZGVidWcuc2xvd01vLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdkZWJ1Z2dpbmdQb3J0JyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcG9ydCBudW1iZXIgZm9yIGRlYnVnZ2luZycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmRlYnVnZ2luZ1BvcnQudmFsdWVcbiAgICB9XG4gIF1cbn07XG5cbi8vIEFic29sdXRlIHByb3BzIHRoYXQsIGluIGNhc2Ugb2YgbWVyZ2luZyByZWN1cnNpdmVseSwgbmVlZCB0byBiZSBmb3JjZSBtZXJnZWRcbmV4cG9ydCBjb25zdCBhYnNvbHV0ZVByb3BzID0gW1xuICAnb3B0aW9ucycsXG4gICdnbG9iYWxPcHRpb25zJyxcbiAgJ3RoZW1lT3B0aW9ucycsXG4gICdyZXNvdXJjZXMnLFxuICAncGF5bG9hZCdcbl07XG5cbi8vIEFyZ3VtZW50IG5lc3RpbmcgbGV2ZWwgb2YgYWxsIGV4cG9ydCBzZXJ2ZXIgb3B0aW9uc1xuZXhwb3J0IGNvbnN0IG5lc3RlZEFyZ3MgPSB7fTtcblxuLyoqXG4gKiBSZWN1cnNpdmVseSBjcmVhdGVzIGEgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50cyBmcm9tIGFuIG9iamVjdC5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqIC0gVGhlIG9iamVjdCBjb250YWluaW5nIG5lc3RlZCBhcmd1bWVudHMuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvcENoYWluIC0gVGhlIGN1cnJlbnQgY2hhaW4gb2YgbmVzdGVkIHByb3BlcnRpZXNcbiAqICh1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbikuXG4gKi9cbmNvbnN0IGNyZWF0ZU5lc3RlZEFyZ3MgPSAob2JqLCBwcm9wQ2hhaW4gPSAnJykgPT4ge1xuICBPYmplY3Qua2V5cyhvYmopLmZvckVhY2goKGspID0+IHtcbiAgICBpZiAoIVsncHVwcGV0ZWVyJywgJ2hpZ2hjaGFydHMnXS5pbmNsdWRlcyhrKSkge1xuICAgICAgY29uc3QgZW50cnkgPSBvYmpba107XG4gICAgICBpZiAodHlwZW9mIGVudHJ5LnZhbHVlID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICAvLyBHbyBkZWVwZXIgaW4gdGhlIG5lc3RlZCBhcmd1bWVudHNcbiAgICAgICAgY3JlYXRlTmVzdGVkQXJncyhlbnRyeSwgYCR7cHJvcENoYWlufS4ke2t9YCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBDcmVhdGUgdGhlIGNoYWluIG9mIG5lc3RlZCBhcmd1bWVudHNcbiAgICAgICAgbmVzdGVkQXJnc1tlbnRyeS5jbGlOYW1lIHx8IGtdID0gYCR7cHJvcENoYWlufS4ke2t9YC5zdWJzdHJpbmcoMSk7XG5cbiAgICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcbiAgICAgICAgaWYgKGVudHJ5LmxlZ2FjeU5hbWUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIG5lc3RlZEFyZ3NbZW50cnkubGVnYWN5TmFtZV0gPSBgJHtwcm9wQ2hhaW59LiR7a31gLnN1YnN0cmluZygxKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfSk7XG59O1xuXG5jcmVhdGVOZXN0ZWRBcmdzKGRlZmF1bHRDb25maWcpO1xuIiwiLyoqXG4gKiBAZmlsZW92ZXJ2aWV3XG4gKiBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlcyB3aXRoIHRoZSAnem9kJ1xuICogbGlicmFyeS4gVGhlIHBhcnNlZCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHRoZW4gZXhwb3J0ZWQgdG8gYmUgdXNlZFxuICogaW4gdGhlIGFwcGxpY2F0aW9uIGFzIFwiZW52c1wiLiBXZSBzaG91bGQgbm90IHVzZSBwcm9jZXNzLmVudiBkaXJlY3RseVxuICogaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXG4gKlxuICogVGhlIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgcGFyc2VkIGFuZCB2YWxpZGF0ZWQgb25seSBvbmNlIHdoZW5cbiAqIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lclxuICogZm9yIGVhY2ggb2YgdGhlIG9wdGlvbnMuXG4gKi9cblxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xuaW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XG5cbmltcG9ydCB7IHNjcmlwdHNOYW1lcyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xuXG4vLyBMb2FkIC5lbnYgaW50byBlbnZpcm9ubWVudCB2YXJpYWJsZXNcbmRvdGVudi5jb25maWcoKTtcblxuLy8gT2JqZWN0IHdpdGggY3VzdG9tIHZhbGlkYXRvcnMgYW5kIHRyYW5zZm9ybWVycywgdG8gYXZvaWQgcmVwZXRpdGlvblxuLy8gaW4gdGhlIENvbmZpZyBvYmplY3RcbmNvbnN0IHYgPSB7XG4gIC8vIFNwbGl0cyBzdHJpbmcgdmFsdWUgaW50byBlbGVtZW50cyBpbiBhbiBhcnJheSwgdHJpbXMgZXZlcnkgZWxlbWVudCwgY2hlY2tzXG4gIC8vIGlmIGFuIGFycmF5IGlzIGNvcnJlY3QsIGlmIGl0IGlzIGVtcHR5LCBhbmQgaWYgaXQgaXMsIHJldHVybnMgdW5kZWZpbmVkXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XG4gICAgelxuICAgICAgLnN0cmluZygpXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cbiAgICAgICAgdmFsdWVcbiAgICAgICAgICAuc3BsaXQoJywnKVxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXG4gICAgICAgICAgLmZpbHRlcigodmFsdWUpID0+IGZpbHRlckFycmF5LmluY2x1ZGVzKHZhbHVlKSlcbiAgICAgIClcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcblxuICAvLyBBbGxvd3Mgb25seSB0cnVlLCBmYWxzZSBhbmQgY29ycmVjdGx5IHBhcnNlIHRoZSB2YWx1ZSB0byBib29sZWFuXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXG4gIGJvb2xlYW46ICgpID0+XG4gICAgelxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA9PT0gJ3RydWUnIDogdW5kZWZpbmVkKSksXG5cbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxuICAvLyBiZSB1bmRlZmluZWRcbiAgZW51bTogKHZhbHVlcykgPT5cbiAgICB6XG4gICAgICAuZW51bShbLi4udmFsdWVzLCAnJ10pXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXG5cbiAgLy8gVHJpbXMgdGhlIHN0cmluZyB2YWx1ZSBhbmQgY2hlY2tzIGlmIGl0IGlzIGVtcHR5IG9yIGNvbnRhaW5zIHN0cmluZ2lmaWVkXG4gIC8vIHZhbHVlcyBzdWNoIGFzIGZhbHNlLCB1bmRlZmluZWQsIG51bGwsIE5hTiwgaWYgaXQgZG9lcywgcmV0dXJucyB1bmRlZmluZWRcbiAgc3RyaW5nOiAoKSA9PlxuICAgIHpcbiAgICAgIC5zdHJpbmcoKVxuICAgICAgLnRyaW0oKVxuICAgICAgLnJlZmluZShcbiAgICAgICAgKHZhbHVlKSA9PlxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxuICAgICAgICAgIHZhbHVlID09PSAnJyxcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXG4gICAgICAgIH0pXG4gICAgICApXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXG5cbiAgLy8gQWxsb3dzIHBvc2l0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxuICAvLyBiZSB1bmRlZmluZWRcbiAgcG9zaXRpdmVOdW06ICgpID0+XG4gICAgelxuICAgICAgLnN0cmluZygpXG4gICAgICAudHJpbSgpXG4gICAgICAucmVmaW5lKFxuICAgICAgICAodmFsdWUpID0+XG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID4gMCksXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXG4gICAgICAgIH0pXG4gICAgICApXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXG5cbiAgLy8gQWxsb3dzIG5vbi1uZWdhdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXG4gIG5vbk5lZ2F0aXZlTnVtOiAoKSA9PlxuICAgIHpcbiAgICAgIC5zdHJpbmcoKVxuICAgICAgLnRyaW0oKVxuICAgICAgLnJlZmluZShcbiAgICAgICAgKHZhbHVlKSA9PlxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+PSAwKSxcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXG4gICAgICAgIH0pXG4gICAgICApXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcbn07XG5cbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XG4gIC8vIGhpZ2hjaGFydHNcbiAgSElHSENIQVJUU19WRVJTSU9OOiB6XG4gICAgLnN0cmluZygpXG4gICAgLnRyaW0oKVxuICAgIC5yZWZpbmUoXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxuICAgICAgKHZhbHVlKSA9PiAoe1xuICAgICAgICBtZXNzYWdlOiBgSElHSENIQVJUU19WRVJTSU9OIG11c3QgYmUgJ2xhdGVzdCcsIGEgbWFqb3IgdmVyc2lvbiwgb3IgaW4gdGhlIGZvcm0gWFguWVkuWlosIHJlY2VpdmVkICcke3ZhbHVlfSdgXG4gICAgICB9KVxuICAgIClcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXG4gIEhJR0hDSEFSVFNfQ0ROX1VSTDogelxuICAgIC5zdHJpbmcoKVxuICAgIC50cmltKClcbiAgICAucmVmaW5lKFxuICAgICAgKHZhbHVlKSA9PlxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwczovLycpIHx8XG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHA6Ly8nKSB8fFxuICAgICAgICB2YWx1ZSA9PT0gJycsXG4gICAgICAodmFsdWUpID0+ICh7XG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke3ZhbHVlfSdgXG4gICAgICB9KVxuICAgIClcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXG4gIEhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTOiB2LmFycmF5KHNjcmlwdHNOYW1lcy5jb3JlKSxcbiAgSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUzogdi5hcnJheShzY3JpcHRzTmFtZXMubW9kdWxlcyksXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLmluZGljYXRvcnMpLFxuICBISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIOiB2LmJvb2xlYW4oKSxcbiAgSElHSENIQVJUU19DQUNIRV9QQVRIOiB2LnN0cmluZygpLFxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxuXG4gIC8vIGV4cG9ydFxuICBFWFBPUlRfVFlQRTogdi5lbnVtKFsnanBlZycsICdwbmcnLCAncGRmJywgJ3N2ZyddKSxcbiAgRVhQT1JUX0NPTlNUUjogdi5lbnVtKFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10pLFxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcbiAgRVhQT1JUX0RFRkFVTFRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcbiAgRVhQT1JUX0RFRkFVTFRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcbiAgRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxuXG4gIC8vIGN1c3RvbVxuICBDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT046IHYuYm9vbGVhbigpLFxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxuXG4gIC8vIHNlcnZlclxuICBTRVJWRVJfRU5BQkxFOiB2LmJvb2xlYW4oKSxcbiAgU0VSVkVSX0hPU1Q6IHYuc3RyaW5nKCksXG4gIFNFUlZFUl9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxuXG4gIC8vIHNlcnZlciBwcm94eVxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcbiAgU0VSVkVSX1BST1hZX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcbiAgU0VSVkVSX1BST1hZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcblxuICAvLyBzZXJ2ZXIgcmF0ZSBsaW1pdGluZ1xuICBTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEU6IHYuYm9vbGVhbigpLFxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVk6IHYuc3RyaW5nKCksXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU46IHYuc3RyaW5nKCksXG5cbiAgLy8gc2VydmVyIHNzbFxuICBTRVJWRVJfU1NMX0VOQUJMRTogdi5ib29sZWFuKCksXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxuICBTRVJWRVJfU1NMX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcbiAgU0VSVkVSX1NTTF9DRVJUX1BBVEg6IHYuc3RyaW5nKCksXG5cbiAgLy8gcG9vbFxuICBQT09MX01JTl9XT1JLRVJTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9XT1JLX0xJTUlUOiB2LnBvc2l0aXZlTnVtKCksXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9ERVNUUk9ZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9SRUFQRVJfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxuXG4gIC8vIGxvZ2dlclxuICBMT0dHSU5HX0xFVkVMOiB6XG4gICAgLnN0cmluZygpXG4gICAgLnRyaW0oKVxuICAgIC5yZWZpbmUoXG4gICAgICAodmFsdWUpID0+XG4gICAgICAgIHZhbHVlID09PSAnJyB8fFxuICAgICAgICAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJlxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcbiAgICAgICAgICBwYXJzZUZsb2F0KHZhbHVlKSA8PSA1KSxcbiAgICAgICh2YWx1ZSkgPT4gKHtcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcbiAgICAgIH0pXG4gICAgKVxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcbiAgTE9HR0lOR19GSUxFOiB2LnN0cmluZygpLFxuICBMT0dHSU5HX0RFU1Q6IHYuc3RyaW5nKCksXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXG4gIExPR0dJTkdfVE9fRklMRTogdi5ib29sZWFuKCksXG5cbiAgLy8gdWlcbiAgVUlfRU5BQkxFOiB2LmJvb2xlYW4oKSxcbiAgVUlfUk9VVEU6IHYuc3RyaW5nKCksXG5cbiAgLy8gb3RoZXJcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxuICBPVEhFUl9OT19MT0dPOiB2LmJvb2xlYW4oKSxcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcblxuICAvLyBkZWJ1Z2dlclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxuICBERUJVR19IRUFETEVTUzogdi5ib29sZWFuKCksXG4gIERFQlVHX0RFVlRPT0xTOiB2LmJvb2xlYW4oKSxcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxuICBERUJVR19EVU1QSU86IHYuYm9vbGVhbigpLFxuICBERUJVR19TTE9XX01POiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcbn0pO1xuXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcblxuLy8gVGhlIGF2YWlsYWJsZSBjb2xvcnNcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcblxuLy8gVGhlIGRlZmF1bHQgbG9nZ2luZyBjb25maWdcbmxldCBsb2dnaW5nID0ge1xuICAvLyBGbGFncyBmb3IgbG9nZ2luZyBzdGF0dXNcbiAgdG9Db25zb2xlOiB0cnVlLFxuICB0b0ZpbGU6IGZhbHNlLFxuICBwYXRoQ3JlYXRlZDogZmFsc2UsXG4gIC8vIExvZyBsZXZlbHNcbiAgbGV2ZWxzRGVzYzogW1xuICAgIHtcbiAgICAgIHRpdGxlOiAnZXJyb3InLFxuICAgICAgY29sb3I6IGNvbG9yc1swXVxuICAgIH0sXG4gICAge1xuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcbiAgICAgIGNvbG9yOiBjb2xvcnNbMV1cbiAgICB9LFxuICAgIHtcbiAgICAgIHRpdGxlOiAnbm90aWNlJyxcbiAgICAgIGNvbG9yOiBjb2xvcnNbMl1cbiAgICB9LFxuICAgIHtcbiAgICAgIHRpdGxlOiAndmVyYm9zZScsXG4gICAgICBjb2xvcjogY29sb3JzWzNdXG4gICAgfSxcbiAgICB7XG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXG4gICAgICBjb2xvcjogY29sb3JzWzRdXG4gICAgfVxuICBdLFxuICAvLyBMb2cgbGlzdGVuZXJzXG4gIGxpc3RlbmVyczogW11cbn07XG5cbi8qKlxuICogTG9ncyB0aGUgcHJvdmlkZWQgdGV4dHMgdG8gYSBmaWxlLCBpZiBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZC4gSXQgY3JlYXRlc1xuICogdGhlIG5lY2Vzc2FyeSBkaXJlY3Rvcnkgc3RydWN0dXJlIGlmIG5vdCBhbHJlYWR5IGNyZWF0ZWQgYW5kIGFwcGVuZHMgdGhlXG4gKiBjb250ZW50LCBpbmNsdWRpbmcgYW4gb3B0aW9uYWwgcHJlZml4LCB0byB0aGUgc3BlY2lmaWVkIGxvZyBmaWxlLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nW119IHRleHRzIC0gQW4gYXJyYXkgb2YgdGV4dHMgdG8gYmUgbG9nZ2VkLlxuICogQHBhcmFtIHtzdHJpbmd9IHByZWZpeCAtIEFuIG9wdGlvbmFsIHByZWZpeCB0byBiZSBhZGRlZCB0byBlYWNoIGxvZyBlbnRyeS5cbiAqL1xuY29uc3QgbG9nVG9GaWxlID0gKHRleHRzLCBwcmVmaXgpID0+IHtcbiAgaWYgKCFsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XG4gICAgLy8gQ3JlYXRlIGlmIGRvZXMgbm90IGV4aXN0XG4gICAgIWV4aXN0c1N5bmMobG9nZ2luZy5kZXN0KSAmJiBta2RpclN5bmMobG9nZ2luZy5kZXN0KTtcblxuICAgIC8vIFdlIG5vdyBhc3N1bWUgdGhlIHBhdGggaXMgYXZhaWxhYmxlLCBlLmcuIGl0J3MgdGhlIHJlc3BvbnNpYmlsaXR5XG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cbiAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcbiAgfVxuXG4gIC8vIEFkZCB0aGUgY29udGVudCB0byBhIGZpbGVcbiAgYXBwZW5kRmlsZShcbiAgICBgJHtsb2dnaW5nLmRlc3R9JHtsb2dnaW5nLmZpbGV9YCxcbiAgICBbcHJlZml4XS5jb25jYXQodGV4dHMpLmpvaW4oJyAnKSArICdcXG4nLFxuICAgIChlcnJvcikgPT4ge1xuICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGU6ICR7ZXJyb3J9YCk7XG4gICAgICAgIGxvZ2dpbmcudG9GaWxlID0gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuICApO1xufTtcblxuLyoqXG4gKiBMb2dzIGEgbWVzc2FnZS4gQWNjZXB0cyBhIHZhcmlhYmxlIGFtb3VudCBvZiBhcmd1bWVudHMuIEFyZ3VtZW50cyBhZnRlclxuICogYGxldmVsYCB3aWxsIGJlIHBhc3NlZCBkaXJlY3RseSB0byBjb25zb2xlLmxvZywgYW5kL29yIHdpbGwgYmUgam9pbmVkXG4gKiBhbmQgYXBwZW5kZWQgdG8gdGhlIGxvZyBmaWxlLlxuICpcbiAqIEBwYXJhbSB7YW55fSBhcmdzIC0gQW4gYXJyYXkgb2YgYXJndW1lbnRzIHdoZXJlIHRoZSBmaXJzdCBpcyB0aGUgbG9nIGxldmVsXG4gKiBhbmQgdGhlIHJlc3QgYXJlIHN0cmluZ3MgdG8gYnVpbGQgYSBtZXNzYWdlIHdpdGguXG4gKi9cbmV4cG9ydCBjb25zdCBsb2cgPSAoLi4uYXJncykgPT4ge1xuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XG5cbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcbiAgY29uc3QgeyBsZXZlbHNEZXNjLCBsZXZlbCB9ID0gbG9nZ2luZztcblxuICAvLyBDaGVjayBpZiBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZSBvciBpcyBhIGJlbmNobWFyayBsb2dcbiAgaWYgKFxuICAgIG5ld0xldmVsICE9PSA1ICYmXG4gICAgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aClcbiAgKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gR2V0IHJpZCBvZiB0aGUgR01UIHRleHQgaW5mb3JtYXRpb25cbiAgY29uc3QgbmV3RGF0ZSA9IG5ldyBEYXRlKCkudG9TdHJpbmcoKS5zcGxpdCgnKCcpWzBdLnRyaW0oKTtcblxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XG4gIGNvbnN0IHByZWZpeCA9IGAke25ld0RhdGV9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xuXG4gIC8vIENhbGwgYXZhaWxhYmxlIGxvZyBsaXN0ZW5lcnNcbiAgbG9nZ2luZy5saXN0ZW5lcnMuZm9yRWFjaCgoZm4pID0+IHtcbiAgICBmbihwcmVmaXgsIHRleHRzLmpvaW4oJyAnKSk7XG4gIH0pO1xuXG4gIC8vIExvZyB0byBjb25zb2xlXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxuICAgICAgdW5kZWZpbmVkLFxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KHRleHRzKVxuICAgICk7XG4gIH1cblxuICAvLyBMb2cgdG8gZmlsZVxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcbiAgICBsb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XG4gIH1cbn07XG5cbi8qKlxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXG4gKiBjYW4gYmUgcHJvdmlkZWQuXG4gKlxuICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxvZyBsZXZlbC5cbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21NZXNzYWdlIC0gQW4gb3B0aW9uYWwgY3VzdG9tIG1lc3NhZ2UgdG8gYmUgbG9nZ2VkIGFsb25nXG4gKiB3aXRoIHRoZSBlcnJvci5cbiAqL1xuZXhwb3J0IGNvbnN0IGxvZ1dpdGhTdGFjayA9IChuZXdMZXZlbCwgZXJyb3IsIGN1c3RvbU1lc3NhZ2UpID0+IHtcbiAgLy8gR2V0IHRoZSBtYWluIG1lc3NhZ2VcbiAgY29uc3QgbWFpbk1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlIHx8IGVycm9yLm1lc3NhZ2U7XG5cbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcblxuICAvLyBDaGVjayBpZiBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZVxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gR2V0IHJpZCBvZiB0aGUgR01UIHRleHQgaW5mb3JtYXRpb25cbiAgY29uc3QgbmV3RGF0ZSA9IG5ldyBEYXRlKCkudG9TdHJpbmcoKS5zcGxpdCgnKCcpWzBdLnRyaW0oKTtcblxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XG4gIGNvbnN0IHByZWZpeCA9IGAke25ld0RhdGV9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xuXG4gIC8vIElmIHRoZSBjdXN0b21NZXNzYWdlIGV4aXN0cywgd2Ugd2FudCB0byBkaXNwbGF5IHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9XG4gICAgZXJyb3IubWVzc2FnZSAhPT0gZXJyb3Iuc3RhY2tNZXNzYWdlIHx8IGVycm9yLnN0YWNrTWVzc2FnZSA9PT0gdW5kZWZpbmVkXG4gICAgICA/IGVycm9yLnN0YWNrXG4gICAgICA6IGVycm9yLnN0YWNrLnNwbGl0KCdcXG4nKS5zbGljZSgxKS5qb2luKCdcXG4nKTtcblxuICAvLyBDb21iaW5lIGN1c3RvbSBtZXNzYWdlIG9yIGVycm9yIG1lc3NhZ2Ugd2l0aCBlcnJvciBzdGFjayBtZXNzYWdlXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlLCAnXFxuJywgc3RhY2tNZXNzYWdlXTtcblxuICAvLyBMb2cgdG8gY29uc29sZVxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcbiAgICBjb25zb2xlLmxvZy5hcHBseShcbiAgICAgIHVuZGVmaW5lZCxcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXG4gICAgICAgIG1haW5NZXNzYWdlW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcbiAgICAgICAgJ1xcbicsXG4gICAgICAgIHN0YWNrTWVzc2FnZVxuICAgICAgXSlcbiAgICApO1xuICB9XG5cbiAgLy8gQ2FsbCBhdmFpbGFibGUgbG9nIGxpc3RlbmVyc1xuICBsb2dnaW5nLmxpc3RlbmVycy5mb3JFYWNoKChmbikgPT4ge1xuICAgIGZuKHByZWZpeCwgdGV4dHMuam9pbignICcpKTtcbiAgfSk7XG5cbiAgLy8gTG9nIHRvIGZpbGVcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XG4gICAgbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xuICB9XG59O1xuXG4vKipcbiAqIFNldHMgdGhlIGxvZyBsZXZlbCB0byB0aGUgc3BlY2lmaWVkIHZhbHVlLiBMb2cgbGV2ZWxzIGFyZSAoMCA9IG5vIGxvZ2dpbmcsXG4gKiAxID0gZXJyb3IsIDIgPSB3YXJuaW5nLCAzID0gbm90aWNlLCA0ID0gdmVyYm9zZSBvciA1ID0gYmVuY2htYXJrKVxuICpcbiAqIEBwYXJhbSB7bnVtYmVyfSBuZXdMZXZlbCAtIFRoZSBuZXcgbG9nIGxldmVsIHRvIGJlIHNldC5cbiAqL1xuZXhwb3J0IGNvbnN0IHNldExvZ0xldmVsID0gKG5ld0xldmVsKSA9PiB7XG4gIGlmIChuZXdMZXZlbCA+PSAwICYmIG5ld0xldmVsIDw9IGxvZ2dpbmcubGV2ZWxzRGVzYy5sZW5ndGgpIHtcbiAgICBsb2dnaW5nLmxldmVsID0gbmV3TGV2ZWw7XG4gIH1cbn07XG5cbi8qKlxuICogRW5hYmxlcyBmaWxlIGxvZ2dpbmcgd2l0aCB0aGUgc3BlY2lmaWVkIGRlc3RpbmF0aW9uIGFuZCBsb2cgZmlsZS5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gbG9nRGVzdCAtIFRoZSBkZXN0aW5hdGlvbiBwYXRoIGZvciBsb2cgZmlsZXMuXG4gKiBAcGFyYW0ge3N0cmluZ30gbG9nRmlsZSAtIFRoZSBsb2cgZmlsZSBuYW1lLlxuICovXG5leHBvcnQgY29uc3QgZW5hYmxlRmlsZUxvZ2dpbmcgPSAobG9nRGVzdCwgbG9nRmlsZSkgPT4ge1xuICAvLyBVcGRhdGUgbG9nZ2luZyBvcHRpb25zXG4gIGxvZ2dpbmcgPSB7XG4gICAgLi4ubG9nZ2luZyxcbiAgICBkZXN0OiBsb2dEZXN0IHx8IGxvZ2dpbmcuZGVzdCxcbiAgICBmaWxlOiBsb2dGaWxlIHx8IGxvZ2dpbmcuZmlsZSxcbiAgICB0b0ZpbGU6IHRydWVcbiAgfTtcblxuICBpZiAobG9nZ2luZy5kZXN0Lmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBsb2coMSwgJ1tsb2dnZXJdIEZpbGUgbG9nZ2luZyBpbml0aWFsaXphdGlvbjogbm8gcGF0aCBzdXBwbGllZC4nKTtcbiAgfVxuXG4gIGlmICghbG9nZ2luZy5kZXN0LmVuZHNXaXRoKCcvJykpIHtcbiAgICBsb2dnaW5nLmRlc3QgKz0gJy8nO1xuICB9XG59O1xuXG4vKipcbiAqIEluaXRpYWxpemVzIGxvZ2dpbmcgd2l0aCB0aGUgc3BlY2lmaWVkIGxvZ2dpbmcgY29uZmlndXJhdGlvbi5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBUaGUgbG9nZ2luZyBjb25maWd1cmF0aW9uIG9iamVjdC5cbiAqL1xuZXhwb3J0IGNvbnN0IGluaXRMb2dnaW5nID0gKGxvZ2dpbmdPcHRpb25zKSA9PiB7XG4gIC8vIFNldCBhbGwgdGhlIGxvZ2dpbmcgb3B0aW9ucyBvbiBvdXIgbG9nZ2luZyBtb2R1bGUgb2JqZWN0XG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGxvZ2dpbmdPcHRpb25zKSkge1xuICAgIGxvZ2dpbmdba2V5XSA9IHZhbHVlO1xuICB9XG5cbiAgLy8gU2V0IHRoZSBsb2cgbGV2ZWxcbiAgc2V0TG9nTGV2ZWwobG9nZ2luZ09wdGlvbnMgJiYgcGFyc2VJbnQobG9nZ2luZ09wdGlvbnMubGV2ZWwpKTtcblxuICAvLyBTZXQgdGhlIGxvZyBmaWxlIHBhdGggYW5kIG5hbWVcbiAgaWYgKGxvZ2dpbmdPcHRpb25zICYmIGxvZ2dpbmdPcHRpb25zLmRlc3QgJiYgbG9nZ2luZ09wdGlvbnMudG9GaWxlKSB7XG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXG4gICAgICBsb2dnaW5nT3B0aW9ucy5kZXN0LFxuICAgICAgbG9nZ2luZ09wdGlvbnMuZmlsZSB8fCAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZydcbiAgICApO1xuICB9XG59O1xuXG4vKipcbiAqIEFkZHMgYSBsaXN0ZW5lciBmdW5jdGlvbiB0byB0aGUgbG9nZ2luZyBzeXN0ZW0uXG4gKlxuICogQHBhcmFtIHtmdW5jdGlvbn0gZm4gLSBUaGUgbGlzdGVuZXIgZnVuY3Rpb24gdG8gYmUgYWRkZWQuXG4gKi9cbmV4cG9ydCBjb25zdCBsaXN0ZW4gPSAoZm4pID0+IHtcbiAgbG9nZ2luZy5saXN0ZW5lcnMucHVzaChmbik7XG59O1xuXG5leHBvcnQgZGVmYXVsdCB7XG4gIGxvZyxcbiAgbG9nV2l0aFN0YWNrLFxuICBzZXRMb2dMZXZlbCxcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXG4gIGluaXRMb2dnaW5nLFxuICBsaXN0ZW5cbn07XG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcblxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XG5cbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cblxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXG5cblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cblxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCc7XG5cbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuLi9saWIvc2NoZW1hcy9jb25maWcuanMnO1xuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5cbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcblxuZXhwb3J0IGNvbnN0IF9fZGlybmFtZSA9IGZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLi4vLicsIGltcG9ydC5tZXRhLnVybCkpO1xuXG4vKipcbiAqIENsZWFycyBhbmQgc3RhbmRhcmRpemVzIHRleHQgYnkgcmVwbGFjaW5nIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2VcbiAqIGNoYXJhY3RlcnMgd2l0aCBhIHNpbmdsZSBzcGFjZSBhbmQgdHJpbW1pbmcgYW55IGxlYWRpbmcgb3IgdHJhaWxpbmdcbiAqIHdoaXRlc3BhY2UuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxuICogQHBhcmFtIHtSZWdFeHB9IFtydWxlPS9cXHNcXHMrL2ddIC0gVGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBydWxlIHRvIG1hdGNoXG4gKiBtdWx0aXBsZSBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlIGNoYXJhY3RlcnMuXG4gKiBAcGFyYW0ge3N0cmluZ30gW3JlcGxhY2VyPScgJ10gLSBUaGUgc3RyaW5nIHVzZWQgdG8gcmVwbGFjZSBtdWx0aXBsZVxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLlxuICpcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxuICovXG5leHBvcnQgY29uc3QgY2xlYXJUZXh0ID0gKHRleHQsIHJ1bGUgPSAvXFxzXFxzKy9nLCByZXBsYWNlciA9ICcgJykgPT5cbiAgdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XG5cbi8qKlxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXG4gKiBhIGNlcnRhaW4gbnVtYmVyIG9mIGF0dGVtcHRzIGFyZSByZWFjaGVkLlxuICpcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGJlIHJldHJpZWQuXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci5cbiAqIEBwYXJhbSB7Li4uYW55fSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXG4gKlxuICogQHJldHVybnMge1Byb21pc2V9IC0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIHJlc3VsdCBvZiB0aGUgZnVuY3Rpb25cbiAqIGlmIHN1Y2Nlc3NmdWwuXG4gKlxuICogQHRocm93cyB7RXJyb3J9IC0gVGhyb3dzIGFuIGVycm9yIGlmIHRoZSBtYXhpbXVtIG51bWJlciBvZiBhdHRlbXB0c1xuICogaXMgcmVhY2hlZC5cbiAqL1xuZXhwb3J0IGNvbnN0IGV4cEJhY2tvZmYgPSBhc3luYyAoZm4sIGF0dGVtcHQgPSAwLCAuLi5hcmdzKSA9PiB7XG4gIHRyeSB7XG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXG4gICAgcmV0dXJuIGF3YWl0IGZuKC4uLmFyZ3MpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xuICAgIGNvbnN0IGRlbGF5SW5NcyA9IDIgKiogYXR0ZW1wdCAqIDEwMDA7XG5cbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlYXBlYXQsIHRocm93IGFuIGVycm9yXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgZGVsYXlJbk1zKSk7XG4gICAgbG9nKFxuICAgICAgMyxcbiAgICAgIGBbcG9vbF0gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIGlkOiAke2FyZ3NbMF19LmBcbiAgICApO1xuXG4gICAgLy8gVHJ5IGFnYWluXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xuICB9XG59O1xuXG4vKipcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXG4gKiBAcGFyYW0ge3N0cmluZ30gb3V0ZmlsZSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS5cbiAqXG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIFRoZSBjb3JyZWN0ZWQgZXhwb3J0IHR5cGUuXG4gKi9cbmV4cG9ydCBjb25zdCBmaXhUeXBlID0gKHR5cGUsIG91dGZpbGUpID0+IHtcbiAgLy8gTUlNRSB0eXBlc1xuICBjb25zdCBtaW1lVHlwZXMgPSB7XG4gICAgJ2ltYWdlL3BuZyc6ICdwbmcnLFxuICAgICdpbWFnZS9qcGVnJzogJ2pwZWcnLFxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcbiAgICAnaW1hZ2Uvc3ZnK3htbCc6ICdzdmcnXG4gIH07XG5cbiAgLy8gRm9ybWF0c1xuICBjb25zdCBmb3JtYXRzID0gWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ107XG5cbiAgLy8gQ2hlY2sgaWYgdHlwZSBhbmQgb3V0ZmlsZSdzIGV4dGVuc2lvbnMgYXJlIHRoZSBzYW1lXG4gIGlmIChvdXRmaWxlKSB7XG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcblxuICAgIGlmIChvdXRUeXBlID09PSAnanBnJykge1xuICAgICAgdHlwZSA9ICdqcGVnJztcbiAgICB9IGVsc2UgaWYgKGZvcm1hdHMuaW5jbHVkZXMob3V0VHlwZSkgJiYgdHlwZSAhPT0gb3V0VHlwZSkge1xuICAgICAgdHlwZSA9IG91dFR5cGU7XG4gICAgfVxuICB9XG5cbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXG4gIHJldHVybiBtaW1lVHlwZXNbdHlwZV0gfHwgZm9ybWF0cy5maW5kKCh0KSA9PiB0ID09PSB0eXBlKSB8fCAncG5nJztcbn07XG5cbi8qKlxuICogSGFuZGxlcyBhbmQgdmFsaWRhdGVzIHJlc291cmNlcyBmb3IgZXhwb3J0LlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fHN0cmluZ30gcmVzb3VyY2VzIC0gVGhlIHJlc291cmNlcyB0byBiZSBoYW5kbGVkLiBDYW4gYmUgZWl0aGVyXG4gKiBhIEpTT04gb2JqZWN0LCBzdHJpbmdpZmllZCBKU09OIG9yIGEgcGF0aCB0byBhIEpTT04gZmlsZS5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gV2hldGhlciB0byBhbGxvdyBsb2FkaW5nIHJlc291cmNlcyBmcm9tXG4gKiBmaWxlcy5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fHVuZGVmaW5lZH0gLSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgdW5kZWZpbmVkIGlmIG5vIHZhbGlkXG4gKiByZXNvdXJjZXMgYXJlIGZvdW5kLlxuICovXG5leHBvcnQgY29uc3QgaGFuZGxlUmVzb3VyY2VzID0gKHJlc291cmNlcyA9IGZhbHNlLCBhbGxvd0ZpbGVSZXNvdXJjZXMpID0+IHtcbiAgY29uc3QgYWxsb3dlZFByb3BzID0gWydqcycsICdjc3MnLCAnZmlsZXMnXTtcblxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcbiAgbGV0IGNvcnJlY3RSZXNvdXJjZXMgPSBmYWxzZTtcblxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcbiAgaWYgKGFsbG93RmlsZVJlc291cmNlcyAmJiByZXNvdXJjZXMuZW5kc1dpdGgoJy5qc29uJykpIHtcbiAgICB0cnkge1xuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQ29ycmVjdEpTT04ocmVhZEZpbGVTeW5jKHJlc291cmNlcywgJ3V0ZjgnKSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHJldHVybiBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbY2xpXSBObyByZXNvdXJjZXMgZm91bmQuYCk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0NvcnJlY3RKU09OKHJlc291cmNlcyk7XG5cbiAgICAvLyBHZXQgcmlkIG9mIHRoZSBmaWxlcyBzZWN0aW9uXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XG4gICAgfVxuICB9XG5cbiAgLy8gRmlsdGVyIGZyb20gdW5uZWNlc3NhcnkgcHJvcGVydGllc1xuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzW3Byb3BOYW1lXTtcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICAvLyBDaGVjayBpZiBhdCBsZWFzdCBvbmUgb2YgYWxsb3dlZCBwcm9wZXJ0aWVzIGlzIHByZXNlbnRcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XG4gICAgcmV0dXJuIGxvZygzLCBgW2NsaV0gTm8gcmVzb3VyY2VzIGZvdW5kLmApO1xuICB9XG5cbiAgLy8gSGFuZGxlIGZpbGVzIHNlY3Rpb25cbiAgaWYgKGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMpIHtcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcbiAgICBpZiAoIWhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgfHwgaGFuZGxlZFJlc291cmNlcy5maWxlcy5sZW5ndGggPD0gMCkge1xuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XG4gICAgfVxuICB9XG5cbiAgLy8gUmV0dXJuIHJlc291cmNlc1xuICByZXR1cm4gaGFuZGxlZFJlc291cmNlcztcbn07XG5cbi8qKlxuICogVmFsaWRhdGVzIGFuZCBwYXJzZXMgSlNPTiBkYXRhLiBDaGVja3MgaWYgcHJvdmlkZWQgZGF0YSBpcyBvciBjYW5cbiAqIGJlIGEgY29ycmVjdCBKU09OLiBJZiBhIHByaW1pdGl2ZSBpcyBwcm92aWRlZCwgaXQgaXMgc3RyaW5naWZpZWQgYW5kIHJldHVybmVkLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fHN0cmluZ30gZGF0YSAtIFRoZSBKU09OIGRhdGEgdG8gYmUgdmFsaWRhdGVkIGFuZCBwYXJzZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvU3RyaW5nIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvblxuICogb2YgdGhlIHBhcnNlZCBKU09OLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R8c3RyaW5nfGJvb2xlYW59IC0gVGhlIHBhcnNlZCBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTixcbiAqIG9yIGZhbHNlIGlmIHZhbGlkYXRpb24gZmFpbHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0NvcnJlY3RKU09OKGRhdGEsIHRvU3RyaW5nKSB7XG4gIHRyeSB7XG4gICAgLy8gR2V0IHRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb24gaWYgbm90IGFscmVhZHkgYmVmb3JlIHBhcnNpbmdcbiAgICBjb25zdCBwYXJzZWREYXRhID0gSlNPTi5wYXJzZShcbiAgICAgIHR5cGVvZiBkYXRhICE9PSAnc3RyaW5nJyA/IEpTT04uc3RyaW5naWZ5KGRhdGEpIDogZGF0YVxuICAgICk7XG5cbiAgICAvLyBSZXR1cm4gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvbiBvZiBhIEpTT04gaWYgcmVxdWlyZWRcbiAgICBpZiAodHlwZW9mIHBhcnNlZERhdGEgIT09ICdzdHJpbmcnICYmIHRvU3RyaW5nKSB7XG4gICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkocGFyc2VkRGF0YSk7XG4gICAgfVxuXG4gICAgLy8gUmV0dXJuIGEgSlNPTlxuICAgIHJldHVybiBwYXJzZWREYXRhO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIGl0ZW0gaXMgYW4gb2JqZWN0LlxuICpcbiAqIEBwYXJhbSB7YW55fSBpdGVtIC0gVGhlIGl0ZW0gdG8gYmUgY2hlY2tlZC5cbiAqXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBpdGVtIGlzIGFuIG9iamVjdCwgZmFsc2Ugb3RoZXJ3aXNlLlxuICovXG5leHBvcnQgY29uc3QgaXNPYmplY3QgPSAoaXRlbSkgPT5cbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGl0ZW0pICYmIGl0ZW0gIT09IG51bGw7XG5cbi8qKlxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW0gLSBUaGUgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXG4gKlxuICogQHJldHVybnMge2Jvb2xlYW59IC0gVHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGVtcHR5LCBmYWxzZSBvdGhlcndpc2UuXG4gKi9cbmV4cG9ydCBjb25zdCBpc09iamVjdEVtcHR5ID0gKGl0ZW0pID0+XG4gIHR5cGVvZiBpdGVtID09PSAnb2JqZWN0JyAmJlxuICAhQXJyYXkuaXNBcnJheShpdGVtKSAmJlxuICBpdGVtICE9PSBudWxsICYmXG4gIE9iamVjdC5rZXlzKGl0ZW0pLmxlbmd0aCA9PT0gMDtcblxuLyoqXG4gKiBDaGVja3MgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCBpbiB0aGUgZ2l2ZW4gc3RyaW5nLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxuICpcbiAqIEByZXR1cm5zIHtib29sZWFufSAtIFRydWUgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCwgZmFsc2VcbiAqIG90aGVyd2lzZS5cbiAqL1xuZXhwb3J0IGNvbnN0IGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQgPSAoaXRlbSkgPT4ge1xuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT9sb2NhbGhvc3RcXGIvLFxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE3MlxcLigxWzYtOV18MlswLTldfDNbMC0xXSlcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTkyXFwuMTY4XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi9cbiAgXTtcblxuICByZXR1cm4gcmVnZXhQYXR0ZXJucy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3QoaXRlbSkpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXG4gKlxuICogQHBhcmFtIHtPYmplY3R8QXJyYXl9IG9iaiAtIFRoZSBvYmplY3Qgb3IgYXJyYXkgdG8gYmUgZGVlcGx5IGNvcGllZC5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fEFycmF5fSAtIFRoZSBkZWVwIGNvcHkgb2YgdGhlIHByb3ZpZGVkIG9iamVjdCBvciBhcnJheS5cbiAqL1xuZXhwb3J0IGNvbnN0IGRlZXBDb3B5ID0gKG9iaikgPT4ge1xuICBpZiAob2JqID09PSBudWxsIHx8IHR5cGVvZiBvYmogIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuIG9iajtcbiAgfVxuXG4gIGNvbnN0IGNvcHkgPSBBcnJheS5pc0FycmF5KG9iaikgPyBbXSA6IHt9O1xuXG4gIGZvciAoY29uc3Qga2V5IGluIG9iaikge1xuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBrZXkpKSB7XG4gICAgICBjb3B5W2tleV0gPSBkZWVwQ29weShvYmpba2V5XSk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGNvcHk7XG59O1xuXG4vKipcbiAqIENvbnZlcnRzIHRoZSBwcm92aWRlZCBvcHRpb25zIG9iamVjdCB0byBhIEpTT04tZm9ybWF0dGVkIHN0cmluZyB3aXRoIHRoZVxuICogb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RnVuY3Rpb25zIC0gSWYgc2V0IHRvIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkXG4gKiBpbiB0aGUgb3V0cHV0LlxuICpcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXG4gKi9cbmV4cG9ydCBjb25zdCBvcHRpb25zU3RyaW5naWZ5ID0gKG9wdGlvbnMsIGFsbG93RnVuY3Rpb25zKSA9PiB7XG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAobmFtZSwgdmFsdWUpID0+IHtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XG5cbiAgICAgIC8vIElmIGFsbG93RnVuY3Rpb25zIGlzIHNldCB0byB0cnVlLCBwcmVzZXJ2ZSBmdW5jdGlvbnNcbiAgICAgIGlmIChcbiAgICAgICAgKHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCcpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgnKSkgJiZcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKVxuICAgICAgKSB7XG4gICAgICAgIHZhbHVlID0gYWxsb3dGdW5jdGlvbnNcbiAgICAgICAgICA/IGBFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxufFxcdHxcXHIvZywgJyAnKX1FWFBfRlVOYFxuICAgICAgICAgIDogdW5kZWZpbmVkO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbidcbiAgICAgID8gYEVYUF9GVU4keyh2YWx1ZSArICcnKS5yZXBsYWNlQWxsKC9cXG58XFx0fFxcci9nLCAnICcpfUVYUF9GVU5gXG4gICAgICA6IHZhbHVlO1xuICB9O1xuXG4gIC8vIFN0cmluZ2lmeSBvcHRpb25zIGFuZCBpZiByZXF1aXJlZCwgcmVwbGFjZSBzcGVjaWFsIGZ1bmN0aW9ucyBtYXJrc1xuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcbiAgICAvXCJFWFBfRlVOfEVYUF9GVU5cIi9nLFxuICAgICcnXG4gICk7XG59O1xuXG4vKipcbiAqIFByaW50cyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGxvZ28gYW5kIHZlcnNpb24gaW5mb3JtYXRpb24uXG4gKlxuICogQHBhcmFtIHtib29sZWFufSBub0xvZ28gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB2ZXJzaW9uIGluZm9ybWF0aW9uIHdpdGhvdXRcbiAqIHRoZSBsb2dvLlxuICovXG5leHBvcnQgY29uc3QgcHJpbnRMb2dvID0gKG5vTG9nbykgPT4ge1xuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGVudiBvciBmcm9tIHBhY2thZ2UuanNvblxuICBjb25zdCBwYWNrYWdlVmVyc2lvbiA9IEpTT04ucGFyc2UoXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpXG4gICkudmVyc2lvbjtcblxuICAvLyBQcmludCB0ZXh0IG9ubHlcbiAgaWYgKG5vTG9nbykge1xuICAgIGNvbnNvbGUubG9nKGBTdGFydGluZyBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259Li4uYCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gUHJpbnQgdGhlIGxvZ29cbiAgY29uc29sZS5sb2coXG4gICAgcmVhZEZpbGVTeW5jKF9fZGlybmFtZSArICcvbXNnL3N0YXJ0dXAubXNnJykudG9TdHJpbmcoKS5ib2xkLnllbGxvdyxcbiAgICBgdiR7cGFja2FnZVZlcnNpb259XFxuYC5ib2xkXG4gICk7XG59O1xuXG4vKipcbiAqIFByaW50cyB0aGUgdXNhZ2UgaW5mb3JtYXRpb24gZm9yIENMSSBhcmd1bWVudHMuIElmIHJlcXVpcmVkLCBpdCBjYW4gbGlzdFxuICogcHJvcGVydGllcyByZWN1cnNpdmVseVxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRVc2FnZSgpIHtcbiAgY29uc3QgcGFkID0gNDg7XG4gIGNvbnN0IHJlYWRtZSA9ICdodHRwczovL2dpdGh1Yi5jb20vaGlnaGNoYXJ0cy9ub2RlLWV4cG9ydC1zZXJ2ZXIjcmVhZG1lJztcblxuICAvLyBEaXNwbGF5IHJlYWRtZSBpbmZvcm1hdGlvblxuICBjb25zb2xlLmxvZyhcbiAgICAnXFxuVXNhZ2Ugb2YgQ0xJIGFyZ3VtZW50czonLmJvbGQsXG4gICAgJ1xcbi0tLS0tLScsXG4gICAgYFxcbkZvciBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uLCB2aXNpdCB0aGUgcmVhZG1lIGF0OiAke3JlYWRtZS5ib2xkLnllbGxvd30uYFxuICApO1xuXG4gIGNvbnN0IGN5Y2xlQ2F0ZWdvcmllcyA9IChvcHRpb25zKSA9PiB7XG4gICAgZm9yIChjb25zdCBbbmFtZSwgb3B0aW9uXSBvZiBPYmplY3QuZW50cmllcyhvcHRpb25zKSkge1xuICAgICAgLy8gSWYgY2F0ZWdvcnkgaGFzIG1vcmUgbGV2ZWxzLCBnbyBmdXJ0aGVyXG4gICAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvcHRpb24sICd2YWx1ZScpKSB7XG4gICAgICAgIGN5Y2xlQ2F0ZWdvcmllcyhvcHRpb24pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbGV0IGRlc2NOYW1lID0gYCAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9ICR7XG4gICAgICAgICAgKCc8JyArIG9wdGlvbi50eXBlICsgJz4nKS5ncmVlblxuICAgICAgICB9IGA7XG4gICAgICAgIGlmIChkZXNjTmFtZS5sZW5ndGggPCBwYWQpIHtcbiAgICAgICAgICBmb3IgKGxldCBpID0gZGVzY05hbWUubGVuZ3RoOyBpIDwgcGFkOyBpKyspIHtcbiAgICAgICAgICAgIGRlc2NOYW1lICs9ICcuJztcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgIGRlc2NOYW1lLFxuICAgICAgICAgIG9wdGlvbi5kZXNjcmlwdGlvbixcbiAgICAgICAgICBgW0RlZmF1bHQ6ICR7b3B0aW9uLnZhbHVlLnRvU3RyaW5nKCkuYm9sZH1dYC5ibHVlXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIC8vIEN5Y2xlIHRocm91Z2ggb3B0aW9ucyBvZiBlYWNoIGNhdGVnb3JpZXMgYW5kIGRpc3BsYXkgdGhlIHVzYWdlIGluZm9cbiAgT2JqZWN0LmtleXMoZGVmYXVsdENvbmZpZykuZm9yRWFjaCgoY2F0ZWdvcnkpID0+IHtcbiAgICAvLyBPbmx5IHB1cHBldGVlciBhbmQgaGlnaGNoYXJ0cyBjYXRlZ29yaWVzIGNhbm5vdCBiZSBjb25maWd1cmVkIHRocm91Z2ggQ0xJXG4gICAgaWYgKCFbJ3B1cHBldGVlcicsICdoaWdoY2hhcnRzJ10uaW5jbHVkZXMoY2F0ZWdvcnkpKSB7XG4gICAgICBjb25zb2xlLmxvZyhgXFxuJHtjYXRlZ29yeS50b1VwcGVyQ2FzZSgpfWAucmVkKTtcbiAgICAgIGN5Y2xlQ2F0ZWdvcmllcyhkZWZhdWx0Q29uZmlnW2NhdGVnb3J5XSk7XG4gICAgfVxuICB9KTtcbiAgY29uc29sZS5sb2coJ1xcbicpO1xufVxuXG4vKipcbiAqIFJvdW5kcyBhIG51bWJlciB0byB0aGUgc3BlY2lmaWVkIHByZWNpc2lvbi5cbiAqXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXG4gKiBAcGFyYW0ge251bWJlcn0gcHJlY2lzaW9uIC0gVGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyB0byByb3VuZCB0by5cbiAqXG4gKiBAcmV0dXJucyB7bnVtYmVyfSAtIFRoZSByb3VuZGVkIG51bWJlci5cbiAqL1xuZXhwb3J0IGNvbnN0IHJvdW5kTnVtYmVyID0gKHZhbHVlLCBwcmVjaXNpb24gPSAxKSA9PiB7XG4gIGNvbnN0IG11bHRpcGxpZXIgPSBNYXRoLnBvdygxMCwgcHJlY2lzaW9uIHx8IDApO1xuICByZXR1cm4gTWF0aC5yb3VuZCgrdmFsdWUgKiBtdWx0aXBsaWVyKSAvIG11bHRpcGxpZXI7XG59O1xuXG4vKipcbiAqIENvbnZlcnRzIGEgdmFsdWUgdG8gYSBib29sZWFuLlxuICpcbiAqIEBwYXJhbSB7YW55fSBpdGVtIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCB0byBhIGJvb2xlYW4uXG4gKlxuICogQHJldHVybnMge2Jvb2xlYW59IC0gVGhlIGJvb2xlYW4gcmVwcmVzZW50YXRpb24gb2YgdGhlIGlucHV0IHZhbHVlLlxuICovXG5leHBvcnQgY29uc3QgdG9Cb29sZWFuID0gKGl0ZW0pID0+XG4gIFsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJywgJzAnLCAnJ10uaW5jbHVkZXMoaXRlbSlcbiAgICA/IGZhbHNlXG4gICAgOiAhIWl0ZW07XG5cbi8qKlxuICogV3JhcHMgY3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBpdCBzYWZlbHkuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tIGNvZGUgdG8gYmUgd3JhcHBlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gRmxhZyB0byBhbGxvdyBsb2FkaW5nIGNvZGUgZnJvbSBhIGZpbGUuXG4gKlxuICogQHJldHVybnMge3N0cmluZ3xib29sZWFufSAtIFRoZSB3cmFwcGVkIGN1c3RvbSBjb2RlIG9yIGZhbHNlIGlmIHdyYXBwaW5nXG4gKiBmYWlscy5cbiAqL1xuZXhwb3J0IGNvbnN0IHdyYXBBcm91bmQgPSAoY3VzdG9tQ29kZSwgYWxsb3dGaWxlUmVzb3VyY2VzKSA9PiB7XG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcblxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xuICAgICAgICA/IHdyYXBBcm91bmQocmVhZEZpbGVTeW5jKGN1c3RvbUNvZGUsICd1dGY4JykpXG4gICAgICAgIDogZmFsc2U7XG4gICAgfSBlbHNlIGlmIChcbiAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24oKScpIHx8XG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgpJykgfHxcbiAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpID0+JylcbiAgICApIHtcbiAgICAgIHJldHVybiBgKCR7Y3VzdG9tQ29kZX0pKClgO1xuICAgIH1cbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcbiAgfVxufTtcblxuLyoqXG4gKiBVdGlsaXR5IHRvIG1lYXN1cmUgZWxhcHNlZCB0aW1lIHVzaW5nIHRoZSBOb2RlLmpzIHByb2Nlc3MuaHJ0aW1lKCkgbWV0aG9kLlxuICpcbiAqIEByZXR1cm5zIHtmdW5jdGlvbigpOiBudW1iZXJ9IC0gQSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGVsYXBzZWQgdGltZVxuICogaW4gbWlsbGlzZWNvbmRzLlxuICovXG5leHBvcnQgY29uc3QgbWVhc3VyZVRpbWUgPSAoKSA9PiB7XG4gIGNvbnN0IHN0YXJ0ID0gcHJvY2Vzcy5ocnRpbWUuYmlnaW50KCk7XG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xufTtcblxuZXhwb3J0IGRlZmF1bHQge1xuICBfX2Rpcm5hbWUsXG4gIGNsZWFyVGV4dCxcbiAgZXhwQmFja29mZixcbiAgZml4VHlwZSxcbiAgaGFuZGxlUmVzb3VyY2VzLFxuICBpc0NvcnJlY3RKU09OLFxuICBpc09iamVjdCxcbiAgaXNPYmplY3RFbXB0eSxcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcbiAgb3B0aW9uc1N0cmluZ2lmeSxcbiAgcHJpbnRMb2dvLFxuICBwcmludFVzYWdlLFxuICByb3VuZE51bWJlcixcbiAgdG9Cb29sZWFuLFxuICB3cmFwQXJvdW5kLFxuICBtZWFzdXJlVGltZVxufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyBleGlzdHNTeW5jLCByZWFkRmlsZVN5bmMsIHByb21pc2VzIGFzIGZzUHJvbWlzZXMgfSBmcm9tICdmcyc7XG5cbmltcG9ydCBwcm9tcHRzIGZyb20gJ3Byb21wdHMnO1xuXG5pbXBvcnQge1xuICBhYnNvbHV0ZVByb3BzLFxuICBkZWZhdWx0Q29uZmlnLFxuICBuZXN0ZWRBcmdzLFxuICBwcm9tcHRzQ29uZmlnXG59IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcbmltcG9ydCB7IGRlZXBDb3B5LCBpc09iamVjdCwgcHJpbnRVc2FnZSwgdG9Cb29sZWFuIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmxldCBnZW5lcmFsT3B0aW9ucyA9IHt9O1xuXG4vKipcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgZ2VuZXJhbCBvcHRpb25zIGZvciB0aGUgZXhwb3J0IHByb2Nlc3MuXG4gKlxuICogQHJldHVybnMge09iamVjdH0gVGhlIGdlbmVyYWwgb3B0aW9ucyBvYmplY3QuXG4gKi9cbmV4cG9ydCBjb25zdCBnZXRPcHRpb25zID0gKCkgPT4gZ2VuZXJhbE9wdGlvbnM7XG5cbi8qKlxuICogSW5pdGlhbGl6ZXMgYW5kIHNldHMgdGhlIGdlbmVyYWwgb3B0aW9ucyBmb3IgdGhlIHNlcnZlciBpbnN0YWNlLCBrZWVwaW5nXG4gKiB0aGUgcHJpbmNpcGxlIG9mIHRoZSBvcHRpb25zIGxvYWQgcHJpb3JpdHkuIEl0IGFjY2VwdHMgb3B0aW9uYWwgdXNlck9wdGlvbnNcbiAqIGFuZCBhcmdzIGZyb20gdGhlIENMSS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gdXNlck9wdGlvbnMgLSBVc2VyLXByb3ZpZGVkIG9wdGlvbnMgZm9yIGN1c3RvbWl6YXRpb24uXG4gKiBAcGFyYW0ge0FycmF5fSBhcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uXG4gKiAoQ0xJIHVzYWdlKS5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnZW5lcmFsIG9wdGlvbnMgb2JqZWN0LlxuICovXG5leHBvcnQgY29uc3Qgc2V0T3B0aW9ucyA9ICh1c2VyT3B0aW9ucywgYXJncykgPT4ge1xuICAvLyBPbmx5IGZvciB0aGUgQ0xJIHVzYWdlXG4gIGlmIChhcmdzPy5sZW5ndGgpIHtcbiAgICAvLyBHZXQgdGhlIGFkZGl0aW9uYWwgb3B0aW9ucyBmcm9tIHRoZSBjdXN0b20gSlNPTiBmaWxlXG4gICAgZ2VuZXJhbE9wdGlvbnMgPSBsb2FkQ29uZmlnRmlsZShhcmdzKTtcbiAgfVxuXG4gIC8vIFVwZGF0ZSB0aGUgZGVmYXVsdCBjb25maWcgd2l0aCBhIGNvcnJlY3Qgb3B0aW9uIHZhbHVlc1xuICB1cGRhdGVEZWZhdWx0Q29uZmlnKGRlZmF1bHRDb25maWcsIGdlbmVyYWxPcHRpb25zKTtcblxuICAvLyBTZXQgdmFsdWVzIGZvciBzZXJ2ZXIncyBvcHRpb25zIGFuZCByZXR1cm5zIHRoZW1cbiAgZ2VuZXJhbE9wdGlvbnMgPSBpbml0T3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcblxuICAvLyBBcHBseSB1c2VyIG9wdGlvbnMgaWYgdGhlcmUgYXJlIGFueVxuICBpZiAodXNlck9wdGlvbnMpIHtcbiAgICAvLyBNZXJnZSB1c2VyIG9wdGlvbnNcbiAgICBnZW5lcmFsT3B0aW9ucyA9IG1lcmdlQ29uZmlnT3B0aW9ucyhcbiAgICAgIGdlbmVyYWxPcHRpb25zLFxuICAgICAgdXNlck9wdGlvbnMsXG4gICAgICBhYnNvbHV0ZVByb3BzXG4gICAgKTtcbiAgfVxuXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcbiAgaWYgKGFyZ3M/Lmxlbmd0aCkge1xuICAgIC8vIFBhaXIgcHJvdmlkZWQgYXJndW1lbnRzXG4gICAgZ2VuZXJhbE9wdGlvbnMgPSBwYWlyQXJndW1lbnRWYWx1ZShnZW5lcmFsT3B0aW9ucywgYXJncywgZGVmYXVsdENvbmZpZyk7XG4gIH1cblxuICAvLyBSZXR1cm4gZmluYWwgZ2VuZXJhbCBvcHRpb25zXG4gIHJldHVybiBnZW5lcmFsT3B0aW9ucztcbn07XG5cbi8qKlxuICogQWxsb3dzIG1hbnVhbCBjb25maWd1cmF0aW9uIGJhc2VkIG9uIHNwZWNpZmllZCBwcm9tcHRzIGFuZCBzYXZlc1xuICogdGhlIGNvbmZpZ3VyYXRpb24gdG8gYSBmaWxlLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBjb25maWdGaWxlTmFtZSAtIFRoZSBuYW1lIG9mIHRoZSBjb25maWd1cmF0aW9uIGZpbGUuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgb25jZSB0aGUgbWFudWFsXG4gKiBjb25maWd1cmF0aW9uIGlzIGNvbXBsZXRlZCBhbmQgc2F2ZWQuXG4gKi9cbmV4cG9ydCBjb25zdCBtYW51YWxDb25maWcgPSBhc3luYyAoY29uZmlnRmlsZU5hbWUpID0+IHtcbiAgLy8gUHJlcGFyZSBhIGNvbmZpZyBvYmplY3RcbiAgbGV0IGNvbmZpZ0ZpbGUgPSB7fTtcblxuICAvLyBDaGVjayBpZiBwcm92aWRlZCBjb25maWcgZmlsZSBleGlzdHNcbiAgaWYgKGV4aXN0c1N5bmMoY29uZmlnRmlsZU5hbWUpKSB7XG4gICAgY29uZmlnRmlsZSA9IEpTT04ucGFyc2UocmVhZEZpbGVTeW5jKGNvbmZpZ0ZpbGVOYW1lLCAndXRmOCcpKTtcbiAgfVxuXG4gIC8vIFF1ZXN0aW9uIGFib3V0IGEgY29uZmlndXJhdGlvbiBjYXRlZ29yeVxuICBjb25zdCBvblN1Ym1pdCA9IGFzeW5jIChwLCBjYXRlZ29yaWVzKSA9PiB7XG4gICAgbGV0IHF1ZXN0aW9uc0NvdW50ZXIgPSAwO1xuICAgIGxldCBhbGxRdWVzdGlvbnMgPSBbXTtcblxuICAgIC8vIENyZWF0ZSBhIGNvcnJlc3BvbmRpbmcgcHJvcGVydHkgaW4gdGhlIG1hbnVhbENvbmZpZyBvYmplY3RcbiAgICBmb3IgKGNvbnN0IHNlY3Rpb24gb2YgY2F0ZWdvcmllcykge1xuICAgICAgLy8gTWFyayBlYWNoIG9wdGlvbiB3aXRoIGEgc2VjdGlvblxuICAgICAgcHJvbXB0c0NvbmZpZ1tzZWN0aW9uXSA9IHByb21wdHNDb25maWdbc2VjdGlvbl0ubWFwKChvcHRpb24pID0+ICh7XG4gICAgICAgIC4uLm9wdGlvbixcbiAgICAgICAgc2VjdGlvblxuICAgICAgfSkpO1xuXG4gICAgICAvLyBDb2xsZWN0IHRoZSBxdWVzdGlvbnNcbiAgICAgIGFsbFF1ZXN0aW9ucyA9IFsuLi5hbGxRdWVzdGlvbnMsIC4uLnByb21wdHNDb25maWdbc2VjdGlvbl1dO1xuICAgIH1cblxuICAgIGF3YWl0IHByb21wdHMoYWxsUXVlc3Rpb25zLCB7XG4gICAgICBvblN1Ym1pdDogYXN5bmMgKHByb21wdCwgYW5zd2VyKSA9PiB7XG4gICAgICAgIC8vIEdldCB0aGUgZGVmYXVsdCBtb2R1bGUgc2NyaXB0c1xuICAgICAgICBpZiAocHJvbXB0Lm5hbWUgPT09ICdtb2R1bGVTY3JpcHRzJykge1xuICAgICAgICAgIGFuc3dlciA9IGFuc3dlci5sZW5ndGhcbiAgICAgICAgICAgID8gYW5zd2VyLm1hcCgobW9kdWxlKSA9PiBwcm9tcHQuY2hvaWNlc1ttb2R1bGVdKVxuICAgICAgICAgICAgOiBwcm9tcHQuY2hvaWNlcztcblxuICAgICAgICAgIGNvbmZpZ0ZpbGVbcHJvbXB0LnNlY3Rpb25dW3Byb21wdC5uYW1lXSA9IGFuc3dlcjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25maWdGaWxlW3Byb21wdC5zZWN0aW9uXSA9IHJlY3Vyc2l2ZVByb3BzKFxuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbih7fSwgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl0gfHwge30pLFxuICAgICAgICAgICAgcHJvbXB0Lm5hbWUuc3BsaXQoJy4nKSxcbiAgICAgICAgICAgIHByb21wdC5jaG9pY2VzID8gcHJvbXB0LmNob2ljZXNbYW5zd2VyXSA6IGFuc3dlclxuICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoKytxdWVzdGlvbnNDb3VudGVyID09PSBhbGxRdWVzdGlvbnMubGVuZ3RoKSB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGF3YWl0IGZzUHJvbWlzZXMud3JpdGVGaWxlKFxuICAgICAgICAgICAgICBjb25maWdGaWxlTmFtZSxcbiAgICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoY29uZmlnRmlsZSwgbnVsbCwgMiksXG4gICAgICAgICAgICAgICd1dGY4J1xuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgbG9nV2l0aFN0YWNrKFxuICAgICAgICAgICAgICAxLFxuICAgICAgICAgICAgICBlcnJvcixcbiAgICAgICAgICAgICAgYFtjb25maWddIEFuIGVycm9yIG9jY3VycmVkIHdoaWxlIGNyZWF0aW5nIHRoZSAke2NvbmZpZ0ZpbGVOYW1lfSBmaWxlLmBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfTtcblxuICAvLyBGaW5kIHRoZSBjYXRlZ29yaWVzXG4gIGNvbnN0IGNob2ljZXMgPSBPYmplY3Qua2V5cyhwcm9tcHRzQ29uZmlnKS5tYXAoKGNob2ljZSkgPT4gKHtcbiAgICB0aXRsZTogYCR7Y2hvaWNlfSBvcHRpb25zYCxcbiAgICB2YWx1ZTogY2hvaWNlXG4gIH0pKTtcblxuICAvLyBDYXRlZ29yeSBwcm9tcHRcbiAgcmV0dXJuIHByb21wdHMoXG4gICAge1xuICAgICAgdHlwZTogJ211bHRpc2VsZWN0JyxcbiAgICAgIG5hbWU6ICdjYXRlZ29yeScsXG4gICAgICBtZXNzYWdlOiAnV2hpY2ggY2F0ZWdvcnkgZG8geW91IHdhbnQgdG8gY29uZmlndXJlPycsXG4gICAgICBoaW50OiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0uJyxcbiAgICAgIGluc3RydWN0aW9uczogJycsXG4gICAgICBjaG9pY2VzXG4gICAgfSxcbiAgICB7IG9uU3VibWl0IH1cbiAgKTtcbn07XG5cbi8qKlxuICogTWFwcyBvbGQtc3RydWN0dXJlZCAoUGhhbnRvbUpTKSBvcHRpb25zIHRvIGEgbmV3IGNvbmZpZ3VyYXRpb24gZm9ybWF0XG4gKiAoUHVwcGV0ZWVyKS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIE9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnMgdG8gYmUgbWFwcGVkLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R9IE5ldyBvcHRpb25zIHN0cnVjdHVyZWQgYmFzZWQgb24gdGhlIGRlZmluZWQgbmVzdGVkQXJnc1xuICogbWFwcGluZy5cbiAqL1xuZXhwb3J0IGNvbnN0IG1hcFRvTmV3Q29uZmlnID0gKG9sZE9wdGlvbnMpID0+IHtcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xuICAvLyBDeWNsZSB0aHJvdWdoIG9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnNcbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcbiAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRBcmdzW2tleV0gPyBuZXN0ZWRBcmdzW2tleV0uc3BsaXQoJy4nKSA6IFtdO1xuXG4gICAgLy8gUG9wdWxhdGUgb2JqZWN0IGluIGNvcnJlY3QgcHJvcGVydGllcyBsZXZlbHNcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKFxuICAgICAgKG9iaiwgcHJvcCwgaW5kZXgpID0+XG4gICAgICAgIChvYmpbcHJvcF0gPVxuICAgICAgICAgIHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCA/IHZhbHVlIDogb2JqW3Byb3BdIHx8IHt9KSxcbiAgICAgIG5ld09wdGlvbnNcbiAgICApO1xuICB9XG4gIHJldHVybiBuZXdPcHRpb25zO1xufTtcblxuLyoqXG4gKiBNZXJnZXMgdHdvIHNldHMgb2YgY29uZmlndXJhdGlvbiBvcHRpb25zLCBjb25zaWRlcmluZyBhYnNvbHV0ZSBwcm9wZXJ0aWVzLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT3JpZ2luYWwgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cbiAqIEBwYXJhbSB7QXJyYXl9IGFic29sdXRlUHJvcHMgLSBMaXN0IG9mIHByb3BlcnRpZXMgdGhhdCBzaG91bGRcbiAqIG5vdCBiZSByZWN1cnNpdmVseSBtZXJnZWQuXG4gKlxuICogQHJldHVybnMge09iamVjdH0gTWVyZ2VkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cbiAqL1xuZXhwb3J0IGNvbnN0IG1lcmdlQ29uZmlnT3B0aW9ucyA9IChvcHRpb25zLCBuZXdPcHRpb25zLCBhYnNvbHV0ZVByb3BzID0gW10pID0+IHtcbiAgY29uc3QgbWVyZ2VkT3B0aW9ucyA9IGRlZXBDb3B5KG9wdGlvbnMpO1xuXG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XG4gICAgbWVyZ2VkT3B0aW9uc1trZXldID1cbiAgICAgIGlzT2JqZWN0KHZhbHVlKSAmJlxuICAgICAgIWFic29sdXRlUHJvcHMuaW5jbHVkZXMoa2V5KSAmJlxuICAgICAgbWVyZ2VkT3B0aW9uc1trZXldICE9PSB1bmRlZmluZWRcbiAgICAgICAgPyBtZXJnZUNvbmZpZ09wdGlvbnMobWVyZ2VkT3B0aW9uc1trZXldLCB2YWx1ZSwgYWJzb2x1dGVQcm9wcylcbiAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgPyB2YWx1ZVxuICAgICAgICAgIDogbWVyZ2VkT3B0aW9uc1trZXldO1xuICB9XG5cbiAgcmV0dXJuIG1lcmdlZE9wdGlvbnM7XG59O1xuXG4vKipcbiAqIEluaXRpYWxpemVzIGV4cG9ydCBzZXR0aW5ncyBiYXNlZCBvbiBwcm92aWRlZCBleHBvcnRPcHRpb25zXG4gKiBhbmQgZ2VuZXJhbE9wdGlvbnMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBPcHRpb25zIHNwZWNpZmljIHRvIHRoZSBleHBvcnQgcHJvY2Vzcy5cbiAqIEBwYXJhbSB7T2JqZWN0fSBnZW5lcmFsT3B0aW9ucyAtIEdlbmVyYWwgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R9IEluaXRpYWxpemVkIGV4cG9ydCBzZXR0aW5ncy5cbiAqL1xuZXhwb3J0IGNvbnN0IGluaXRFeHBvcnRTZXR0aW5ncyA9IChleHBvcnRPcHRpb25zLCBnZW5lcmFsT3B0aW9ucyA9IHt9KSA9PiB7XG4gIGxldCBvcHRpb25zID0ge307XG5cbiAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnKSB7XG4gICAgb3B0aW9ucyA9IGRlZXBDb3B5KGdlbmVyYWxPcHRpb25zKTtcbiAgICBvcHRpb25zLmV4cG9ydC50eXBlID0gZXhwb3J0T3B0aW9ucy50eXBlIHx8IGV4cG9ydE9wdGlvbnMuZXhwb3J0LnR5cGU7XG4gICAgb3B0aW9ucy5leHBvcnQuc2NhbGUgPSBleHBvcnRPcHRpb25zLnNjYWxlIHx8IGV4cG9ydE9wdGlvbnMuZXhwb3J0LnNjYWxlO1xuICAgIG9wdGlvbnMuZXhwb3J0Lm91dGZpbGUgPVxuICAgICAgZXhwb3J0T3B0aW9ucy5vdXRmaWxlIHx8IGV4cG9ydE9wdGlvbnMuZXhwb3J0Lm91dGZpbGU7XG4gICAgb3B0aW9ucy5wYXlsb2FkID0ge1xuICAgICAgc3ZnOiBleHBvcnRPcHRpb25zLnN2Z1xuICAgIH07XG4gIH0gZWxzZSB7XG4gICAgb3B0aW9ucyA9IG1lcmdlQ29uZmlnT3B0aW9ucyhcbiAgICAgIGdlbmVyYWxPcHRpb25zLFxuICAgICAgZXhwb3J0T3B0aW9ucyxcbiAgICAgIC8vIE9taXQgZ29pbmcgZG93biByZWN1cnNpdmVseSB3aXRoIHRoZSBiZWxvd3NcbiAgICAgIGFic29sdXRlUHJvcHNcbiAgICApO1xuICB9XG5cbiAgb3B0aW9ucy5leHBvcnQub3V0ZmlsZSA9XG4gICAgb3B0aW9ucy5leHBvcnQ/Lm91dGZpbGUgfHwgYGNoYXJ0LiR7b3B0aW9ucy5leHBvcnQ/LnR5cGUgfHwgJ3BuZyd9YDtcbiAgcmV0dXJuIG9wdGlvbnM7XG59O1xuXG4vKipcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgdXNpbmdcbiAqIHRoZSAtLWxvYWRDb25maWcgb3B0aW9uLlxuICpcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIHRvIGNoZWNrIGZvclxuICogdGhlIC0tbG9hZENvbmZpZyBvcHRpb24uXG4gKlxuICogQHJldHVybnMge09iamVjdH0gQWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGxvYWRlZCBmcm9tIHRoZSBzcGVjaWZpZWQgZmlsZSxcbiAqIG9yIGFuIGVtcHR5IG9iamVjdCBpZiBub3QgZm91bmQgb3IgaW52YWxpZC5cbiAqL1xuZnVuY3Rpb24gbG9hZENvbmZpZ0ZpbGUoYXJncykge1xuICAvLyBDaGVjayBpZiB0aGUgLS1sb2FkQ29uZmlnIG9wdGlvbiB3YXMgdXNlZFxuICBjb25zdCBjb25maWdJbmRleCA9IGFyZ3MuZmluZEluZGV4KFxuICAgIChhcmcpID0+IGFyZy5yZXBsYWNlKC8tL2csICcnKSA9PT0gJ2xvYWRDb25maWcnXG4gICk7XG5cbiAgLy8gQ2hlY2sgaWYgdGhlIC0tbG9hZENvbmZpZyBoYXMgYSB2YWx1ZVxuICBpZiAoY29uZmlnSW5kZXggPiAtMSAmJiBhcmdzW2NvbmZpZ0luZGV4ICsgMV0pIHtcbiAgICBjb25zdCBmaWxlTmFtZSA9IGFyZ3NbY29uZmlnSW5kZXggKyAxXTtcbiAgICB0cnkge1xuICAgICAgLy8gQ2hlY2sgaWYgYW4gYWRkaXRpb25hbCBjb25maWcgZmlsZSBpcyBhIGNvcnJlY3QgSlNPTiBmaWxlXG4gICAgICBpZiAoZmlsZU5hbWUgJiYgZmlsZU5hbWUuZW5kc1dpdGgoJy5qc29uJykpIHtcbiAgICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxuICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMoZmlsZU5hbWUpKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nV2l0aFN0YWNrKFxuICAgICAgICAyLFxuICAgICAgICBlcnJvcixcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7ZmlsZU5hbWV9IGZpbGUuYFxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvLyBObyBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gcmV0dXJuXG4gIHJldHVybiB7fTtcbn1cblxuLyoqXG4gKiBVcGRhdGVzIHRoZSBkZWZhdWx0IGNvbmZpZ3VyYXRpb24gb2JqZWN0IHdpdGggdmFsdWVzIGZyb20gYSBjdXN0b20gb2JqZWN0XG4gKiBhbmQgZW52aXJvbm1lbnQgdmFyaWFibGVzLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWdPYmogLSBUaGUgZGVmYXVsdCBjb25maWd1cmF0aW9uIG9iamVjdC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21PYmogLSBDdXN0b20gY29uZmlndXJhdGlvbiBvYmplY3QgdG8gb3ZlcnJpZGUgZGVmYXVsdHMuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvcENoYWluIC0gUHJvcGVydHkgY2hhaW4gZm9yIHRyYWNraW5nIG5lc3RlZCBwcm9wZXJ0aWVzXG4gKiBkdXJpbmcgcmVjdXJzaW9uLlxuICovXG5mdW5jdGlvbiB1cGRhdGVEZWZhdWx0Q29uZmlnKGNvbmZpZ09iaiwgY3VzdG9tT2JqID0ge30sIHByb3BDaGFpbiA9ICcnKSB7XG4gIE9iamVjdC5rZXlzKGNvbmZpZ09iaikuZm9yRWFjaCgoa2V5KSA9PiB7XG4gICAgY29uc3QgZW50cnkgPSBjb25maWdPYmpba2V5XTtcbiAgICBjb25zdCBjdXN0b21WYWx1ZSA9IGN1c3RvbU9iaiAmJiBjdXN0b21PYmpba2V5XTtcblxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XG4gICAgICB1cGRhdGVEZWZhdWx0Q29uZmlnKGVudHJ5LCBjdXN0b21WYWx1ZSwgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGEgY3VzdG9tIEpTT04gZXhpc3RzLCBpdCB0YWtlIHByZWNlZGVuY2VcbiAgICAgIGlmIChjdXN0b21WYWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGVudHJ5LnZhbHVlID0gY3VzdG9tVmFsdWU7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIGEgdmFsdWUgZnJvbSBhbiBlbnYgdmFyaWFibGUgZXhpc3RzLCBpdCB0YWtlIHByZWNlZGVuY2VcbiAgICAgIGlmIChlbnRyeS5lbnZMaW5rIGluIGVudnMgJiYgZW52c1tlbnRyeS5lbnZMaW5rXSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGVudHJ5LnZhbHVlID0gZW52c1tlbnRyeS5lbnZMaW5rXTtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xufVxuXG4vKipcbiAqIEluaXRpYWxpemVzIG9wdGlvbnMgb2JqZWN0IGJhc2VkIG9uIHByb3ZpZGVkIGl0ZW1zLCBzZXR0aW5nIHZhbHVlcyBmcm9tXG4gKiBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gaXRlbXMgLSBDb25maWd1cmF0aW9uIGl0ZW1zIHRvIGJlIHVzZWQgZm9yIGluaXRpYWxpemluZ1xuICogb3B0aW9ucy5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBJbml0aWFsaXplZCBvcHRpb25zIG9iamVjdC5cbiAqL1xuZnVuY3Rpb24gaW5pdE9wdGlvbnMoaXRlbXMpIHtcbiAgbGV0IG9wdGlvbnMgPSB7fTtcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoaXRlbXMpKSB7XG4gICAgb3B0aW9uc1tuYW1lXSA9IE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChpdGVtLCAndmFsdWUnKVxuICAgICAgPyBpdGVtLnZhbHVlXG4gICAgICA6IGluaXRPcHRpb25zKGl0ZW0pO1xuICB9XG4gIHJldHVybiBvcHRpb25zO1xufVxuXG4vKipcbiAqIFBhaXJzIGFyZ3VtZW50IHZhbHVlcyB3aXRoIGNvcnJlc3BvbmRpbmcgb3B0aW9ucyBpbiB0aGUgY29uZmlndXJhdGlvbixcbiAqIHVwZGF0aW5nIHRoZSBvcHRpb25zIG9iamVjdC5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBvYmplY3QgdG8gYmUgdXBkYXRlZC5cbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIGNvbnRhaW5pbmcgdmFsdWVzIGZvciBzcGVjaWZpY1xuICogb3B0aW9ucy5cbiAqIEBwYXJhbSB7T2JqZWN0fSBkZWZhdWx0Q29uZmlnIC0gRGVmYXVsdCBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgcmVmZXJlbmNlLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R9IFVwZGF0ZWQgb3B0aW9ucyBvYmplY3QuXG4gKi9cbmZ1bmN0aW9uIHBhaXJBcmd1bWVudFZhbHVlKG9wdGlvbnMsIGFyZ3MsIGRlZmF1bHRDb25maWcpIHtcbiAgbGV0IHNob3dVc2FnZSA9IGZhbHNlO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyZ3MubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBvcHRpb24gPSBhcmdzW2ldLnJlcGxhY2UoLy0vZywgJycpO1xuXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcbiAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRBcmdzW29wdGlvbl1cbiAgICAgID8gbmVzdGVkQXJnc1tvcHRpb25dLnNwbGl0KCcuJylcbiAgICAgIDogW107XG5cbiAgICAvLyBHZXQgdGhlIGNvcnJlY3QgdHlwZSBmb3IgQ0xJIGFyZ3Mgd2hpY2ggYXJlIHBhc3NlZCBhcyBzdHJpbmdzXG4gICAgbGV0IGFyZ3VtZW50VHlwZTtcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XG4gICAgICBpZiAocHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4KSB7XG4gICAgICAgIGFyZ3VtZW50VHlwZSA9IG9ialtwcm9wXS50eXBlO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcbiAgICB9LCBkZWZhdWx0Q29uZmlnKTtcblxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcbiAgICAgICAgLy8gRmluZHMgYW4gb3B0aW9uIGFuZCBzZXQgYSBjb3JyZXNwb25kaW5nIHZhbHVlXG4gICAgICAgIGlmICh0eXBlb2Ygb2JqW3Byb3BdICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgIGlmIChhcmdzWysraV0pIHtcbiAgICAgICAgICAgIGlmIChhcmd1bWVudFR5cGUgPT09ICdib29sZWFuJykge1xuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSB0b0Jvb2xlYW4oYXJnc1tpXSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gK2FyZ3NbaV07XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50VHlwZS5pbmRleE9mKCddJykgPj0gMCkge1xuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSBhcmdzW2ldLnNwbGl0KCcsJyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSBhcmdzW2ldO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBsb2coXG4gICAgICAgICAgICAgIDIsXG4gICAgICAgICAgICAgIGBbY29uZmlnXSBNaXNzaW5nIHZhbHVlIGZvciB0aGUgJyR7b3B0aW9ufScgYXJndW1lbnQuIFVzaW5nIHRoZSBkZWZhdWx0IHZhbHVlLmBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBzaG93VXNhZ2UgPSB0cnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcbiAgICB9LCBvcHRpb25zKTtcbiAgfVxuXG4gIC8vIERpc3BsYXkgdGhlIHVzYWdlIGZvciB0aGUgcmVmZXJlbmNlIGlmIG5lZWRlZFxuICBpZiAoc2hvd1VzYWdlKSB7XG4gICAgcHJpbnRVc2FnZShkZWZhdWx0Q29uZmlnKTtcbiAgfVxuXG4gIHJldHVybiBvcHRpb25zO1xufVxuXG4vKipcbiAqIFJlY3Vyc2l2ZWx5IHVwZGF0ZXMgcHJvcGVydGllcyBpbiBhbiBvYmplY3QgYmFzZWQgb24gbmVzdGVkIG5hbWVzIGFuZCBhc3NpZ25zXG4gKiB0aGUgZmluYWwgdmFsdWUuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IG9iamVjdFRvVXBkYXRlIC0gVGhlIG9iamVjdCB0byBiZSB1cGRhdGVkLlxuICogQHBhcmFtIHtBcnJheX0gbmVzdGVkTmFtZXMgLSBBcnJheSBvZiBuZXN0ZWQgcHJvcGVydHkgbmFtZXMuXG4gKiBAcGFyYW0ge2FueX0gdmFsdWUgLSBUaGUgZmluYWwgdmFsdWUgdG8gYmUgYXNzaWduZWQuXG4gKlxuICogQHJldHVybnMge09iamVjdH0gVXBkYXRlZCBvYmplY3Qgd2l0aCBhc3NpZ25lZCB2YWx1ZXMuXG4gKi9cbmZ1bmN0aW9uIHJlY3Vyc2l2ZVByb3BzKG9iamVjdFRvVXBkYXRlLCBuZXN0ZWROYW1lcywgdmFsdWUpIHtcbiAgd2hpbGUgKG5lc3RlZE5hbWVzLmxlbmd0aCA+IDEpIHtcbiAgICBjb25zdCBwcm9wTmFtZSA9IG5lc3RlZE5hbWVzLnNoaWZ0KCk7XG5cbiAgICAvLyBDcmVhdGUgYSBwcm9wZXJ0eSBpbiBvYmplY3QgaWYgaXQgZG9lc24ndCBleGlzdFxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdFRvVXBkYXRlLCBwcm9wTmFtZSkpIHtcbiAgICAgIG9iamVjdFRvVXBkYXRlW3Byb3BOYW1lXSA9IHt9O1xuICAgIH1cblxuICAgIC8vIENhbGwgZnVuY3Rpb24gYWdhaW4gaWYgdGhlcmUgc3RpbGwgbmFtZXMgdG8gZ29cbiAgICBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0gPSByZWN1cnNpdmVQcm9wcyhcbiAgICAgIE9iamVjdC5hc3NpZ24oe30sIG9iamVjdFRvVXBkYXRlW3Byb3BOYW1lXSksXG4gICAgICBuZXN0ZWROYW1lcyxcbiAgICAgIHZhbHVlXG4gICAgKTtcblxuICAgIHJldHVybiBvYmplY3RUb1VwZGF0ZTtcbiAgfVxuXG4gIC8vIEFzc2lnbiB0aGUgZmluYWwgdmFsdWVcbiAgb2JqZWN0VG9VcGRhdGVbbmVzdGVkTmFtZXNbMF1dID0gdmFsdWU7XG4gIHJldHVybiBvYmplY3RUb1VwZGF0ZTtcbn1cblxuZXhwb3J0IGRlZmF1bHQge1xuICBnZXRPcHRpb25zLFxuICBzZXRPcHRpb25zLFxuICBtYW51YWxDb25maWcsXG4gIG1hcFRvTmV3Q29uZmlnLFxuICBtZXJnZUNvbmZpZ09wdGlvbnMsXG4gIGluaXRFeHBvcnRTZXR0aW5nc1xufTtcbiIsIi8qKlxuICogVGhpcyBtb2R1bGUgZXhwb3J0cyB0d28gZnVuY3Rpb25zOiBmZXRjaCAoZm9yIEdFVCByZXF1ZXN0cykgYW5kIHBvc3QgKGZvciBQT1NUIHJlcXVlc3RzKS5cbiAqL1xuXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XG5cbi8qKlxuICogUmV0dXJucyB0aGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgYmFzZWQgb24gdGhlIHByb3ZpZGVkIFVSTC5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBkZXRlcm1pbmUgdGhlIHByb3RvY29sLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSAoaHR0cCBvciBodHRwcykuXG4gKi9cbmNvbnN0IGdldFByb3RvY29sID0gKHVybCkgPT4gKHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwKTtcblxuLyoqXG4gKiBGZXRjaGVzIGRhdGEgZnJvbSB0aGUgc3BlY2lmaWVkIFVSTCB1c2luZyBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBmZXRjaCBkYXRhIGZyb20uXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBPcHRpb25zIGZvciB0aGUgSFRUUCByZXF1ZXN0IChvcHRpb25hbCkuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIEhUVFAgcmVzcG9uc2Ugb2JqZWN0XG4gKiB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZmV0Y2godXJsLCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3QgcHJvdG9jb2wgPSBnZXRQcm90b2NvbCh1cmwpO1xuXG4gICAgcHJvdG9jb2xcbiAgICAgIC5nZXQodXJsLCByZXF1ZXN0T3B0aW9ucywgKHJlcykgPT4ge1xuICAgICAgICBsZXQgZGF0YSA9ICcnO1xuXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZC5cbiAgICAgICAgcmVzLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XG4gICAgICAgICAgZGF0YSArPSBjaHVuaztcbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkLlxuICAgICAgICByZXMub24oJ2VuZCcsICgpID0+IHtcbiAgICAgICAgICBpZiAoIWRhdGEpIHtcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmVzLnRleHQgPSBkYXRhO1xuICAgICAgICAgIHJlc29sdmUocmVzKTtcbiAgICAgICAgfSk7XG4gICAgICB9KVxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xuICAgICAgICByZWplY3QoZXJyb3IpO1xuICAgICAgfSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIFNlbmRzIGEgUE9TVCByZXF1ZXN0IHRvIHRoZSBzcGVjaWZpZWQgVVJMIHdpdGggdGhlIHByb3ZpZGVkIEpTT04gYm9keSB1c2luZ1xuICogZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gc2VuZCB0aGUgUE9TVCByZXF1ZXN0IHRvLlxuICogQHBhcmFtIHtPYmplY3R9IGJvZHkgLSBUaGUgSlNPTiBib2R5IHRvIGluY2x1ZGUgaW4gdGhlIFBPU1QgcmVxdWVzdFxuICogKG9wdGlvbmFsLCBkZWZhdWx0IGlzIGFuIGVtcHR5IG9iamVjdCkuXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBPcHRpb25zIGZvciB0aGUgSFRUUCByZXF1ZXN0IChvcHRpb25hbCkuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIEhUVFAgcmVzcG9uc2Ugb2JqZWN0IHdpdGhcbiAqIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcG9zdCh1cmwsIGJvZHkgPSB7fSwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGNvbnN0IHByb3RvY29sID0gZ2V0UHJvdG9jb2wodXJsKTtcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoYm9keSk7XG5cbiAgICAvLyBTZXQgZGVmYXVsdCBoZWFkZXJzIGFuZCBtZXJnZSB3aXRoIHJlcXVlc3RPcHRpb25zXG4gICAgY29uc3Qgb3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oXG4gICAgICB7XG4gICAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICAgICAgICAnQ29udGVudC1MZW5ndGgnOiBkYXRhLmxlbmd0aFxuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgcmVxdWVzdE9wdGlvbnNcbiAgICApO1xuXG4gICAgY29uc3QgcmVxID0gcHJvdG9jb2xcbiAgICAgIC5yZXF1ZXN0KHVybCwgb3B0aW9ucywgKHJlcykgPT4ge1xuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XG5cbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkLlxuICAgICAgICByZXMub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZC5cbiAgICAgICAgcmVzLm9uKCdlbmQnLCAoKSA9PiB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJlcy50ZXh0ID0gcmVzcG9uc2VEYXRhO1xuICAgICAgICAgICAgcmVzb2x2ZShyZXMpO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9KVxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xuICAgICAgICByZWplY3QoZXJyb3IpO1xuICAgICAgfSk7XG5cbiAgICAvLyBXcml0ZSB0aGUgcmVxdWVzdCBib2R5IGFuZCBlbmQgdGhlIHJlcXVlc3QuXG4gICAgcmVxLndyaXRlKGRhdGEpO1xuICAgIHJlcS5lbmQoKTtcbiAgfSk7XG59XG5cbmV4cG9ydCBkZWZhdWx0IGZldGNoO1xuZXhwb3J0IHsgZmV0Y2gsIHBvc3QgfTtcbiIsImNsYXNzIEV4cG9ydEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlKSB7XG4gICAgc3VwZXIoKTtcbiAgICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xuICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gbWVzc2FnZTtcbiAgfVxuXG4gIHNldEVycm9yKGVycm9yKSB7XG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xuICAgIGlmIChlcnJvci5uYW1lKSB7XG4gICAgICB0aGlzLm5hbWUgPSBlcnJvci5uYW1lO1xuICAgIH1cbiAgICBpZiAoZXJyb3Iuc3RhdHVzQ29kZSkge1xuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZTtcbiAgICB9XG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XG4gICAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XG4gICAgfVxuICAgIHJldHVybiB0aGlzO1xuICB9XG59XG5cbmV4cG9ydCBkZWZhdWx0IEV4cG9ydEVycm9yO1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbi8vIFRoZSBjYWNoZSBtYW5hZ2VyIG1hbmFnZXMgdGhlIEhpZ2hjaGFydHMgbGlicmFyeSBhbmQgaXRzIGRlcGVuZGVuY2llcy5cbi8vIFRoZSBjYWNoZSBpdHNlbGYgaXMgc3RvcmVkIGluIC5jYWNoZSwgYW5kIGlzIGNoZWNrZWQgYnkgdGhlIGNvbmZpZyBzeXN0ZW1cbi8vIGJlZm9yZSBzdGFydGluZyB0aGUgc2VydmljZVxuXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcblxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xuXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XG5pbXBvcnQgeyBmZXRjaCB9IGZyb20gJy4vZmV0Y2guanMnO1xuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XG5cbmNvbnN0IGNhY2hlID0ge1xuICBjZG5VUkw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20vJyxcbiAgYWN0aXZlTWFuaWZlc3Q6IHt9LFxuICBzb3VyY2VzOiAnJyxcbiAgaGNWZXJzaW9uOiAnJ1xufTtcblxuLyoqXG4gKiBFeHRyYWN0cyBhbmQgY2FjaGVzIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgc291cmNlcyBzdHJpbmcuXG4gKlxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBIaWdoY2hhcnRzIHZlcnNpb24uXG4gKi9cbmV4cG9ydCBjb25zdCBleHRyYWN0VmVyc2lvbiA9IChjYWNoZSkgPT4ge1xuICByZXR1cm4gY2FjaGUuc291cmNlc1xuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGUuc291cmNlcy5pbmRleE9mKCcqLycpKVxuICAgIC5yZXBsYWNlKCcvKicsICcnKVxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxuICAgIC5yZXBsYWNlKC9cXG4vZywgJycpXG4gICAgLnRyaW0oKTtcbn07XG5cbi8qKlxuICogRXh0cmFjdHMgdGhlIEhpZ2hjaGFydHMgbW9kdWxlIG5hbWUgYmFzZWQgb24gdGhlIHNjcmlwdFBhdGguXG4gKi9cbmV4cG9ydCBjb25zdCBleHRyYWN0TW9kdWxlTmFtZSA9IChzY3JpcHRQYXRoKSA9PiB7XG4gIHJldHVybiBzY3JpcHRQYXRoLnJlcGxhY2UoXG4gICAgLyguKilcXC98KC4qKW1vZHVsZXNcXC98c3RvY2tcXC8oLiopaW5kaWNhdG9yc1xcL3xtYXBzXFwvKC4qKW1vZHVsZXNcXC8vZ2ksXG4gICAgJydcbiAgKTtcbn07XG5cbi8qKlxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcbiAqIGZpbGUuXG4gKlxuICogQHBhcmFtIHtvYmplY3R9IGNvbmZpZyAtIEhpZ2hjaGFydHMtcmVsYXRlZCBjb25maWd1cmF0aW9uIG9iamVjdC5cbiAqIEBwYXJhbSB7b2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIG1hcHBlZCBuYW1lcyBvZlxuICogZmV0Y2hlZCBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gdXNlLlxuICpcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgYW4gZXJyb3Igb2NjdXJzIHdoaWxlIHdyaXRpbmdcbiAqIHRoZSBjYWNoZSBtYW5pZmVzdC5cbiAqL1xuZXhwb3J0IGNvbnN0IHNhdmVDb25maWdUb01hbmlmZXN0ID0gYXN5bmMgKGNvbmZpZywgZmV0Y2hlZE1vZHVsZXMpID0+IHtcbiAgY29uc3QgbmV3TWFuaWZlc3QgPSB7XG4gICAgdmVyc2lvbjogY29uZmlnLnZlcnNpb24sXG4gICAgbW9kdWxlczogZmV0Y2hlZE1vZHVsZXMgfHwge31cbiAgfTtcblxuICAvLyBVcGRhdGUgY2FjaGUgb2JqZWN0IHdpdGggdGhlIGN1cnJlbnQgbW9kdWxlc1xuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xuXG4gIGxvZygzLCAnW2NhY2hlXSBXcml0aW5nIGEgbmV3IG1hbmlmZXN0LicpO1xuICB0cnkge1xuICAgIHdyaXRlRmlsZVN5bmMoXG4gICAgICBqb2luKF9fZGlybmFtZSwgY29uZmlnLmNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKSxcbiAgICAgIEpTT04uc3RyaW5naWZ5KG5ld01hbmlmZXN0KSxcbiAgICAgICd1dGY4J1xuICAgICk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2FjaGVdIEVycm9yIHdyaXRpbmcgdGhlIGNhY2hlIG1hbmlmZXN0LicpLnNldEVycm9yKFxuICAgICAgZXJyb3JcbiAgICApO1xuICB9XG59O1xuXG4vKipcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBmZXRjaGVkTW9kdWxlcyBhY2NvcmRpbmdseS5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0IC0gQSBwYXRoIHRvIHNjcmlwdCB0byBnZXQuXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxuICogdG8gdXNlIGZvciBhIHJlcXVlc3QuXG4gKiBAcGFyYW0ge09iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHNcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHNob3VsZFRocm93RXJyb3IgLSBBIGZsYWcgdG8gaW5kaWNhdGUgaWYgdGhlIGVycm9yIHNob3VsZCBiZVxuICogdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgdGV4dCByZXByZXNlbnRhdGlvblxuICogb2YgdGhlIGZldGNoZWQgc2NyaXB0LlxuICpcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgdGhlcmUgaXMgYSBwcm9ibGVtIHdpdGhcbiAqIGZldGNoaW5nIHRoZSBzY3JpcHQuXG4gKi9cbmV4cG9ydCBjb25zdCBmZXRjaEFuZFByb2Nlc3NTY3JpcHQgPSBhc3luYyAoXG4gIHNjcmlwdCxcbiAgcmVxdWVzdE9wdGlvbnMsXG4gIGZldGNoZWRNb2R1bGVzLFxuICBzaG91bGRUaHJvd0Vycm9yID0gZmFsc2VcbikgPT4ge1xuICAvLyBHZXQgcmlkIG9mIHRoZSAuanMgZnJvbSB0aGUgY3VzdG9tIHN0cmluZ3NcbiAgaWYgKHNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcbiAgICBzY3JpcHQgPSBzY3JpcHQuc3Vic3RyaW5nKDAsIHNjcmlwdC5sZW5ndGggLSAzKTtcbiAgfVxuXG4gIGxvZyg0LCBgW2NhY2hlXSBGZXRjaGluZyBzY3JpcHQgLSAke3NjcmlwdH0uanNgKTtcblxuICAvLyBGZXRjaCB0aGUgc2NyaXB0XG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goYCR7c2NyaXB0fS5qc2AsIHJlcXVlc3RPcHRpb25zKTtcblxuICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXG4gIGlmIChyZXNwb25zZS5zdGF0dXNDb2RlID09PSAyMDAgJiYgdHlwZW9mIHJlc3BvbnNlLnRleHQgPT0gJ3N0cmluZycpIHtcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcbiAgICAgIGNvbnN0IG1vZHVsZU5hbWUgPSBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHQpO1xuICAgICAgZmV0Y2hlZE1vZHVsZXNbbW9kdWxlTmFtZV0gPSAxO1xuICAgIH1cblxuICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xuICB9XG5cbiAgaWYgKHNob3VsZFRocm93RXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICBgQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbiAoc3RhdHVzIGNvZGU6ICR7cmVzcG9uc2Uuc3RhdHVzQ29kZX0pLmBcbiAgICApLnNldEVycm9yKHJlc3BvbnNlKTtcbiAgfSBlbHNlIHtcbiAgICBsb2coXG4gICAgICAyLFxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXG4gICAgKTtcbiAgfVxuXG4gIHJldHVybiAnJztcbn07XG5cbi8qKlxuICogRmV0Y2hlcyBIaWdoY2hhcnRzIHNjcmlwdHMgYW5kIGN1c3RvbVNjcmlwdHMgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gY29yZVNjcmlwdHMgLSBBcnJheSBvZiBIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBtb2R1bGVTY3JpcHRzIC0gQXJyYXkgb2YgSGlnaGNoYXJ0cyBtb2R1bGVzIHRvIGZldGNoLlxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbVNjcmlwdHMgLSBBcnJheSBvZiBjdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoXG4gKiAoZnVsbCBVUkxzKS5cbiAqIEBwYXJhbSB7b2JqZWN0fSBwcm94eU9wdGlvbnMgLSBPcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnQgdG8gdXNlIGZvclxuICogYSByZXF1ZXN0LlxuICogQHBhcmFtIHtvYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IFRoZSBmZXRjaGVkIHNjcmlwdHMgY29udGVudCBqb2luZWQuXG4gKi9cbmV4cG9ydCBjb25zdCBmZXRjaFNjcmlwdHMgPSBhc3luYyAoXG4gIGNvcmVTY3JpcHRzLFxuICBtb2R1bGVTY3JpcHRzLFxuICBjdXN0b21TY3JpcHRzLFxuICBwcm94eU9wdGlvbnMsXG4gIGZldGNoZWRNb2R1bGVzXG4pID0+IHtcbiAgLy8gQ29uZmlndXJlIHByb3h5IGlmIGV4aXN0c1xuICBsZXQgcHJveHlBZ2VudDtcbiAgY29uc3QgcHJveHlIb3N0ID0gcHJveHlPcHRpb25zLmhvc3Q7XG4gIGNvbnN0IHByb3h5UG9ydCA9IHByb3h5T3B0aW9ucy5wb3J0O1xuXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxuICBpZiAocHJveHlIb3N0ICYmIHByb3h5UG9ydCkge1xuICAgIHRyeSB7XG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XG4gICAgICAgIGhvc3Q6IHByb3h5SG9zdCxcbiAgICAgICAgcG9ydDogcHJveHlQb3J0XG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2FjaGVdIENvdWxkIG5vdCBjcmVhdGUgYSBQcm94eSBBZ2VudC4nKS5zZXRFcnJvcihcbiAgICAgICAgZXJyb3JcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLy8gSWYgZXhpc3RzLCBhZGQgcHJveHkgYWdlbnQgdG8gcmVxdWVzdCBvcHRpb25zXG4gIGNvbnN0IHJlcXVlc3RPcHRpb25zID0gcHJveHlBZ2VudFxuICAgID8ge1xuICAgICAgICBhZ2VudDogcHJveHlBZ2VudCxcbiAgICAgICAgdGltZW91dDogZW52cy5TRVJWRVJfUFJPWFlfVElNRU9VVFxuICAgICAgfVxuICAgIDoge307XG5cbiAgY29uc3QgYWxsRmV0Y2hQcm9taXNlcyA9IFtcbiAgICAuLi5jb3JlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxuICAgICksXG4gICAgLi4ubW9kdWxlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzKVxuICAgICksXG4gICAgLi4uY3VzdG9tU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMpXG4gICAgKVxuICBdO1xuXG4gIGNvbnN0IGZldGNoZWRTY3JpcHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoYWxsRmV0Y2hQcm9taXNlcyk7XG4gIHJldHVybiBmZXRjaGVkU2NyaXB0cy5qb2luKCc7XFxuJyk7XG59O1xuXG4vKipcbiAqIFVwZGF0ZXMgdGhlIGxvY2FsIGNhY2hlIHdpdGggSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCB0aGVpciB2ZXJzaW9ucy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIE9iamVjdCBjb250YWluaW5nIGFsbCBvcHRpb25zLlxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPG9iamVjdD59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gb2JqZWN0IHJlcHJlc2VudGluZ1xuICogdGhlIGZldGNoZWQgbW9kdWxlcy5cbiAqXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGFuIGlzc3VlIHVwZGF0aW5nXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cbiAqL1xuZXhwb3J0IGNvbnN0IHVwZGF0ZUNhY2hlID0gYXN5bmMgKFxuICBoaWdoY2hhcnRzT3B0aW9ucyxcbiAgcHJveHlPcHRpb25zLFxuICBzb3VyY2VQYXRoXG4pID0+IHtcbiAgY29uc3QgdmVyc2lvbiA9IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb247XG4gIGNvbnN0IGhjVmVyc2lvbiA9IHZlcnNpb24gPT09ICdsYXRlc3QnIHx8ICF2ZXJzaW9uID8gJycgOiBgJHt2ZXJzaW9ufS9gO1xuICBjb25zdCBjZG5VUkwgPSBoaWdoY2hhcnRzT3B0aW9ucy5jZG5VUkwgfHwgY2FjaGUuY2RuVVJMO1xuXG4gIGxvZyhcbiAgICAzLFxuICAgIGBbY2FjaGVdIFVwZGF0aW5nIGNhY2hlIHZlcnNpb24gdG8gSGlnaGNoYXJ0czogJHtoY1ZlcnNpb24gfHwgJ2xhdGVzdCd9LmBcbiAgKTtcblxuICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xuICB0cnkge1xuICAgIGNhY2hlLnNvdXJjZXMgPSBhd2FpdCBmZXRjaFNjcmlwdHMoXG4gICAgICBbXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoYykgPT4gYCR7Y2RuVVJMfSR7aGNWZXJzaW9ufSR7Y31gKVxuICAgICAgXSxcbiAgICAgIFtcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMubW9kdWxlU2NyaXB0cy5tYXAoKG0pID0+XG4gICAgICAgICAgbSA9PT0gJ21hcCdcbiAgICAgICAgICAgID8gYCR7Y2RuVVJMfW1hcHMvJHtoY1ZlcnNpb259bW9kdWxlcy8ke219YFxuICAgICAgICAgICAgOiBgJHtjZG5VUkx9JHtoY1ZlcnNpb259bW9kdWxlcy8ke219YFxuICAgICAgICApLFxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5pbmRpY2F0b3JTY3JpcHRzLm1hcChcbiAgICAgICAgICAoaSkgPT4gYCR7Y2RuVVJMfXN0b2NrLyR7aGNWZXJzaW9ufWluZGljYXRvcnMvJHtpfWBcbiAgICAgICAgKVxuICAgICAgXSxcbiAgICAgIGhpZ2hjaGFydHNPcHRpb25zLmN1c3RvbVNjcmlwdHMsXG4gICAgICBwcm94eU9wdGlvbnMsXG4gICAgICBmZXRjaGVkTW9kdWxlc1xuICAgICk7XG5cbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XG5cbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcbiAgICByZXR1cm4gZmV0Y2hlZE1vZHVsZXM7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgJ1tjYWNoZV0gVW5hYmxlIHRvIHVwZGF0ZSB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS4nXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XG4gIH1cbn07XG5cbi8qKlxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xuICogdGhlIGNhY2hlIGZvciB0aGUgbmV3IHZlcnNpb24uXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IG5ld1ZlcnNpb24gLSBUaGUgbmV3IEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSBhcHBsaWVkLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPChvYmplY3R8Ym9vbGVhbik+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSB1cGRhdGVkXG4gKiBjb25maWd1cmF0aW9uIHdpdGggdGhlIG5ldyB2ZXJzaW9uLCBvciBmYWxzZSBpZiBubyBhcHBsaWVkIGNvbmZpZ3VyYXRpb25cbiAqIGV4aXN0cy5cbiAqL1xuZXhwb3J0IGNvbnN0IHVwZGF0ZVZlcnNpb24gPSBhc3luYyAobmV3VmVyc2lvbikgPT4ge1xuICBjb25zdCBvcHRpb25zID0gZ2V0T3B0aW9ucygpO1xuICBpZiAob3B0aW9ucz8uaGlnaGNoYXJ0cykge1xuICAgIG9wdGlvbnMuaGlnaGNoYXJ0cy52ZXJzaW9uID0gbmV3VmVyc2lvbjtcbiAgfVxuICBhd2FpdCBjaGVja0FuZFVwZGF0ZUNhY2hlKG9wdGlvbnMpO1xufTtcblxuLyoqXG4gKiBDaGVja3MgdGhlIGNhY2hlIGZvciBIaWdoY2hhcnRzIGRlcGVuZGVuY2llcywgdXBkYXRlcyB0aGUgY2FjaGUgaWYgbmVlZGVkLFxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT2JqZWN0IGNvbnRhaW5pbmcgYWxsIG9wdGlvbnMuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGNhY2hlIGlzIGNoZWNrZWRcbiAqIGFuZCB1cGRhdGVkLlxuICpcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgdGhlcmUgaXMgYW4gaXNzdWUgdXBkYXRpbmdcbiAqIG9yIHJlYWRpbmcgdGhlIGNhY2hlLlxuICovXG5leHBvcnQgY29uc3QgY2hlY2tBbmRVcGRhdGVDYWNoZSA9IGFzeW5jIChvcHRpb25zKSA9PiB7XG4gIGNvbnN0IHsgaGlnaGNoYXJ0cywgc2VydmVyIH0gPSBvcHRpb25zO1xuICBjb25zdCBjYWNoZVBhdGggPSBqb2luKF9fZGlybmFtZSwgaGlnaGNoYXJ0cy5jYWNoZVBhdGgpO1xuXG4gIGxldCBmZXRjaGVkTW9kdWxlcztcbiAgLy8gUHJlcGFyZSBwYXRocyB0byBtYW5pZmVzdCBhbmQgc291cmNlcyBmcm9tIHRoZSAuY2FjaGUgZm9sZGVyXG4gIGNvbnN0IG1hbmlmZXN0UGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnbWFuaWZlc3QuanNvbicpO1xuICBjb25zdCBzb3VyY2VQYXRoID0gam9pbihjYWNoZVBhdGgsICdzb3VyY2VzLmpzJyk7XG5cbiAgLy8gQ3JlYXRlIHRoZSBjYWNoZSBkZXN0aW5hdGlvbiBpZiBpdCBkb2Vzbid0IGV4aXN0IGFscmVhZHlcbiAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoKTtcblxuICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIG1hbmlmZXN0Lmpzb24gZG9lcyBub3QgZXhpc3RcbiAgLy8gb3IgaWYgdGhlIGZvcmNlRmV0Y2ggb3B0aW9uIGlzIGVuYWJsZWRcbiAgaWYgKCFleGlzdHNTeW5jKG1hbmlmZXN0UGF0aCkgfHwgaGlnaGNoYXJ0cy5mb3JjZUZldGNoKSB7XG4gICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xuICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgdXBkYXRlQ2FjaGUoaGlnaGNoYXJ0cywgc2VydmVyLnByb3h5LCBzb3VyY2VQYXRoKTtcbiAgfSBlbHNlIHtcbiAgICBsZXQgcmVxdWVzdFVwZGF0ZSA9IGZhbHNlO1xuXG4gICAgLy8gUmVhZCB0aGUgbWFuaWZlc3QgSlNPTlxuICAgIGNvbnN0IG1hbmlmZXN0ID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMobWFuaWZlc3RQYXRoKSk7XG5cbiAgICAvLyBDaGVjayBpZiB0aGUgbW9kdWxlcyBpcyBhbiBhcnJheSwgaWYgc28sIHdlIHJld3JpdGUgaXQgdG8gYSBtYXAgdG8gbWFrZVxuICAgIC8vIGl0IGVhc2llciB0byByZXNvbHZlIG1vZHVsZXMuXG4gICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xuICAgICAgY29uc3QgbW9kdWxlTWFwID0ge307XG4gICAgICBtYW5pZmVzdC5tb2R1bGVzLmZvckVhY2goKG0pID0+IChtb2R1bGVNYXBbbV0gPSAxKSk7XG4gICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xuICAgIH1cblxuICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9IGhpZ2hjaGFydHM7XG4gICAgY29uc3QgbnVtYmVyT2ZNb2R1bGVzID1cbiAgICAgIGNvcmVTY3JpcHRzLmxlbmd0aCArIG1vZHVsZVNjcmlwdHMubGVuZ3RoICsgaW5kaWNhdG9yU2NyaXB0cy5sZW5ndGg7XG5cbiAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgaGlnaGNoYXJ0cyBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXG4gICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcbiAgICAvLyBhbmQgYmFrZSB0aGVtIGludG8gYSBnaWFudCBibG9iLiBTYXZlIHRoZSBibG9iLlxuICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBoaWdoY2hhcnRzLnZlcnNpb24pIHtcbiAgICAgIGxvZyhcbiAgICAgICAgMixcbiAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcbiAgICAgICk7XG4gICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcbiAgICB9IGVsc2UgaWYgKE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzKSB7XG4gICAgICBsb2coXG4gICAgICAgIDIsXG4gICAgICAgICdbY2FjaGVdIFRoZSBjYWNoZSBhbmQgdGhlIHJlcXVlc3RlZCBtb2R1bGVzIGRvIG5vdCBtYXRjaCwgbmVlZCB0byByZS1mZXRjaC4nXG4gICAgICApO1xuICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIENoZWNrIGVhY2ggbW9kdWxlLCBpZiBhbnl0aGluZyBpcyBtaXNzaW5nIHJlZmV0Y2ggZXZlcnl0aGluZ1xuICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XG4gICAgICAgIGlmICghbWFuaWZlc3QubW9kdWxlc1ttb2R1bGVOYW1lXSkge1xuICAgICAgICAgIGxvZyhcbiAgICAgICAgICAgIDIsXG4gICAgICAgICAgICBgW2NhY2hlXSBUaGUgJHttb2R1bGVOYW1lfSBpcyBtaXNzaW5nIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC5gXG4gICAgICAgICAgKTtcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgaWYgKHJlcXVlc3RVcGRhdGUpIHtcbiAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgdXBkYXRlQ2FjaGUoaGlnaGNoYXJ0cywgc2VydmVyLnByb3h5LCBzb3VyY2VQYXRoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgbG9nKDMsICdbY2FjaGVdIERlcGVuZGVuY3kgY2FjaGUgaXMgdXAgdG8gZGF0ZSwgcHJvY2VlZGluZy4nKTtcblxuICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xuICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xuXG4gICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBtYW5pZmVzdC5tb2R1bGVzO1xuXG4gICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XG4gICAgfVxuICB9XG5cbiAgLy8gRmluYWxseSwgc2F2ZSB0aGUgbmV3IG1hbmlmZXN0LCB3aGljaCBpcyBiYXNpY2FsbHkgb3VyIGN1cnJlbnQgY29uZmlnXG4gIC8vIGluIGEgc2xpZ2h0bHkgZGlmZmVyZW50IGZvcm1hdFxuICBhd2FpdCBzYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzLCBmZXRjaGVkTW9kdWxlcyk7XG59O1xuXG5leHBvcnQgY29uc3QgZ2V0Q2FjaGVQYXRoID0gKCkgPT5cbiAgam9pbihfX2Rpcm5hbWUsIGdldE9wdGlvbnMoKS5oaWdoY2hhcnRzLmNhY2hlUGF0aCk7XG5cbmV4cG9ydCBjb25zdCBnZXRDYWNoZSA9ICgpID0+IGNhY2hlO1xuXG5leHBvcnQgY29uc3QgaGlnaGNoYXJ0cyA9ICgpID0+IGNhY2hlLnNvdXJjZXM7XG5cbmV4cG9ydCBjb25zdCB2ZXJzaW9uID0gKCkgPT4gY2FjaGUuaGNWZXJzaW9uO1xuXG5leHBvcnQgZGVmYXVsdCB7XG4gIGNoZWNrQW5kVXBkYXRlQ2FjaGUsXG4gIGdldENhY2hlUGF0aCxcbiAgdXBkYXRlVmVyc2lvbixcbiAgZ2V0Q2FjaGUsXG4gIGhpZ2hjaGFydHMsXG4gIHZlcnNpb25cbn07XG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcblxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XG5cbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cblxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXG5cblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cblxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuLyogZXNsaW50LWRpc2FibGUgbm8tdW5kZWYgKi9cblxuLyoqXG4gKiBTZXR0aW5nIHRoZSBhbmltT2JqZWN0LiBDYWxsZWQgd2hlbiBpbml0aW5nIHRoZSBwYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xuICBIaWdoY2hhcnRzLmFuaW1PYmplY3QgPSBmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuIHsgZHVyYXRpb246IDAgfTtcbiAgfTtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIHRoZSBhY3R1YWwgY2hhcnQuXG4gKlxuICogQHBhcmFtIHtvYmplY3R9IGNoYXJ0T3B0aW9ucyAtIFRoZSBvcHRpb25zIGZvciB0aGUgSGlnaGNoYXJ0cyBjaGFydC5cbiAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gVGhlIGV4cG9ydCBvcHRpb25zLlxuICogQHBhcmFtIHtib29sZWFufSBkaXNwbGF5RXJyb3JzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciB0byBkaXNwbGF5IGVycm9ycy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHRyaWdnZXJFeHBvcnQoY2hhcnRPcHRpb25zLCBvcHRpb25zLCBkaXNwbGF5RXJyb3JzKSB7XG4gIC8vIERpc3BsYXkgZXJyb3JzIGZsYWcgdGFrZW4gZnJvbSBjaGFydCBvcHRpb25zIG5hZCBkZWJ1Z2dlciBtb2R1bGVcbiAgd2luZG93Ll9kaXNwbGF5RXJyb3JzID0gZGlzcGxheUVycm9ycztcblxuICAvLyBHZXQgcmVxdWlyZWQgZnVuY3Rpb25zXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgbWVyZ2UsIHNldE9wdGlvbnMsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XG5cbiAgLy8gQ3JlYXRlIGEgc2VwYXJhdGUgb2JqZWN0IGZvciBhIHBvdGVudGlhbCBzZXRPcHRpb25zIHVzYWdlcyBpbiBvcmRlciB0b1xuICAvLyBwcmV2ZW50IGZyb20gcG9sbHV0aW5nIG90aGVyIGV4cG9ydHMgdGhhdCBjYW4gaGFwcGVuIG9uIHRoZSBzYW1lIHBhZ2VcbiAgSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqID0gbWVyZ2UoZmFsc2UsIHt9LCBnZXRPcHRpb25zKCkpO1xuXG4gIC8vIFRyaWdnZXIgY3VzdG9tIGNvZGVcbiAgaWYgKG9wdGlvbnMuY3VzdG9tTG9naWMuY3VzdG9tQ29kZSkge1xuICAgIG5ldyBGdW5jdGlvbihvcHRpb25zLmN1c3RvbUxvZ2ljLmN1c3RvbUNvZGUpKCk7XG4gIH1cblxuICAvLyBCeSBkZWZhdWx0IGFuaW1hdGlvbiBpcyBkaXNhYmxlZFxuICBjb25zdCBjaGFydCA9IHtcbiAgICBhbmltYXRpb246IGZhbHNlXG4gIH07XG5cbiAgLy8gV2hlbiBzdHJhaWdodCBpbmplY3QsIHRoZSBzaXplIGlzIHNldCB0aHJvdWdoIENTUyBvbmx5XG4gIGlmIChvcHRpb25zLmV4cG9ydC5zdHJJbmopIHtcbiAgICBjaGFydC5oZWlnaHQgPSBjaGFydE9wdGlvbnMuY2hhcnQuaGVpZ2h0O1xuICAgIGNoYXJ0LndpZHRoID0gY2hhcnRPcHRpb25zLmNoYXJ0LndpZHRoO1xuICB9XG5cbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XG4gIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gZmFsc2U7XG4gIHdyYXAoSGlnaGNoYXJ0cy5DaGFydC5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIHVzZXJPcHRpb25zLCBjYikge1xuICAgIC8vIE92ZXJyaWRlIHVzZXJPcHRpb25zIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xuICAgIHVzZXJPcHRpb25zID0gbWVyZ2UodXNlck9wdGlvbnMsIHtcbiAgICAgIGV4cG9ydGluZzoge1xuICAgICAgICBlbmFibGVkOiBmYWxzZVxuICAgICAgfSxcbiAgICAgIHBsb3RPcHRpb25zOiB7XG4gICAgICAgIHNlcmllczoge1xuICAgICAgICAgIGxhYmVsOiB7XG4gICAgICAgICAgICBlbmFibGVkOiBmYWxzZVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB1c2VyT3B0aW9ucyB3aGVuIGZvckV4cG9ydCBpcyB0cnVlLlxuICAgICAgICBodHRwczovL2dpdGh1Yi5jb20vaGlnaGNoYXJ0cy9oaWdoY2hhcnRzL2Jsb2IvM2FkNDMwYTM1M2I4MDU2YjllNzY0YWE0ZTVjZDY4MjhhYTQ3OWRiMi9qcy9wYXJ0cy9DaGFydC5qcyNMMjQxXG4gICAgICAgICovXG4gICAgICB0b29sdGlwOiB7fVxuICAgIH0pO1xuXG4gICAgKHVzZXJPcHRpb25zLnNlcmllcyB8fCBbXSkuZm9yRWFjaChmdW5jdGlvbiAoc2VyaWVzKSB7XG4gICAgICBzZXJpZXMuYW5pbWF0aW9uID0gZmFsc2U7XG4gICAgfSk7XG5cbiAgICAvLyBBZGQgZmxhZyB0byBrbm93IGlmIGNoYXJ0IHJlbmRlciBoYXMgYmVlbiBjYWxsZWQuXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XG4gICAgICB3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyID0gSGlnaGNoYXJ0cy5hZGRFdmVudCh0aGlzLCAncmVuZGVyJywgKCkgPT4ge1xuICAgICAgICB3aW5kb3cuaXNSZW5kZXJDb21wbGV0ZSA9IHRydWU7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcbiAgfSk7XG5cbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbY2hhcnQsIG9wdGlvbnNdKTtcbiAgfSk7XG5cbiAgLy8gR2V0IHRoZSB1c2VyIG9wdGlvbnNcbiAgY29uc3QgdXNlck9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydC5zdHJJbmpcbiAgICA/IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7b3B0aW9ucy5leHBvcnQuc3RySW5qfWApKClcbiAgICA6IGNoYXJ0T3B0aW9ucztcblxuICAvLyBNZXJnZSB0aGUgZ2xvYmFsT3B0aW9ucywgdGhlbWVPcHRpb25zLCBvcHRpb25zIGZyb20gdGhlIHdyYXBwZWRcbiAgLy8gc2V0T3B0aW9ucyBmdW5jdGlvbiBhbmQgdXNlciBvcHRpb25zIHRvIGNyZWF0ZSB0aGUgZmluYWwgb3B0aW9ucyBvYmplY3RcbiAgY29uc3QgZmluYWxPcHRpb25zID0gbWVyZ2UoXG4gICAgZmFsc2UsXG4gICAgSlNPTi5wYXJzZShvcHRpb25zLmV4cG9ydC50aGVtZU9wdGlvbnMpLFxuICAgIHVzZXJPcHRpb25zLFxuICAgIC8vIFBsYWNlZCBpdCBoZXJlIGluc3RlYWQgaW4gdGhlIGluaXQgYmVjYXVzZSBvZiB0aGUgc2l6ZSBpc3N1ZXNcbiAgICB7IGNoYXJ0IH1cbiAgKTtcblxuICBjb25zdCBmaW5hbENhbGxiYWNrID0gb3B0aW9ucy5jdXN0b21Mb2dpYy5jYWxsYmFja1xuICAgID8gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtvcHRpb25zLmN1c3RvbUxvZ2ljLmNhbGxiYWNrfWApKClcbiAgICA6IHVuZGVmaW5lZDtcblxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XG4gIGNvbnN0IGdsb2JhbE9wdGlvbnMgPSBKU09OLnBhcnNlKG9wdGlvbnMuZXhwb3J0Lmdsb2JhbE9wdGlvbnMpO1xuICBpZiAoZ2xvYmFsT3B0aW9ucykge1xuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XG4gIH1cblxuICBIaWdoY2hhcnRzW29wdGlvbnMuZXhwb3J0LmNvbnN0ciB8fCAnY2hhcnQnXShcbiAgICAnY29udGFpbmVyJyxcbiAgICBmaW5hbE9wdGlvbnMsXG4gICAgZmluYWxDYWxsYmFja1xuICApO1xuXG4gIC8vIEdldCB0aGUgY3VycmVudCBnbG9iYWwgb3B0aW9uc1xuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcblxuICAvLyBDbGVhciBpdCBqdXN0IGluIGNhc2UgKGUuZy4gdGhlIHNldE9wdGlvbnMgd2FzIHVzZWQgaW4gdGhlIGN1c3RvbUNvZGUpXG4gIGZvciAoY29uc3QgcHJvcCBpbiBkZWZhdWx0T3B0aW9ucykge1xuICAgIGlmICh0eXBlb2YgZGVmYXVsdE9wdGlvbnNbcHJvcF0gIT09ICdmdW5jdGlvbicpIHtcbiAgICAgIGRlbGV0ZSBkZWZhdWx0T3B0aW9uc1twcm9wXTtcbiAgICB9XG4gIH1cblxuICAvLyBTZXQgdGhlIGRlZmF1bHQgb3B0aW9ucyBiYWNrXG4gIHNldE9wdGlvbnMoSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqKTtcblxuICAvLyBFbXB0eSB0aGUgY3VzdG9tIGdsb2JhbCBvcHRpb25zIG9iamVjdFxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSB7fTtcbn1cbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcblxuaW1wb3J0IHB1cHBldGVlciBmcm9tICdwdXBwZXRlZXInO1xuXG5pbXBvcnQgeyBnZXRDYWNoZVBhdGggfSBmcm9tICcuL2NhY2hlLmpzJztcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcblxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcblxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgdGhlIHBhZ2VcbmNvbnN0IHRlbXBsYXRlID0gcmVhZEZpbGVTeW5jKF9fZGlybmFtZSArICcvdGVtcGxhdGVzL3RlbXBsYXRlLmh0bWwnLCAndXRmOCcpO1xuXG5sZXQgYnJvd3NlcjtcblxuLyoqXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPG9iamVjdD59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFB1cHBldGVlciBicm93c2VyXG4gKiBpbnN0YW5jZS5cbiAqXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIG5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW5cbiAqIGNyZWF0ZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXQoKSB7XG4gIGlmICghYnJvd3Nlcikge1xuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nKTtcbiAgfVxuICByZXR1cm4gYnJvd3Nlcjtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cbiAqXG4gKiBAcGFyYW0ge0FycmF5fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlciBsYXVuY2guXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8b2JqZWN0Pn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgUHVwcGV0ZWVyIGJyb3dzZXJcbiAqIGluc3RhbmNlLlxuICpcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgbWF4IHJldHJpZXMgdG8gb3BlbiBhIGJyb3dzZXJcbiAqIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyIHJldHJpZXMuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGUocHVwcGV0ZWVyQXJncykge1xuICAvLyBHZXQgZGVidWcgYW5kIG90aGVyIG9wdGlvbnNcbiAgY29uc3QgeyBkZWJ1Zywgb3RoZXIgfSA9IGdldE9wdGlvbnMoKTtcblxuICAvLyBHZXQgdGhlIGRlYnVnIG9wdGlvbnNcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWdPcHRpb25zIH0gPSBkZWJ1ZztcblxuICBjb25zdCBsYXVuY2hPcHRpb25zID0ge1xuICAgIGhlYWRsZXNzOiBvdGhlci5icm93c2VyU2hlbGxNb2RlID8gJ3NoZWxsJyA6IHRydWUsXG4gICAgdXNlckRhdGFEaXI6ICcuL3RtcC8nLFxuICAgIGFyZ3M6IHB1cHBldGVlckFyZ3MsXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcbiAgICBoYW5kbGVTSUdURVJNOiBmYWxzZSxcbiAgICBoYW5kbGVTSUdIVVA6IGZhbHNlLFxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXG4gICAgZGVmYXVsdFZpZXdwb3J0OiBudWxsLFxuICAgIC4uLihlbmFibGVkRGVidWcgJiYgZGVidWdPcHRpb25zKVxuICB9O1xuXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXJcbiAgaWYgKCFicm93c2VyKSB7XG4gICAgbGV0IHRyeUNvdW50ID0gMDtcblxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBsb2coXG4gICAgICAgICAgMyxcbiAgICAgICAgICBgW2Jyb3dzZXJdIEF0dGVtcHRpbmcgdG8gZ2V0IGEgYnJvd3NlciBpbnN0YW5jZSAodHJ5ICR7Kyt0cnlDb3VudH0pLmBcbiAgICAgICAgKTtcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dXaXRoU3RhY2soXG4gICAgICAgICAgMSxcbiAgICAgICAgICBlcnJvcixcbiAgICAgICAgICAnW2Jyb3dzZXJdIEZhaWxlZCB0byBsYXVuY2ggYSBicm93c2VyIGluc3RhbmNlLidcbiAgICAgICAgKTtcblxuICAgICAgICAvLyBSZXRyeSB0byBsYXVuY2ggYnJvd3NlciB1bnRpbCByZWFjaGluZyBtYXggYXR0ZW1wdHNcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIDQwMDApKTtcbiAgICAgICAgICBhd2FpdCBvcGVuKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IG9wZW4oKTtcblxuICAgICAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cbiAgICAgIGlmIChsYXVuY2hPcHRpb25zLmhlYWRsZXNzID09PSAnc2hlbGwnKSB7XG4gICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIExhdW5jaGVkIGJyb3dzZXIgaW4gc2hlbGwgbW9kZS5gKTtcbiAgICAgIH1cblxuICAgICAgLy8gRGVidWcgbW9kZSBpbmZvcm1cbiAgICAgIGlmIChlbmFibGVkRGVidWcpIHtcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAgICdbYnJvd3Nlcl0gTWF4aW11bSByZXRyaWVzIHRvIG9wZW4gYSBicm93c2VyIGluc3RhbmNlIHJlYWNoZWQuJ1xuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XG4gICAgfVxuXG4gICAgaWYgKCFicm93c2VyKSB7XG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nKTtcbiAgICB9XG4gIH1cblxuICAvLyBSZXR1cm4gYSBicm93c2VyIHByb21pc2VcbiAgcmV0dXJuIGJyb3dzZXI7XG59XG5cbi8qKlxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdHJ1ZSBhZnRlciB0aGUgYnJvd3NlclxuICogaXMgY2xvc2VkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2UoKSB7XG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubm5lY3RlZFxuICBpZiAoYnJvd3Nlcj8uY29ubmVjdGVkKSB7XG4gICAgYXdhaXQgYnJvd3Nlci5jbG9zZSgpO1xuICB9XG4gIGxvZyg0LCAnW2Jyb3dzZXJdIENsb3NlZCB0aGUgYnJvd3Nlci4nKTtcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgbmV3IFB1cHBldGVlciBQYWdlIHdpdGhpbiBhbiBleGlzdGluZyBicm93c2VyIGluc3RhbmNlLlxuICpcbiAqIElmIHRoZSBicm93c2VyIGluc3RhbmNlIGlzIG5vdCBhdmFpbGFibGUsIHJldHVybnMgZmFsc2UuXG4gKlxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXG4gKiBzZXRQYWdlQ29udGVudCgpLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgUGFnZS5cbiAqXG4gKiBAcmV0dXJucyB7KGJvb2xlYW58b2JqZWN0KX0gUmV0dXJucyBmYWxzZSBpZiB0aGUgYnJvd3NlciBpbnN0YW5jZSBpcyBub3RcbiAqIGF2YWlsYWJsZSwgb3IgYSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgcmVwcmVzZW50aW5nIHRoZSBuZXdseSBjcmVhdGVkIHBhZ2UuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBuZXdQYWdlKCkge1xuICBpZiAoIWJyb3dzZXIpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvLyBDcmVhdGUgYSBwYWdlXG4gIGNvbnN0IHBhZ2UgPSBhd2FpdCBicm93c2VyLm5ld1BhZ2UoKTtcblxuICAvLyBEaXNhYmxlIGNhY2hlXG4gIGF3YWl0IHBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcblxuICAvLyBTZXQgdGhlIGNvbnRlbnRcbiAgYXdhaXQgc2V0UGFnZUNvbnRlbnQocGFnZSk7XG5cbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXG4gIHNldFBhZ2VFdmVudHMocGFnZSk7XG5cbiAgcmV0dXJuIHBhZ2U7XG59XG5cbi8qKlxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBQYWdlIG9iamVjdCB0byBiZSBjbGVhcmVkLlxuICogQHBhcmFtIHtib29sZWFufSBoYXJkUmVzZXQgLSBBIGZsYWcgaW5kaWNhdGluZyB0aGUgdHlwZSBvZiBjbGVhcmluZ1xuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gJ2Fib3V0OmJsYW5rJyBhbmQgcmVzZXRzIGNvbnRlbnRcbiAqIGFuZCBzY3JpcHRzLiBJZiBmYWxzZSwgY2xlYXJzIHRoZSBib2R5IGNvbnRlbnQgYnkgc2V0dGluZyBhIHByZWRlZmluZWQgSFRNTFxuICogc3RydWN0dXJlLlxuICpcbiAqIEB0aHJvd3Mge0Vycm9yfSBMb2dzIHRocm93biBlcnJvciBpZiBjbGVhcmluZyB0aGUgcGFnZSBjb250ZW50IGZhaWxzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlKHBhZ2UsIGhhcmRSZXNldCA9IGZhbHNlKSB7XG4gIHRyeSB7XG4gICAgaWYgKCFwYWdlLmlzQ2xvc2VkKCkpIHtcbiAgICAgIGlmIChoYXJkUmVzZXQpIHtcbiAgICAgICAgLy8gTmF2aWdhdGUgdG8gYWJvdXQ6YmxhbmtcbiAgICAgICAgYXdhaXQgcGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XG5cbiAgICAgICAgLy8gU2V0IHRoZSBjb250ZW50IGFuZCBhbmQgc2NyaXB0cyBhZ2FpblxuICAgICAgICBhd2FpdCBzZXRQYWdlQ29udGVudChwYWdlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxuICAgICAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LmlubmVySFRNTCA9XG4gICAgICAgICAgICAnPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPjxkaXYgaWQ9XCJjb250YWluZXJcIj48L2Rpdj48L2Rpdj4nO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nV2l0aFN0YWNrKFxuICAgICAgMixcbiAgICAgIGVycm9yLFxuICAgICAgJ1ticm93c2VyXSBDb3VsZCBub3QgY2xlYXIgdGhlIGNvbnRlbnQgb2YgdGhlIHBhZ2UuJ1xuICAgICk7XG4gIH1cbn1cblxuLyoqXG4gKiBBZGRzIGN1c3RvbSBKUyBhbmQgQ1NTIHJlc291cmNlcyB0byBhIFB1cHBldGVlciBQYWdlIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWRcbiAqIG9wdGlvbnMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHJlc291cmNlcyB3aWxsIGJlXG4gKiBhZGRlZC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQWxsIG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8QXJyYXk8T2JqZWN0Pj59IC0gUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gYXJyYXkgb2YgaW5qZWN0ZWRcbiAqIHJlc291cmNlcy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgb3B0aW9ucykge1xuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXlcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcblxuICAvLyBVc2UgcmVzb3VyY2VzXG4gIGNvbnN0IHJlc291cmNlcyA9IG9wdGlvbnMuY3VzdG9tTG9naWMucmVzb3VyY2VzO1xuICBpZiAocmVzb3VyY2VzKSB7XG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xuXG4gICAgLy8gTG9hZCBjdXN0b20gSlMgY29kZVxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcbiAgICAgIGluamVjdGVkSnMucHVzaCh7XG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5qc1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xuICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcbiAgICAgIGZvciAoY29uc3QgZmlsZSBvZiByZXNvdXJjZXMuZmlsZXMpIHtcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9ICFmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRydWUgOiBmYWxzZTtcblxuICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gc2NyaXB0IGZyb20gcmVzb3VyY2VzJyBmaWxlc1xuICAgICAgICBpbmplY3RlZEpzLnB1c2goXG4gICAgICAgICAgaXNMb2NhbFxuICAgICAgICAgICAgPyB7XG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGZpbGUsICd1dGY4JylcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgOiB7XG4gICAgICAgICAgICAgICAgdXJsOiBmaWxlXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGpzUmVzb3VyY2Ugb2YgaW5qZWN0ZWRKcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhqc1Jlc291cmNlKSk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbZXhwb3J0XSBUaGUgSlMgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaW5qZWN0ZWRKcy5sZW5ndGggPSAwO1xuXG4gICAgLy8gTG9hZCBDU1NcbiAgICBjb25zdCBpbmplY3RlZENzcyA9IFtdO1xuICAgIGlmIChyZXNvdXJjZXMuY3NzKSB7XG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XG4gICAgICBpZiAoY3NzSW1wb3J0cykge1xuICAgICAgICAvLyBIYW5kbGUgY3NzIHNlY3Rpb25cbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XG4gICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGgpIHtcbiAgICAgICAgICAgIGNzc0ltcG9ydFBhdGggPSBjc3NJbXBvcnRQYXRoXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCdAaW1wb3J0JywgJycpXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJylcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC87LywgJycpXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cXCkvZywgJycpXG4gICAgICAgICAgICAgIC50cmltKCk7XG5cbiAgICAgICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBjc3MgZnJvbSByZXNvdXJjZXNcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xuICAgICAgICAgICAgICBpbmplY3RlZENzcy5wdXNoKHtcbiAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xuICAgICAgICAgICAgICAgIHBhdGg6IHBhdGguam9pbihfX2Rpcm5hbWUsIGNzc0ltcG9ydFBhdGgpXG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBUaGUgcmVzdCBvZiB0aGUgQ1NTIHNlY3Rpb24gd2lsbCBiZSBjb250ZW50IGJ5IG5vd1xuICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXG4gICAgICB9KTtcblxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyhjc3NSZXNvdXJjZSkpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFtleHBvcnRdIFRoZSBDU1MgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaW5qZWN0ZWRDc3MubGVuZ3RoID0gMDtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGluamVjdGVkUmVzb3VyY2VzO1xufVxuXG4vKipcbiAqIENsZWFycyBvdXQgYWxsIHN0YXRlIHNldCBvbiB0aGUgcGFnZSB3aXRoIGFkZFNjcmlwdFRhZy9hZGRTdHlsZVRhZy4gUmVtb3Zlc1xuICogaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS4gQWRkaXRpb25hbGx5LFxuICogaXQgZGVzdHJveXMgcHJldmlvdXNseSBleGlzdGluZyBjaGFydHMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcbiAqIGJlIGNsZWFyZWQuXG4gKiBAcGFyYW0ge0FycmF5PE9iamVjdD59IGluamVjdGVkUmVzb3VyY2VzIC0gQXJyYXkgb2YgaW5qZWN0ZWQgcmVzb3VyY2VzXG4gKiB0byBiZSBjbGVhcmVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKSB7XG4gIGZvciAoY29uc3QgcmVzb3VyY2Ugb2YgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcbiAgICBhd2FpdCByZXNvdXJjZS5kaXNwb3NlKCk7XG4gIH1cblxuICAvLyBEZXN0cm95IG9sZCBjaGFydHMgYWZ0ZXIgZXhwb3J0IGlzIGRvbmUgYW5kIHJlc2V0IGFsbCBDU1MgYW5kIHNjcmlwdCB0YWdzXG4gIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xuICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCBlLGcsIHdoZW4gZG9pbmcgU1ZHXG4gICAgLy8gZXhwb3J0c1xuICAgIGlmICh0eXBlb2YgSGlnaGNoYXJ0cyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxuICAgICAgY29uc3Qgb2xkQ2hhcnRzID0gSGlnaGNoYXJ0cy5jaGFydHM7XG5cbiAgICAgIC8vIENoZWNrIGluIGFueSBhbHJlYWR5IGV4aXN0aW5nIGNoYXJ0c1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XG4gICAgICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0c1xuICAgICAgICBmb3IgKGNvbnN0IG9sZENoYXJ0IG9mIG9sZENoYXJ0cykge1xuICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcbiAgICAgICAgICBIaWdoY2hhcnRzLmNoYXJ0cy5zaGlmdCgpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXG4gICAgY29uc3QgWy4uLnNjcmlwdHNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXG4gICAgY29uc3QgWywgLi4uc3R5bGVzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3N0eWxlJyk7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXG4gICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcblxuICAgIC8vIFJlbW92ZSB0YWdzXG4gICAgZm9yIChjb25zdCBlbGVtZW50IG9mIFtcbiAgICAgIC4uLnNjcmlwdHNUb1JlbW92ZSxcbiAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxuICAgICAgLi4ubGlua3NUb1JlbW92ZVxuICAgIF0pIHtcbiAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XG4gICAgfVxuICB9KTtcbn1cblxuLyoqXG4gKiBTZXRzIHRoZSBjb250ZW50IGZvciBhIFB1cHBldGVlciBQYWdlIHVzaW5nIGEgcHJlZGVmaW5lZCB0ZW1wbGF0ZVxuICogYW5kIGFkZGl0aW9uYWwgc2NyaXB0cy4gQWxzbywgc2V0cyB0aGUgcGFnZWVycm9yIGluIG9yZGVyIHRvIGNhdGNoXG4gKiBhbmQgZGlzcGxheSBlcnJvcnMgZnJvbSB0aGUgd2luZG93IGNvbnRleHQuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IGZvciB3aGljaCB0aGUgY29udGVudFxuICogaXMgYmVpbmcgc2V0LlxuICovXG5hc3luYyBmdW5jdGlvbiBzZXRQYWdlQ29udGVudChwYWdlKSB7XG4gIGF3YWl0IHBhZ2Uuc2V0Q29udGVudCh0ZW1wbGF0ZSwgeyB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJyB9KTtcblxuICAvLyBBZGQgYWxsIHJlZ2lzdGVyZWQgSGlnY2hhcnRzIHNjcmlwdHMsIHF1aXRlIGRlbWFuZGluZ1xuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGAke2dldENhY2hlUGF0aCgpfS9zb3VyY2VzLmpzYCB9KTtcblxuICAvLyBTZXQgdGhlIGluaXRpYWwgYW5pbU9iamVjdFxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKHNldHVwSGlnaGNoYXJ0cyk7XG59XG5cbi8qKlxuICogU2V0IGV2ZW50cyBmb3IgYSBQdXBwZXRlZXIgUGFnZS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgdG8gc2V0IGV2ZW50cyB0by5cbiAqL1xuZnVuY3Rpb24gc2V0UGFnZUV2ZW50cyhwYWdlKSB7XG4gIC8vIEdldCBkZWJ1ZyBvcHRpb25zXG4gIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcblxuICAvLyBTZXQgdGhlIGNvbnNvbGUgbGlzdGVuZXIsIGlmIG5lZWRlZFxuICBpZiAoZGVidWcuZW5hYmxlICYmIGRlYnVnLmxpc3RlblRvQ29uc29sZSkge1xuICAgIHBhZ2Uub24oJ2NvbnNvbGUnLCAobWVzc2FnZSkgPT4ge1xuICAgICAgY29uc29sZS5sb2coYFtkZWJ1Z10gJHttZXNzYWdlLnRleHQoKX1gKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8vIFNldCB0aGUgcGFnZWVycm9yIGxpc3RlbmVyXG4gIHBhZ2Uub24oJ3BhZ2VlcnJvcicsIGFzeW5jIChlcnJvcikgPT4ge1xuICAgIC8vIFRPRE86IENvbnNpZGVyIGFkZGluZyBhIHN3aXRjaCBoZXJlIHRoYXQgdHVybnMgb24gbG9nKDApIGxvZ2dpbmdcbiAgICAvLyBvbiBwYWdlIGVycm9ycy5cbiAgICBhd2FpdCBwYWdlLiRldmFsKFxuICAgICAgJyNjb250YWluZXInLFxuICAgICAgKGVsZW1lbnQsIGVycm9yTWVzc2FnZSkgPT4ge1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcbiAgICAgICAgaWYgKHdpbmRvdy5fZGlzcGxheUVycm9ycykge1xuICAgICAgICAgIGVsZW1lbnQuaW5uZXJIVE1MID0gZXJyb3JNZXNzYWdlO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgYDxoMT5DaGFydCBpbnB1dCBkYXRhIGVycm9yOiA8L2gxPiR7ZXJyb3IudG9TdHJpbmcoKX1gXG4gICAgKTtcbiAgfSk7XG59XG5cbmV4cG9ydCBkZWZhdWx0IHtcbiAgZ2V0LFxuICBjcmVhdGUsXG4gIGNsb3NlLFxuICBuZXdQYWdlLFxuICBjbGVhclBhZ2UsXG4gIGFkZFBhZ2VSZXNvdXJjZXMsXG4gIGNsZWFyUGFnZVJlc291cmNlc1xufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyBhZGRQYWdlUmVzb3VyY2VzLCBjbGVhclBhZ2VSZXNvdXJjZXMgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xuaW1wb3J0IHsgZ2V0Q2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcbmltcG9ydCB7IHRyaWdnZXJFeHBvcnQgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuXG5pbXBvcnQgc3ZnVGVtcGxhdGUgZnJvbSAnLi8uLi90ZW1wbGF0ZXMvc3ZnX2V4cG9ydC9zdmdfZXhwb3J0LmpzJztcblxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcblxuLyoqXG4gKiBSZXRyaWV2ZXMgdGhlIGNsaXBwaW5nIHJlZ2lvbiBjb29yZGluYXRlcyBvZiB0aGUgc3BlY2lmaWVkIHBhZ2UgZWxlbWVudCB3aXRoXG4gKiB0aGUgaWQgJ2NoYXJ0LWNvbnRhaW5lcicuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gb2JqZWN0IGNvbnRhaW5pbmdcbiAqIHgsIHksIHdpZHRoLCBhbmQgaGVpZ2h0IHByb3BlcnRpZXMuXG4gKi9cbmNvbnN0IGdldENsaXBSZWdpb24gPSAocGFnZSkgPT5cbiAgcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XG4gICAgY29uc3QgeyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH0gPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICAgIHJldHVybiB7XG4gICAgICB4LFxuICAgICAgeSxcbiAgICAgIHdpZHRoLFxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXG4gICAgfTtcbiAgfSk7XG5cbi8qKlxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIHNjcmVlbnNob3QgZnVuY3Rpb25hbGl0eSB3aXRoXG4gKiBzcGVjaWZpZWQgb3B0aW9ucy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gSW1hZ2UgdHlwZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBlbmNvZGluZyAtIEltYWdlIGVuY29kaW5nLlxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXG4gKiBpbiBtaWxsaXNlY29uZHMuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIGltYWdlIGJ1ZmZlciBvciByZWplY3RpbmdcbiAqIHdpdGggYW4gRXhwb3J0RXJyb3IgZm9yIHRpbWVvdXQuXG4gKi9cbmNvbnN0IGNyZWF0ZUltYWdlID0gKHBhZ2UsIHR5cGUsIGVuY29kaW5nLCBjbGlwLCByYXN0ZXJpemF0aW9uVGltZW91dCkgPT5cbiAgUHJvbWlzZS5yYWNlKFtcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xuICAgICAgdHlwZSxcbiAgICAgIGVuY29kaW5nLFxuICAgICAgY2xpcCxcbiAgICAgIGNhcHR1cmVCZXlvbmRWaWV3cG9ydDogdHJ1ZSxcbiAgICAgIGZ1bGxQYWdlOiBmYWxzZSxcbiAgICAgIG9wdGltaXplRm9yU3BlZWQ6IHRydWUsXG4gICAgICAuLi4odHlwZSAhPT0gJ3BuZycgPyB7IHF1YWxpdHk6IDgwIH0gOiB7fSksXG5cbiAgICAgIC8vICM0NDcsICM0NjMgLSBhbHdheXMgcmVuZGVyIG9uIGEgdHJhbnNwYXJlbnQgcGFnZSBpZiB0aGUgZXhwZWN0ZWQgdHlwZVxuICAgICAgLy8gZm9ybWF0IGlzIFBOR1xuICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZydcbiAgICB9KSxcbiAgICBuZXcgUHJvbWlzZSgoX3Jlc29sdmUsIHJlamVjdCkgPT5cbiAgICAgIHNldFRpbWVvdXQoXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpKSxcbiAgICAgICAgcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxuICAgICAgKVxuICAgIClcbiAgXSk7XG5cbi8qKlxuICogQ3JlYXRlcyBhIFBERiB1c2luZyBQdXBwZXRlZXIncyBwYWdlIHBkZiBmdW5jdGlvbmFsaXR5IHdpdGggc3BlY2lmaWVkXG4gKiBvcHRpb25zLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxuICogQHBhcmFtIHtudW1iZXJ9IGhlaWdodCAtIFBERiBoZWlnaHQuXG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGggLSBQREYgd2lkdGguXG4gKiBAcGFyYW0ge3N0cmluZ30gZW5jb2RpbmcgLSBQREYgZW5jb2RpbmcuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFBERiBidWZmZXIuXG4gKi9cbmNvbnN0IGNyZWF0ZVBERiA9IGFzeW5jIChcbiAgcGFnZSxcbiAgaGVpZ2h0LFxuICB3aWR0aCxcbiAgZW5jb2RpbmcsXG4gIHJhc3Rlcml6YXRpb25UaW1lb3V0XG4pID0+IHtcbiAgYXdhaXQgcGFnZS5lbXVsYXRlTWVkaWFUeXBlKCdzY3JlZW4nKTtcbiAgcmV0dXJuIFByb21pc2UucmFjZShbXG4gICAgcGFnZS5wZGYoe1xuICAgICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXG4gICAgICBoZWlnaHQ6IGhlaWdodCArIDEsXG4gICAgICB3aWR0aCxcbiAgICAgIGVuY29kaW5nXG4gICAgfSksXG4gICAgbmV3IFByb21pc2UoKF9yZXNvbHZlLCByZWplY3QpID0+XG4gICAgICBzZXRUaW1lb3V0KFxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEV4cG9ydEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnKSksXG4gICAgICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcbiAgICAgIClcbiAgICApXG4gIF0pO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGFuIFNWRyBzdHJpbmcgYnkgZXZhbHVhdGluZyB0aGUgb3V0ZXJIVE1MIG9mIHRoZSBmaXJzdCAnc3ZnJyBlbGVtZW50XG4gKiBpbnNpZGUgYW4gZWxlbWVudCB3aXRoIHRoZSBpZCAnY29udGFpbmVyJy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgU1ZHIHN0cmluZy5cbiAqL1xuY29uc3QgY3JlYXRlU1ZHID0gKHBhZ2UpID0+XG4gIHBhZ2UuJGV2YWwoJyNjb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnLCAoZWxlbWVudCkgPT4gZWxlbWVudC5vdXRlckhUTUwpO1xuXG4vKipcbiAqIFNldHMgdGhlIHNwZWNpZmllZCBjaGFydCBhbmQgb3B0aW9ucyBhcyBjb25maWd1cmF0aW9uIGludG8gdGhlIHRyaWdnZXJFeHBvcnRcbiAqIGZ1bmN0aW9uIHdpdGhpbiB0aGUgd2luZG93IGNvbnRleHQgdXNpbmcgcGFnZS5ldmFsdWF0ZS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cbiAqIEBwYXJhbSB7YW55fSBjaGFydCAtIFRoZSBjaGFydCBvYmplY3QgdG8gYmUgY29uZmlndXJlZC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgY2hhcnQuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFByb21pc2UgcmVzb2x2aW5nIGFmdGVyIHRoZSBjb25maWd1cmF0aW9uIGlzIHNldC5cbiAqL1xuY29uc3Qgc2V0QXNDb25maWcgPSBhc3luYyAocGFnZSwgY2hhcnQsIG9wdGlvbnMsIGRpc3BsYXlFcnJvcnMpID0+XG4gIHBhZ2UuZXZhbHVhdGUodHJpZ2dlckV4cG9ydCwgY2hhcnQsIG9wdGlvbnMsIGRpc3BsYXlFcnJvcnMpO1xuXG4vKipcbiAqIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZSB1c2luZyBQdXBwZXRlZXIuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXG4gKiBAcGFyYW0ge2FueX0gY2hhcnQgLSBUaGUgY2hhcnQgb2JqZWN0IG9yIFNWRyBjb25maWd1cmF0aW9uIHRvIGJlIGV4cG9ydGVkLlxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbi5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmcgfCBCdWZmZXIgfCBFeHBvcnRFcnJvcj59IFByb21pc2UgcmVzb2x2aW5nIHRvXG4gKiB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBFeHBvcnRFcnJvci5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgYXN5bmMgKHBhZ2UsIGNoYXJ0LCBvcHRpb25zKSA9PiB7XG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheSAoYWRkaXRpb25hbCBKUyBhbmQgQ1NTKVxuICBsZXQgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcblxuICB0cnkge1xuICAgIGxvZyg0LCAnW2V4cG9ydF0gRGV0ZXJtaW5pbmcgZXhwb3J0IHBhdGguJyk7XG5cbiAgICBjb25zdCBleHBvcnRPcHRpb25zID0gb3B0aW9ucy5leHBvcnQ7XG5cbiAgICAvLyBEZWNpZGUgd2hldGhlciBkaXNwbGF5IGVycm9yIG9yIGRlYmJ1Z2VyIHdyYXBwZXIgYXJvdW5kIGl0XG4gICAgY29uc3QgZGlzcGxheUVycm9ycyA9XG4gICAgICBleHBvcnRPcHRpb25zPy5vcHRpb25zPy5jaGFydD8uZGlzcGxheUVycm9ycyAmJlxuICAgICAgZ2V0Q2FjaGUoKS5hY3RpdmVNYW5pZmVzdC5tb2R1bGVzLmRlYnVnZ2VyO1xuXG4gICAgbGV0IGlzU1ZHO1xuICAgIGlmIChcbiAgICAgIGNoYXJ0LmluZGV4T2YgJiZcbiAgICAgIChjaGFydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fCBjaGFydC5pbmRleE9mKCc8P3htbCcpID49IDApXG4gICAgKSB7XG4gICAgICAvLyBTVkcgaW5wdXQgaGFuZGxpbmdcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgU1ZHLicpO1xuXG4gICAgICAvLyBJZiBpbnB1dCBpcyBhbHNvIFNWRywganVzdCByZXR1cm4gaXRcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XG4gICAgICAgIHJldHVybiBjaGFydDtcbiAgICAgIH1cblxuICAgICAgaXNTVkcgPSB0cnVlO1xuICAgICAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKGNoYXJ0KSwge1xuICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEpTT04gY29uZmlnIGhhbmRsaW5nXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIGNvbmZpZy4nKTtcblxuICAgICAgLy8gTmVlZCB0byBwZXJmb3JtIHN0cmFpZ2h0IGluamVjdFxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuc3RySW5qKSB7XG4gICAgICAgIC8vIEluamVjdGlvbiBiYXNlZCBjb25maWd1cmF0aW9uIGV4cG9ydFxuICAgICAgICBhd2FpdCBzZXRBc0NvbmZpZyhcbiAgICAgICAgICBwYWdlLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGNoYXJ0OiB7XG4gICAgICAgICAgICAgIGhlaWdodDogZXhwb3J0T3B0aW9ucy5oZWlnaHQsXG4gICAgICAgICAgICAgIHdpZHRoOiBleHBvcnRPcHRpb25zLndpZHRoXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSxcbiAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgIGRpc3BsYXlFcnJvcnNcbiAgICAgICAgKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEJhc2ljIGNvbmZpZ3VyYXRpb24gZXhwb3J0XG4gICAgICAgIGNoYXJ0LmNoYXJ0LmhlaWdodCA9IGV4cG9ydE9wdGlvbnMuaGVpZ2h0O1xuICAgICAgICBjaGFydC5jaGFydC53aWR0aCA9IGV4cG9ydE9wdGlvbnMud2lkdGg7XG5cbiAgICAgICAgYXdhaXQgc2V0QXNDb25maWcocGFnZSwgY2hhcnQsIG9wdGlvbnMsIGRpc3BsYXlFcnJvcnMpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEtlZXBzIHRyYWNrIG9mIGFsbCByZXNvdXJjZXMgYWRkZWQgb24gdGhlIHBhZ2Ugd2l0aCBhZGRYWFhUYWcuIGV0Y1xuICAgIC8vIEl0J3MgVklUQUwgdGhhdCBhbGwgYWRkZWQgcmVzb3VyY2VzIGVuZHMgdXAgaGVyZSBzbyB3ZSBjYW4gY2xlYXIgdGhpbmdzXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXG4gICAgaW5qZWN0ZWRSZXNvdXJjZXMgPSBhd2FpdCBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIG9wdGlvbnMpO1xuXG4gICAgLy8gR2V0IHRoZSByZWFsIGNoYXJ0IHNpemUgYW5kIHNldCB0aGUgem9vbSBhY2NvcmRpbmdseVxuICAgIGNvbnN0IHNpemUgPSBpc1NWR1xuICAgICAgPyBhd2FpdCBwYWdlLmV2YWx1YXRlKChzY2FsZSkgPT4ge1xuICAgICAgICAgIGNvbnN0IHN2Z0VsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFxuICAgICAgICAgICAgJyNjaGFydC1jb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIC8vIEdldCB0aGUgdmFsdWVzIGNvcnJlY3RseSBzY2FsZWRcbiAgICAgICAgICBjb25zdCBjaGFydEhlaWdodCA9IHN2Z0VsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcbiAgICAgICAgICBjb25zdCBjaGFydFdpZHRoID0gc3ZnRWxlbWVudC53aWR0aC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XG5cbiAgICAgICAgICAvLyBJbiBjYXNlIG9mIFNWRyB0aGUgem9vbSBtdXN0IGJlIHNldCBkaXJlY3RseSBmb3IgYm9keVxuICAgICAgICAgIC8vIFNldCB0aGUgem9vbSBhcyBzY2FsZVxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IHNjYWxlO1xuXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBjaGFydEhlaWdodCxcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcbiAgICAgICAgICB9O1xuICAgICAgICB9LCBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpKVxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcbiAgICAgICAgICBjb25zdCB7IGNoYXJ0SGVpZ2h0LCBjaGFydFdpZHRoIH0gPSB3aW5kb3cuSGlnaGNoYXJ0cy5jaGFydHNbMF07XG5cbiAgICAgICAgICAvLyBObyBuZWVkIGZvciBzdWNoIHNjYWxlIG1hbmlwdWxhdGlvbiBpbiBjYXNlIG9mIG90aGVyIHR5cGVzIG9mIGV4cG9ydHNcbiAgICAgICAgICAvLyBSZXNldCB0aGUgem9vbSBmb3Igb3RoZXIgZXhwb3J0cyB0aGFuIHRvIFNWR3NcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxuICAgICAgICAgICAgY2hhcnRXaWR0aFxuICAgICAgICAgIH07XG4gICAgICAgIH0pO1xuXG4gICAgLy8gU2V0IGZpbmFsIGhlaWdodCBhbmQgd2lkdGggZm9yIHZpZXdwb3J0XG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmNlaWwoc2l6ZS5jaGFydEhlaWdodCB8fCBleHBvcnRPcHRpb25zLmhlaWdodCk7XG4gICAgY29uc3Qgdmlld3BvcnRXaWR0aCA9IE1hdGguY2VpbChzaXplLmNoYXJ0V2lkdGggfHwgZXhwb3J0T3B0aW9ucy53aWR0aCk7XG5cbiAgICAvLyBHZXQgdGhlIGNsaXAgcmVnaW9uIGZvciB0aGUgcGFnZVxuICAgIGNvbnN0IHsgeCwgeSB9ID0gYXdhaXQgZ2V0Q2xpcFJlZ2lvbihwYWdlKTtcblxuICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcbiAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcbiAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcbiAgICAgIGRldmljZVNjYWxlRmFjdG9yOiBpc1NWRyA/IDEgOiBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpXG4gICAgfSk7XG5cbiAgICBsZXQgZGF0YTtcbiAgICAvLyBSYXN0ZXJpemF0aW9uIHByb2Nlc3NcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xuICAgICAgLy8gU1ZHXG4gICAgICBkYXRhID0gYXdhaXQgY3JlYXRlU1ZHKHBhZ2UpO1xuICAgIH0gZWxzZSBpZiAoWydwbmcnLCAnanBlZyddLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMudHlwZSkpIHtcbiAgICAgIC8vIFBORyBvciBKUEVHXG4gICAgICBkYXRhID0gYXdhaXQgY3JlYXRlSW1hZ2UoXG4gICAgICAgIHBhZ2UsXG4gICAgICAgIGV4cG9ydE9wdGlvbnMudHlwZSxcbiAgICAgICAgJ2Jhc2U2NCcsXG4gICAgICAgIHtcbiAgICAgICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcbiAgICAgICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxuICAgICAgICAgIHgsXG4gICAgICAgICAgeVxuICAgICAgICB9LFxuICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XG4gICAgICApO1xuICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAncGRmJykge1xuICAgICAgLy8gUERGXG4gICAgICBkYXRhID0gYXdhaXQgY3JlYXRlUERGKFxuICAgICAgICBwYWdlLFxuICAgICAgICB2aWV3cG9ydEhlaWdodCxcbiAgICAgICAgdmlld3BvcnRXaWR0aCxcbiAgICAgICAgJ2Jhc2U2NCcsXG4gICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcbiAgICAgICAgYFtleHBvcnRdIFVuc3VwcG9ydGVkIG91dHB1dCBmb3JtYXQgJHtleHBvcnRPcHRpb25zLnR5cGV9LmBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gQ2xlYXIgcHJldmlvdXNseSBpbmplY3RlZCBKUyBhbmQgQ1NTIHJlc291cmNlc1xuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XG4gICAgcmV0dXJuIGRhdGE7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcbiAgICByZXR1cm4gZXJyb3I7XG4gIH1cbn07XG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcblxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XG5cbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cblxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXG5cblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cblxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuaW1wb3J0IGNzc1RlbXBsYXRlIGZyb20gJy4vY3NzLmpzJztcblxuZXhwb3J0IGRlZmF1bHQgKGNoYXJ0KSA9PiBgXG48IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9J2VuLVVTJz5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgaHR0cC1lcXVpdj1cIkNvbnRlbnQtVHlwZVwiIGNvbnRlbnQ9XCJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLThcIj5cbiAgICA8dGl0bGU+SGlnaGNoYXJ0cyBFeHBvcnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxzdHlsZT5cbiAgICAke2Nzc1RlbXBsYXRlKCl9XG4gIDwvc3R5bGU+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cbiAgICAgICR7Y2hhcnR9XG4gICAgPC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD5cblxuYDtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyBQb29sIH0gZnJvbSAndGFybic7XG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XG5cbmltcG9ydCB7XG4gIGNyZWF0ZSBhcyBjcmVhdGVCcm93c2VyLFxuICBjbG9zZSBhcyBjbG9zZUJyb3dzZXIsXG4gIG5ld1BhZ2UsXG4gIGNsZWFyUGFnZVxufSBmcm9tICcuL2Jyb3dzZXIuanMnO1xuaW1wb3J0IHB1cHBldGVlckV4cG9ydCBmcm9tICcuL2V4cG9ydC5qcyc7XG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcbmltcG9ydCB7IG1lYXN1cmVUaW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XG5cbi8vIFRoZSBwb29sIGluc3RhbmNlXG5sZXQgcG9vbCA9IGZhbHNlO1xuXG4vLyBQb29sIHN0YXRpc3RpY3NcbmV4cG9ydCBjb25zdCBzdGF0cyA9IHtcbiAgcGVyZm9ybWVkRXhwb3J0czogMCxcbiAgZXhwb3J0QXR0ZW1wdHM6IDAsXG4gIGV4cG9ydEZyb21TdmdBdHRlbXB0czogMCxcbiAgdGltZVNwZW50OiAwLFxuICBkcm9wcGVkRXhwb3J0czogMCxcbiAgc3BlbnRBdmVyYWdlOiAwXG59O1xuXG5sZXQgcG9vbENvbmZpZyA9IHt9O1xuXG5jb25zdCBmYWN0b3J5ID0ge1xuICAvKipcbiAgICogQ3JlYXRlcyBhIG5ldyB3b3JrZXIgcGFnZSBmb3IgdGhlIGV4cG9ydCBwb29sLlxuICAgKlxuICAgKiBAcmV0dXJucyB7T2JqZWN0fSAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSB3b3JrZXIgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZVxuICAgKiBicm93c2VyIHBhZ2UsIGFuZCBpbml0aWFsIHdvcmsgY291bnQuXG4gICAqXG4gICAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSAtIElmIHRoZXJlJ3MgYW4gZXJyb3IgZHVyaW5nIHRoZSBjcmVhdGlvbiBvZiB0aGUgbmV3XG4gICAqIHBhZ2UuXG4gICAqL1xuICBjcmVhdGU6IGFzeW5jICgpID0+IHtcbiAgICBsZXQgcGFnZSA9IGZhbHNlO1xuXG4gICAgY29uc3QgaWQgPSB1dWlkKCk7XG4gICAgY29uc3Qgc3RhcnREYXRlID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XG5cbiAgICB0cnkge1xuICAgICAgcGFnZSA9IGF3YWl0IG5ld1BhZ2UoKTtcblxuICAgICAgaWYgKCFwYWdlIHx8IHBhZ2UuaXNDbG9zZWQoKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1RoZSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLicpO1xuICAgICAgfVxuXG4gICAgICBsb2coXG4gICAgICAgIDMsXG4gICAgICAgIGBbcG9vbF0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIgJHtpZH0gLSB0b29rICR7XG4gICAgICAgICAgbmV3IERhdGUoKS5nZXRUaW1lKCkgLSBzdGFydERhdGVcbiAgICAgICAgfSBtcy5gXG4gICAgICApO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAgICdFcnJvciBlbmNvdW50ZXJlZCB3aGVuIGNyZWF0aW5nIGEgbmV3IHBhZ2UuJ1xuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGlkLFxuICAgICAgcGFnZSxcbiAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcbiAgICAgIHdvcmtDb3VudDogTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogKHBvb2xDb25maWcud29ya0xpbWl0IC8gMikpXG4gICAgfTtcbiAgfSxcblxuICAvKipcbiAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcbiAgICogdGhlIHdvcmsgbGltaXQuXG4gICAqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSB3b3JrZXJIYW5kbGUgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmcgdGhlXG4gICAqIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cbiAgICpcbiAgICogQHJldHVybnMge2Jvb2xlYW59IC0gUmV0dXJucyB0cnVlIGlmIHRoZSB3b3JrZXIgaXMgdmFsaWQgYW5kIHdpdGhpblxuICAgKiB0aGUgd29yayBsaW1pdDsgb3RoZXJ3aXNlLCByZXR1cm5zIGZhbHNlLlxuICAgKi9cbiAgdmFsaWRhdGU6IGFzeW5jICh3b3JrZXJIYW5kbGUpID0+IHtcbiAgICBpZiAoXG4gICAgICBwb29sQ29uZmlnLndvcmtMaW1pdCAmJlxuICAgICAgKyt3b3JrZXJIYW5kbGUud29ya0NvdW50ID4gcG9vbENvbmZpZy53b3JrTGltaXRcbiAgICApIHtcbiAgICAgIGxvZyhcbiAgICAgICAgMyxcbiAgICAgICAgYFtwb29sXSBXb3JrZXIgZmFpbGVkIHZhbGlkYXRpb246IGV4Y2VlZGVkIHdvcmsgbGltaXQgKGxpbWl0IGlzICR7cG9vbENvbmZpZy53b3JrTGltaXR9KS5gXG4gICAgICApO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSxcblxuICAvKipcbiAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXG4gICAqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSB3b3JrZXJIYW5kbGUgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcbiAgICogdGhlIHdvcmtlcidzIElEIGFuZCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLlxuICAgKi9cbiAgZGVzdHJveTogYXN5bmMgKHdvcmtlckhhbmRsZSkgPT4ge1xuICAgIGxvZygzLCBgW3Bvb2xdIERlc3Ryb3lpbmcgcG9vbCBlbnRyeSAke3dvcmtlckhhbmRsZS5pZH0uYCk7XG5cbiAgICBpZiAod29ya2VySGFuZGxlLnBhZ2UpIHtcbiAgICAgIC8vIFdlIGRvbid0IHJlYWxseSBuZWVkIHRvIHdhaXQgYXJvdW5kIGZvciB0aGlzXG4gICAgICBhd2FpdCB3b3JrZXJIYW5kbGUucGFnZS5jbG9zZSgpO1xuICAgIH1cbiAgfVxufTtcblxuLyoqXG4gKiBJbml0aWFsaXplcyB0aGUgZXhwb3J0IHBvb2wgd2l0aCB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiwgY3JlYXRpbmdcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhbmQgc2V0dGluZyB1cCB3b3JrZXIgcmVzb3VyY2VzLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBDb25maWd1cmF0aW9uIG9wdGlvbnMgZm9yIHRoZSBleHBvcnQgcG9vbCBhbG9uZ1xuICogd2l0aCBjdXN0b20gcHVwcGV0ZWVyIGFyZ3VtZW50cyBmb3IgdGhlIHB1cHBldGVlci5sYXVuY2ggZnVuY3Rpb24uXG4gKi9cbmV4cG9ydCBjb25zdCBpbml0UG9vbCA9IGFzeW5jIChjb25maWcpID0+IHtcbiAgLy8gRm9yIHRoZSBtb2R1bGUgc2NvcGUgdXNhZ2VcbiAgcG9vbENvbmZpZyA9IGNvbmZpZyAmJiBjb25maWcucG9vbCA/IHsgLi4uY29uZmlnLnBvb2wgfSA6IHt9O1xuXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgcHVwcGV0ZWVyIGFyZ3VtZW50c1xuICBhd2FpdCBjcmVhdGVCcm93c2VyKGNvbmZpZy5wdXBwZXRlZXJBcmdzKTtcblxuICBsb2coXG4gICAgMyxcbiAgICBgW3Bvb2xdIEluaXRpYWxpemluZyBwb29sIHdpdGggd29ya2VyczogbWluICR7cG9vbENvbmZpZy5taW5Xb3JrZXJzfSwgbWF4ICR7cG9vbENvbmZpZy5tYXhXb3JrZXJzfS5gXG4gICk7XG5cbiAgaWYgKHBvb2wpIHtcbiAgICByZXR1cm4gbG9nKFxuICAgICAgNCxcbiAgICAgICdbcG9vbF0gQWxyZWFkeSBpbml0aWFsaXplZCwgcGxlYXNlIGtpbGwgaXQgYmVmb3JlIGNyZWF0aW5nIGEgbmV3IG9uZS4nXG4gICAgKTtcbiAgfVxuXG4gIGlmIChwYXJzZUludChwb29sQ29uZmlnLm1pbldvcmtlcnMpID4gcGFyc2VJbnQocG9vbENvbmZpZy5tYXhXb3JrZXJzKSkge1xuICAgIHBvb2xDb25maWcubWluV29ya2VycyA9IHBvb2xDb25maWcubWF4V29ya2VycztcbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gQ3JlYXRlIGEgcG9vbCBhbG9uZyB3aXRoIGEgbWluaW1hbCBudW1iZXIgb2YgcmVzb3VyY2VzXG4gICAgcG9vbCA9IG5ldyBQb29sKHtcbiAgICAgIC8vIEdldCB0aGUgY3JlYXRlL3ZhbGlkYXRlL2Rlc3Ryb3kvbG9nIGZ1bmN0aW9uc1xuICAgICAgLi4uZmFjdG9yeSxcbiAgICAgIG1pbjogcGFyc2VJbnQocG9vbENvbmZpZy5taW5Xb3JrZXJzKSxcbiAgICAgIG1heDogcGFyc2VJbnQocG9vbENvbmZpZy5tYXhXb3JrZXJzKSxcbiAgICAgIGFjcXVpcmVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmFjcXVpcmVUaW1lb3V0LFxuICAgICAgY3JlYXRlVGltZW91dE1pbGxpczogcG9vbENvbmZpZy5jcmVhdGVUaW1lb3V0LFxuICAgICAgZGVzdHJveVRpbWVvdXRNaWxsaXM6IHBvb2xDb25maWcuZGVzdHJveVRpbWVvdXQsXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcG9vbENvbmZpZy5pZGxlVGltZW91dCxcbiAgICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXM6IHBvb2xDb25maWcuY3JlYXRlUmV0cnlJbnRlcnZhbCxcbiAgICAgIHJlYXBJbnRlcnZhbE1pbGxpczogcG9vbENvbmZpZy5yZWFwZXJJbnRlcnZhbCxcbiAgICAgIHByb3BhZ2F0ZUNyZWF0ZUVycm9yOiBmYWxzZVxuICAgIH0pO1xuXG4gICAgLy8gU2V0IGV2ZW50c1xuICAgIHBvb2wub24oJ3JlbGVhc2UnLCBhc3luYyAocmVzb3VyY2UpID0+IHtcbiAgICAgIC8vIENsZWFyIHBhZ2VcbiAgICAgIGF3YWl0IGNsZWFyUGFnZShyZXNvdXJjZS5wYWdlLCBmYWxzZSk7XG4gICAgICBsb2coNCwgYFtwb29sXSBSZWxlYXNpbmcgYSB3b3JrZXIgd2l0aCBJRCAke3Jlc291cmNlLmlkfS5gKTtcbiAgICB9KTtcblxuICAgIHBvb2wub24oJ2Rlc3Ryb3lTdWNjZXNzJywgKGV2ZW50SWQsIHJlc291cmNlKSA9PiB7XG4gICAgICBsb2coNCwgYFtwb29sXSBEZXN0cm95ZWQgYSB3b3JrZXIgd2l0aCBJRCAke3Jlc291cmNlLmlkfS5gKTtcbiAgICB9KTtcblxuICAgIGNvbnN0IGluaXRpYWxSZXNvdXJjZXMgPSBbXTtcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwb29sQ29uZmlnLm1pbldvcmtlcnM7IGkrKykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xuICAgICAgICBpbml0aWFsUmVzb3VyY2VzLnB1c2gocmVzb3VyY2UpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW3Bvb2xdIENvdWxkIG5vdCBjcmVhdGUgYW4gaW5pdGlhbCByZXNvdXJjZS4nKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBSZWxlYXNlIHRoZSBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXMgYmFjayB0byB0aGUgcG9vbFxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcbiAgICAgIHBvb2wucmVsZWFzZShyZXNvdXJjZSk7XG4gICAgfSk7XG5cbiAgICBsb2coXG4gICAgICAzLFxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGggPyBgIHdpdGggJHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aH0gaW5pdGlhbCByZXNvdXJjZXMgd2FpdGluZy5gIDogJy4nfWBcbiAgICApO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcbiAgICAgICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLidcbiAgICApLnNldEVycm9yKGVycm9yKTtcbiAgfVxufTtcblxuLyoqXG4gKiBLaWxscyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcbiAqIGluc3RhbmNlLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgd29ya2VycyBhcmVcbiAqIGtpbGxlZCwgdGhlIHBvb2wgaXMgZGVzdHJveWVkLCBhbmQgdGhlIGJyb3dzZXIgaXMgY2xvc2VkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XG4gIGxvZygzLCAnW3Bvb2xdIEtpbGxpbmcgcG9vbCB3aXRoIGFsbCB3b3JrZXJzIGFuZCBjbG9zaW5nIGJyb3dzZXIuJyk7XG5cbiAgLy8gSWYgc3RpbGwgYWxpdmUsIGRlc3Ryb3kgdGhlIHBvb2wgb2YgcGFnZXMgYmVmb3JlIGNsb3NpbmcgYSBicm93c2VyXG4gIGlmIChwb29sKSB7XG4gICAgLy8gRnJlZSB1cCBub3QgcmVsZWFzZWQgd29ya2Vyc1xuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHBvb2wudXNlZCkge1xuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlci5yZXNvdXJjZSk7XG4gICAgfVxuXG4gICAgLy8gRGVzdHJveSB0aGUgcG9vbCBpZiBpdCBpcyBzdGlsbCBhdmFpbGFibGVcbiAgICBpZiAoIXBvb2wuZGVzdHJveWVkKSB7XG4gICAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcbiAgICAgIGxvZyg0LCAnW2Jyb3dzZXJdIERlc3Ryb3llZCB0aGUgcG9vbCBvZiByZXNvdXJjZXMuJyk7XG4gICAgfVxuICB9XG5cbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcbiAgYXdhaXQgY2xvc2VCcm93c2VyKCk7XG59XG5cbi8qKlxuICogUHJvY2Vzc2VzIHRoZSBleHBvcnQgd29yayB1c2luZyBhIHdvcmtlciBmcm9tIHRoZSBwb29sLiBBY3F1aXJlcyBhIHdvcmtlclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcbiAqIHRoZSB3b3JrZXIgaGFuZGxlIGJhY2sgdG8gdGhlIHBvb2wuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IGNoYXJ0IC0gVGhlIGNoYXJ0IGRhdGEgb3IgY29uZmlndXJhdGlvbiB0byBiZSBleHBvcnRlZC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2l0aCB0aGUgZXhwb3J0IHJlc3VsdGFuZFxuICogb3B0aW9ucy5cbiAqXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gSWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZyB0aGUgZXhwb3J0IHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBwb3N0V29yayA9IGFzeW5jIChjaGFydCwgb3B0aW9ucykgPT4ge1xuICBsZXQgd29ya2VySGFuZGxlO1xuXG4gIHRyeSB7XG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcblxuICAgICsrc3RhdHMuZXhwb3J0QXR0ZW1wdHM7XG4gICAgaWYgKHBvb2xDb25maWcuYmVuY2htYXJraW5nKSB7XG4gICAgICBnZXRQb29sSW5mbygpO1xuICAgIH1cblxuICAgIGlmICghcG9vbCkge1xuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdXb3JrIHJlY2VpdmVkLCBidXQgcG9vbCBoYXMgbm90IGJlZW4gc3RhcnRlZC4nKTtcbiAgICB9XG5cbiAgICAvLyBBY3F1aXJlIHRoZSB3b3JrZXIgYWxvbmcgd2l0aCB0aGUgaWQgb2YgcmVzb3VyY2UgYW5kIHdvcmsgY291bnRcbiAgICBjb25zdCBhY3F1aXJlQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XG4gICAgdHJ5IHtcbiAgICAgIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUuJyk7XG4gICAgICB3b3JrZXJIYW5kbGUgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xuXG4gICAgICAvLyBDaGVjayB0aGUgcGFnZSBhY3F1aXJlIHRpbWVcbiAgICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcbiAgICAgICAgbG9nKFxuICAgICAgICAgIDUsXG4gICAgICAgICAgb3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcbiAgICAgICAgICAgID8gYFtiZW5jaG1hcmtdIFJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtYFxuICAgICAgICAgICAgOiAnW2JlbmNobWFya10nLFxuICAgICAgICAgIGBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGU6ICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcbiAgICAgICAgKG9wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkXG4gICAgICAgICAgPyBgRm9yIHJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtIGBcbiAgICAgICAgICA6ICcnKSArXG4gICAgICAgICAgYEVycm9yIGVuY291bnRlcmVkIHdoZW4gYWNxdWlyaW5nIGFuIGF2YWlsYWJsZSBlbnRyeTogJHthY3F1aXJlQ291bnRlcigpfW1zLmBcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xuICAgIH1cbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGUuJyk7XG5cbiAgICBpZiAoIXdvcmtlckhhbmRsZS5wYWdlKSB7XG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAgICdSZXNvbHZlZCB3b3JrZXIgcGFnZSBpcyBpbnZhbGlkOiB0aGUgcG9vbCBzZXR1cCBpcyB3b25reS4nXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIFNhdmUgdGhlIHN0YXJ0IHRpbWVcbiAgICBsZXQgd29ya1N0YXJ0ID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XG5cbiAgICBsb2coNCwgYFtwb29sXSBTdGFydGluZyB3b3JrIG9uIHBvb2wgZW50cnkgd2l0aCBJRCAke3dvcmtlckhhbmRsZS5pZH0uYCk7XG5cbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxuICAgIGNvbnN0IGV4cG9ydENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHB1cHBldGVlckV4cG9ydCh3b3JrZXJIYW5kbGUucGFnZSwgY2hhcnQsIG9wdGlvbnMpO1xuXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxuICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgLy8gVE9ETzogSWYgdGhlIGV4cG9ydCBmYWlsZWQgYmVjYXVzZSBwdXBwZXRlZXIgdGltZWQgb3V0LCB3ZSBuZWVkIHRvIGZvcmNlIGtpbGwgdGhlIHdvcmtlciBzbyB3ZSBnZXQgYSBuZXcgcGFnZS4gVGhhdCBuZWVkcyB0byBiZSBoYW5kbGVkIGJldHRlciB0aGFuIHRoaXMgaGFjay5cbiAgICAgIGlmIChyZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpIHtcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UuY2xvc2UoKTtcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UgPSBhd2FpdCBuZXdQYWdlKCk7XG4gICAgICB9XG5cbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcbiAgICAgICAgKG9wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkXG4gICAgICAgICAgPyBgRm9yIHJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtIGBcbiAgICAgICAgICA6ICcnKSArIGBFcnJvciBlbmNvdW50ZXJlZCBkdXJpbmcgZXhwb3J0OiAke2V4cG9ydENvdW50ZXIoKX1tcy5gXG4gICAgICApLnNldEVycm9yKHJlc3VsdCk7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgdGhlIFB1cHBldGVlciBleHBvcnQgdGltZVxuICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcbiAgICAgIGxvZyhcbiAgICAgICAgNSxcbiAgICAgICAgb3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcbiAgICAgICAgICA/IGBbYmVuY2htYXJrXSBSZXF1ZXN0IHdpdGggSUQgJHtvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZH0gLWBcbiAgICAgICAgICA6ICdbYmVuY2htYXJrXScsXG4gICAgICAgIGBFeHBvcnRlZCBhIGNoYXJ0IHN1Y2Vzc2Z1bGx5OiAke2V4cG9ydENvdW50ZXIoKX1tcy5gXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIFJlbGVhc2UgdGhlIHJlc291cmNlIGJhY2sgdG8gdGhlIHBvb2xcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcblxuICAgIC8vIFVzZWQgZm9yIHN0YXRpc3RpY3MgaW4gYXZlcmFnZVRpbWUgYW5kIHByb2Nlc3NlZFdvcmtDb3VudCwgd2hpY2hcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXG4gICAgY29uc3Qgd29ya0VuZCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xuICAgIGNvbnN0IGV4cG9ydFRpbWUgPSB3b3JrRW5kIC0gd29ya1N0YXJ0O1xuICAgIHN0YXRzLnRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xuICAgIHN0YXRzLnNwZW50QXZlcmFnZSA9IHN0YXRzLnRpbWVTcGVudCAvICsrc3RhdHMucGVyZm9ybWVkRXhwb3J0cztcblxuICAgIGxvZyg0LCBgW3Bvb2xdIFdvcmsgY29tcGxldGVkIGluICR7ZXhwb3J0VGltZX0gbXMuYCk7XG5cbiAgICAvLyBPdGhlcndpc2UgcmV0dXJuIHRoZSByZXN1bHRcbiAgICByZXR1cm4ge1xuICAgICAgcmVzdWx0LFxuICAgICAgb3B0aW9uc1xuICAgIH07XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgKytzdGF0cy5kcm9wcGVkRXhwb3J0cztcblxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xuICAgIH1cblxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW3Bvb2xdIEluIHBvb2wucG9zdFdvcms6ICR7ZXJyb3IubWVzc2FnZX1gKS5zZXRFcnJvcihcbiAgICAgIGVycm9yXG4gICAgKTtcbiAgfVxufTtcblxuLyoqXG4gKiBSZXRyaWV2ZXMgdGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZS5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fG51bGx9IFRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UgaWYgaW5pdGlhbGl6ZWQsIG9yIG51bGxcbiAqIGlmIHRoZSBwb29sIGhhcyBub3QgYmVlbiBjcmVhdGVkLlxuICovXG5leHBvcnQgY29uc3QgZ2V0UG9vbCA9ICgpID0+IHBvb2w7XG5cbi8qKlxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXG4gKiB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmUgcmVxdWVzdHMuXG4gKlxuICogQHJldHVybnMge09iamVjdH0gUG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdC5cbiAqL1xuZXhwb3J0IGNvbnN0IGdldFBvb2xJbmZvSlNPTiA9ICgpID0+ICh7XG4gIG1pbjogcG9vbC5taW4sXG4gIG1heDogcG9vbC5tYXgsXG4gIGFsbDogcG9vbC5udW1GcmVlKCkgKyBwb29sLm51bVVzZWQoKSxcbiAgYXZhaWxhYmxlOiBwb29sLm51bUZyZWUoKSxcbiAgdXNlZDogcG9vbC5udW1Vc2VkKCksXG4gIHBlbmRpbmc6IHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKClcbn0pO1xuXG4vKipcbiAqIExvZ3MgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHBvb2wsIGluY2x1ZGluZyB0aGUgbWluaW11bVxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXG4gKiByZXF1ZXN0cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvKCkge1xuICBjb25zdCB7IG1pbiwgbWF4LCBhbGwsIGF2YWlsYWJsZSwgdXNlZCwgcGVuZGluZyB9ID0gZ2V0UG9vbEluZm9KU09OKCk7XG5cbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1pbmltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWlufS5gKTtcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWF4fS5gKTtcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgY3JlYXRlZCByZXNvdXJjZXM6ICR7YWxsfS5gKTtcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhdmFpbGFibGUgcmVzb3VyY2VzOiAke2F2YWlsYWJsZX0uYCk7XG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWNxdWlyZWQgcmVzb3VyY2VzOiAke3VzZWR9LmApO1xuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGFjcXVpcmVkOiAke3BlbmRpbmd9LmApO1xufVxuXG5leHBvcnQgZGVmYXVsdCB7XG4gIGluaXRQb29sLFxuICBraWxsUG9vbCxcbiAgcG9zdFdvcmssXG4gIGdldFBvb2wsXG4gIGdldFBvb2xJbmZvLFxuICBnZXRQb29sSW5mb0pTT04sXG4gIGdldFN0YXRzOiAoKSA9PiBzdGF0c1xufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XG5cbmltcG9ydCB7IGdldE9wdGlvbnMsIGluaXRFeHBvcnRTZXR0aW5ncyB9IGZyb20gJy4vY29uZmlnLmpzJztcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuaW1wb3J0IHsga2lsbFBvb2wsIHBvc3RXb3JrLCBzdGF0cyB9IGZyb20gJy4vcG9vbC5qcyc7XG5pbXBvcnQge1xuICBmaXhUeXBlLFxuICBoYW5kbGVSZXNvdXJjZXMsXG4gIGlzQ29ycmVjdEpTT04sXG4gIG9wdGlvbnNTdHJpbmdpZnksXG4gIHJvdW5kTnVtYmVyLFxuICB0b0Jvb2xlYW4sXG4gIHdyYXBBcm91bmRcbn0gZnJvbSAnLi91dGlscy5qcyc7XG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcblxubGV0IGFsbG93Q29kZUV4ZWN1dGlvbiA9IGZhbHNlO1xuXG4vKipcbiAqIFN0YXJ0cyBhbiBleHBvcnQgcHJvY2Vzcy4gVGhlIGBzZXR0aW5nc2AgY29udGFpbnMgZmluYWwgb3B0aW9ucyBnYXRoZXJlZFxuICogZnJvbSBhbGwgcG9zc2libGUgc291cmNlcyAoY29uZmlnLCBlbnYsIGNsaSwganNvbikuIFRoZSBgZW5kQ2FsbGJhY2tgIGlzXG4gKiBjYWxsZWQgd2hlbiB0aGUgZXhwb3J0IGlzIGNvbXBsZXRlZCwgd2l0aCBhbiBlcnJvciBvYmplY3QgYXMgdGhlIGZpcnN0XG4gKiBhcmd1bWVudCBhbmQgdGhlIHNlY29uZCBjb250YWluaW5nIHRoZSBiYXNlNjQgcmVzcHJlc2VudGF0aW9uIG9mIGEgY2hhcnQuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHNldHRpbmdzIC0gVGhlIHNldHRpbmdzIG9iamVjdCBjb250YWluaW5nIGV4cG9ydFxuICogY29uZmlndXJhdGlvbi5cbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxuICogZmluYWxpemluZyB3b3JrIG9yIHVwb24gZXJyb3Igb2NjdXJhbmNlIG9mIHRoZSBleHBvcnRpbmcgcHJvY2Vzcy5cbiAqXG4gKiBAcmV0dXJucyB7dm9pZH0gVGhpcyBmdW5jdGlvbiBkb2VzIG5vdCByZXR1cm4gYSB2YWx1ZSBkaXJlY3RseTsgaW5zdGVhZCxcbiAqIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgZW5kQ2FsbGJhY2suXG4gKi9cbmV4cG9ydCBjb25zdCBzdGFydEV4cG9ydCA9IGFzeW5jIChzZXR0aW5ncywgZW5kQ2FsbGJhY2spID0+IHtcbiAgLy8gU3RhcnRpbmcgZXhwb3J0aW5nIHByb2Nlc3MgbWVzc2FnZVxuICBsb2coNCwgJ1tjaGFydF0gU3RhcnRpbmcgdGhlIGV4cG9ydGluZyBwcm9jZXNzLicpO1xuXG4gIC8vIEluaXRpYWxpemUgb3B0aW9uc1xuICBjb25zdCBvcHRpb25zID0gaW5pdEV4cG9ydFNldHRpbmdzKHNldHRpbmdzLCBnZXRPcHRpb25zKCkpO1xuXG4gIC8vIEdldCB0aGUgZXhwb3J0IG9wdGlvbnNcbiAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xuXG4gIC8vIElmIFNWRyBpcyBhbiBpbnB1dCAoYXJndW1lbnQgY2FuIGJlIHNlbnQgb25seSBieSB0aGUgcmVxdWVzdClcbiAgaWYgKG9wdGlvbnMucGF5bG9hZD8uc3ZnICYmIG9wdGlvbnMucGF5bG9hZC5zdmcgIT09ICcnKSB7XG4gICAgdHJ5IHtcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgU1ZHIGlucHV0LicpO1xuXG4gICAgICBjb25zdCByZXN1bHQgPSBleHBvcnRBc1N0cmluZyhcbiAgICAgICAgc2FuaXRpemUob3B0aW9ucy5wYXlsb2FkLnN2ZyksIC8vICMyMDlcbiAgICAgICAgb3B0aW9ucyxcbiAgICAgICAgZW5kQ2FsbGJhY2tcbiAgICAgICk7XG5cbiAgICAgICsrc3RhdHMuZXhwb3J0RnJvbVN2Z0F0dGVtcHRzO1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxuICAgICAgICBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBTVkcgaW5wdXQuJykuc2V0RXJyb3IoZXJyb3IpXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8vIEV4cG9ydCB1c2luZyBvcHRpb25zIGZyb20gdGhlIGZpbGVcbiAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICYmIGV4cG9ydE9wdGlvbnMuaW5maWxlLmxlbmd0aCkge1xuICAgIC8vIFRyeSB0byByZWFkIHRoZSBmaWxlIHRvIGdldCB0aGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uXG4gICAgdHJ5IHtcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIGlucHV0IGZpbGUuJyk7XG4gICAgICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IHJlYWRGaWxlU3luYyhleHBvcnRPcHRpb25zLmluZmlsZSwgJ3V0ZjgnKTtcbiAgICAgIHJldHVybiBleHBvcnRBc1N0cmluZyhvcHRpb25zLmV4cG9ydC5pbnN0ci50cmltKCksIG9wdGlvbnMsIGVuZENhbGxiYWNrKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxuICAgICAgICBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBpbnB1dCBmaWxlLicpLnNldEVycm9yKGVycm9yKVxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvLyBFeHBvcnQgd2l0aCBvcHRpb25zIGZyb20gdGhlIHJhdyByZXByZXNlbnRhdGlvblxuICBpZiAoXG4gICAgKGV4cG9ydE9wdGlvbnMuaW5zdHIgJiYgZXhwb3J0T3B0aW9ucy5pbnN0ciAhPT0gJycpIHx8XG4gICAgKGV4cG9ydE9wdGlvbnMub3B0aW9ucyAmJiBleHBvcnRPcHRpb25zLm9wdGlvbnMgIT09ICcnKVxuICApIHtcbiAgICB0cnkge1xuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYSByYXcgaW5wdXQuJyk7XG5cbiAgICAgIC8vIFBlcmZvcm0gYSBkaXJlY3QgaW5qZWN0IHdoZW4gZm9yY2VkXG4gICAgICBpZiAodG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tTG9naWM/LmFsbG93Q29kZUV4ZWN1dGlvbikpIHtcbiAgICAgICAgcmV0dXJuIGRvU3RyYWlnaHRJbmplY3Qob3B0aW9ucywgZW5kQ2FsbGJhY2spO1xuICAgICAgfVxuXG4gICAgICAvLyBFaXRoZXIgdHJ5IHRvIHBhcnNlIHRvIEpTT04gZmlyc3Qgb3IgZG8gdGhlIGRpcmVjdCBleHBvcnRcbiAgICAgIHJldHVybiB0eXBlb2YgZXhwb3J0T3B0aW9ucy5pbnN0ciA9PT0gJ3N0cmluZydcbiAgICAgICAgPyBleHBvcnRBc1N0cmluZyhleHBvcnRPcHRpb25zLmluc3RyLnRyaW0oKSwgb3B0aW9ucywgZW5kQ2FsbGJhY2spXG4gICAgICAgIDogZG9FeHBvcnQoXG4gICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciB8fCBleHBvcnRPcHRpb25zLm9wdGlvbnMsXG4gICAgICAgICAgICBlbmRDYWxsYmFja1xuICAgICAgICAgICk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKCdbY2hhcnRdIEVycm9yIGxvYWRpbmcgcmF3IGlucHV0LicpLnNldEVycm9yKGVycm9yKVxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcbiAgcmV0dXJuIGVuZENhbGxiYWNrKFxuICAgIG5ldyBFeHBvcnRFcnJvcihcbiAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gXG4gICAgKVxuICApO1xufTtcblxuLyoqXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIHRoZSBpbmZvcm1hdGlvblxuICogaW4gdGhlIGJhdGNoIG9wdGlvbi4gVGhlIGJhdGNoIGlzIGEgc3RyaW5nIGluIHRoZSBmb2xsb3dpbmcgZm9ybWF0OlxuICogXCJpbmZpbGUxLmpzb249b3V0ZmlsZTEucG5nO2luZmlsZTIuanNvbj1vdXRmaWxlMi5wbmc7Li4uXCJcbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIGNvbmZpZ3VyYXRpb24gZm9yXG4gKiBhIGJhdGNoIGV4cG9ydC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cbiAqXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcbiAqIGFueSBvZiB0aGUgYmF0Y2ggZXhwb3J0IHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBiYXRjaEV4cG9ydCA9IGFzeW5jIChvcHRpb25zKSA9PiB7XG4gIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XG5cbiAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIC0tYmF0Y2ggYXJndW1lbnRzXG4gIGZvciAobGV0IHBhaXIgb2Ygb3B0aW9ucy5leHBvcnQuYmF0Y2guc3BsaXQoJzsnKSkge1xuICAgIHBhaXIgPSBwYWlyLnNwbGl0KCc9Jyk7XG4gICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XG4gICAgICBiYXRjaEZ1bmN0aW9ucy5wdXNoKFxuICAgICAgICBzdGFydEV4cG9ydChcbiAgICAgICAgICB7XG4gICAgICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgICAgICAgZXhwb3J0OiB7XG4gICAgICAgICAgICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxuICAgICAgICAgICAgICBpbmZpbGU6IHBhaXJbMF0sXG4gICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9LFxuICAgICAgICAgIChlcnJvciwgaW5mbykgPT4ge1xuICAgICAgICAgICAgLy8gVGhyb3cgYW4gZXJyb3JcbiAgICAgICAgICAgIGlmIChlcnJvcikge1xuICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gU2F2ZSB0aGUgYmFzZTY0IGZyb20gYSBidWZmZXIgdG8gYSBjb3JyZWN0IGltYWdlIGZpbGVcbiAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXG4gICAgICAgICAgICAgIGluZm8ub3B0aW9ucy5leHBvcnQub3V0ZmlsZSxcbiAgICAgICAgICAgICAgaW5mby5vcHRpb25zLmV4cG9ydC50eXBlICE9PSAnc3ZnJ1xuICAgICAgICAgICAgICAgID8gQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKVxuICAgICAgICAgICAgICAgIDogaW5mby5yZXN1bHRcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICApXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gQXdhaXQgYWxsIGV4cG9ydHMgYXJlIGRvbmVcbiAgICBhd2FpdCBQcm9taXNlLmFsbChiYXRjaEZ1bmN0aW9ucyk7XG5cbiAgICAvLyBLaWxsIHBvb2wgYW5kIGNsb3NlIGJyb3dzZXIgYWZ0ZXIgZmluaXNoaW5nIGJhdGNoIGV4cG9ydFxuICAgIGF3YWl0IGtpbGxQb29sKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgJ1tjaGFydF0gRXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGJhdGNoIGV4cG9ydC4nXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XG4gIH1cbn07XG5cbi8qKlxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIGNvbmZpZ3VyYXRpb24gZm9yXG4gKiBhIHNpbmdsZSBleHBvcnQuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIHNpbmdsZSBleHBvcnRcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxuICpcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xuICogdGhlIHNpbmdsZSBleHBvcnQgcHJvY2Vzcy5cbiAqL1xuZXhwb3J0IGNvbnN0IHNpbmdsZUV4cG9ydCA9IGFzeW5jIChvcHRpb25zKSA9PiB7XG4gIC8vIFVzZSBpbnN0ciBvciBpdHMgYWxpYXMsIG9wdGlvbnNcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBvcHRpb25zLmV4cG9ydC5pbnN0ciB8fCBvcHRpb25zLmV4cG9ydC5vcHRpb25zO1xuXG4gIC8vIFBlcmZvcm0gYW4gZXhwb3J0XG4gIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIGFzeW5jIChlcnJvciwgaW5mbykgPT4ge1xuICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yXG4gICAgaWYgKGVycm9yKSB7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG5cbiAgICBjb25zdCB7IG91dGZpbGUsIHR5cGUgfSA9IGluZm8ub3B0aW9ucy5leHBvcnQ7XG5cbiAgICAvLyBTYXZlIHRoZSBiYXNlNjQgZnJvbSBhIGJ1ZmZlciB0byBhIGNvcnJlY3QgaW1hZ2UgZmlsZVxuICAgIHdyaXRlRmlsZVN5bmMoXG4gICAgICBvdXRmaWxlIHx8IGBjaGFydC4ke3R5cGV9YCxcbiAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKSA6IGluZm8ucmVzdWx0XG4gICAgKTtcblxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxuICAgIGF3YWl0IGtpbGxQb29sKCk7XG4gIH0pO1xufTtcblxuLyoqXG4gKiBEZXRlcm1pbmVzIHRoZSBzaXplIGFuZCBzY2FsZSBmb3IgY2hhcnQgZXhwb3J0IGJhc2VkIG9uIHRoZSBwcm92aWRlZCBvcHRpb25zLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcbiAqIGNoYXJ0IGV4cG9ydC5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgY2FsY3VsYXRlZCBoZWlnaHQsIHdpZHRoLFxuICogYW5kIHNjYWxlIGZvciB0aGUgY2hhcnQgZXhwb3J0LlxuICovXG5leHBvcnQgY29uc3QgZmluZENoYXJ0U2l6ZSA9IChvcHRpb25zKSA9PiB7XG4gIGNvbnN0IHsgY2hhcnQsIGV4cG9ydGluZyB9ID1cbiAgICBvcHRpb25zLmV4cG9ydD8ub3B0aW9ucyB8fCBpc0NvcnJlY3RKU09OKG9wdGlvbnMuZXhwb3J0Py5pbnN0cik7XG5cbiAgLy8gU2VlIGlmIGdsb2JhbE9wdGlvbnMgaG9sZHMgY2hhcnQgb3IgZXhwb3J0aW5nIHNpemVcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IGlzQ29ycmVjdEpTT04ob3B0aW9ucy5leHBvcnQ/Lmdsb2JhbE9wdGlvbnMpO1xuXG4gIC8vIFNlY3VyZSBzY2FsZSB2YWx1ZVxuICBsZXQgc2NhbGUgPVxuICAgIG9wdGlvbnMuZXhwb3J0Py5zY2FsZSB8fFxuICAgIGV4cG9ydGluZz8uc2NhbGUgfHxcbiAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNjYWxlIHx8XG4gICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRTY2FsZSB8fFxuICAgIDE7XG5cbiAgLy8gdGhlIHNjYWxlIGNhbm5vdCBiZSBsb3dlciB0aGFuIDAuMSBhbmQgY2Fubm90IGJlIGhpZ2hlciB0aGFuIDUuMFxuICBzY2FsZSA9IE1hdGgubWF4KDAuMSwgTWF0aC5taW4oc2NhbGUsIDUuMCkpO1xuXG4gIC8vIHdlIHdhbnQgdG8gcm91bmQgdGhlIG51bWJlcnMgbGlrZSAwLjIzMjM0IC0+IDAuMjNcbiAgc2NhbGUgPSByb3VuZE51bWJlcihzY2FsZSwgMik7XG5cbiAgLy8gRmluZCBjaGFydCBzaXplIGFuZCBzY2FsZVxuICBjb25zdCBzaXplID0ge1xuICAgIGhlaWdodDpcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5oZWlnaHQgfHxcbiAgICAgIGV4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XG4gICAgICBjaGFydD8uaGVpZ2h0IHx8XG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxuICAgICAgZ2xvYmFsT3B0aW9ucz8uY2hhcnQ/LmhlaWdodCB8fFxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRIZWlnaHQgfHxcbiAgICAgIDQwMCxcbiAgICB3aWR0aDpcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py53aWR0aCB8fFxuICAgICAgZXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxuICAgICAgY2hhcnQ/LndpZHRoIHx8XG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XG4gICAgICBnbG9iYWxPcHRpb25zPy5jaGFydD8ud2lkdGggfHxcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5kZWZhdWx0V2lkdGggfHxcbiAgICAgIDYwMCxcbiAgICBzY2FsZVxuICB9O1xuXG4gIC8vIEdldCByaWQgb2YgcG90ZW50aWFsIHB4IGFuZCAlXG4gIGZvciAobGV0IFtwYXJhbSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHNpemUpKSB7XG4gICAgc2l6ZVtwYXJhbV0gPVxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcbiAgfVxuICByZXR1cm4gc2l6ZTtcbn07XG5cbi8qKlxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBiZWZvcmUgZXhwb3J0LlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cbiAqIEBwYXJhbSB7T2JqZWN0fSBjaGFydEpzb24gLSBUaGUgSlNPTiByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBjYWxsZWQgdXBvblxuICogY29tcGxldGlvbiBvciBlcnJvci5cbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcbiAqIGlzIGNvbXBsZXRlZC5cbiAqL1xuY29uc3QgZG9FeHBvcnQgPSBhc3luYyAob3B0aW9ucywgY2hhcnRKc29uLCBlbmRDYWxsYmFjaywgc3ZnKSA9PiB7XG4gIGxldCB7IGV4cG9ydDogZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWM6IGN1c3RvbUxvZ2ljT3B0aW9ucyB9ID0gb3B0aW9ucztcblxuICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQgPVxuICAgIHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uID09PSAnYm9vbGVhbidcbiAgICAgID8gY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvblxuICAgICAgOiBhbGxvd0NvZGVFeGVjdXRpb247XG5cbiAgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMpIHtcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMgPSBvcHRpb25zLmN1c3RvbUxvZ2ljID0ge307XG4gIH0gZWxzZSBpZiAoYWxsb3dDb2RlRXhlY3V0aW9uU2NvcGVkKSB7XG4gICAgaWYgKHR5cGVvZiBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcbiAgICAgIC8vIFByb2Nlc3MgcmVzb3VyY2VzXG4gICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9IGhhbmRsZVJlc291cmNlcyhcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMsXG4gICAgICAgIHRvQm9vbGVhbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93RmlsZVJlc291cmNlcylcbiAgICAgICk7XG4gICAgfSBlbHNlIGlmICghb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc291cmNlcyA9IHJlYWRGaWxlU3luYygncmVzb3VyY2VzLmpzb24nLCAndXRmOCcpO1xuICAgICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9IGhhbmRsZVJlc291cmNlcyhcbiAgICAgICAgICByZXNvdXJjZXMsXG4gICAgICAgICAgdG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzKVxuICAgICAgICApO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxuICAgICAgICAgIDIsXG4gICAgICAgICAgZXJyb3IsXG4gICAgICAgICAgYFtjaGFydF0gVW5hYmxlIHRvIGxvYWQgdGhlIGRlZmF1bHQgcmVzb3VyY2VzLmpzb24gZmlsZS5gXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gSWYgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnIGlzbid0IHNldCwgd2Ugc2hvdWxkIHJlZnVzZSB0aGUgdXNhZ2VcbiAgLy8gb2YgY2FsbGJhY2ssIHJlc291cmNlcywgYW5kIGN1c3RvbSBjb2RlLiBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbFxuICAvLyByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LiBQcmlvcml0aXplZCBzaG91bGQgYmUgdGhlIHNjb3BlZFxuICAvLyBvcHRpb24sIHRoZW4gd2Ugc2hvdWxkIHRha2UgYSBsb29rIGF0IHRoZSBvdmVyYWxsIHBvb2wgb3B0aW9uLlxuICBpZiAoIWFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCAmJiBjdXN0b21Mb2dpY09wdGlvbnMpIHtcbiAgICBpZiAoXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlXG4gICAgKSB7XG4gICAgICAvLyBTZW5kIGJhY2sgYSBmcmllbmRseSBtZXNzYWdlIHNheWluZyB0aGF0IHRoZSBleHBvcnRlciBkb2VzIG5vdCBzdXBwb3J0XG4gICAgICAvLyB0aGVzZSBzZXR0aW5ncy5cbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJyBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gXG4gICAgICAgIClcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gUmVzZXQgYWxsIGFkZGl0aW9uYWwgY3VzdG9tIGNvZGVcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gZmFsc2U7XG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBmYWxzZTtcbiAgfVxuXG4gIC8vIENsZWFuIHByb3BlcnRpZXMgdG8ga2VlcCBpdCBsZWFuIGFuZCBtZWFuXG4gIGlmIChjaGFydEpzb24pIHtcbiAgICBjaGFydEpzb24uY2hhcnQgPSBjaGFydEpzb24uY2hhcnQgfHwge307XG4gICAgY2hhcnRKc29uLmV4cG9ydGluZyA9IGNoYXJ0SnNvbi5leHBvcnRpbmcgfHwge307XG4gICAgY2hhcnRKc29uLmV4cG9ydGluZy5lbmFibGVkID0gZmFsc2U7XG4gIH1cblxuICBleHBvcnRPcHRpb25zLmNvbnN0ciA9IGV4cG9ydE9wdGlvbnMuY29uc3RyIHx8ICdjaGFydCc7XG4gIGV4cG9ydE9wdGlvbnMudHlwZSA9IGZpeFR5cGUoZXhwb3J0T3B0aW9ucy50eXBlLCBleHBvcnRPcHRpb25zLm91dGZpbGUpO1xuICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggPSBmYWxzZTtcbiAgfVxuXG4gIC8vIFByZXBhcmUgZ2xvYmFsIGFuZCB0aGVtZSBvcHRpb25zXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xuICAgIHRyeSB7XG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucyAmJiBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSkge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgdHlwZW9mIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID09PSAnc3RyaW5nJyAmJlxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLmVuZHNXaXRoKCcuanNvbicpXG4gICAgICAgICkge1xuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNDb3JyZWN0SlNPTihcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSwgJ3V0ZjgnKSxcbiAgICAgICAgICAgIHRydWVcbiAgICAgICAgICApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNDb3JyZWN0SlNPTihcbiAgICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLFxuICAgICAgICAgICAgdHJ1ZVxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSB7fTtcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFtjaGFydF0gVGhlICcke29wdGlvbnNOYW1lfScgY2Fubm90IGJlIGxvYWRlZC5gKTtcbiAgICB9XG4gIH0pO1xuXG4gIC8vIFByZXBhcmUgdGhlIGN1c3RvbUNvZGVcbiAgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24pIHtcbiAgICB0cnkge1xuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlc1xuICAgICAgKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJ2N1c3RvbUNvZGUnIGNhbm5vdCBiZSBsb2FkZWQuYCk7XG4gICAgfVxuICB9XG5cbiAgLy8gR2V0IHRoZSBjYWxsYmFja1xuICBpZiAoXG4gICAgY3VzdG9tTG9naWNPcHRpb25zICYmXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrICYmXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrPy5pbmRleE9mKCd7JykgPCAwXG4gICkge1xuICAgIC8vIFRoZSBhbGxvd0ZpbGVSZXNvdXJjZXMgaXMgYWx3YXlzIHNldCB0byBmYWxzZSBmb3IgSFRUUCByZXF1ZXN0cyB0byBhdm9pZFxuICAgIC8vIGluamVjdGluZyBhcmJpdHJhcnkgZmlsZXMgZnJvbSB0aGUgZnNcbiAgICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gcmVhZEZpbGVTeW5jKFxuICAgICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayxcbiAgICAgICAgICAndXRmOCdcbiAgICAgICAgKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IGZhbHNlO1xuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snIGNhbm5vdCBiZSBsb2FkZWQuYCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8vIFNpemUgc2VhcmNoXG4gIG9wdGlvbnMuZXhwb3J0ID0ge1xuICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxuICAgIC4uLmZpbmRDaGFydFNpemUob3B0aW9ucylcbiAgfTtcblxuICAvLyBQb3N0IHRoZSB3b3JrIHRvIHRoZSBwb29sXG4gIHRyeSB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcG9zdFdvcmsoXG4gICAgICBleHBvcnRPcHRpb25zLnN0ckluaiB8fCBjaGFydEpzb24gfHwgc3ZnLFxuICAgICAgb3B0aW9uc1xuICAgICk7XG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKGZhbHNlLCByZXN1bHQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHJldHVybiBlbmRDYWxsYmFjayhlcnJvcik7XG4gIH1cbn07XG5cbi8qKlxuICogUGVyZm9ybXMgYSBkaXJlY3QgaW5qZWN0IG9mIG9wdGlvbnMgYmVmb3JlIGV4cG9ydC4gVGhlIGZ1bmN0aW9uIGF0dGVtcHRzXG4gKiB0byBzdHJpbmdpZnkgdGhlIHByb3ZpZGVkIG9wdGlvbnMgYW5kIHJlbW92ZXMgdW5uZWNlc3NhcnkgY2hhcmFjdGVycyxcbiAqIGVuc3VyaW5nIGEgY2xlYW4gYW5kIGZvcm1hdHRlZCBpbnB1dC4gVGhlIHJlc3VsdGluZyBzdHJpbmcgaXMgc2F2ZWQgYXNcbiAqIGEgXCJzdHJpZ2h0IGluamVjdFwiIHN0cmluZyBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIEl0IHRoZW4gaW52b2tlcyB0aGVcbiAqIGRvRXhwb3J0IGZ1bmN0aW9uIHdpdGggdGhlIHVwZGF0ZWQgb3B0aW9ucy5cbiAqXG4gKiBJTVBPUlRBTlQ6IERhbmdlcm91cyBhbmQgbXVzdCBiZSB1c2VkIGRlbGliZXJhdGVseSBieSBzb21lb25lIHdobyBzZXRzIHVwXG4gKiBhIHNlcnZlciAoc2VlIHRoZSAgLS1hbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uKS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBleHBvcnQgb3B0aW9ucyBjb250YWluaW5nIHRoZSBpbnB1dFxuICogdG8gYmUgaW5qZWN0ZWQuXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkXG4gKiBhdCB0aGUgZW5kIG9mIHRoZSBwcm9jZXNzLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlfSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSByZXN1bHQgb2YgdGhlIGV4cG9ydFxuICogb3BlcmF0aW9uIG9yIHJlamVjdHMgd2l0aCBhbiBlcnJvciBpZiBhbnkgaXNzdWVzIG9jY3VyIGR1cmluZyB0aGUgcHJvY2Vzcy5cbiAqL1xuY29uc3QgZG9TdHJhaWdodEluamVjdCA9IChvcHRpb25zLCBlbmRDYWxsYmFjaykgPT4ge1xuICB0cnkge1xuICAgIGxldCBzdHJJbmo7XG4gICAgbGV0IGluc3RyID0gb3B0aW9ucy5leHBvcnQuaW5zdHIgfHwgb3B0aW9ucy5leHBvcnQub3B0aW9ucztcblxuICAgIGlmICh0eXBlb2YgaW5zdHIgIT09ICdzdHJpbmcnKSB7XG4gICAgICAvLyBUcnkgdG8gc3RyaW5naWZ5IG9wdGlvbnNcbiAgICAgIHN0ckluaiA9IGluc3RyID0gb3B0aW9uc1N0cmluZ2lmeShcbiAgICAgICAgaW5zdHIsXG4gICAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWM/LmFsbG93Q29kZUV4ZWN1dGlvblxuICAgICAgKTtcbiAgICB9XG4gICAgc3RySW5qID0gaW5zdHIucmVwbGFjZUFsbCgvXFx0fFxcbnxcXHIvZywgJycpLnRyaW0oKTtcblxuICAgIC8vIEdldCByaWQgb2YgdGhlIDtcbiAgICBpZiAoc3RySW5qW3N0ckluai5sZW5ndGggLSAxXSA9PT0gJzsnKSB7XG4gICAgICBzdHJJbmogPSBzdHJJbmouc3Vic3RyaW5nKDAsIHN0ckluai5sZW5ndGggLSAxKTtcbiAgICB9XG5cbiAgICAvLyBTYXZlIGFzIHN0cmlnaHQgaW5qZWN0IHN0cmluZ1xuICAgIG9wdGlvbnMuZXhwb3J0LnN0ckluaiA9IHN0ckluajtcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgZmFsc2UsIGVuZENhbGxiYWNrKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soXG4gICAgICBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAgIGBbY2hhcnRdIE1hbGZvcm1lZCBpbnB1dCBkZXRlY3RlZCBmb3IgJHtvcHRpb25zLmV4cG9ydD8ucmVxdWVzdElkIHx8ICc/J30uIFBsZWFzZSBtYWtlIHN1cmUgdGhhdCB5b3VyIEpTT04vSmF2YVNjcmlwdCBvcHRpb25zIGFyZSBzZW50IHVzaW5nIHRoZSBcIm9wdGlvbnNcIiBhdHRyaWJ1dGUsIGFuZCB0aGF0IGlmIHlvdSdyZSB1c2luZyBTVkcsIGl0IGlzIHVuZXNjYXBlZC5gXG4gICAgICApLnNldEVycm9yKGVycm9yKVxuICAgICk7XG4gIH1cbn07XG5cbi8qKlxuICogRXhwb3J0cyBhIHN0cmluZyBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgb3B0aW9ucyBhbmQgaW52b2tlcyBhbiBlbmQgY2FsbGJhY2suXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHN0cmluZ1RvRXhwb3J0IC0gVGhlIHN0cmluZyBjb250ZW50IHRvIGJlIGV4cG9ydGVkLlxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucywgaW5jbHVkaW5nIGN1c3RvbUxvZ2ljIHdpdGhcbiAqIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZW5kQ2FsbGJhY2sgLSBDYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkIGF0IHRoZSBlbmRcbiAqIG9mIHRoZSBleHBvcnQgcHJvY2Vzcy5cbiAqXG4gKiBAcmV0dXJucyB7YW55fSBSZXN1bHQgb2YgdGhlIGV4cG9ydCBwcm9jZXNzIG9yIGFuIGVycm9yIGlmIGVuY291bnRlcmVkLlxuICovXG5jb25zdCBleHBvcnRBc1N0cmluZyA9IChzdHJpbmdUb0V4cG9ydCwgb3B0aW9ucywgZW5kQ2FsbGJhY2spID0+IHtcbiAgY29uc3QgeyBhbGxvd0NvZGVFeGVjdXRpb24gfSA9IG9wdGlvbnMuY3VzdG9tTG9naWM7XG5cbiAgLy8gQ2hlY2sgaWYgaXQgaXMgU1ZHXG4gIGlmIChcbiAgICBzdHJpbmdUb0V4cG9ydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fFxuICAgIHN0cmluZ1RvRXhwb3J0LmluZGV4T2YoJzw/eG1sJykgPj0gMFxuICApIHtcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XG4gICAgcmV0dXJuIGRvRXhwb3J0KG9wdGlvbnMsIGZhbHNlLCBlbmRDYWxsYmFjaywgc3RyaW5nVG9FeHBvcnQpO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBUcnkgdG8gcGFyc2UgdG8gSlNPTiBhbmQgY2FsbCB0aGUgZG9FeHBvcnQgZnVuY3Rpb25cbiAgICBjb25zdCBjaGFydEpTT04gPSBKU09OLnBhcnNlKHN0cmluZ1RvRXhwb3J0LnJlcGxhY2VBbGwoL1xcdHxcXG58XFxyL2csICcgJykpO1xuXG4gICAgLy8gSWYgYSBjb3JyZWN0IEpTT04sIGRvIHRoZSBleHBvcnRcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgY2hhcnRKU09OLCBlbmRDYWxsYmFjayk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgLy8gTm90IGEgdmFsaWQgSlNPTlxuICAgIGlmICh0b0Jvb2xlYW4oYWxsb3dDb2RlRXhlY3V0aW9uKSkge1xuICAgICAgcmV0dXJuIGRvU3RyYWlnaHRJbmplY3Qob3B0aW9ucywgZW5kQ2FsbGJhY2spO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBEbyBub3QgYWxsb3cgc3RyYWlnaHQgaW5qZWN0aW9uIHdpdGhvdXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcihcbiAgICAgICAgICAnW2NoYXJ0XSBPbmx5IEpTT04gY29uZmlndXJhdGlvbnMgYW5kIFNWRyBhcmUgYWxsb3dlZCBmb3IgdGhpcyBzZXJ2ZXIuIElmIHRoaXMgaXMgeW91ciBzZXJ2ZXIsIEphdmFTY3JpcHQgY3VzdG9tIGNvZGUgY2FuIGJlIGVuYWJsZWQgYnkgc3RhcnRpbmcgdGhlIHNlcnZlciB3aXRoIHRoZSAtLWFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLidcbiAgICAgICAgKS5zZXRFcnJvcihlcnJvcilcbiAgICAgICk7XG4gICAgfVxuICB9XG59O1xuXG4vKipcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgY29kZSBleGVjdXRpb24gcGVybWlzc2lvbi5cbiAqXG4gKiBAcmV0dXJucyB7YW55fSBUaGUgdmFsdWUgb2YgYWxsb3dDb2RlRXhlY3V0aW9uLlxuICovXG5leHBvcnQgY29uc3QgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uID0gKCkgPT4gYWxsb3dDb2RlRXhlY3V0aW9uO1xuXG4vKipcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXG4gKlxuICogQHBhcmFtIHthbnl9IHZhbHVlIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCBhbmQgYXNzaWduZWRcbiAqIHRvIGFsbG93Q29kZUV4ZWN1dGlvbi5cbiAqL1xuZXhwb3J0IGNvbnN0IHNldEFsbG93Q29kZUV4ZWN1dGlvbiA9ICh2YWx1ZSkgPT4ge1xuICBhbGxvd0NvZGVFeGVjdXRpb24gPSB0b0Jvb2xlYW4odmFsdWUpO1xufTtcblxuZXhwb3J0IGRlZmF1bHQge1xuICBiYXRjaEV4cG9ydCxcbiAgc2luZ2xlRXhwb3J0LFxuICBnZXRBbGxvd0NvZGVFeGVjdXRpb24sXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbixcbiAgc3RhcnRFeHBvcnQsXG4gIGZpbmRDaGFydFNpemVcbn07XG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcblxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XG5cbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cblxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXG5cblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cblxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuLyoqXG4gKiBAb3ZlcnZpZXcgVXNlZCB0byBzYW5pdGl6ZSB0aGUgc3RyaW5ncyBjb21pbmcgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZVxuICogdG8gcHJldmVudCBYU1MgYXR0YWNrcyAod2l0aCB0aGUgRE9NUHVyaWZ5IGxpYnJhcnkpLlxuICoqL1xuXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcblxuLyoqXG4gKiBTYW5pdGl6ZXMgYSBnaXZlbiBIVE1MIHN0cmluZyBieSByZW1vdmluZyA8c2NyaXB0PiB0YWdzLlxuICogVGhpcyBmdW5jdGlvbiB1c2VzIGEgcmVndWxhciBleHByZXNzaW9uIHRvIGZpbmQgYW5kIHJlbW92ZSBhbGxcbiAqIG9jY3VycmVuY2VzIG9mIDxzY3JpcHQ+Li4uPC9zY3JpcHQ+IHRhZ3MgYW5kIGFueSBjb250ZW50IHdpdGhpbiB0aGVtLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCBUaGUgSFRNTCBzdHJpbmcgdG8gYmUgc2FuaXRpemVkLlxuICogQHJldHVybnMge3N0cmluZ30gVGhlIHNhbml0aXplZCBIVE1MIHN0cmluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplKGlucHV0KSB7XG4gIGNvbnN0IHdpbmRvdyA9IG5ldyBKU0RPTSgnJykud2luZG93O1xuICBjb25zdCBwdXJpZnkgPSBET01QdXJpZnkod2luZG93KTtcbiAgcmV0dXJuIHB1cmlmeS5zYW5pdGl6ZShpbnB1dCwgeyBBRERfVEFHUzogWydmb3JlaWduT2JqZWN0J10gfSk7XG59XG5cbmV4cG9ydCBkZWZhdWx0IHNhbml0aXplO1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcblxuLy8gQXJyYXkgdGhhdCBjb250YWlucyBpZHMgb2YgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzXG5jb25zdCBpbnRlcnZhbElkcyA9IFtdO1xuXG4vKipcbiAqIEFkZHMgaWQgb2YgYSBzZXRJbnRlcnZhbCB0byB0aGUgaW50ZXJ2YWxJZHMgYXJyYXkuXG4gKlxuICogQHBhcmFtIHtOb2RlSlMuVGltZW91dH0gaWQgLSBJZCBvZiBhbiBpbnRlcnZhbC5cbiAqL1xuZXhwb3J0IGNvbnN0IGFkZEludGVydmFsID0gKGlkKSA9PiB7XG4gIGludGVydmFsSWRzLnB1c2goaWQpO1xufTtcblxuLyoqXG4gKiBDbGVhcnMgYWxsIG9mIG9uZ29pbmcgaW50ZXJ2YWxzIGJ5IGlkcyBnYXRoZXJlZCBpbiB0aGUgaW50ZXJ2YWxJZHMgYXJyYXkuXG4gKi9cbmV4cG9ydCBjb25zdCBjbGVhckFsbEludGVydmFscyA9ICgpID0+IHtcbiAgbG9nKDQsIGBbc2VydmVyXSBDbGVhcmluZyBhbGwgcmVnaXN0ZXJlZCBpbnRlcnZhbHMuYCk7XG4gIGZvciAoY29uc3QgaWQgb2YgaW50ZXJ2YWxJZHMpIHtcbiAgICBjbGVhckludGVydmFsKGlkKTtcbiAgfVxufTtcblxuZXhwb3J0IGRlZmF1bHQge1xuICBhZGRJbnRlcnZhbCxcbiAgY2xlYXJBbGxJbnRlcnZhbHNcbn07XG4iLCJpbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi4vZW52cy5qcyc7XG5pbXBvcnQgeyBsb2dXaXRoU3RhY2sgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xuXG4vKipcbiAqIE1pZGRsZXdhcmUgZm9yIGxvZ2dpbmcgZXJyb3JzIHdpdGggc3RhY2sgdHJhY2UgYW5kIGhhbmRsaW5nIGVycm9yIHJlc3BvbnNlLlxuICpcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXEgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXG4gKi9cbmNvbnN0IGxvZ0Vycm9yTWlkZGxld2FyZSA9IChlcnJvciwgcmVxLCByZXMsIG5leHQpID0+IHtcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XG4gIGxvZ1dpdGhTdGFjaygxLCBlcnJvcik7XG5cbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XG4gIGlmIChlbnZzLk9USEVSX05PREVfRU5WICE9PSAnZGV2ZWxvcG1lbnQnKSB7XG4gICAgZGVsZXRlIGVycm9yLnN0YWNrO1xuICB9XG5cbiAgLy8gQ2FsbCB0aGUgcmV0dXJuRXJyb3JNaWRkbGV3YXJlXG4gIG5leHQoZXJyb3IpO1xufTtcblxuLyoqXG4gKiBNaWRkbGV3YXJlIGZvciByZXR1cm5pbmcgZXJyb3IgcmVzcG9uc2UuXG4gKlxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcSAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXMgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cbiAqL1xuY29uc3QgcmV0dXJuRXJyb3JNaWRkbGV3YXJlID0gKGVycm9yLCByZXEsIHJlcywgbmV4dCkgPT4ge1xuICAvLyBHYXRoZXIgYWxsIHJlcXVpZWQgaW5mb3JtYXRpb24gZm9yIHRoZSByZXNwb25zZVxuICBjb25zdCB7IHN0YXR1c0NvZGU6IHN0Q29kZSwgc3RhdHVzLCBtZXNzYWdlLCBzdGFjayB9ID0gZXJyb3I7XG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBzdENvZGUgfHwgc3RhdHVzIHx8IDUwMDtcblxuICAvLyBTZXQgYW5kIHJldHVybiByZXNwb25zZVxuICByZXMuc3RhdHVzKHN0YXR1c0NvZGUpLmpzb24oeyBzdGF0dXNDb2RlLCBtZXNzYWdlLCBzdGFjayB9KTtcbn07XG5cbmV4cG9ydCBkZWZhdWx0IChhcHApID0+IHtcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXG4gIGFwcC51c2UobG9nRXJyb3JNaWRkbGV3YXJlKTtcblxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcbiAgYXBwLnVzZShyZXR1cm5FcnJvck1pZGRsZXdhcmUpO1xufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgcmF0ZUxpbWl0IGZyb20gJ2V4cHJlc3MtcmF0ZS1saW1pdCc7XG5cbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XG5cbi8qKlxuICogTWlkZGxld2FyZSBmb3IgZW5hYmxpbmcgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc3BlY2lmaWVkIEV4cHJlc3MgYXBwLlxuICpcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxuICogQHBhcmFtIHtPYmplY3R9IGxpbWl0Q29uZmlnIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciByYXRlIGxpbWl0aW5nLlxuICovXG5leHBvcnQgZGVmYXVsdCAoYXBwLCBsaW1pdENvbmZpZykgPT4ge1xuICBjb25zdCBtc2cgPVxuICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcblxuICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXG4gIGNvbnN0IHJhdGVPcHRpb25zID0ge1xuICAgIG1heDogbGltaXRDb25maWcubWF4UmVxdWVzdHMgfHwgMzAsXG4gICAgd2luZG93OiBsaW1pdENvbmZpZy53aW5kb3cgfHwgMSxcbiAgICBkZWxheTogbGltaXRDb25maWcuZGVsYXkgfHwgMCxcbiAgICB0cnVzdFByb3h5OiBsaW1pdENvbmZpZy50cnVzdFByb3h5IHx8IGZhbHNlLFxuICAgIHNraXBLZXk6IGxpbWl0Q29uZmlnLnNraXBLZXkgfHwgZmFsc2UsXG4gICAgc2tpcFRva2VuOiBsaW1pdENvbmZpZy5za2lwVG9rZW4gfHwgZmFsc2VcbiAgfTtcblxuICAvLyBTZXQgaWYgYmVoaW5kIGEgcHJveHlcbiAgaWYgKHJhdGVPcHRpb25zLnRydXN0UHJveHkpIHtcbiAgICBhcHAuZW5hYmxlKCd0cnVzdCBwcm94eScpO1xuICB9XG5cbiAgLy8gQ3JlYXRlIGEgbGltaXRlclxuICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcbiAgICB3aW5kb3dNczogcmF0ZU9wdGlvbnMud2luZG93ICogNjAgKiAxMDAwLFxuICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciB3aW5kb3dNc1xuICAgIG1heDogcmF0ZU9wdGlvbnMubWF4LFxuICAgIC8vIERpc2FibGUgZGVsYXlpbmcsIGZ1bGwgc3BlZWQgdW50aWwgdGhlIG1heCBsaW1pdCBpcyByZWFjaGVkXG4gICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXG4gICAgaGFuZGxlcjogKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XG4gICAgICByZXNwb25zZS5mb3JtYXQoe1xuICAgICAgICBqc29uOiAoKSA9PiB7XG4gICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZCh7IG1lc3NhZ2U6IG1zZyB9KTtcbiAgICAgICAgfSxcbiAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xuICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobXNnKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSxcbiAgICBza2lwOiAocmVxdWVzdCkgPT4ge1xuICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcbiAgICAgIGlmIChcbiAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcEtleSAhPT0gZmFsc2UgJiZcbiAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBmYWxzZSAmJlxuICAgICAgICByZXF1ZXN0LnF1ZXJ5LmtleSA9PT0gcmF0ZU9wdGlvbnMuc2tpcEtleSAmJlxuICAgICAgICByZXF1ZXN0LnF1ZXJ5LmFjY2Vzc190b2tlbiA9PT0gcmF0ZU9wdGlvbnMuc2tpcFRva2VuXG4gICAgICApIHtcbiAgICAgICAgbG9nKDQsICdbcmF0ZSBsaW1pdGluZ10gU2tpcHBpbmcgcmF0ZSBsaW1pdGVyLicpO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH0pO1xuXG4gIC8vIFVzZSBhIGxpbWl0ZXIgYXMgYSBtaWRkbGV3YXJlXG4gIGFwcC51c2UobGltaXRlcik7XG5cbiAgbG9nKFxuICAgIDMsXG4gICAgYFtyYXRlIGxpbWl0aW5nXSBFbmFibGVkIHJhdGUgbGltaXRpbmcgd2l0aCAke3JhdGVPcHRpb25zLm1heH0gcmVxdWVzdHMgcGVyICR7cmF0ZU9wdGlvbnMud2luZG93fSBtaW51dGUgZm9yIGVhY2ggSVAsIHRydXN0aW5nIHByb3h5OiAke3JhdGVPcHRpb25zLnRydXN0UHJveHl9LmBcbiAgKTtcbn07XG4iLCJpbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9FeHBvcnRFcnJvci5qcyc7XG5cbmNsYXNzIEh0dHBFcnJvciBleHRlbmRzIEV4cG9ydEVycm9yIHtcbiAgY29uc3RydWN0b3IobWVzc2FnZSwgc3RhdHVzKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgdGhpcy5zdGF0dXMgPSB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXM7XG4gIH1cblxuICBzZXRTdGF0dXMoc3RhdHVzKSB7XG4gICAgdGhpcy5zdGF0dXMgPSBzdGF0dXM7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgSHR0cEVycm9yO1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IHVwZGF0ZVZlcnNpb24sIHZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi4vLi4vZW52cy5qcyc7XG5cbmltcG9ydCBIdHRwRXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0h0dHBFcnJvci5qcyc7XG5cbi8qKlxuICogQWRkcyB0aGUgUE9TVCAvY2hhbmdlX2hjX3ZlcnNpb24vOm5ld1ZlcnNpb24gcm91dGUgdGhhdCBjYW4gYmUgdXRpbGl6ZWQgdG8gbW9kaWZ5XG4gKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXG4gKlxuICogVE9ETzogQWRkIGF1dGggdG9rZW4gYW5kIGNvbm5lY3QgdG8gQVBJXG4gKi9cbmV4cG9ydCBkZWZhdWx0IChhcHApID0+XG4gICFhcHBcbiAgICA/IGZhbHNlXG4gICAgOiBhcHAucG9zdChcbiAgICAgICAgJy92ZXJzaW9uL2NoYW5nZS86bmV3VmVyc2lvbicsXG4gICAgICAgIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBhZG1pblRva2VuID0gZW52cy5ISUdIQ0hBUlRTX0FETUlOX1RPS0VOO1xuXG4gICAgICAgICAgICAvLyBDaGVjayB0aGUgZXhpc3RlbmNlIG9mIHRoZSB0b2tlblxuICAgICAgICAgICAgaWYgKCFhZG1pblRva2VuIHx8ICFhZG1pblRva2VuLmxlbmd0aCkge1xuICAgICAgICAgICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxuICAgICAgICAgICAgICAgICdUaGUgc2VydmVyIGlzIG5vdCBjb25maWd1cmVkIHRvIHBlcmZvcm0gcnVuLXRpbWUgdmVyc2lvbiBjaGFuZ2VzOiBISUdIQ0hBUlRTX0FETUlOX1RPS0VOIGlzIG5vdCBzZXQuJyxcbiAgICAgICAgICAgICAgICA0MDFcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXG4gICAgICAgICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XG4gICAgICAgICAgICBpZiAoIXRva2VuIHx8IHRva2VuICE9PSBhZG1pblRva2VuKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXG4gICAgICAgICAgICAgICAgJ0ludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcbiAgICAgICAgICAgICAgICA0MDFcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xuICAgICAgICAgICAgY29uc3QgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XG4gICAgICAgICAgICBpZiAobmV3VmVyc2lvbikge1xuICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tbmFtZWQtYXMtZGVmYXVsdC1tZW1iZXJcbiAgICAgICAgICAgICAgICBhd2FpdCB1cGRhdGVWZXJzaW9uKG5ld1ZlcnNpb24pO1xuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXG4gICAgICAgICAgICAgICAgICBgVmVyc2lvbiBjaGFuZ2U6ICR7ZXJyb3IubWVzc2FnZX1gLFxuICAgICAgICAgICAgICAgICAgZXJyb3Iuc3RhdHVzQ29kZVxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgLy8gU3VjY2Vzc1xuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoMjAwKS5zZW5kKHtcbiAgICAgICAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXG4gICAgICAgICAgICAgICAgdmVyc2lvbjogdmVyc2lvbigpLFxuICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoJ05vIG5ldyB2ZXJzaW9uIHN1cHBsaWVkLicsIDQwMCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIG5leHQoZXJyb3IpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgKTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XG5cbmltcG9ydCB7IGdldEFsbG93Q29kZUV4ZWN1dGlvbiwgc3RhcnRFeHBvcnQgfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XG5pbXBvcnQgeyBnZXRPcHRpb25zLCBtZXJnZUNvbmZpZ09wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcbmltcG9ydCB7XG4gIGZpeFR5cGUsXG4gIGlzQ29ycmVjdEpTT04sXG4gIGlzT2JqZWN0RW1wdHksXG4gIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQsXG4gIG9wdGlvbnNTdHJpbmdpZnksXG4gIG1lYXN1cmVUaW1lXG59IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcblxuaW1wb3J0IEh0dHBFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvSHR0cEVycm9yLmpzJztcblxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xuY29uc3QgcmV2ZXJzZWRNaW1lID0ge1xuICBwbmc6ICdpbWFnZS9wbmcnLFxuICBqcGVnOiAnaW1hZ2UvanBlZycsXG4gIGdpZjogJ2ltYWdlL2dpZicsXG4gIHBkZjogJ2FwcGxpY2F0aW9uL3BkZicsXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXG59O1xuXG4vLyBUaGUgcmVxdWVzdHMgY291bnRlclxubGV0IHJlcXVlc3RzQ291bnRlciA9IDA7XG5cbi8vIFRoZSBhcnJheSBvZiBjYWxsYmFja3MgdG8gY2FsbCBiZWZvcmUgYSByZXF1ZXN0XG5jb25zdCBiZWZvcmVSZXF1ZXN0ID0gW107XG5cbi8vIFRoZSBhcnJheSBvZiBjYWxsYmFja3MgdG8gY2FsbCBhZnRlciBhIHJlcXVlc3RcbmNvbnN0IGFmdGVyUmVxdWVzdCA9IFtdO1xuXG4vKipcbiAqIEludm9rZXMgYW4gYXJyYXkgb2YgY2FsbGJhY2sgZnVuY3Rpb25zIHdpdGggc3BlY2lmaWVkIHBhcmFtZXRlcnMsIGFsbG93aW5nXG4gKiBjdXN0b21pemF0aW9uIG9mIHJlcXVlc3QgaGFuZGxpbmcuXG4gKlxuICogQHBhcmFtIHtGdW5jdGlvbltdfSBjYWxsYmFja3MgLSBBbiBhcnJheSBvZiBjYWxsYmFjayBmdW5jdGlvbnNcbiAqIHRvIGJlIGV4ZWN1dGVkLlxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YSAtIEFuIG9iamVjdCBjb250YWluaW5nIHBhcmFtZXRlcnMgbGlrZSBpZCwgdW5pcXVlSWQsXG4gKiB0eXBlLCBhbmQgYm9keS5cbiAqXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBSZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHRoZSBvdmVyYWxsIHJlc3VsdFxuICogb2YgdGhlIGNhbGxiYWNrIGludm9jYXRpb25zLlxuICovXG5jb25zdCBkb0NhbGxiYWNrcyA9IChjYWxsYmFja3MsIHJlcXVlc3QsIHJlc3BvbnNlLCBkYXRhKSA9PiB7XG4gIGxldCByZXN1bHQgPSB0cnVlO1xuICBjb25zdCB7IGlkLCB1bmlxdWVJZCwgdHlwZSwgYm9keSB9ID0gZGF0YTtcblxuICBjYWxsYmFja3Muc29tZSgoY2FsbGJhY2spID0+IHtcbiAgICBpZiAoY2FsbGJhY2spIHtcbiAgICAgIGxldCBjYWxsUmVzcG9uc2UgPSBjYWxsYmFjayhyZXF1ZXN0LCByZXNwb25zZSwgaWQsIHVuaXF1ZUlkLCB0eXBlLCBib2R5KTtcblxuICAgICAgaWYgKGNhbGxSZXNwb25zZSAhPT0gdW5kZWZpbmVkICYmIGNhbGxSZXNwb25zZSAhPT0gdHJ1ZSkge1xuICAgICAgICByZXN1bHQgPSBjYWxsUmVzcG9uc2U7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfSk7XG5cbiAgcmV0dXJuIHJlc3VsdDtcbn07XG5cbi8qKlxuICogSGFuZGxlcyB0aGUgZXhwb3J0IHJlcXVlc3RzIGZyb20gdGhlIGNsaWVudC5cbiAqXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGV4cG9ydCBwcm9jZXNzXG4gKiBpcyBjb21wbGV0ZS5cbiAqL1xuY29uc3QgZXhwb3J0SGFuZGxlciA9IGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xuICB0cnkge1xuICAgIC8vIFN0YXJ0IGNvdW50aW5nIHRpbWVcbiAgICBjb25zdCBzdG9wQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XG5cbiAgICAvLyBDcmVhdGUgYSB1bmlxdWUgSUQgZm9yIGEgcmVxdWVzdFxuICAgIGNvbnN0IHVuaXF1ZUlkID0gdXVpZCgpLnJlcGxhY2UoLy0vZywgJycpO1xuXG4gICAgLy8gR2V0IHRoZSBjdXJyZW50IHNlcnZlcidzIGdlbmVyYWwgb3B0aW9uc1xuICAgIGNvbnN0IGRlZmF1bHRPcHRpb25zID0gZ2V0T3B0aW9ucygpO1xuXG4gICAgY29uc3QgYm9keSA9IHJlcXVlc3QuYm9keTtcbiAgICBjb25zdCBpZCA9ICsrcmVxdWVzdHNDb3VudGVyO1xuXG4gICAgbGV0IHR5cGUgPSBmaXhUeXBlKGJvZHkudHlwZSk7XG5cbiAgICAvLyBUaHJvdyAnQmFkIFJlcXVlc3QnIGlmIHRoZXJlJ3Mgbm8gYm9keVxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxuICAgICAgICAnVGhlIHJlcXVlc3QgYm9keSBpcyByZXF1aXJlZC4gUGxlYXNlIGVuc3VyZSB0aGF0IHlvdXIgQ29udGVudC1UeXBlIGhlYWRlciBpcyBjb3JyZWN0IChhY2NlcHRlZCB0eXBlcyBhcmUgYXBwbGljYXRpb24vanNvbiBhbmQgbXVsdGlwYXJ0L2Zvcm0tZGF0YSkuJyxcbiAgICAgICAgNDAwXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIEFsbCBvZiB0aGUgYmVsb3cgY2FuIGJlIHVzZWRcbiAgICBsZXQgaW5zdHIgPSBpc0NvcnJlY3RKU09OKGJvZHkuaW5maWxlIHx8IGJvZHkub3B0aW9ucyB8fCBib2R5LmRhdGEpO1xuXG4gICAgLy8gVGhyb3cgJ0JhZCBSZXF1ZXN0JyBpZiB0aGVyZSdzIG5vIEpTT04gb3IgU1ZHIHRvIGV4cG9ydFxuICAgIGlmICghaW5zdHIgJiYgIWJvZHkuc3ZnKSB7XG4gICAgICBsb2coXG4gICAgICAgIDIsXG4gICAgICAgIGBUaGUgcmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9IGZyb20gJHtcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUGF5bG9hZCByZWNlaXZlZDogJHtKU09OLnN0cmluZ2lmeShib2R5KX0uYFxuICAgICAgKTtcblxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcbiAgICAgICAgXCJObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLlwiLFxuICAgICAgICA0MDBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgbGV0IGNhbGxSZXNwb25zZSA9IGZhbHNlO1xuXG4gICAgLy8gQ2FsbCB0aGUgYmVmb3JlIHJlcXVlc3QgZnVuY3Rpb25zXG4gICAgY2FsbFJlc3BvbnNlID0gZG9DYWxsYmFja3MoYmVmb3JlUmVxdWVzdCwgcmVxdWVzdCwgcmVzcG9uc2UsIHtcbiAgICAgIGlkLFxuICAgICAgdW5pcXVlSWQsXG4gICAgICB0eXBlLFxuICAgICAgYm9keVxuICAgIH0pO1xuXG4gICAgLy8gQmxvY2sgdGhlIHJlcXVlc3QgaWYgb25lIG9mIGEgY2FsbGJhY2tzIGZhaWxlZFxuICAgIGlmIChjYWxsUmVzcG9uc2UgIT09IHRydWUpIHtcbiAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGNhbGxSZXNwb25zZSk7XG4gICAgfVxuXG4gICAgbGV0IGNvbm5lY3Rpb25BYm9ydGVkID0gZmFsc2U7XG5cbiAgICAvLyBJbiBjYXNlIHRoZSBjb25uZWN0aW9uIGlzIGNsb3NlZCwgZm9yY2UgdG8gYWJvcnQgZnVydGhlciBhY3Rpb25zXG4gICAgcmVxdWVzdC5zb2NrZXQub24oJ2Nsb3NlJywgKCkgPT4ge1xuICAgICAgY29ubmVjdGlvbkFib3J0ZWQgPSB0cnVlO1xuICAgIH0pO1xuXG4gICAgbG9nKDQsIGBbZXhwb3J0XSBHb3QgYW4gaW5jb21pbmcgSFRUUCByZXF1ZXN0IHdpdGggSUQgJHt1bmlxdWVJZH0uYCk7XG5cbiAgICBib2R5LmNvbnN0ciA9ICh0eXBlb2YgYm9keS5jb25zdHIgPT09ICdzdHJpbmcnICYmIGJvZHkuY29uc3RyKSB8fCAnY2hhcnQnO1xuXG4gICAgLy8gR2F0aGVyIGFuZCBvcmdhbml6ZSBvcHRpb25zIGZyb20gdGhlIHBheWxvYWRcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHtcbiAgICAgIGV4cG9ydDoge1xuICAgICAgICBpbnN0cixcbiAgICAgICAgdHlwZSxcbiAgICAgICAgY29uc3RyOiBib2R5LmNvbnN0clswXS50b0xvd2VyQ2FzZSgpICsgYm9keS5jb25zdHIuc3Vic3RyKDEpLFxuICAgICAgICBoZWlnaHQ6IGJvZHkuaGVpZ2h0LFxuICAgICAgICB3aWR0aDogYm9keS53aWR0aCxcbiAgICAgICAgc2NhbGU6IGJvZHkuc2NhbGUgfHwgZGVmYXVsdE9wdGlvbnMuZXhwb3J0LnNjYWxlLFxuICAgICAgICBnbG9iYWxPcHRpb25zOiBpc0NvcnJlY3RKU09OKGJvZHkuZ2xvYmFsT3B0aW9ucywgdHJ1ZSksXG4gICAgICAgIHRoZW1lT3B0aW9uczogaXNDb3JyZWN0SlNPTihib2R5LnRoZW1lT3B0aW9ucywgdHJ1ZSlcbiAgICAgIH0sXG4gICAgICBjdXN0b21Mb2dpYzoge1xuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb246IGdldEFsbG93Q29kZUV4ZWN1dGlvbigpLFxuICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IGZhbHNlLFxuICAgICAgICByZXNvdXJjZXM6IGlzQ29ycmVjdEpTT04oYm9keS5yZXNvdXJjZXMsIHRydWUpLFxuICAgICAgICBjYWxsYmFjazogYm9keS5jYWxsYmFjayxcbiAgICAgICAgY3VzdG9tQ29kZTogYm9keS5jdXN0b21Db2RlXG4gICAgICB9XG4gICAgfTtcblxuICAgIGlmIChpbnN0cikge1xuICAgICAgLy8gU3RyaW5naWZ5IEpTT04gd2l0aCBvcHRpb25zXG4gICAgICByZXF1ZXN0T3B0aW9ucy5leHBvcnQuaW5zdHIgPSBvcHRpb25zU3RyaW5naWZ5KFxuICAgICAgICBpbnN0cixcbiAgICAgICAgcmVxdWVzdE9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIE1lcmdlIHRoZSByZXF1ZXN0IG9wdGlvbnMgaW50byBkZWZhdWx0IG9uZXNcbiAgICBjb25zdCBvcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKGRlZmF1bHRPcHRpb25zLCByZXF1ZXN0T3B0aW9ucyk7XG5cbiAgICAvLyBTYXZlIHRoZSBKU09OIGlmIGV4aXN0c1xuICAgIG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnMgPSBpbnN0cjtcblxuICAgIC8vIExhc3RseSwgYWRkIHRoZSBzZXJ2ZXIgc3BlY2lmaWMgYXJndW1lbnRzIGludG8gb3B0aW9ucyBhcyBwYXlsb2FkXG4gICAgb3B0aW9ucy5wYXlsb2FkID0ge1xuICAgICAgc3ZnOiBib2R5LnN2ZyB8fCBmYWxzZSxcbiAgICAgIGI2NDogYm9keS5iNjQgfHwgZmFsc2UsXG4gICAgICBub0Rvd25sb2FkOiBib2R5Lm5vRG93bmxvYWQgfHwgZmFsc2UsXG4gICAgICByZXF1ZXN0SWQ6IHVuaXF1ZUlkXG4gICAgfTtcblxuICAgIC8vIFRlc3QgeGxpbms6aHJlZiBlbGVtZW50cyBmcm9tIHBheWxvYWQncyBTVkdcbiAgICBpZiAoYm9keS5zdmcgJiYgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChvcHRpb25zLnBheWxvYWQuc3ZnKSkge1xuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcbiAgICAgICAgJ1NWRyBwb3RlbnRpYWxseSBjb250YWluIGF0IGxlYXN0IG9uZSBmb3JiaWRkZW4gVVJMIGluIHhsaW5rOmhyZWYgZWxlbWVudC4gUGxlYXNlIHJldmlldyB0aGUgU1ZHIGNvbnRlbnQgYW5kIGVuc3VyZSB0aGF0IGFsbCByZWZlcmVuY2VkIFVSTHMgY29tcGx5IHdpdGggc2VjdXJpdHkgcG9saWNpZXMuJyxcbiAgICAgICAgNDAwXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xuICAgIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIChlcnJvciwgaW5mbykgPT4ge1xuICAgICAgLy8gUmVtb3ZlIHRoZSBjbG9zZSBldmVudCBmcm9tIHRoZSBzb2NrZXRcbiAgICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcblxuICAgICAgLy8gQWZ0ZXIgdGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzXG4gICAgICBpZiAoZGVmYXVsdE9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xuICAgICAgICBsb2coXG4gICAgICAgICAgNSxcbiAgICAgICAgICBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9IC0gQWZ0ZXIgdGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzOiAke3N0b3BDb3VudGVyKCl9bXMuYFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICAvLyBJZiB0aGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkLCBkbyBub3RoaW5nXG4gICAgICBpZiAoY29ubmVjdGlvbkFib3J0ZWQpIHtcbiAgICAgICAgcmV0dXJuIGxvZyhcbiAgICAgICAgICAzLFxuICAgICAgICAgIGBbZXhwb3J0XSBUaGUgY2xpZW50IGNsb3NlZCB0aGUgY29ubmVjdGlvbiBiZWZvcmUgdGhlIGNoYXJ0IGZpbmlzaGVkIHByb2Nlc3NpbmcuYFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICAvLyBJZiBlcnJvciwgbG9nIGl0IGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXG4gICAgICBpZiAoZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIGRhdGEgaXMgbWlzc2luZywgbG9nIHRoZSBtZXNzYWdlIGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXG4gICAgICBpZiAoIWluZm8gfHwgIWluZm8ucmVzdWx0KSB7XG4gICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXG4gICAgICAgICAgYFVuZXhwZWN0ZWQgcmV0dXJuIGZyb20gY2hhcnQgZ2VuZXJhdGlvbi4gUGxlYXNlIGNoZWNrIHlvdXIgcmVxdWVzdCBkYXRhLiBGb3IgdGhlIHJlcXVlc3Qgd2l0aCBJRCAke3VuaXF1ZUlkfSwgdGhlIHJlc3VsdCBpcyAke2luZm8ucmVzdWx0fS5gLFxuICAgICAgICAgIDQwMFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICAvLyBHZXQgdGhlIHR5cGUgZnJvbSBvcHRpb25zXG4gICAgICB0eXBlID0gaW5mby5vcHRpb25zLmV4cG9ydC50eXBlO1xuXG4gICAgICAvLyBUaGUgYWZ0ZXIgcmVxdWVzdCBjYWxsYmFja3NcbiAgICAgIGRvQ2FsbGJhY2tzKGFmdGVyUmVxdWVzdCwgcmVxdWVzdCwgcmVzcG9uc2UsIHsgaWQsIGJvZHk6IGluZm8ucmVzdWx0IH0pO1xuXG4gICAgICBpZiAoaW5mby5yZXN1bHQpIHtcbiAgICAgICAgLy8gSWYgb25seSBiYXNlNjQgaXMgcmVxdWlyZWQsIHJldHVybiBpdFxuICAgICAgICBpZiAoYm9keS5iNjQpIHtcbiAgICAgICAgICAvLyBTVkcgRXhjZXB0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyAxMS4zLjAgdmVyc2lvblxuICAgICAgICAgIGlmICh0eXBlID09PSAncGRmJyB8fCB0eXBlID09ICdzdmcnKSB7XG4gICAgICAgICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChcbiAgICAgICAgICAgICAgQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICd1dGY4JykudG9TdHJpbmcoJ2Jhc2U2NCcpXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGluZm8ucmVzdWx0KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFNldCBjb3JyZWN0IGNvbnRlbnQgdHlwZVxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XG5cbiAgICAgICAgLy8gRGVjaWRlIHdoZXRoZXIgdG8gZG93bmxvYWQgb3Igbm90IGNoYXJ0IGZpbGVcbiAgICAgICAgaWYgKCFib2R5Lm5vRG93bmxvYWQpIHtcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KFxuICAgICAgICAgICAgYCR7cmVxdWVzdC5wYXJhbXMuZmlsZW5hbWUgfHwgcmVxdWVzdC5ib2R5LmZpbGVuYW1lIHx8ICdjaGFydCd9LiR7XG4gICAgICAgICAgICAgIHR5cGUgfHwgJ3BuZydcbiAgICAgICAgICAgIH1gXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIElmIFNWRywgcmV0dXJuIHBsYWluIGNvbnRlbnRcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGluZm8ucmVzdWx0KVxuICAgICAgICAgIDogcmVzcG9uc2Uuc2VuZChCdWZmZXIuZnJvbShpbmZvLnJlc3VsdCwgJ2Jhc2U2NCcpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBuZXh0KGVycm9yKTtcbiAgfVxufTtcblxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT4ge1xuICAvKipcbiAgICogQWRkcyB0aGUgUE9TVCAvIGEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3QgZW5kcG9pbnQuXG4gICAqL1xuICBhcHAucG9zdCgnLycsIGV4cG9ydEhhbmRsZXIpO1xuXG4gIC8qKlxuICAgKiBBZGRzIHRoZSBQT1NUIC86ZmlsZW5hbWUgYSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyB3aXRoXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cbiAgICovXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgZXhwb3J0SGFuZGxlcik7XG59O1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcbmltcG9ydCB7IGpvaW4gYXMgcGF0aGVyIH0gZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xuXG5pbXBvcnQgeyB2ZXJzaW9uIH0gZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xuaW1wb3J0IHsgYWRkSW50ZXJ2YWwgfSBmcm9tICcuLi8uLi9pbnRlcnZhbHMuanMnO1xuaW1wb3J0IHBvb2wgZnJvbSAnLi4vLi4vcG9vbC5qcyc7XG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XG5cbmNvbnN0IHBrZ0ZpbGUgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhwYXRoZXIoX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpKTtcblxuY29uc3Qgc2VydmVyU3RhcnRUaW1lID0gbmV3IERhdGUoKTtcblxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XG5jb25zdCByZWNvcmRJbnRlcnZhbCA9IDYwICogMTAwMDsgLy8gcmVjb3JkIGV2ZXJ5IG1pbnV0ZVxuY29uc3Qgd2luZG93U2l6ZSA9IDMwOyAvLyAzMCBtaW51dGVzXG5cbi8qKlxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgc3VjY2Vzc1JhdGVzXG4gKiBhcnJheS5cbiAqXG4gKiBAcmV0dXJucyB7bnVtYmVyfSAtIEEgbW92aW5nIGF2ZXJhZ2UgZm9yIHN1Y2Nlc3MgcmF0aW8gb2YgdGhlIHNlcnZlciBleHBvcnRzLlxuICovXG5mdW5jdGlvbiBjYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCkge1xuICBjb25zdCBzdW0gPSBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCk7XG4gIHJldHVybiBzdW0gLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xufVxuXG4vKipcbiAqIFN0YXJ0cyB0aGUgaW50ZXJ2YWwgcmVzcG9uc2libGUgZm9yIGNhbGN1bGF0aW5nIGN1cnJlbnQgc3VjY2VzcyByYXRlIHJhdGlvXG4gKiBhbmQgZ2F0aGVyc1xuICpcbiAqIEByZXR1cm5zIHtOb2RlSlMuVGltZW91dH0gaWQgLSBJZCBvZiBhbiBpbnRlcnZhbC5cbiAqL1xuZXhwb3J0IGNvbnN0IHN0YXJ0U3VjY2Vzc1JhdGUgPSAoKSA9PlxuICBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgY29uc3Qgc3RhdHMgPSBwb29sLmdldFN0YXRzKCk7XG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID1cbiAgICAgIHN0YXRzLmV4cG9ydEF0dGVtcHRzID09PSAwXG4gICAgICAgID8gMVxuICAgICAgICA6IChzdGF0cy5wZXJmb3JtZWRFeHBvcnRzIC8gc3RhdHMuZXhwb3J0QXR0ZW1wdHMpICogMTAwO1xuXG4gICAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcbiAgICBpZiAoc3VjY2Vzc1JhdGVzLmxlbmd0aCA+IHdpbmRvd1NpemUpIHtcbiAgICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xuICAgIH1cbiAgfSwgcmVjb3JkSW50ZXJ2YWwpO1xuXG4vKipcbiAqIEFkZHMgdGhlIC9oZWFsdGggYW5kIC9zdWNjZXNzLW1vdmluZy1hdmVyYWdlIHJvdXRlc1xuICogd2hpY2ggb3V0cHV0IGJhc2ljIHN0YXRzIGZvciB0aGUgc2VydmVyLlxuICovXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBhZGRIZWFsdGhSb3V0ZXMoYXBwKSB7XG4gIGlmICghYXBwKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgLy8gU3RhcnQgcHJvY2Vzc2luZyBzdWNjZXNzIHJhdGUgcmF0aW8gaW50ZXJ2YWwgYW5kIHNhdmUgaXRzIGlkIHRvIHRoZSBhcnJheVxuICAvLyBmb3IgdGhlIGdyYWNlZnVsIGNsZWFyaW5nIG9uIHNodXRkb3duIHdpdGggaW5qZWN0ZWQgYWRkSW50ZXJ2YWwgZnVudGlvblxuICBhZGRJbnRlcnZhbChzdGFydFN1Y2Nlc3NSYXRlKCkpO1xuXG4gIGFwcC5nZXQoJy9oZWFsdGgnLCAoXywgcmVzKSA9PiB7XG4gICAgY29uc3Qgc3RhdHMgPSBwb29sLmdldFN0YXRzKCk7XG4gICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcbiAgICBjb25zdCBtb3ZpbmdBdmVyYWdlID0gY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpO1xuXG4gICAgbG9nKDQsICdbaGVhbHRoLmpzXSBHRVQgL2hlYWx0aCBbMjAwXSAtIHJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xuXG4gICAgcmVzLnNlbmQoe1xuICAgICAgc3RhdHVzOiAnT0snLFxuICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcbiAgICAgIHVwdGltZTpcbiAgICAgICAgTWF0aC5mbG9vcihcbiAgICAgICAgICAobmV3IERhdGUoKS5nZXRUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MFxuICAgICAgICApICsgJyBtaW51dGVzJyxcbiAgICAgIHZlcnNpb246IHBrZ0ZpbGUudmVyc2lvbixcbiAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiB2ZXJzaW9uKCksXG4gICAgICBhdmVyYWdlUHJvY2Vzc2luZ1RpbWU6IHN0YXRzLnNwZW50QXZlcmFnZSxcbiAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLnBlcmZvcm1lZEV4cG9ydHMsXG4gICAgICBmYWlsZWRFeHBvcnRzOiBzdGF0cy5kcm9wcGVkRXhwb3J0cyxcbiAgICAgIGV4cG9ydEF0dGVtcHRzOiBzdGF0cy5leHBvcnRBdHRlbXB0cyxcbiAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMucGVyZm9ybWVkRXhwb3J0cyAvIHN0YXRzLmV4cG9ydEF0dGVtcHRzKSAqIDEwMCxcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tbmFtZWQtYXMtZGVmYXVsdC1tZW1iZXJcbiAgICAgIHBvb2w6IHBvb2wuZ2V0UG9vbEluZm9KU09OKCksXG5cbiAgICAgIC8vIE1vdmluZyBhdmVyYWdlXG4gICAgICBwZXJpb2QsXG4gICAgICBtb3ZpbmdBdmVyYWdlLFxuICAgICAgbWVzc2FnZTpcbiAgICAgICAgaXNOYU4obW92aW5nQXZlcmFnZSkgfHwgIXN1Y2Nlc3NSYXRlcy5sZW5ndGhcbiAgICAgICAgICA/ICdUb28gZWFybHkgdG8gcmVwb3J0LiBObyBleHBvcnRzIG1hZGUgeWV0LiBQbGVhc2UgY2hlY2sgYmFjayBzb29uLidcbiAgICAgICAgICA6IGBMYXN0ICR7cGVyaW9kfSBtaW51dGVzIGhhZCBhIHN1Y2Nlc3MgcmF0ZSBvZiAke21vdmluZ0F2ZXJhZ2UudG9GaXhlZCgyKX0lLmAsXG5cbiAgICAgIC8vIFNWRy9KU09OIGF0dGVtcHRzXG4gICAgICBzdmdFeHBvcnRBdHRlbXB0czogc3RhdHMuZXhwb3J0RnJvbVN2Z0F0dGVtcHRzLFxuICAgICAganNvbkV4cG9ydEF0dGVtcHRzOiBzdGF0cy5wZXJmb3JtZWRFeHBvcnRzIC0gc3RhdHMuZXhwb3J0RnJvbVN2Z0F0dGVtcHRzXG4gICAgfSk7XG4gIH0pO1xufVxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IHByb21pc2VzIGFzIGZzUHJvbWlzZXMgfSBmcm9tICdmcyc7XG5pbXBvcnQgeyBwb3NpeCB9IGZyb20gJ3BhdGgnO1xuXG5pbXBvcnQgY29ycyBmcm9tICdjb3JzJztcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xuXG5pbXBvcnQgZXJyb3JIYW5kbGVyIGZyb20gJy4vZXJyb3IuanMnO1xuaW1wb3J0IHJhdGVMaW1pdCBmcm9tICcuL3JhdGVfbGltaXQuanMnO1xuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xuXG5pbXBvcnQgdlN3aXRjaFJvdXRlIGZyb20gJy4vcm91dGVzL2NoYW5nZV9oY192ZXJzaW9uLmpzJztcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcbmltcG9ydCBoZWFsdGhSb3V0ZSBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xuaW1wb3J0IHVpUm91dGUgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xuXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcblxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcbmNvbnN0IGFjdGl2ZVNlcnZlcnMgPSBuZXcgTWFwKCk7XG5cbi8vIENyZWF0ZSBleHByZXNzIGFwcFxuY29uc3QgYXBwID0gZXhwcmVzcygpO1xuXG4vLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXG5hcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XG5cbi8vIEVuYWJsZSBDT1JTIHN1cHBvcnRcbmFwcC51c2UoY29ycygpKTtcblxuLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBNdWx0ZXIgcGFja2FnZVxuY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XG5jb25zdCB1cGxvYWQgPSBtdWx0ZXIoe1xuICBzdG9yYWdlLFxuICBsaW1pdHM6IHtcbiAgICBmaWVsZFNpemU6IDUwICogMTAyNCAqIDEwMjRcbiAgfVxufSk7XG5cbi8vIEVuYWJsZSBib2R5IHBhcnNlclxuYXBwLnVzZShleHByZXNzLmpzb24oeyBsaW1pdDogNTAgKiAxMDI0ICogMTAyNCB9KSk7XG5hcHAudXNlKGV4cHJlc3MudXJsZW5jb2RlZCh7IGV4dGVuZGVkOiB0cnVlLCBsaW1pdDogNTAgKiAxMDI0ICogMTAyNCB9KSk7XG5cbi8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xuYXBwLnVzZSh1cGxvYWQubm9uZSgpKTtcblxuLyoqXG4gKiBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgdG8gdGhlIHNlcnZlci5cbiAqXG4gKiBAcGFyYW0ge2h0dHAuU2VydmVyfSBzZXJ2ZXIgLSBUaGUgSFRUUC9IVFRQUyBzZXJ2ZXIgaW5zdGFuY2UuXG4gKi9cbmNvbnN0IGF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMgPSAoc2VydmVyKSA9PiB7XG4gIHNlcnZlci5vbignY2xpZW50RXJyb3InLCAoZXJyb3IpID0+IHtcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBDbGllbnQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgfSk7XG5cbiAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNlcnZlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xuICB9KTtcblxuICBzZXJ2ZXIub24oJ2Nvbm5lY3Rpb24nLCAoc29ja2V0KSA9PiB7XG4gICAgc29ja2V0Lm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xuICAgICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XG4gICAgfSk7XG4gIH0pO1xufTtcblxuLyoqXG4gKiBTdGFydHMgYW4gSFRUUCBzZXJ2ZXIgYmFzZWQgb24gdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24uIFRoZSBgc2VydmVyQ29uZmlnYFxuICogb2JqZWN0IGNvbnRhaW5zIGFsbCBzZXJ2ZXIgcmVsYXRlZCBwcm9wZXJ0aWVzIChzZWUgdGhlIGBzZXJ2ZXJgIHNlY3Rpb25cbiAqIGluIHRoZSBgbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIGZvciBhIHJlZmVyZW5jZSkuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlckNvbmZpZyAtIFRoZSBzZXJ2ZXIgY29uZmlndXJhdGlvbiBvYmplY3QuXG4gKlxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IC0gVGhyb3dzIGFuIGVycm9yIGlmIHRoZSBzZXJ2ZXIgY2Fubm90IGJlIGNvbmZpZ3VyZWRcbiAqIGFuZCBzdGFydGVkLlxuICovXG5leHBvcnQgY29uc3Qgc3RhcnRTZXJ2ZXIgPSBhc3luYyAoc2VydmVyQ29uZmlnKSA9PiB7XG4gIHRyeSB7XG4gICAgLy8gU3RvcCBpZiBub3QgZW5hYmxlZFxuICAgIGlmICghc2VydmVyQ29uZmlnLmVuYWJsZSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIExpc3RlbiBIVFRQIHNlcnZlclxuICAgIGlmICghc2VydmVyQ29uZmlnLnNzbC5mb3JjZSkge1xuICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFApXG4gICAgICBjb25zdCBodHRwU2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoYXBwKTtcblxuICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxuICAgICAgYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcblxuICAgICAgLy8gTGlzdGVuXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJDb25maWcucG9ydCwgc2VydmVyQ29uZmlnLmhvc3QpO1xuXG4gICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUCBzZXJ2ZXJcbiAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlckNvbmZpZy5wb3J0LCBodHRwU2VydmVyKTtcblxuICAgICAgbG9nKFxuICAgICAgICAzLFxuICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlckNvbmZpZy5ob3N0fToke3NlcnZlckNvbmZpZy5wb3J0fS5gXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIExpc3RlbiBIVFRQUyBzZXJ2ZXJcbiAgICBpZiAoc2VydmVyQ29uZmlnLnNzbC5lbmFibGUpIHtcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cbiAgICAgIGxldCBrZXksIGNlcnQ7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIEdldCB0aGUgU1NMIGtleVxuICAgICAgICBrZXkgPSBhd2FpdCBmc1Byb21pc2VzLnJlYWRGaWxlKFxuICAgICAgICAgIHBvc2l4LmpvaW4oc2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aCwgJ3NlcnZlci5rZXknKSxcbiAgICAgICAgICAndXRmOCdcbiAgICAgICAgKTtcblxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBjZXJ0aWZpY2F0ZVxuICAgICAgICBjZXJ0ID0gYXdhaXQgZnNQcm9taXNlcy5yZWFkRmlsZShcbiAgICAgICAgICBwb3NpeC5qb2luKHNlcnZlckNvbmZpZy5zc2wuY2VydFBhdGgsICdzZXJ2ZXIuY3J0JyksXG4gICAgICAgICAgJ3V0ZjgnXG4gICAgICAgICk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2coXG4gICAgICAgICAgMixcbiAgICAgICAgICBgW3NlcnZlcl0gVW5hYmxlIHRvIGxvYWQga2V5L2NlcnRpZmljYXRlIGZyb20gdGhlICcke3NlcnZlckNvbmZpZy5zc2wuY2VydFBhdGh9JyBwYXRoLiBDb3VsZCBub3QgcnVuIHNlY3VyZWQgbGF5ZXIgc2VydmVyLmBcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgaWYgKGtleSAmJiBjZXJ0KSB7XG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcbiAgICAgICAgY29uc3QgaHR0cHNTZXJ2ZXIgPSBodHRwcy5jcmVhdGVTZXJ2ZXIoeyBrZXksIGNlcnQgfSwgYXBwKTtcblxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXG4gICAgICAgIGF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xuXG4gICAgICAgIC8vIExpc3RlblxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyQ29uZmlnLnNzbC5wb3J0LCBzZXJ2ZXJDb25maWcuaG9zdCk7XG5cbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFBTIHNlcnZlclxuICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJDb25maWcuc3NsLnBvcnQsIGh0dHBzU2VydmVyKTtcblxuICAgICAgICBsb2coXG4gICAgICAgICAgMyxcbiAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQUyBzZXJ2ZXIgb24gJHtzZXJ2ZXJDb25maWcuaG9zdH06JHtzZXJ2ZXJDb25maWcuc3NsLnBvcnR9LmBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBFbmFibGUgdGhlIHJhdGUgbGltaXRlciBpZiBjb25maWcgc2F5cyBzb1xuICAgIGlmIChcbiAgICAgIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcgJiZcbiAgICAgIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcuZW5hYmxlICYmXG4gICAgICAhWzAsIE5hTl0uaW5jbHVkZXMoc2VydmVyQ29uZmlnLnJhdGVMaW1pdGluZy5tYXhSZXF1ZXN0cylcbiAgICApIHtcbiAgICAgIHJhdGVMaW1pdChhcHAsIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcpO1xuICAgIH1cblxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcbiAgICBhcHAudXNlKGV4cHJlc3Muc3RhdGljKHBvc2l4LmpvaW4oX19kaXJuYW1lLCAncHVibGljJykpKTtcblxuICAgIC8vIFNldCB1cCByb3V0ZXNcbiAgICBoZWFsdGhSb3V0ZShhcHApO1xuICAgIGV4cG9ydFJvdXRlcyhhcHApO1xuICAgIHVpUm91dGUoYXBwKTtcbiAgICB2U3dpdGNoUm91dGUoYXBwKTtcblxuICAgIC8vIFNldCB1cCBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXG4gICAgZXJyb3JIYW5kbGVyKGFwcCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgJ1tzZXJ2ZXJdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHN0YXJ0IHRoZSBzZXJ2ZXIuJ1xuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xuICB9XG59O1xuXG4vKipcbiAqIENsb3NlcyBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXG4gKi9cbmV4cG9ydCBjb25zdCBjbG9zZVNlcnZlcnMgPSAoKSA9PiB7XG4gIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2luZyBhbGwgc2VydmVycy5gKTtcbiAgZm9yIChjb25zdCBbcG9ydCwgc2VydmVyXSBvZiBhY3RpdmVTZXJ2ZXJzKSB7XG4gICAgc2VydmVyLmNsb3NlKCgpID0+IHtcbiAgICAgIGFjdGl2ZVNlcnZlcnMuZGVsZXRlKHBvcnQpO1xuICAgICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zZWQgc2VydmVyIG9uIHBvcnQ6ICR7cG9ydH0uYCk7XG4gICAgfSk7XG4gIH1cbn07XG5cbi8qKlxuICogR2V0IGFsbCBzZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cbiAqXG4gKiBAcmV0dXJucyB7QXJyYXl9IC0gU2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXG4gKi9cbmV4cG9ydCBjb25zdCBnZXRTZXJ2ZXJzID0gKCkgPT4gYWN0aXZlU2VydmVycztcblxuLyoqXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gbGltaXRDb25maWcgLSBDb25maWd1cmF0aW9uIG9iamVjdCBmb3IgcmF0ZSBsaW1pdGluZy5cbiAqL1xuZXhwb3J0IGNvbnN0IGVuYWJsZVJhdGVMaW1pdGluZyA9IChsaW1pdENvbmZpZykgPT4gcmF0ZUxpbWl0KGFwcCwgbGltaXRDb25maWcpO1xuXG4vKipcbiAqIEdldCB0aGUgRXhwcmVzcyBpbnN0YW5jZS5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSAtIFRoZSBFeHByZXNzIGluc3RhbmNlLlxuICovXG5leHBvcnQgY29uc3QgZ2V0RXhwcmVzcyA9ICgpID0+IGV4cHJlc3M7XG5cbi8qKlxuICogR2V0IHRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cbiAqL1xuZXhwb3J0IGNvbnN0IGdldEFwcCA9ICgpID0+IGFwcDtcblxuLyoqXG4gKiBBcHBseSBtaWRkbGV3YXJlKHMpIHRvIGEgc3BlY2lmaWMgcGF0aC5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbnMgdG8gYmUgYXBwbGllZC5cbiAqL1xuZXhwb3J0IGNvbnN0IHVzZSA9IChwYXRoLCAuLi5taWRkbGV3YXJlcykgPT4ge1xuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcbn07XG5cbi8qKlxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBHRVQgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHJvdXRlIHBhdGguXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9ucyB0byBiZSBhcHBsaWVkLlxuICovXG5leHBvcnQgY29uc3QgZ2V0ID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XG4gIGFwcC5nZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xufTtcblxuLyoqXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIFBPU1QgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHJvdXRlIHBhdGguXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9ucyB0byBiZSBhcHBsaWVkLlxuICovXG5leHBvcnQgY29uc3QgcG9zdCA9IChwYXRoLCAuLi5taWRkbGV3YXJlcykgPT4ge1xuICBhcHAucG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XG59O1xuXG5leHBvcnQgZGVmYXVsdCB7XG4gIHN0YXJ0U2VydmVyLFxuICBjbG9zZVNlcnZlcnMsXG4gIGdldFNlcnZlcnMsXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcbiAgZ2V0RXhwcmVzcyxcbiAgZ2V0QXBwLFxuICB1c2UsXG4gIGdldCxcbiAgcG9zdFxufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XG5cbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcblxuLyoqXG4gKiBBZGRzIHRoZSBHRVQgLyByb3V0ZSBmb3IgYSBVSSB3aGVuIGVuYWJsZWQgb24gdGhlIGV4cG9ydCBzZXJ2ZXIuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IChhcHApID0+XG4gICFhcHBcbiAgICA/IGZhbHNlXG4gICAgOiBhcHAuZ2V0KCcvJywgKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XG4gICAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSk7XG4gICAgICB9KTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyBjbGVhckFsbEludGVydmFscyB9IGZyb20gJy4vaW50ZXJ2YWxzLmpzJztcbmltcG9ydCB7IGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcbmltcG9ydCB7IGNsb3NlU2VydmVycyB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XG5cbi8qKlxuICogQ2xlYW4gdXAgZnVuY3Rpb24gdG8gdHJpZ2dlciBiZWZvcmUgZW5kaW5nIHByb2Nlc3MgZm9yIHRoZSBncmFjZWZ1bCBzaHV0ZG93bi5cbiAqXG4gKiBAcGFyYW0ge251bWJlcn0gZXhpdENvZGUgLSBBbiBleGl0IGNvZGUgZm9yIHRoZSBwcm9jZXNzLmV4aXQoKSBmdW5jdGlvbi5cbiAqL1xuZXhwb3J0IGNvbnN0IHNodXRkb3duQ2xlYW5VcCA9IGFzeW5jIChleGl0Q29kZSkgPT4ge1xuICAvLyBBd2FpdCBmcmVlaW5nIGFsbCByZXNvdXJjZXNcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcbiAgICAvLyBDbGVhciBhbGwgb25nb2luZyBpbnRlcnZhbHNcbiAgICBjbGVhckFsbEludGVydmFscygpLFxuXG4gICAgLy8gR2V0IGF2YWlsYWJsZSBzZXJ2ZXIgaW5zdGFuY2VzIChIVFRQL0hUVFBTKSBhbmQgY2xvc2UgdGhlbVxuICAgIGNsb3NlU2VydmVycygpLFxuXG4gICAgLy8gQ2xvc2UgcG9vbCBhbG9uZyB3aXRoIGl0cyB3b3JrZXJzIGFuZCB0aGUgYnJvd3NlciBpbnN0YW5jZSwgaWYgZXhpc3RzXG4gICAga2lsbFBvb2woKVxuICBdKTtcblxuICAvLyBFeGl0IHByb2Nlc3Mgd2l0aCBhIGNvcnJlY3QgY29kZVxuICBwcm9jZXNzLmV4aXQoZXhpdENvZGUpO1xufTtcblxuZXhwb3J0IGRlZmF1bHQge1xuICBzaHV0ZG93bkNsZWFuVXBcbn07XG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcblxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XG5cbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cblxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXG5cblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cblxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuaW1wb3J0ICdjb2xvcnMnO1xuXG5pbXBvcnQgeyBjaGVja0FuZFVwZGF0ZUNhY2hlIH0gZnJvbSAnLi9jYWNoZS5qcyc7XG5pbXBvcnQge1xuICBiYXRjaEV4cG9ydCxcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxuICBzaW5nbGVFeHBvcnQsXG4gIHN0YXJ0RXhwb3J0XG59IGZyb20gJy4vY2hhcnQuanMnO1xuaW1wb3J0IHsgbWFwVG9OZXdDb25maWcsIG1hbnVhbENvbmZpZywgc2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcbmltcG9ydCB7XG4gIGluaXRMb2dnaW5nLFxuICBsb2csXG4gIGxvZ1dpdGhTdGFjayxcbiAgc2V0TG9nTGV2ZWwsXG4gIGVuYWJsZUZpbGVMb2dnaW5nXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcbmltcG9ydCB7IGluaXRQb29sLCBraWxsUG9vbCB9IGZyb20gJy4vcG9vbC5qcyc7XG5pbXBvcnQgeyBzaHV0ZG93bkNsZWFuVXAgfSBmcm9tICcuL3Jlc291cmNlX3JlbGVhc2UuanMnO1xuaW1wb3J0IHNlcnZlciwgeyBzdGFydFNlcnZlciB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XG5pbXBvcnQgeyBwcmludExvZ28sIHByaW50VXNhZ2UgfSBmcm9tICcuL3V0aWxzLmpzJztcblxuLyoqXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXG4gKiBhbmQgdGVybWluYXRpb24gb24gZXhpdCBzaWduYWxzLiBIYW5kbGVzICdleGl0JywgJ1NJR0lOVCcsICdTSUdURVJNJywgYW5kXG4gKiAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cbiAqL1xuY29uc3QgYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMgPSAoKSA9PiB7XG4gIGxvZygzLCAnW3Byb2Nlc3NdIEF0dGFjaGluZyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2Vzcy4nKTtcblxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ2V4aXQnXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoY29kZSkgPT4ge1xuICAgIGxvZyg0LCBgUHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0uYCk7XG4gIH0pO1xuXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSU5UJ1xuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgwKTtcbiAgfSk7XG5cbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdURVJNJ1xuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcbiAgICBsb2coNCwgYFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoMCk7XG4gIH0pO1xuXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xuICBwcm9jZXNzLm9uKCdTSUdIVVAnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgwKTtcbiAgfSk7XG5cbiAgLy8gSGFuZGxlciBmb3IgdGhlICd1bmNhdWdodEV4Y2VwdGlvbidcbiAgcHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCBhc3luYyAoZXJyb3IsIG5hbWUpID0+IHtcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBUaGUgJHtuYW1lfSBlcnJvci5gKTtcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoMSk7XG4gIH0pO1xufTtcblxuLyoqXG4gKiBJbml0aWFsaXplcyB0aGUgZXhwb3J0IHByb2Nlc3MuIFRhc2tzIHN1Y2ggYXMgY29uZmlndXJpbmcgbG9nZ2luZywgY2hlY2tpbmdcbiAqIGNhY2hlIGFuZCBzb3VyY2VzLCBhbmQgaW5pdGlhbGl6aW5nIHRoZSBwb29sIG9mIHJlc291cmNlcyBoYXBwZW4gZHVyaW5nXG4gKiB0aGlzIHN0YWdlLiBGdW5jdGlvbiB0aGF0IGlzIHJlcXVpcmVkIHRvIGJlIGNhbGxlZCBiZWZvcmUgdHJ5aW5nIHRvIGV4cG9ydCBjaGFydHMgb3Igc2V0dGluZyBhIHNlcnZlci4gVGhlIGBvcHRpb25zYCBpcyBhbiBvYmplY3QgdGhhdCBjb250YWlucyBhbGwgb3B0aW9ucy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIEFsbCBleHBvcnQgb3B0aW9ucy5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgdXBkYXRlZCBleHBvcnQgb3B0aW9ucy5cbiAqL1xuY29uc3QgaW5pdEV4cG9ydCA9IGFzeW5jIChvcHRpb25zKSA9PiB7XG4gIC8vIFNldCB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIHBlciBleHBvcnQgbW9kdWxlIHNjb3BlXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbihcbiAgICBvcHRpb25zLmN1c3RvbUxvZ2ljICYmIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXG4gICk7XG5cbiAgLy8gSW5pdCB0aGUgbG9nZ2luZ1xuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xuXG4gIC8vIEF0dGFjaCBwcm9jZXNzJyBleGl0IGxpc3RlbmVyc1xuICBpZiAob3B0aW9ucy5vdGhlci5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xuICAgIGF0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCk7XG4gIH1cblxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucyk7XG5cbiAgLy8gSW5pdCB0aGUgcG9vbFxuICBhd2FpdCBpbml0UG9vbCh7XG4gICAgcG9vbDogb3B0aW9ucy5wb29sIHx8IHtcbiAgICAgIG1pbldvcmtlcnM6IDEsXG4gICAgICBtYXhXb3JrZXJzOiAxXG4gICAgfSxcbiAgICBwdXBwZXRlZXJBcmdzOiBvcHRpb25zLnB1cHBldGVlci5hcmdzIHx8IFtdXG4gIH0pO1xuXG4gIC8vIFJldHVybiB1cGRhdGVkIG9wdGlvbnNcbiAgcmV0dXJuIG9wdGlvbnM7XG59O1xuXG5leHBvcnQgZGVmYXVsdCB7XG4gIC8vIFNlcnZlclxuICBzZXJ2ZXIsXG4gIHN0YXJ0U2VydmVyLFxuXG4gIC8vIEV4cG9ydGluZ1xuICBpbml0RXhwb3J0LFxuICBzaW5nbGVFeHBvcnQsXG4gIGJhdGNoRXhwb3J0LFxuICBzdGFydEV4cG9ydCxcblxuICAvLyBQb29sXG4gIGluaXRQb29sLFxuICBraWxsUG9vbCxcblxuICAvLyBPdGhlclxuICBzZXRPcHRpb25zLFxuICBzaHV0ZG93bkNsZWFuVXAsXG5cbiAgLy8gTG9nc1xuICBsb2csXG4gIGxvZ1dpdGhTdGFjayxcbiAgc2V0TG9nTGV2ZWwsXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxuXG4gIC8vIFV0aWxzXG4gIG1hcFRvTmV3Q29uZmlnLFxuICBtYW51YWxDb25maWcsXG4gIHByaW50TG9nbyxcbiAgcHJpbnRVc2FnZVxufTtcbiJdLCJuYW1lcyI6WyJzY3JpcHRzTmFtZXMiLCJjb3JlIiwibW9kdWxlcyIsImluZGljYXRvcnMiLCJjdXN0b20iLCJkZWZhdWx0Q29uZmlnIiwicHVwcGV0ZWVyIiwiYXJncyIsInZhbHVlIiwidHlwZSIsImRlc2NyaXB0aW9uIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJlbnZMaW5rIiwiY2RuVVJMIiwiY29yZVNjcmlwdHMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiZXhwb3J0IiwiaW5maWxlIiwiaW5zdHIiLCJvcHRpb25zIiwib3V0ZmlsZSIsImNvbnN0ciIsImRlZmF1bHRIZWlnaHQiLCJkZWZhdWx0V2lkdGgiLCJkZWZhdWx0U2NhbGUiLCJoZWlnaHQiLCJ3aWR0aCIsInNjYWxlIiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsImJhdGNoIiwicmFzdGVyaXphdGlvblRpbWVvdXQiLCJjdXN0b21Mb2dpYyIsImFsbG93Q29kZUV4ZWN1dGlvbiIsImFsbG93RmlsZVJlc291cmNlcyIsImN1c3RvbUNvZGUiLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiY2xpTmFtZSIsImhvc3QiLCJwb3J0IiwiYmVuY2htYXJraW5nIiwicHJveHkiLCJ0aW1lb3V0IiwicmF0ZUxpbWl0aW5nIiwibWF4UmVxdWVzdHMiLCJ3aW5kb3ciLCJkZWxheSIsInRydXN0UHJveHkiLCJza2lwS2V5Iiwic2tpcFRva2VuIiwic3NsIiwiZm9yY2UiLCJjZXJ0UGF0aCIsInBvb2wiLCJtaW5Xb3JrZXJzIiwibWF4V29ya2VycyIsIndvcmtMaW1pdCIsImFjcXVpcmVUaW1lb3V0IiwiY3JlYXRlVGltZW91dCIsImRlc3Ryb3lUaW1lb3V0IiwiaWRsZVRpbWVvdXQiLCJjcmVhdGVSZXRyeUludGVydmFsIiwicmVhcGVySW50ZXJ2YWwiLCJsb2dnaW5nIiwibGV2ZWwiLCJmaWxlIiwiZGVzdCIsInRvQ29uc29sZSIsInRvRmlsZSIsInVpIiwicm91dGUiLCJvdGhlciIsIm5vZGVFbnYiLCJsaXN0ZW5Ub1Byb2Nlc3NFeGl0cyIsIm5vTG9nbyIsImhhcmRSZXNldFBhZ2UiLCJicm93c2VyU2hlbGxNb2RlIiwiZGVidWciLCJoZWFkbGVzcyIsImRldnRvb2xzIiwibGlzdGVuVG9Db25zb2xlIiwiZHVtcGlvIiwic2xvd01vIiwiZGVidWdnaW5nUG9ydCIsInByb21wdHNDb25maWciLCJuYW1lIiwibWVzc2FnZSIsImluaXRpYWwiLCJqb2luIiwic2VwYXJhdG9yIiwiaW5zdHJ1Y3Rpb25zIiwiY2hvaWNlcyIsImhpbnQiLCJtaW4iLCJtYXgiLCJyb3VuZCIsImFic29sdXRlUHJvcHMiLCJuZXN0ZWRBcmdzIiwiY3JlYXRlTmVzdGVkQXJncyIsIm9iaiIsInByb3BDaGFpbiIsIk9iamVjdCIsImtleXMiLCJmb3JFYWNoIiwiayIsImluY2x1ZGVzIiwiZW50cnkiLCJzdWJzdHJpbmciLCJ1bmRlZmluZWQiLCJkb3RlbnYiLCJjb25maWciLCJ2IiwiZmlsdGVyQXJyYXkiLCJ6Iiwic3RyaW5nIiwidHJhbnNmb3JtIiwic3BsaXQiLCJtYXAiLCJ0cmltIiwiZmlsdGVyIiwibGVuZ3RoIiwiZW51bSIsInZhbHVlcyIsInJlZmluZSIsImlzTmFOIiwicGFyc2VGbG9hdCIsImVudnMiLCJvYmplY3QiLCJISUdIQ0hBUlRTX1ZFUlNJT04iLCJ0ZXN0IiwiSElHSENIQVJUU19DRE5fVVJMIiwic3RhcnRzV2l0aCIsIkhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTIiwiSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUyIsIkhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFMiLCJISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIIiwiSElHSENIQVJUU19DQUNIRV9QQVRIIiwiSElHSENIQVJUU19BRE1JTl9UT0tFTiIsIkVYUE9SVF9UWVBFIiwiRVhQT1JUX0NPTlNUUiIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTIiwiU0VSVkVSX0VOQUJMRSIsIlNFUlZFUl9IT1NUIiwiU0VSVkVSX1BPUlQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJwYXJ0aWFsIiwicGFyc2UiLCJwcm9jZXNzIiwiZW52IiwiY29sb3JzIiwicGF0aENyZWF0ZWQiLCJsZXZlbHNEZXNjIiwidGl0bGUiLCJjb2xvciIsImxpc3RlbmVycyIsImxvZ1RvRmlsZSIsInRleHRzIiwicHJlZml4IiwiZXhpc3RzU3luYyIsIm1rZGlyU3luYyIsImFwcGVuZEZpbGUiLCJjb25jYXQiLCJlcnJvciIsImNvbnNvbGUiLCJsb2ciLCJuZXdMZXZlbCIsIkRhdGUiLCJ0b1N0cmluZyIsImZuIiwiYXBwbHkiLCJsb2dXaXRoU3RhY2siLCJjdXN0b21NZXNzYWdlIiwibWFpbk1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInNsaWNlIiwic2V0TG9nTGV2ZWwiLCJlbmFibGVGaWxlTG9nZ2luZyIsImxvZ0Rlc3QiLCJsb2dGaWxlIiwiZW5kc1dpdGgiLCJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInNyYyIsImJhc2VVUkkiLCJmaXhUeXBlIiwiZm9ybWF0cyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsImhhbmRsZVJlc291cmNlcyIsImFsbG93ZWRQcm9wcyIsImhhbmRsZWRSZXNvdXJjZXMiLCJjb3JyZWN0UmVzb3VyY2VzIiwiaXNDb3JyZWN0SlNPTiIsInJlYWRGaWxlU3luYyIsImZpbGVzIiwicHJvcE5hbWUiLCJpdGVtIiwiZGF0YSIsInBhcnNlZERhdGEiLCJKU09OIiwic3RyaW5naWZ5IiwiZGVlcENvcHkiLCJjb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwib3B0aW9uc1N0cmluZ2lmeSIsImFsbG93RnVuY3Rpb25zIiwicmVwbGFjZUFsbCIsInByaW50VXNhZ2UiLCJib2xkIiwieWVsbG93IiwiY3ljbGVDYXRlZ29yaWVzIiwib3B0aW9uIiwiZW50cmllcyIsImRlc2NOYW1lIiwiZ3JlZW4iLCJpIiwiYmx1ZSIsImNhdGVnb3J5IiwidG9VcHBlckNhc2UiLCJyZWQiLCJ0b0Jvb2xlYW4iLCJ3cmFwQXJvdW5kIiwicmVwbGFjZSIsIm1lYXN1cmVUaW1lIiwic3RhcnQiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJnZW5lcmFsT3B0aW9ucyIsImdldE9wdGlvbnMiLCJtZXJnZUNvbmZpZ09wdGlvbnMiLCJuZXdPcHRpb25zIiwibWVyZ2VkT3B0aW9ucyIsInVwZGF0ZURlZmF1bHRDb25maWciLCJjb25maWdPYmoiLCJjdXN0b21PYmoiLCJjdXN0b21WYWx1ZSIsImluaXRPcHRpb25zIiwiaXRlbXMiLCJyZWN1cnNpdmVQcm9wcyIsIm9iamVjdFRvVXBkYXRlIiwibmVzdGVkTmFtZXMiLCJzaGlmdCIsImFzc2lnbiIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwicHJvdG9jb2wiLCJodHRwcyIsImh0dHAiLCJnZXRQcm90b2NvbCIsImdldCIsInJlcyIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiRXhwb3J0RXJyb3IiLCJFcnJvciIsImNvbnN0cnVjdG9yIiwic3VwZXIiLCJ0aGlzIiwic2V0RXJyb3IiLCJzdGF0dXNDb2RlIiwiY2FjaGUiLCJhY3RpdmVNYW5pZmVzdCIsInNvdXJjZXMiLCJoY1ZlcnNpb24iLCJleHRyYWN0VmVyc2lvbiIsImluZGV4T2YiLCJmZXRjaEFuZFByb2Nlc3NTY3JpcHQiLCJzY3JpcHQiLCJmZXRjaGVkTW9kdWxlcyIsInNob3VsZFRocm93RXJyb3IiLCJyZXNwb25zZSIsInVwZGF0ZUNhY2hlIiwiaGlnaGNoYXJ0c09wdGlvbnMiLCJwcm94eU9wdGlvbnMiLCJzb3VyY2VQYXRoIiwicHJveHlBZ2VudCIsInByb3h5SG9zdCIsInByb3h5UG9ydCIsIkh0dHBzUHJveHlBZ2VudCIsImFnZW50IiwiYWxsRmV0Y2hQcm9taXNlcyIsImFsbCIsImZldGNoU2NyaXB0cyIsImMiLCJtIiwid3JpdGVGaWxlU3luYyIsImNoZWNrQW5kVXBkYXRlQ2FjaGUiLCJtYW5pZmVzdFBhdGgiLCJyZXF1ZXN0VXBkYXRlIiwibWFuaWZlc3QiLCJtb2R1bGVNYXAiLCJudW1iZXJPZk1vZHVsZXMiLCJzb21lIiwibW9kdWxlTmFtZSIsIm5ld01hbmlmZXN0Iiwic2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRDYWNoZVBhdGgiLCJzZXR1cEhpZ2hjaGFydHMiLCJIaWdoY2hhcnRzIiwiYW5pbU9iamVjdCIsImR1cmF0aW9uIiwidHJpZ2dlckV4cG9ydCIsImNoYXJ0T3B0aW9ucyIsImRpc3BsYXlFcnJvcnMiLCJfZGlzcGxheUVycm9ycyIsIm1lcmdlIiwic2V0T3B0aW9ucyIsIndyYXAiLCJzZXRPcHRpb25zT2JqIiwiRnVuY3Rpb24iLCJjaGFydCIsImFuaW1hdGlvbiIsInN0ckluaiIsImlzUmVuZGVyQ29tcGxldGUiLCJDaGFydCIsInByb2NlZWQiLCJ1c2VyT3B0aW9ucyIsImNiIiwiZXhwb3J0aW5nIiwiZW5hYmxlZCIsInBsb3RPcHRpb25zIiwic2VyaWVzIiwibGFiZWwiLCJ0b29sdGlwIiwib25IaWdoY2hhcnRzUmVuZGVyIiwiYWRkRXZlbnQiLCJTZXJpZXMiLCJmaW5hbE9wdGlvbnMiLCJmaW5hbENhbGxiYWNrIiwiZGVmYXVsdE9wdGlvbnMiLCJwcm9wIiwidGVtcGxhdGUiLCJicm93c2VyIiwibmV3UGFnZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJzZXRQYWdlQ29udGVudCIsIiRldmFsIiwiZWxlbWVudCIsImVycm9yTWVzc2FnZSIsImlubmVySFRNTCIsInNldFBhZ2VFdmVudHMiLCJjbGVhclBhZ2VSZXNvdXJjZXMiLCJpbmplY3RlZFJlc291cmNlcyIsInJlc291cmNlIiwiZGlzcG9zZSIsImV2YWx1YXRlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJyZW1vdmUiLCJzZXRDb250ZW50Iiwid2FpdFVudGlsIiwiYWRkU2NyaXB0VGFnIiwicGF0aCIsInNldEFzQ29uZmlnIiwicHVwcGV0ZWVyRXhwb3J0IiwiZXhwb3J0T3B0aW9ucyIsImRlYnVnZ2VyIiwiaXNTVkciLCJzdmdUZW1wbGF0ZSIsImluamVjdGVkSnMiLCJqcyIsInB1c2giLCJjb250ZW50IiwiaXNMb2NhbCIsImpzUmVzb3VyY2UiLCJpbmplY3RlZENzcyIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJjc3NSZXNvdXJjZSIsImFkZFN0eWxlVGFnIiwiYWRkUGFnZVJlc291cmNlcyIsInNpemUiLCJzdmdFbGVtZW50IiwicXVlcnlTZWxlY3RvciIsImNoYXJ0SGVpZ2h0IiwiYmFzZVZhbCIsImNoYXJ0V2lkdGgiLCJib2R5Iiwic3R5bGUiLCJ6b29tIiwibWFyZ2luIiwidmlld3BvcnRIZWlnaHQiLCJNYXRoIiwiY2VpbCIsInZpZXdwb3J0V2lkdGgiLCJ4IiwieSIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsInRydW5jIiwiZ2V0Q2xpcFJlZ2lvbiIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJvdXRlckhUTUwiLCJjcmVhdGVTVkciLCJlbmNvZGluZyIsImNsaXAiLCJyYWNlIiwic2NyZWVuc2hvdCIsImNhcHR1cmVCZXlvbmRWaWV3cG9ydCIsImZ1bGxQYWdlIiwib3B0aW1pemVGb3JTcGVlZCIsInF1YWxpdHkiLCJvbWl0QmFja2dyb3VuZCIsIl9yZXNvbHZlIiwic2V0VGltZW91dCIsImNyZWF0ZUltYWdlIiwiZW11bGF0ZU1lZGlhVHlwZSIsInBkZiIsImNyZWF0ZVBERiIsInN0YXRzIiwicGVyZm9ybWVkRXhwb3J0cyIsImV4cG9ydEF0dGVtcHRzIiwiZXhwb3J0RnJvbVN2Z0F0dGVtcHRzIiwidGltZVNwZW50IiwiZHJvcHBlZEV4cG9ydHMiLCJzcGVudEF2ZXJhZ2UiLCJwb29sQ29uZmlnIiwiZmFjdG9yeSIsImNyZWF0ZSIsImlkIiwidXVpZCIsInN0YXJ0RGF0ZSIsImdldFRpbWUiLCJpc0Nsb3NlZCIsIndvcmtDb3VudCIsInJhbmRvbSIsInZhbGlkYXRlIiwid29ya2VySGFuZGxlIiwiY2xvc2UiLCJpbml0UG9vbCIsInB1cHBldGVlckFyZ3MiLCJlbmFibGVkRGVidWciLCJkZWJ1Z09wdGlvbnMiLCJsYXVuY2hPcHRpb25zIiwidXNlckRhdGFEaXIiLCJoYW5kbGVTSUdJTlQiLCJoYW5kbGVTSUdURVJNIiwiaGFuZGxlU0lHSFVQIiwid2FpdEZvckluaXRpYWxQYWdlIiwiZGVmYXVsdFZpZXdwb3J0IiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwiY3JlYXRlQnJvd3NlciIsInBhcnNlSW50IiwiUG9vbCIsImFjcXVpcmVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlVGltZW91dE1pbGxpcyIsImRlc3Ryb3lUaW1lb3V0TWlsbGlzIiwiaWRsZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzIiwicmVhcEludGVydmFsTWlsbGlzIiwicHJvcGFnYXRlQ3JlYXRlRXJyb3IiLCJoYXJkUmVzZXQiLCJnb3RvIiwiY2xlYXJQYWdlIiwiZXZlbnRJZCIsImluaXRpYWxSZXNvdXJjZXMiLCJhY3F1aXJlIiwicHJvbWlzZSIsInJlbGVhc2UiLCJraWxsUG9vbCIsIndvcmtlciIsInVzZWQiLCJkZXN0cm95ZWQiLCJjb25uZWN0ZWQiLCJjbG9zZUJyb3dzZXIiLCJwb3N0V29yayIsImdldFBvb2xJbmZvIiwiYWNxdWlyZUNvdW50ZXIiLCJwYXlsb2FkIiwicmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsInJlc3VsdCIsImV4cG9ydFRpbWUiLCJnZXRQb29sSW5mb0pTT04iLCJudW1GcmVlIiwibnVtVXNlZCIsImF2YWlsYWJsZSIsInBlbmRpbmciLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwb29sJDEiLCJzdGFydEV4cG9ydCIsInNldHRpbmdzIiwiZW5kQ2FsbGJhY2siLCJzdmciLCJpbml0RXhwb3J0U2V0dGluZ3MiLCJleHBvcnRBc1N0cmluZyIsImlucHV0IiwiSlNET00iLCJET01QdXJpZnkiLCJzYW5pdGl6ZSIsIkFERF9UQUdTIiwiZG9TdHJhaWdodEluamVjdCIsImRvRXhwb3J0IiwiZmluZENoYXJ0U2l6ZSIsInByZWNpc2lvbiIsIm11bHRpcGxpZXIiLCJwb3ciLCJyb3VuZE51bWJlciIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJjaGFydEpzb24iLCJjdXN0b21Mb2dpY09wdGlvbnMiLCJhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQiLCJvcHRpb25zTmFtZSIsInN0cmluZ1RvRXhwb3J0IiwiY2hhcnRKU09OIiwiaW50ZXJ2YWxJZHMiLCJjbGVhckFsbEludGVydmFscyIsImNsZWFySW50ZXJ2YWwiLCJsb2dFcnJvck1pZGRsZXdhcmUiLCJyZXEiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RDb2RlIiwic3RhdHVzIiwianNvbiIsInJhdGVMaW1pdCIsImFwcCIsImxpbWl0Q29uZmlnIiwibXNnIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwid2luZG93TXMiLCJkZWxheU1zIiwiaGFuZGxlciIsInJlcXVlc3QiLCJmb3JtYXQiLCJzZW5kIiwiZGVmYXVsdCIsInNraXAiLCJxdWVyeSIsImFjY2Vzc190b2tlbiIsInVzZSIsIkh0dHBFcnJvciIsInNldFN0YXR1cyIsInZTd2l0Y2hSb3V0ZSIsInBvc3QiLCJhZG1pblRva2VuIiwidG9rZW4iLCJuZXdWZXJzaW9uIiwicGFyYW1zIiwidXBkYXRlVmVyc2lvbiIsInJldmVyc2VkTWltZSIsInBuZyIsImpwZWciLCJnaWYiLCJyZXF1ZXN0c0NvdW50ZXIiLCJiZWZvcmVSZXF1ZXN0IiwiYWZ0ZXJSZXF1ZXN0IiwiZG9DYWxsYmFja3MiLCJjYWxsYmFja3MiLCJ1bmlxdWVJZCIsImNhbGxSZXNwb25zZSIsImV4cG9ydEhhbmRsZXIiLCJzdG9wQ291bnRlciIsImhlYWRlcnMiLCJjb25uZWN0aW9uIiwicmVtb3RlQWRkcmVzcyIsImNvbm5lY3Rpb25BYm9ydGVkIiwic29ja2V0IiwidG9Mb3dlckNhc2UiLCJzdWJzdHIiLCJiNjQiLCJub0Rvd25sb2FkIiwicGF0dGVybiIsImlzUHJpdmF0ZVJhbmdlVXJsRm91bmQiLCJpbmZvIiwicmVtb3ZlQWxsTGlzdGVuZXJzIiwiQnVmZmVyIiwiZnJvbSIsImhlYWRlciIsImF0dGFjaG1lbnQiLCJmaWxlbmFtZSIsInBrZ0ZpbGUiLCJwYXRoZXIiLCJzZXJ2ZXJTdGFydFRpbWUiLCJzdWNjZXNzUmF0ZXMiLCJhZGRIZWFsdGhSb3V0ZXMiLCJzZXRJbnRlcnZhbCIsInN1Y2Nlc3NSYXRpbyIsIl8iLCJwZXJpb2QiLCJtb3ZpbmdBdmVyYWdlIiwicmVkdWNlIiwiYSIsImIiLCJib290VGltZSIsInVwdGltZSIsImZsb29yIiwiaGlnaGNoYXJ0c1ZlcnNpb24iLCJhdmVyYWdlUHJvY2Vzc2luZ1RpbWUiLCJmYWlsZWRFeHBvcnRzIiwic3VjZXNzUmF0aW8iLCJ0b0ZpeGVkIiwic3ZnRXhwb3J0QXR0ZW1wdHMiLCJqc29uRXhwb3J0QXR0ZW1wdHMiLCJhY3RpdmVTZXJ2ZXJzIiwiTWFwIiwiZXhwcmVzcyIsImRpc2FibGUiLCJjb3JzIiwic3RvcmFnZSIsIm11bHRlciIsIm1lbW9yeVN0b3JhZ2UiLCJ1cGxvYWQiLCJsaW1pdHMiLCJmaWVsZFNpemUiLCJsaW1pdCIsInVybGVuY29kZWQiLCJleHRlbmRlZCIsIm5vbmUiLCJhdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJDb25maWciLCJodHRwU2VydmVyIiwiY3JlYXRlU2VydmVyIiwibGlzdGVuIiwic2V0IiwiY2VydCIsImZzUHJvbWlzZXMiLCJyZWFkRmlsZSIsInBvc2l4IiwiaHR0cHNTZXJ2ZXIiLCJOYU4iLCJzdGF0aWMiLCJoZWFsdGhSb3V0ZSIsImV4cG9ydFJvdXRlcyIsInNlbmRGaWxlIiwidWlSb3V0ZSIsImVycm9ySGFuZGxlciIsImNsb3NlU2VydmVycyIsImRlbGV0ZSIsImdldFNlcnZlcnMiLCJlbmFibGVSYXRlTGltaXRpbmciLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwibWlkZGxld2FyZXMiLCJzaHV0ZG93bkNsZWFuVXAiLCJleGl0Q29kZSIsImFsbFNldHRsZWQiLCJleGl0IiwiaW5kZXgiLCJpbml0RXhwb3J0IiwibG9nZ2luZ09wdGlvbnMiLCJpbml0TG9nZ2luZyIsImNvZGUiLCJzaW5nbGVFeHBvcnQiLCJiYXRjaEV4cG9ydCIsImJhdGNoRnVuY3Rpb25zIiwicGFpciIsImNvbmZpZ0luZGV4IiwiZmluZEluZGV4IiwiYXJnIiwiZmlsZU5hbWUiLCJsb2FkQ29uZmlnRmlsZSIsInNob3dVc2FnZSIsInByb3BlcnRpZXNDaGFpbiIsImFyZ3VtZW50VHlwZSIsInBhaXJBcmd1bWVudFZhbHVlIiwibWFwVG9OZXdDb25maWciLCJvbGRPcHRpb25zIiwibWFudWFsQ29uZmlnIiwiY29uZmlnRmlsZU5hbWUiLCJjb25maWdGaWxlIiwiY2hvaWNlIiwicHJvbXB0cyIsIm9uU3VibWl0IiwicCIsImNhdGVnb3JpZXMiLCJxdWVzdGlvbnNDb3VudGVyIiwiYWxsUXVlc3Rpb25zIiwic2VjdGlvbiIsInByb21wdCIsImFuc3dlciIsIm1vZHVsZSIsInByb21pc2VzIiwid3JpdGVGaWxlIiwicHJpbnRMb2dvIiwicGFja2FnZVZlcnNpb24iXSwibWFwcGluZ3MiOiIrY0FlTyxNQUFNQSxFQUFlLENBQzFCQyxLQUFNLENBQUMsYUFBYyxrQkFBbUIsaUJBQ3hDQyxRQUFTLENBQ1AsUUFDQSxNQUNBLFFBQ0EsWUFDQSx1QkFDQSxnQkFFQSxlQUNBLFFBQ0EsT0FDQSxhQUNBLG1CQUNBLGVBQ0EsY0FDQSxVQUNBLFVBQ0EsY0FDQSxXQUNBLFVBQ0EsWUFDQSxjQUNBLFlBQ0Esc0JBQ0EsU0FDQSxTQUNBLFdBQ0EsYUFDQSxZQUNBLGVBQ0EseUJBQ0EsU0FDQSxlQUNBLFlBQ0Esa0JBQ0EsU0FDQSxjQUNBLG1CQUNBLGVBQ0Esa0JBQ0EsY0FDQSxlQUVBLGNBQ0EsV0FDQSxlQUNBLFdBQ0EsU0FDQSxPQUNBLFdBQ0EsWUFDQSxTQUNBLHFCQUNBLGFBQ0EsV0FDQSxXQUNBLFdBQ0EsV0FDQSxlQUNBLFVBQ0Esa0JBQ0Esb0JBQ0EsYUFDQSxVQUNBLGNBQ0EsWUFDQSxZQUVGQyxXQUFZLENBQUMsa0JBQ2JDLE9BQVEsQ0FDTix3RUFDQSxtR0FNU0MsRUFBZ0IsQ0FDM0JDLFVBQVcsQ0FDVEMsS0FBTSxDQUNKQyxNQUFPLENBQ0wsbUNBQ0Esa0JBQ0EsMENBQ0EsMkJBQ0Esa0NBQ0Esa0NBQ0Esd0NBQ0EsMkNBQ0EscUJBQ0EsNEJBQ0EsMkNBQ0EsdURBQ0EsNkJBQ0EseUJBQ0EsMEJBQ0EsK0JBQ0EsdUJBQ0EsdUZBQ0EseUJBQ0Esb0NBQ0Esb0JBQ0EsMEJBQ0EsOENBQ0EsMkJBQ0EsMEJBQ0EsNkJBQ0EsbUNBQ0Esd0NBQ0EsbUNBQ0EsMkJBQ0Esa0NBQ0EsdUJBQ0EsaUJBQ0EseUJBQ0EsOEJBQ0Esb0JBQ0EsMkJBQ0EsZUFDQSw2QkFDQSxpQkFDQSxhQUNBLGVBQ0Esc0JBQ0EsY0FDQSx5QkFDQSxvQkFDQSx1QkFFRkMsS0FBTSxXQUNOQyxZQUFhLDBDQUdqQkMsV0FBWSxDQUNWQyxRQUFTLENBQ1BKLE1BQU8sU0FDUEMsS0FBTSxT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 +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),promises=require("fs/promises"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?e:path.join(__dirname$1,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t.message,{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=e}function enableFileLogging(e,t,o){logging.toFile=o,o&&(logging.dest=e,logging.file=t)}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initGlobalOptions(defaultConfig);function getOptions(e=!0){return e?globalOptions:deepCopy(globalOptions)}function setOptions(e={},t=[],o=!1){let r={},n={};t.length&&(r=_loadConfigFile(t),n=_pairArgumentValue(nestedProps,t));const i=getOptions(o);return _updateOptions(defaultConfig,i,r,e,n),i}function mergeOptions(e,t){if(isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?mergeOptions(e[o],r):void 0!==r?r:e[o];return e}function mapToNewOptions(e){const t={};if("[object Object]"===Object.prototype.toString.call(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initGlobalOptions(e){const t={};for(const[o,r]of Object.entries(e))t[o]=Object.prototype.hasOwnProperty.call(r,"value")?r.value:_initGlobalOptions(r);return t}function _updateOptions(e,t,o,r,n){Object.keys(e).forEach((i=>{const s=e[i],a=o&&o[i],l=r&&r[i],c=n&&n[i];if(void 0===s.value)_updateOptions(s,t[i],a,l,c);else{null!=a&&(t[i]=a);const e=envs[s.envLink];s.envLink in envs&&null!=e&&(t[i]=e),null!=l&&(t[i]=l),null!=c&&(t[i]=c)}}))}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _loadConfigFile(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,""))),o=t>-1&&e[t+1];if(o)try{return JSON.parse(fs.readFileSync(getAbsolutePath(o)))}catch(e){logWithStack(2,e,`[config] Unable to load the configuration from the ${o} file.`)}return{}}function _pairArgumentValue(e,t){const o={};for(let r=0;r{if(i.length-1===s){const i=t[++r];i||log(2,`[config] Missing value for the CLI '--${n}' argument. Using the default value.`),e[o]=i||null}else void 0===e[o]&&(e[o]={});return e[o]}),o)}return o}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n));if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=getOptions();t.highcharts.version=e,await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e){const{getOptions:t,merge:o,setOptions:r,wrap:n}=Highcharts;Highcharts.setOptionsObj=o(!1,{},t()),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=o(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,o){e.apply(this,[t,o])}));const i={chart:{animation:!1,height:e.export.height,width:e.export.width},exporting:{enabled:!1}},s=new Function(`return ${e.export.instr}`)(),a=new Function(`return ${e.export.themeOptions}`)(),l=new Function(`return ${e.export.globalOptions}`)(),c=o(!1,a,s,i),p=e.customLogic.callback?new Function(`return ${e.customLogic.callback}`)():null;e.customLogic.customCode&&new Function("options",e.customLogic.customCode)(s),l&&r(l),Highcharts[e.export.constr]("container",c,p);const u=t();for(const e in u)"function"!=typeof u[e]&&delete u[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:path.join(__dirname$1,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t){const o=[];try{const r=t.export;let n=!1;if(r.svg){if(log(4,"[export] Treating as SVG input."),"svg"===r.type)return r.svg;n=!0,await _setAsSvg(e,r.svg)}else log(4,"[export] Treating as JSON config."),await _setAsOptions(e,t);o.push(...await addPageResources(e,t.customLogic));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(r.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||r.height)),c=Math.abs(Math.ceil(i.chartWidth||r.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(r.scale)}),r.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,r.type,{width:c,height:l,x:s,y:a},r.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,r.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${r.type}.`,400)}return await clearPageResources(e,o),p}catch(t){return await clearPageResources(e,o),t}}async function _setAsSvg(e,t){await e.setContent(svgTemplate(t),{waitUntil:"domcontentloaded"})}async function _setAsOptions(e,t){await e.evaluate(createChart,t)}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e=getOptions().pool,t=[]){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,getOptions().pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,e._requestId?`[benchmark] Request [${e._requestId}] - `:"[benchmark] ",`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+"Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.").setError(i):new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+`Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,e._requestId?`[benchmark] Request [${e._requestId}] - `:"[benchmark] ",`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport(e,(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({...e,export:{...e.export,infile:o[0],outfile:o[1]}},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{log(4,"[chart] Starting the exporting process.");const o=mergeOptions(getOptions(!1),e),r=o.export;if(null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t=getOptions().server.rateLimiting){try{if(t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,max:r.max,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>!1!==r.skipKey&&!1!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.max} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}class HttpError extends ExportError{constructor(e,t){super(e,t)}setStatus(e){return this.statusCode=e,this}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new HttpError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4().replace(/-/g,"");if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new HttpError("[validation] The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.",400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new HttpError("[validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new HttpError("[validation] 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);return e.validatedOptions={_requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${fixType(t.type)}`,type:fixType(t.type,t.outfile),constr:fixConstr(t.constr),b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n._requestId;log(4,`[export] Got an incoming HTTP request with ID ${i}.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new HttpError("[export] Unexpected return of the export result from the chart generation. Please check your request data.",400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"))),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new HttpError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new HttpError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new HttpError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new HttpError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e=getOptions().server){try{if(!e.enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const t=1024*e.uploadLimit*1024,o=multer.memoryStorage(),r=multer({storage:o,limits:{fieldSize:t}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:t})),app.use(express.urlencoded({extended:!0,limit:t})),app.use(r.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),healthRoutes(app),exportRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){rateLimitingMiddleware(app,e)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=mergeOptions(getOptions(!1),e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={server:server,startServer:startServer,getOptions:getOptions,setOptions:setOptions,mergeOptions:mergeOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,checkAndUpdateCache:checkAndUpdateCache,initPool:initPool,killPool:killPool,log:log,logWithStack:logWithStack,setLogLevel:setLogLevel,enableConsoleLogging:enableConsoleLogging,enableFileLogging:enableFileLogging,shutdownCleanUp:shutdownCleanUp};exports.default=index,exports.initExport=initExport; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL2Vycm9ycy9IdHRwRXJyb3IuanMiLCIuLi9saWIvc2VydmVyL21pZGRsZXdhcmVzL3ZhbGlkYXRpb24uanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy9leHBvcnQuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy9oZWFsdGguanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy91aS5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL3ZlcnNpb25DaGFuZ2UuanMiLCIuLi9saWIvc2VydmVyL3NlcnZlci5qcyIsIi4uL2xpYi9yZXNvdXJjZVJlbGVhc2UuanMiLCIuLi9saWIvaW5kZXguanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdXRpbGl0eSBtb2R1bGUgcHJvdmlkZXNcclxuICogYSBjb21wcmVoZW5zaXZlIHNldCBvZiBoZWxwZXIgZnVuY3Rpb25zIGFuZCBjb25zdGFudHMgZGVzaWduZWQgdG8gc3RyZWFtbGluZVxyXG4gKiBhbmQgZW5oYW5jZSB2YXJpb3VzIG9wZXJhdGlvbnMgcmVxdWlyZWQgZm9yIEhpZ2hjaGFydHMgZXhwb3J0IHRhc2tzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgaXNBYnNvbHV0ZSwgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBkaXJlY3RvcnkgcGF0aFxyXG5leHBvcnQgY29uc3QgX19kaXJuYW1lID0gZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuLi8uJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFuZCBzdGFuZGFyZGl6ZXMgdGV4dCBieSByZXBsYWNpbmcgbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZVxyXG4gKiBjaGFyYWN0ZXJzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIHRyaW1taW5nIGFueSBsZWFkaW5nIG9yIHRyYWlsaW5nXHJcbiAqIHdoaXRlc3BhY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhclRleHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyB0aGUgJy9cXHNcXHMrL2cnIFJlZ0V4cC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtyZXBsYWNlcj0nICddIC0gVGhlIHN0cmluZyB1c2VkIHRvIHJlcGxhY2UgbXVsdGlwbGVcclxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgJyAnIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyVGV4dCh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpIHtcclxuICByZXR1cm4gdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBkZWVwQ29weVxyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8QXJyYXkpfSBvYmpBcnIgLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fEFycmF5KX0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDb3B5KG9iakFycikge1xyXG4gIC8vIElmIHRoZSBgb2JqQXJyYCBpcyBudWxsIG9yIG5vdCBvZiB0aGUgYG9iamVjdGAgdHlwZSwgcmV0dXJuIGl0XHJcbiAgaWYgKG9iakFyciA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqQXJyICE9PSAnb2JqZWN0Jykge1xyXG4gICAgcmV0dXJuIG9iakFycjtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZWl0aGVyIGEgbmV3IGFycmF5IG9yIGEgbmV3IG9iamVjdFxyXG4gIGNvbnN0IG9iakFyckNvcHkgPSBBcnJheS5pc0FycmF5KG9iakFycikgPyBbXSA6IHt9O1xyXG5cclxuICAvLyBSZWN1cnNpdmVseSBjb3B5IGVhY2ggcHJvcGVydHlcclxuICBmb3IgKGNvbnN0IGtleSBpbiBvYmpBcnIpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqQXJyLCBrZXkpKSB7XHJcbiAgICAgIG9iakFyckNvcHlba2V5XSA9IGRlZXBDb3B5KG9iakFycltrZXldKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY29waWVkIG9iamVjdFxyXG4gIHJldHVybiBvYmpBcnJDb3B5O1xyXG59XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZXhwQmFja29mZlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBmdW5jdGlvbiB0byBiZSByZXRyaWVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgMC5cclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0XHJcbiAqIG9mIHRoZSBmdW5jdGlvbiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwQmFja29mZihmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlcGVhdCwgdGhyb3cgYW4gZXJyb3JcclxuICAgIGlmICgrK2F0dGVtcHQgPj0gTUFYX0JBQ0tPRkZfQVRURU1QVFMpIHtcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxyXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc3BvbnNlKSA9PiBzZXRUaW1lb3V0KHJlc3BvbnNlLCBkZWxheUluTXMpKTtcclxuXHJcbiAgICAvLy8gVE8gRE86IENvcnJlY3RcclxuICAgIC8vIC8vIEluZm9ybWF0aW9uIGFib3V0IHRoZSByZXNvdXJjZSB0aW1lb3V0XHJcbiAgICAvLyBsb2coXHJcbiAgICAvLyAgIDMsXHJcbiAgICAvLyAgIGBbdXRpbHNdIFdhaXRlZCAke2RlbGF5SW5Nc31tcyB1bnRpbCBuZXh0IGNhbGwgZm9yIHRoZSByZXNvdXJjZSBvZiBJRDogJHthcmdzWzBdfS5gXHJcbiAgICAvLyApO1xyXG5cclxuICAgIC8vIFRyeSBhZ2FpblxyXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkanVzdHMgdGhlIGNvbnN0cnVjdG9yIG5hbWUgYnkgdHJhbnNmb3JtaW5nIGFuZCBub3JtYWxpemluZyBpdCBiYXNlZFxyXG4gKiBvbiBjb21tb24gY2hhcnQgdHlwZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhDb25zdHJcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnN0ciAtIFRoZSBvcmlnaW5hbCBjb25zdHJ1Y3RvciBuYW1lIHRvIGJlIGZpeGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGNvbnN0cnVjdG9yIG5hbWUsIG9yICdjaGFydCcgaWYgdGhlIGlucHV0XHJcbiAqIGlzIG5vdCByZWNvZ25pemVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeENvbnN0cihjb25zdHIpIHtcclxuICB0cnkge1xyXG4gICAgLy8gRml4IHRoZSBjb25zdHJ1Y3RvciBieSBsb3dlcmluZyBjYXNpbmdcclxuICAgIGNvbnN0IGZpeGVkQ29uc3RyID0gYCR7Y29uc3RyLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnY2hhcnQnLCAnJyl9Q2hhcnRgO1xyXG5cclxuICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgcmVzdWx0IGlzIGp1c3QgJ0NoYXJ0J1xyXG4gICAgaWYgKGZpeGVkQ29uc3RyID09PSAnQ2hhcnQnKSB7XHJcbiAgICAgIGZpeGVkQ29uc3RyLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IsIG90aGVyd2lzZSBkZWZhdWx0IHRvICdjaGFydCdcclxuICAgIHJldHVybiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddLmluY2x1ZGVzKFxyXG4gICAgICBmaXhlZENvbnN0clxyXG4gICAgKVxyXG4gICAgICA/IGZpeGVkQ29uc3RyXHJcbiAgICAgIDogJ2NoYXJ0JztcclxuICB9IGNhdGNoIHtcclxuICAgIC8vIERlZmF1bHQgdG8gJ2NoYXJ0JyBpbiBjYXNlIG9mIGFueSBlcnJvclxyXG4gICAgcmV0dXJuICdjaGFydCc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIG91dGZpbGUgYmFzZWQgb24gcHJvdmlkZWQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeE91dGZpbGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBvdXRmaWxlIC0gVGhlIGZpbGUgcGF0aCBvciBuYW1lLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIG91dGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4T3V0ZmlsZSh0eXBlLCBvdXRmaWxlKSB7XHJcbiAgLy8gR2V0IHRoZSBmaWxlIG5hbWUgZnJvbSB0aGUgYG91dGZpbGVgIG9wdGlvblxyXG4gIGNvbnN0IGZpbGVOYW1lID0gZ2V0QWJzb2x1dGVQYXRoKG91dGZpbGUgfHwgJ2NoYXJ0JylcclxuICAgIC5zcGxpdCgnLicpXHJcbiAgICAuc2hpZnQoKTtcclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCBvdXRmaWxlXHJcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke3R5cGV9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhUeXBlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW291dGZpbGU9bnVsbF0gLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIG51bGwuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgZXhwb3J0IHR5cGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4VHlwZSh0eXBlLCBvdXRmaWxlID0gbnVsbCkge1xyXG4gIC8vIE1JTUUgdHlwZXNcclxuICBjb25zdCBtaW1lVHlwZXMgPSB7XHJcbiAgICAnaW1hZ2UvcG5nJzogJ3BuZycsXHJcbiAgICAnaW1hZ2UvanBlZyc6ICdqcGVnJyxcclxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcclxuICAgICdpbWFnZS9zdmcreG1sJzogJ3N2ZydcclxuICB9O1xyXG5cclxuICAvLyBHZXQgZm9ybWF0c1xyXG4gIGNvbnN0IGZvcm1hdHMgPSBPYmplY3QudmFsdWVzKG1pbWVUeXBlcyk7XHJcblxyXG4gIC8vIENoZWNrIGlmIHR5cGUgYW5kIG91dGZpbGUncyBleHRlbnNpb25zIGFyZSB0aGUgc2FtZVxyXG4gIGlmIChvdXRmaWxlKSB7XHJcbiAgICBjb25zdCBvdXRUeXBlID0gb3V0ZmlsZS5zcGxpdCgnLicpLnBvcCgpO1xyXG5cclxuICAgIC8vIFN1cHBvcnQgdGhlIEpQRyB0eXBlXHJcbiAgICBpZiAob3V0VHlwZSA9PT0gJ2pwZycpIHtcclxuICAgICAgdHlwZSA9ICdqcGVnJztcclxuICAgIH0gZWxzZSBpZiAoZm9ybWF0cy5pbmNsdWRlcyhvdXRUeXBlKSAmJiB0eXBlICE9PSBvdXRUeXBlKSB7XHJcbiAgICAgIHR5cGUgPSBvdXRUeXBlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXHJcbiAgcmV0dXJuIG1pbWVUeXBlc1t0eXBlXSB8fCBmb3JtYXRzLmZpbmQoKHQpID0+IHQgPT09IHR5cGUpIHx8ICdwbmcnO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBwYXRoIGlzIHJlbGF0aXZlIG9yIGFic29sdXRlIGFuZCByZXR1cm5zIHRoZSBjb3JyZWN0ZWQsXHJcbiAqIGFic29sdXRlIHBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0Fic29sdXRlUGF0aFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIGJlIGNoZWNrZWQgb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBhYnNvbHV0ZSBwYXRoLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEFic29sdXRlUGF0aChwYXRoKSB7XHJcbiAgcmV0dXJuIGlzQWJzb2x1dGUocGF0aCkgPyBwYXRoIDogam9pbihfX2Rpcm5hbWUsIHBhdGgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ29udmVydHMgaW5wdXQgZGF0YSB0byBhIEJhc2U2NCBzdHJpbmcgYmFzZWQgb24gdGhlIGV4cG9ydCB0eXBlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QmFzZTY0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCAtIFRoZSBpbnB1dCB0byBiZSB0cmFuc2Zvcm1lZCB0byBCYXNlNjQgZm9ybWF0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSBvcmlnaW5hbCBleHBvcnQgdHlwZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIEJhc2U2NCBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGlucHV0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEJhc2U2NChpbnB1dCwgdHlwZSkge1xyXG4gIC8vIEZvciBwZGYgYW5kIHN2ZyB0eXBlcyB0aGUgaW5wdXQgbXVzdCBiZSB0cmFuc2Zvcm1lZCB0byBCYXNlNjQgZnJvbSBhIGJ1ZmZlclxyXG4gIGlmICh0eXBlID09PSAncGRmJyB8fCB0eXBlID09ICdzdmcnKSB7XHJcbiAgICByZXR1cm4gQnVmZmVyLmZyb20oaW5wdXQsICd1dGY4JykudG9TdHJpbmcoJ2Jhc2U2NCcpO1xyXG4gIH1cclxuXHJcbiAgLy8gRm9yIHBuZyBhbmQganBlZyBpbnB1dCBpcyBhbHJlYWR5IGEgQmFzZTY0IHN0cmluZ1xyXG4gIHJldHVybiBpbnB1dDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgc3RyaW5naWZpZWQgZGF0ZSB3aXRob3V0IHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldE5ld0RhdGVcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXROZXdEYXRlKCkge1xyXG4gIC8vIEdldCByaWQgb2YgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uXHJcbiAgcmV0dXJuIG5ldyBEYXRlKCkudG9TdHJpbmcoKS5zcGxpdCgnKCcpWzBdLnRyaW0oKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgdGhlIHN0b3JlZCB0aW1lIHZhbHVlIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldE5ld0RhdGVUaW1lXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3RGF0ZVRpbWUoKSB7XHJcbiAgcmV0dXJuIG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBpdGVtIGlzIGFuIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzT2JqZWN0XHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gaXRlbSAtIFRoZSBpdGVtIHRvIGJlIGNoZWNrZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSBpdGVtIGlzIGFuIG9iamVjdCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzT2JqZWN0KGl0ZW0pIHtcclxuICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKGl0ZW0pID09PSAnW29iamVjdCBPYmplY3RdJztcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gb2JqZWN0IGlzIGVtcHR5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNPYmplY3RFbXB0eVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaXRlbSAtIFRoZSBvYmplY3QgdG8gYmUgY2hlY2tlZC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIG9iamVjdCBpcyBlbXB0eSwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHkoaXRlbSkge1xyXG4gIHJldHVybiAoXHJcbiAgICB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcgJiZcclxuICAgICFBcnJheS5pc0FycmF5KGl0ZW0pICYmXHJcbiAgICBpdGVtICE9PSBudWxsICYmXHJcbiAgICBPYmplY3Qua2V5cyhpdGVtKS5sZW5ndGggPT09IDBcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQgaW4gdGhlIGdpdmVuIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGl0ZW0gLSBUaGUgc3RyaW5nIHRvIGJlIGNoZWNrZWQgZm9yIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKGl0ZW0pIHtcclxuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pP2xvY2FsaG9zdFxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTBcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTcyXFwuKDFbNi05XXwyWzAtOV18M1swLTFdKVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE5MlxcLjE2OFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvXHJcbiAgXTtcclxuXHJcbiAgcmV0dXJuIHJlZ2V4UGF0dGVybnMuc29tZSgocGF0dGVybikgPT4gcGF0dGVybi50ZXN0KGl0ZW0pKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFV0aWxpdHkgdG8gbWVhc3VyZSBlbGFwc2VkIHRpbWUgdXNpbmcgdGhlIE5vZGUuanMgYHByb2Nlc3MuaHJ0aW1lKClgIG1ldGhvZC5cclxuICpcclxuICogQGZ1bmN0aW9uIG1lYXN1cmVUaW1lXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gQSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGVsYXBzZWQgdGltZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWVhc3VyZVRpbWUoKSB7XHJcbiAgY29uc3Qgc3RhcnQgPSBwcm9jZXNzLmhydGltZS5iaWdpbnQoKTtcclxuICByZXR1cm4gKCkgPT4gTnVtYmVyKHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpIC0gc3RhcnQpIC8gMTAwMDAwMDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJvdW5kcyBhIG51bWJlciB0byB0aGUgc3BlY2lmaWVkIHByZWNpc2lvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHJvdW5kTnVtYmVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB2YWx1ZSAtIFRoZSBudW1iZXIgdG8gYmUgcm91bmRlZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHByZWNpc2lvbiAtIFRoZSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgdG8gcm91bmQgdG8uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtudW1iZXJ9IFRoZSByb3VuZGVkIG51bWJlci5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiByb3VuZE51bWJlcih2YWx1ZSwgcHJlY2lzaW9uID0gMSkge1xyXG4gIGNvbnN0IG11bHRpcGxpZXIgPSBNYXRoLnBvdygxMCwgcHJlY2lzaW9uIHx8IDApO1xyXG4gIHJldHVybiBNYXRoLnJvdW5kKCt2YWx1ZSAqIG11bHRpcGxpZXIpIC8gbXVsdGlwbGllcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGEgdmFsdWUgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdG9Cb29sZWFuXHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gaXRlbSAtIFRoZSB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVGhlIGJvb2xlYW4gcmVwcmVzZW50YXRpb24gb2YgdGhlIGlucHV0IHZhbHVlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHRvQm9vbGVhbihpdGVtKSB7XHJcbiAgcmV0dXJuIFsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJywgJzAnLCAnJ10uaW5jbHVkZXMoaXRlbSlcclxuICAgID8gZmFsc2VcclxuICAgIDogISFpdGVtO1xyXG59XHJcblxyXG4vKipcclxuICogV3JhcHMgY3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBpdCBzYWZlbHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB3cmFwQXJvdW5kXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21Db2RlIC0gVGhlIGN1c3RvbSBjb2RlIHRvIGJlIHdyYXBwZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gRmxhZyB0byBhbGxvdyBsb2FkaW5nIGNvZGUgZnJvbSBhIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2lzQ2FsbGJhY2s9ZmFsc2VdIC0gRmxhZyB0aGF0IGluZGljYXRlcyB0aGUgcmV0dXJuZWQgY29kZVxyXG4gKiBtdXN0IGJlIGluIGEgY2FsbGJhY2sgZm9ybWF0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KHN0cmluZ3xudWxsKX0gVGhlIHdyYXBwZWQgY3VzdG9tIGNvZGUgb3IgbnVsbCBpZiB3cmFwcGluZyBmYWlscy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB3cmFwQXJvdW5kKGN1c3RvbUNvZGUsIGFsbG93RmlsZVJlc291cmNlcywgaXNDYWxsYmFjayA9IGZhbHNlKSB7XHJcbiAgaWYgKGN1c3RvbUNvZGUgJiYgdHlwZW9mIGN1c3RvbUNvZGUgPT09ICdzdHJpbmcnKSB7XHJcbiAgICBjdXN0b21Db2RlID0gY3VzdG9tQ29kZS50cmltKCk7XHJcblxyXG4gICAgaWYgKGN1c3RvbUNvZGUuZW5kc1dpdGgoJy5qcycpKSB7XHJcbiAgICAgIC8vIExvYWQgYSBmaWxlIGlmIHRoZSBmaWxlIHJlc291cmNlcyBhcmUgYWxsb3dlZFxyXG4gICAgICByZXR1cm4gYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICAgPyB3cmFwQXJvdW5kKFxyXG4gICAgICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGN1c3RvbUNvZGUpLCAndXRmOCcpLFxyXG4gICAgICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgICAgIGlzQ2FsbGJhY2tcclxuICAgICAgICAgIClcclxuICAgICAgICA6IG51bGw7XHJcbiAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAhaXNDYWxsYmFjayAmJlxyXG4gICAgICAoY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbigpJykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgpJykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpPT4nKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCkgPT4nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBUcmVhdCBhIGZ1bmN0aW9uIGFzIGEgc2VsZi1pbnZva2luZyBleHByZXNzaW9uXHJcbiAgICAgIHJldHVybiBgKCR7Y3VzdG9tQ29kZX0pKClgO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE9yIHJldHVybiBhcyBhIHN0cmluZ2lmaWVkIGNvZGVcclxuICAgIHJldHVybiBjdXN0b21Db2RlLnJlcGxhY2UoLzskLywgJycpO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIF9fZGlybmFtZSxcclxuICBjbGVhclRleHQsXHJcbiAgZGVlcENvcHksXHJcbiAgZXhwQmFja29mZixcclxuICBmaXhDb25zdHIsXHJcbiAgZml4T3V0ZmlsZSxcclxuICBmaXhUeXBlLFxyXG4gIGdldEFic29sdXRlUGF0aCxcclxuICBnZXRCYXNlNjQsXHJcbiAgZ2V0TmV3RGF0ZSxcclxuICBnZXROZXdEYXRlVGltZSxcclxuICBpc09iamVjdCxcclxuICBpc09iamVjdEVtcHR5LFxyXG4gIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQsXHJcbiAgbWVhc3VyZVRpbWUsXHJcbiAgcm91bmROdW1iZXIsXHJcbiAgdG9Cb29sZWFuLFxyXG4gIHdyYXBBcm91bmRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEEgbW9kdWxlIGZvciBtYW5hZ2luZyBsb2dnaW5nIGZ1bmN0aW9uYWxpdHkgd2l0aCBjdXN0b21pemFibGVcclxuICogbG9nIGxldmVscywgY29uc29sZSBhbmQgZmlsZSBsb2dnaW5nIG9wdGlvbnMsIGFuZCBlcnJvciBoYW5kbGluZyBzdXBwb3J0LlxyXG4gKiBUaGUgbW9kdWxlIGFsc28gZW5zdXJlcyB0aGF0IGZpbGUtYmFzZWQgbG9ncyBhcmUgc3RvcmVkIGluIGEgc3RydWN0dXJlZFxyXG4gKiBkaXJlY3RvcnksIGNyZWF0aW5nIHRoZSBuZWNlc3NhcnkgcGF0aHMgYXV0b21hdGljYWxseSBpZiB0aGV5IGRvIG5vdCBleGlzdC5cclxuICovXHJcblxyXG5pbXBvcnQgeyBhcHBlbmRGaWxlLCBleGlzdHNTeW5jLCBta2RpclN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldEFic29sdXRlUGF0aCwgZ2V0TmV3RGF0ZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuLy8gVGhlIGF2YWlsYWJsZSBjb2xvcnNcclxuY29uc3QgY29sb3JzID0gWydyZWQnLCAneWVsbG93JywgJ2JsdWUnLCAnZ3JheScsICdncmVlbiddO1xyXG5cclxuLy8gVGhlIGRlZmF1bHQgbG9nZ2luZyBjb25maWdcclxuY29uc3QgbG9nZ2luZyA9IHtcclxuICAvLyBGbGFncyBmb3IgbG9nZ2luZyBzdGF0dXNcclxuICB0b0NvbnNvbGU6IHRydWUsXHJcbiAgdG9GaWxlOiBmYWxzZSxcclxuICBwYXRoQ3JlYXRlZDogZmFsc2UsXHJcbiAgLy8gRnVsbCBwYXRoIHRvIHRoZSBsb2cgZmlsZVxyXG4gIHBhdGhUb0xvZzogJycsXHJcbiAgLy8gTG9nIGxldmVsc1xyXG4gIGxldmVsc0Rlc2M6IFtcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdlcnJvcicsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMF1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnd2FybmluZycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMV1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnbm90aWNlJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1syXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd2ZXJib3NlJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1szXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdiZW5jaG1hcmsnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzRdXHJcbiAgICB9XHJcbiAgXVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgYSBtZXNzYWdlLiBBY2NlcHRzIGEgdmFyaWFibGUgYW1vdW50IG9mIGFyZ3VtZW50cy4gQXJndW1lbnRzIGFmdGVyXHJcbiAqIHRoZSBgbGV2ZWxgIHdpbGwgYmUgcGFzc2VkIGRpcmVjdGx5IHRvIGBjb25zb2xlLmxvZ2AsIGFuZC9vciB3aWxsIGJlIGpvaW5lZFxyXG4gKiBhbmQgYXBwZW5kZWQgdG8gdGhlIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Li4udW5rbm93bn0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZ1xyXG4gKiBsZXZlbCBhbmQgdGhlIHJlc3QgYXJlIHN0cmluZ3MgdG8gYnVpbGQgYSBtZXNzYWdlIHdpdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt2b2lkfSBFbmRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gd2hlbiBhdHRlbXB0aW5nIHRvIGxvZ1xyXG4gKiBpbmZvcm1hdGlvbiBhdCBhIGhpZ2hlciBsZXZlbCB0aGFuIHdoYXQgaXMgYWxsb3dlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBsb2coLi4uYXJncykge1xyXG4gIGNvbnN0IFtuZXdMZXZlbCwgLi4udGV4dHNdID0gYXJncztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsc0Rlc2MsIGxldmVsIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2Ugb3IgaXMgaXQgYSBiZW5jaG1hcmsgbG9nXHJcbiAgaWYgKFxyXG4gICAgbmV3TGV2ZWwgIT09IDUgJiZcclxuICAgIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpXHJcbiAgKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KHRleHRzKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGFuIGVycm9yIG1lc3NhZ2Ugd2l0aCBpdHMgc3RhY2sgdHJhY2UuIE9wdGlvbmFsbHksIGEgY3VzdG9tIG1lc3NhZ2VcclxuICogY2FuIGJlIHByb3ZpZGVkLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nV2l0aFN0YWNrXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBuZXdMZXZlbCAtIFRoZSBsb2cgbGV2ZWwuXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbU1lc3NhZ2UgLSBBbiBvcHRpb25hbCBjdXN0b20gbWVzc2FnZSB0byBiZSBsb2dnZWRcclxuICogYWxvbmcgd2l0aCB0aGUgZXJyb3IuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt2b2lkfSBFbmRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gd2hlbiBhdHRlbXB0aW5nIHRvIGxvZ1xyXG4gKiBpbmZvcm1hdGlvbiBhdCBhIGhpZ2hlciBsZXZlbCB0aGFuIHdoYXQgaXMgYWxsb3dlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBsb2dXaXRoU3RhY2sobmV3TGV2ZWwsIGVycm9yLCBjdXN0b21NZXNzYWdlKSB7XHJcbiAgLy8gR2V0IHRoZSBtYWluIG1lc3NhZ2VcclxuICBjb25zdCBtYWluTWVzc2FnZSA9IGN1c3RvbU1lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQWRkIHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXHJcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID0gZXJyb3Iuc3RhY2s7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2UsIGlmIGV4aXN0c1xyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlXTtcclxuICBpZiAoc3RhY2tNZXNzYWdlKSB7XHJcbiAgICB0ZXh0cy5wdXNoKCdcXG4nLCBzdGFja01lc3NhZ2UpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcclxuICAgICAgICB0ZXh0cy5zaGlmdCgpW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAuLi50ZXh0c1xyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpbml0TG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgbG9nZ2luZ2Agb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpbml0TG9nZ2luZyhsb2dnaW5nT3B0aW9ucykge1xyXG4gIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGBsb2dnaW5nT3B0aW9uc2Agb2JqZWN0XHJcbiAgY29uc3QgeyBsZXZlbCwgZGVzdCwgZmlsZSwgdG9Db25zb2xlLCB0b0ZpbGUgfSA9IGxvZ2dpbmdPcHRpb25zO1xyXG5cclxuICAvLyBTZXQgdGhlIGxvZ2dpbmcgbGV2ZWxcclxuICBzZXRMb2dMZXZlbChsZXZlbCk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29uc29sZSBsb2dnaW5nXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmcodG9Db25zb2xlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBmaWxlIGxvZ2dpbmdcclxuICBlbmFibGVGaWxlTG9nZ2luZyhkZXN0LCBmaWxlLCB0b0ZpbGUpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgbG9nIGxldmVsIHRvIHRoZSBzcGVjaWZpZWQgdmFsdWUuIExvZyBsZXZlbHMgYXJlICgwID0gbm8gbG9nZ2luZyxcclxuICogMSA9IGVycm9yLCAyID0gd2FybmluZywgMyA9IG5vdGljZSwgNCA9IHZlcmJvc2UsIG9yIDUgPSBiZW5jaG1hcmspLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0TG9nTGV2ZWxcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxvZyBsZXZlbCB0byBiZSBzZXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0TG9nTGV2ZWwobGV2ZWwpIHtcclxuICBpZiAobGV2ZWwgPj0gMCAmJiBsZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICBsb2dnaW5nLmxldmVsID0gbGV2ZWw7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRW5hYmxlcyBjb25zb2xlIGxvZ2dpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVDb25zb2xlTG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvQ29uc29sZSAtIFRoZSBmbGFnIGZvciBzZXR0aW5nIHRoZSBsb2dnaW5nIHRvIHRoZSBjb25zb2xlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZUNvbnNvbGVMb2dnaW5nKHRvQ29uc29sZSkge1xyXG4gIC8vIFVwZGF0ZSBvcHRpb25zIGZvciB0aGUgY29uc29sZSBsb2dnaW5nXHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSB0b0NvbnNvbGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlRmlsZUxvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGRlc3QgLSBUaGUgZGVzdGluYXRpb24gcGF0aCBmb3IgdGhlIGxvZyBmaWxlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZmlsZSAtIFRoZSBsb2cgZmlsZSBuYW1lLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvRmlsZSAtIFRoZSBmbGFnIGZvciBzZXR0aW5nIHRoZSBsb2dnaW5nIHRvIGEgZmlsZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVGaWxlTG9nZ2luZyhkZXN0LCBmaWxlLCB0b0ZpbGUpIHtcclxuICAvLyBVcGRhdGUgb3B0aW9ucyBmb3IgdGhlIGZpbGUgbG9nZ2luZ1xyXG4gIGxvZ2dpbmcudG9GaWxlID0gdG9GaWxlO1xyXG5cclxuICAvLyBTZXQgdGhlIGBkZXN0YCBhbmQgYGZpbGVgIG9ubHkgaWYgdGhlIGZpbGUgbG9nZ2luZyBpcyBlbmFibGVkXHJcbiAgaWYgKHRvRmlsZSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ID0gZGVzdDtcclxuICAgIGxvZ2dpbmcuZmlsZSA9IGZpbGU7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyB0aGUgcHJvdmlkZWQgdGV4dHMgdG8gYSBmaWxlLCBpZiBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZC4gSXQgY3JlYXRlc1xyXG4gKiB0aGUgbmVjZXNzYXJ5IGRpcmVjdG9yeSBzdHJ1Y3R1cmUgaWYgbm90IGFscmVhZHkgY3JlYXRlZCBhbmQgYXBwZW5kc1xyXG4gKiB0aGUgY29udGVudCwgaW5jbHVkaW5nIGFuIG9wdGlvbmFsIHByZWZpeCwgdG8gdGhlIHNwZWNpZmllZCBsb2cgZmlsZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2dUb0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBBbiBvcHRpb25hbCBwcmVmaXggdG8gYmUgYWRkZWQgdG8gZWFjaCBsb2cgZW50cnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpIHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpICYmXHJcbiAgICAgIG1rZGlyU3luYyhnZXRBYnNvbHV0ZVBhdGgobG9nZ2luZy5kZXN0KSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBmdWxsIHBhdGhcclxuICAgIGxvZ2dpbmcucGF0aFRvTG9nID0gZ2V0QWJzb2x1dGVQYXRoKGpvaW4obG9nZ2luZy5kZXN0LCBsb2dnaW5nLmZpbGUpKTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yICYmIGxvZ2dpbmcudG9GaWxlICYmIGxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29uZmlndXJhdGlvbiBtYW5hZ2VtZW50IG1vZHVsZSBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci5cclxuICogUHJvdmlkZXMgZGVmYXVsdCBjb25maWd1cmF0aW9ucyB0aGF0IHN1cHBvcnQgZW52aXJvbm1lbnQgdmFyaWFibGVzLCBDTElcclxuICogYXJndW1lbnRzLCBhbmQgaW50ZXJhY3RpdmUgcHJvbXB0cyBmb3IgY3VzdG9taXphdGlvbiBvZiBvcHRpb25zIGFuZCBmZWF0dXJlcy5cclxuICogQWRkaXRpb25hbGx5LCBpdCBtYXBzIGxlZ2FjeSBvcHRpb25zIHRvIG1vZGVybiBzdHJ1Y3R1cmVzLCBnZW5lcmF0ZXMgbmVzdGVkXHJcbiAqIGFyZ3VtZW50IG1hcHBpbmdzLCBhbmQgZGlzcGxheXMgQ0xJIHVzYWdlIGluZm9ybWF0aW9uLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBhbGwgYXZhaWxhYmxlIG9wdGlvbnMsIG9yZ2FuaXplZFxyXG4gKiBieSBzZWN0aW9ucy5cclxuICpcclxuICogVGhpcyBvYmplY3QgaW5jbHVkZXM6XHJcbiAqIC0gRGVmYXVsdCB2YWx1ZXMgZm9yIGVhY2ggb3B0aW9uXHJcbiAqIC0gRGF0YSB0eXBlcyBmb3IgdmFsaWRhdGlvblxyXG4gKiAtIE5hbWVzIG9mIGNvcnJlc3BvbmRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIC0gRGVzY3JpcHRpb25zIG9mIGVhY2ggcHJvcGVydHlcclxuICogLSBJbmZvcm1hdGlvbiB1c2VkIGZvciBwcm9tcHRzIGluIGludGVyYWN0aXZlIGNvbmZpZ3VyYXRpb25cclxuICogLSBbT3B0aW9uYWxdIENvcnJlc3BvbmRpbmcgQ0xJIGFyZ3VtZW50IG5hbWVzIGZvciBDTEkgdXNhZ2VcclxuICogLSBbT3B0aW9uYWxdIExlZ2FjeSBuYW1lcyBmcm9tIHRoZSBwcmV2aW91cyBQaGFudG9tSlMtYmFzZWQgc2VydmVyXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IHtcclxuICAgIGFyZ3M6IHtcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnLS1hbGxvdy1ydW5uaW5nLWluc2VjdXJlLWNvbnRlbnQnLFxyXG4gICAgICAgICctLWFzaC1uby1udWRnZXMnLFxyXG4gICAgICAgICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICAgICAgICctLWJsb2NrLW5ldy13ZWItY29udGVudHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYWNjZWxlcmF0ZWQtMmQtY2FudmFzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtbmV0d29ya2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLXRpbWVyLXRocm90dGxpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZGluZy1vY2NsdWRlZC13aW5kb3dzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJyZWFrcGFkJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNoZWNrZXItaW1hZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jbGllbnQtc2lkZS1waGlzaGluZy1kZXRlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LWV4dGVuc2lvbnMtd2l0aC1iYWNrZ3JvdW5kLXBhZ2VzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICAgICAgICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAgICAgICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZmVhdHVyZXM9Q2FsY3VsYXRlTmF0aXZlV2luT2NjbHVzaW9uLEludGVyZXN0RmVlZENvbnRlbnRTdWdnZXN0aW9ucyxXZWJPVFAnLFxyXG4gICAgICAgICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWlwYy1mbG9vZGluZy1wcm90ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWxvZ2dpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtbm90aWZpY2F0aW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1vZmZlci1zdG9yZS11bm1hc2tlZC13YWxsZXQtY2FyZHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtcG9wdXAtYmxvY2tpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJpbnQtcHJldmlldycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcm9tcHQtb24tcmVwb3N0JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXJlbmRlcmVyLWJhY2tncm91bmRpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2VhcmNoLWVuZ2luZS1jaG9pY2Utc2NyZWVuJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlc3Npb24tY3Jhc2hlZC1idWJibGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2l0ZS1pc29sYXRpb24tdHJpYWxzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICAgICAgICctLWRpc2FibGUtc3luYycsXHJcbiAgICAgICAgJy0tZW5hYmxlLXVuc2FmZS13ZWJncHUnLFxyXG4gICAgICAgICctLWhpZGUtY3Jhc2gtcmVzdG9yZS1idWJibGUnLFxyXG4gICAgICAgICctLWhpZGUtc2Nyb2xsYmFycycsXHJcbiAgICAgICAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgICAgICAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgICAgICAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAgICAgICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICAgICAgICctLW5vLXBpbmdzJyxcclxuICAgICAgICAnLS1uby1zYW5kYm94JyxcclxuICAgICAgICAnLS1uby1zdGFydHVwLXdpbmRvdycsXHJcbiAgICAgICAgJy0tbm8tenlnb3RlJyxcclxuICAgICAgICAnLS1wYXNzd29yZC1zdG9yZT1iYXNpYycsXHJcbiAgICAgICAgJy0tcHJvY2Vzcy1wZXItdGFiJyxcclxuICAgICAgICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuICAgICAgXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ1BVUFBFVEVFUl9BUkdTJyxcclxuICAgICAgY2xpTmFtZTogJ3B1cHBldGVlckFyZ3MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FycmF5IG9mIFB1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfVkVSU0lPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2RuVXJsOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NETl9VUkwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0NETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGbGFnIHRvIHJlZmV0Y2ggc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdG9yeSBwYXRoIGZvciBjYWNoZWQgSGlnaGNoYXJ0cyBzY3JpcHRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DT1JFX1NDUklQVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtb2R1bGVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ3N0b2NrJyxcclxuICAgICAgICAnbWFwJyxcclxuICAgICAgICAnZ2FudHQnLFxyXG4gICAgICAgICdleHBvcnRpbmcnLFxyXG4gICAgICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAgICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgICAgIC8vICdhbm5vdGF0aW9ucy1hZHZhbmNlZCcsXHJcbiAgICAgICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAgICAgJ2Jvb3N0JyxcclxuICAgICAgICAnZGF0YScsXHJcbiAgICAgICAgJ2RhdGEtdG9vbHMnLFxyXG4gICAgICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICAgICAnc3RhdGljLXNjYWxlJyxcclxuICAgICAgICAnYnJva2VuLWF4aXMnLFxyXG4gICAgICAgICdoZWF0bWFwJyxcclxuICAgICAgICAndGlsZW1hcCcsXHJcbiAgICAgICAgJ3RpbGVkd2VibWFwJyxcclxuICAgICAgICAndGltZWxpbmUnLFxyXG4gICAgICAgICd0cmVlbWFwJyxcclxuICAgICAgICAndHJlZWdyYXBoJyxcclxuICAgICAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgICAgICdkcmlsbGRvd24nLFxyXG4gICAgICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICAgICAnYnVsbGV0JyxcclxuICAgICAgICAnZnVubmVsJyxcclxuICAgICAgICAnZnVubmVsM2QnLFxyXG4gICAgICAgICdnZW9oZWF0bWFwJyxcclxuICAgICAgICAncHlyYW1pZDNkJyxcclxuICAgICAgICAnbmV0d29ya2dyYXBoJyxcclxuICAgICAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAgICAgJ3BhcmV0bycsXHJcbiAgICAgICAgJ3BhdHRlcm4tZmlsbCcsXHJcbiAgICAgICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAgICAgJ3ByaWNlLWluZGljYXRvcicsXHJcbiAgICAgICAgJ3NhbmtleScsXHJcbiAgICAgICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICAgICAnZGVwZW5kZW5jeS13aGVlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1sYWJlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1vbi1wb2ludCcsXHJcbiAgICAgICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICAgICAnc29uaWZpY2F0aW9uJyxcclxuICAgICAgICAvLyAnc3RvY2stdG9vbHMnLFxyXG4gICAgICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAgICAgJ3N1bmJ1cnN0JyxcclxuICAgICAgICAndmFyaWFibGUtcGllJyxcclxuICAgICAgICAndmFyaXdpZGUnLFxyXG4gICAgICAgICd2ZWN0b3InLFxyXG4gICAgICAgICd2ZW5uJyxcclxuICAgICAgICAnd2luZGJhcmInLFxyXG4gICAgICAgICd3b3JkY2xvdWQnLFxyXG4gICAgICAgICd4cmFuZ2UnLFxyXG4gICAgICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgICAgICdkcmFnLXBhbmVzJyxcclxuICAgICAgICAnZGVidWdnZXInLFxyXG4gICAgICAgICdkdW1iYmVsbCcsXHJcbiAgICAgICAgJ2xvbGxpcG9wJyxcclxuICAgICAgICAnY3lsaW5kZXInLFxyXG4gICAgICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgICAgICdkb3RwbG90JyxcclxuICAgICAgICAnbWFya2VyLWNsdXN0ZXJzJyxcclxuICAgICAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgICAgICdoZWlraW5hc2hpJyxcclxuICAgICAgICAnZmxvd21hcCcsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAnbmF2aWdhdG9yJyxcclxuICAgICAgICAndGV4dHBhdGgnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIG1vZHVsZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbmRpY2F0b3JTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2luZGljYXRvcnMtYWxsJ10sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGluZGljYXRvciBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjMwLjEvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuNDUvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBZGRpdGlvbmFsIGN1c3RvbSBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgICAgc2VwYXJhdG9yOiAnOydcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5GSUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0IGZpbGVuYW1lIHdpdGggdHlwZSwgZm9ybWF0dGVkIGNvcnJlY3RseSBhcyBKU09OIG9yIFNWRycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0lOU1RSJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ092ZXJyaWRlcyB0aGUgYGluZmlsZWAgd2l0aCBKU09OLCBzdHJpbmdpZmllZCBKU09OLCBvciBTVkcgaW5wdXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09QVElPTlMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FsaWFzIGZvciB0aGUgYGluc3RyYCBvcHRpb24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzdmc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU1ZHJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTVkcgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCB0byByZW5kZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CQVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdCYXRjaCBqb2Igc3RyaW5nIHdpdGggaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09VVEZJTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3V0cHV0IGZpbGVuYW1lIHdpdGggdHlwZS4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcgYW5kIGlnbm9yZXMgYHR5cGVgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIGV4cG9ydCBmb3JtYXQuIENhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBwbmcnLFxyXG4gICAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0NPTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDaGFydCBjb25zdHJ1Y3Rvci4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgICBoaW50OiAnRGVmYXVsdDogY2hhcnQnLFxyXG4gICAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGI2NDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CNjQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gdGhlIGNoYXJ0IHNob3VsZCBiZSByZWNlaXZlZCBpbiBCYXNlNjQgZm9ybWF0IGluc3RlYWQgb2YgYmluYXJ5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBub0Rvd25sb2FkOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX05PX0RPV05MT0FEJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZXRoZXIgb3Igbm90IHRvIGluY2x1ZGUgb3IgZXhjbHVkZSBhdHRhY2htZW50IGhlYWRlcnMgaW4gdGhlIHJlc3BvbnNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSEVJR0hUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1NDQUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0RlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgbWluOiAwLjEsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9HTE9CQUxfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggZ2xvYmFsIG9wdGlvbnMgZm9yIEhpZ2hjaGFydHMuc2V0T3B0aW9ucycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVEhFTUVfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHdlYnBhZ2UgcmVuZGVyaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgZHVyaW5nIGV4cG9ydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FsbG93cyBvciBkaXNhbGxvd3MgaW5qZWN0aW9uIG9mIGZpbGVzeXN0ZW0gcmVzb3VyY2VzIChkaXNhYmxlZCBpbiBzZXJ2ZXIgbW9kZSknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DQUxMQkFDSycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIENhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgLmpzIGZpbGVuYW1lJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZXMgYXMgSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgZmlsZW5hbWUsIGNvbnRhaW5pbmcgZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSB3aXRoIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdQcm9tcHQtYmFzZWQgb3B0aW9uIHNldHRpbmcsIHNhdmVkIHRvIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVTZXJ2ZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0cyB0aGUgc2VydmVyIHdoZW4gdHJ1ZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaG9zdDoge1xyXG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIb3N0bmFtZSBvZiB0aGUgc2VydmVyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUG9ydCBudW1iZXIgZm9yIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHVwbG9hZExpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiAzLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9VUExPQURfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVxdWVzdCBib2R5IHNpemUgaW4gTUInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAnc2VydmVyQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Rpc3BsYXlzIG9yIG5vdCBhY3Rpb24gZHVyYXRpb25zIGluIG1pbGxpc2Vjb25kcyBkdXJpbmcgc2VydmVyIHJlcXVlc3RzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwcm94eToge1xyXG4gICAgICBob3N0OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eUhvc3QnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnSG9zdCBvZiB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB3aW5kb3c6IHtcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RpbWUgd2luZG93IGluIG1pbnV0ZXMgZm9yIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnRGVsYXkgZHVyYXRpb24gYmV0d2VlbiBzdWNjZXNzaXZlIHJlcXVlc3RzIGJlZm9yZSByZWFjaGluZyB0aGUgbGltaXQnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcEtleToge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0tleSB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwVG9rZW5gJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBUb2tlbjoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVG9rZW4gdG8gYnlwYXNzIHRoZSByYXRlIGxpbWl0ZXIsIHVzZWQgd2l0aCBgc2tpcEtleWAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdGb3JjZXMgdGhlIHNlcnZlciB0byB1c2UgSFRUUFMgb25seSB3aGVuIHRydWUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IGZvciB0aGUgU1NMIHNlcnZlcicsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xDZXJ0UGF0aCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWluaW11bSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICB3b3JrTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDQwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTnVtYmVyIG9mIHRhc2tzIGEgd29ya2VyIGNhbiBoYW5kbGUgYmVmb3JlIHJlc3RhcnRpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpZGxlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMzAwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGlkbGUgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAyMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGJlZm9yZSByZXRyeWluZyByZXNvdXJjZSBjcmVhdGlvbiBvbiBmYWlsdXJlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgdG8gY2hlY2sgYW5kIGRlc3Ryb3kgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1Nob3dzIHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdMb2dnaW5nIHZlcmJvc2l0eSBsZXZlbCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICByb3VuZDogMCxcclxuICAgICAgICBtaW46IDAsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnTG9nIGZpbGUgbmFtZS4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCB0byBiZSBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZXN0OiB7XHJcbiAgICAgIHZhbHVlOiAnbG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFJlcXVpcmVzIGBsb2dUb0ZpbGVgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBjb25zb2xlIGxvZ2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvRmlsZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICB1aToge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVVpJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGVuZHBvaW50IHJvdXRlIGZvciB0aGUgVUknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBOb2RlLmpzIGVudmlyb25tZW50IHR5cGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0Rpc3BsYXkgb3Igc2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoYXJkUmVzZXRQYWdlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfSEFSRF9SRVNFVF9QQUdFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaGV0aGVyIG9yIG5vdCB0byByZXNldCB0aGUgcGFnZSBjb250ZW50IGVudGlyZWx5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBicm93c2VyU2hlbGxNb2RlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHNldCB0aGUgYnJvd3NlciB0byBydW4gaW4gc2hlbGwgbW9kZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBkZWJ1Zzoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZURlYnVnJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSB1bmRlcmx5aW5nIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlYWRsZXNzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBoZWFkbGVzcyBtb2RlIGR1cmluZyBkZWJ1Z2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRldnRvb2xzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgRGV2VG9vbHMgaW4gaGVhZGZ1bCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGxpc3RlbmluZyB0byBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGR1bXBpbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RVTVBJTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdSZWRpcmVjdHMgb3Igbm90IGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gcHJvY2Vzcy5zdGRvdXQgYW5kIHByb2Nlc3Muc3RkZXJyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzbG93TW86IHtcclxuICAgICAgdmFsdWU6IDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVsYXlzIFB1cHBldGVlciBvcGVyYXRpb25zIGJ5IHRoZSBzcGVjaWZpZWQgbWlsbGlzZWNvbmRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IHVzZWQgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLy8gUHJvcGVydGllcyBuZXN0aW5nIGxldmVsIG9mIGFsbCBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRQcm9wcyA9IF9jcmVhdGVOZXN0ZWRQcm9wcyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIFByb3BlcnRpZXMgbmFtZXMgdGhhdCBzaG91bGQgbm90IGJlIHJlY3Vyc2l2ZWx5IG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IF9jcmVhdGVBYnNvbHV0ZVByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdlbmVyYXRlcyBhIG1hcHBpbmcgb2YgbmVzdGVkIGFyZ3VtZW50IGNoYWlucyBmcm9tIGEgbmVzdGVkXHJcbiAqIGNvbmZpZyBvYmplY3QuIFRoaXMgZnVuY3Rpb24gdHJhdmVyc2VzIGEgbmVzdGVkIG9iamVjdCBhbmQgY3JlYXRlcyBhIG1hcHBpbmdcclxuICogd2hlcmUgZWFjaCBrZXkgaXMgYW4gYXJndW1lbnQgbmFtZSAoZWl0aGVyIGZyb20gYGNsaU5hbWVgLCBgbGVnYWN5TmFtZWAsXHJcbiAqIG9yIHRoZSBvcmlnaW5hbCBrZXkpIGFuZCBlYWNoIHZhbHVlIGlzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY2hhaW5cclxuICogb2YgbmVzdGVkIHByb3BlcnRpZXMgbGVhZGluZyB0byB0aGF0IGFyZ3VtZW50LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbbmVzdGVkUHJvcHM9e31dIC0gVGhlIGFjY3VtdWxhdG9yIG9iamVjdCBmb3Igc3RvcmluZ1xyXG4gKiB0aGUgcmVzdWx0aW5nIGFyZ3VtZW50cyBjaGFpbnMuICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcHJvcENoYWluPScnXSAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzLFxyXG4gKiB1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbi4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gb2JqZWN0IG1hcHBpbmcgYXJndW1lbnQgbmFtZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZ1xyXG4gKiBuZXN0ZWQgcHJvcGVydHkgY2hhaW5zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzKGNvbmZpZywgbmVzdGVkUHJvcHMgPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgX2NyZWF0ZU5lc3RlZFByb3BzKGVudHJ5LCBuZXN0ZWRQcm9wcywgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBuZXN0ZWRQcm9wc1tlbnRyeS5jbGlOYW1lIHx8IGtleV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgaWYgKGVudHJ5LmxlZ2FjeU5hbWUgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2tleX1gLnN1YnN0cmluZygxKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIG9iamVjdCB3aXRoIG5lc3RlZCBhcmd1bWVudCBjaGFpbnNcclxuICByZXR1cm4gbmVzdGVkUHJvcHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSBnYXRoZXJzIHRoZSBuYW1lcyBvZiBwcm9wZXJ0aWVzIGZyb20gYSBjb25maWd1cmF0aW9uIG9iamVjdCB0aGF0XHJcbiAqIGNhbiBiZSB0cmVhdGVkIGFzIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZXNlIHByb3BlcnRpZXMgaGF2ZSB2YWx1ZXMgdGhhdFxyXG4gKiBhcmUgb2JqZWN0cyBhbmQgZG8gbm90IGNvbnRhaW4gZnVydGhlciBuZXN0ZWQgZGVwdGggd2hlbiBtZXJnaW5nIGFuIG9iamVjdFxyXG4gKiBjb250YWluaW5nIHRoZXNlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbYWJzb2x1dGVQcm9wcz1bXV0gLSBBbiBhcnJheSB0byBjb2xsZWN0IHRoZSBuYW1lc1xyXG4gKiBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxzdHJpbmc+fSBBbiBhcnJheSBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZVxyXG4gKiBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZUFic29sdXRlUHJvcHMoY29uZmlnLCBhYnNvbHV0ZVByb3BzID0gW10pIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudHlwZXMgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzXHJcbiAgICAgIF9jcmVhdGVBYnNvbHV0ZVByb3BzKGVudHJ5LCBhYnNvbHV0ZVByb3BzKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIHRoZSBvcHRpb24gY2FuIGJlIGFuIG9iamVjdCwgc2F2ZSBpdHMgdHlwZSBpbiB0aGUgYXJyYXlcclxuICAgICAgaWYgKGVudHJ5LnR5cGVzLmluY2x1ZGVzKCdPYmplY3QnKSkge1xyXG4gICAgICAgIGFic29sdXRlUHJvcHMucHVzaChrZXkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgYXJyYXkgd2l0aCB0aGUgbmFtZXMgb2YgYWJzb2x1dGUgcHJvcGVydGllc1xyXG4gIHJldHVybiBhYnNvbHV0ZVByb3BzO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRQcm9wcyxcclxuICBhYnNvbHV0ZVByb3BzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZFxyXG4gKiB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBgZW52c2AuIFdlIHNob3VsZCBub3QgdXNlIHRoZSBgcHJvY2Vzcy5lbnZgXHJcbiAqIGRpcmVjdGx5IGluIHRoZSBhcHBsaWNhdGlvbiBhcyB0aGVzZSB3b3VsZCBub3QgYmUgcGFyc2VkIHByb3Blcmx5LlxyXG4gKlxyXG4gKiBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSBwYXJzZWQgYW5kIHZhbGlkYXRlZCBvbmx5IG9uY2Ugd2hlblxyXG4gKiB0aGUgYXBwbGljYXRpb24gc3RhcnRzLiBXZSBzaG91bGQgd3JpdGUgYSBjdXN0b20gdmFsaWRhdG9yIG9yIGEgdHJhbnNmb3JtZXJcclxuICogZm9yIGVhY2ggb2YgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gcHVwcGV0ZWVyXHJcbiAgUFVQUEVURUVSX0FSR1M6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIGhpZ2hjaGFydHNcclxuICBISUdIQ0hBUlRTX1ZFUlNJT046IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PiAvXihsYXRlc3R8XFxkKyhcXC5cXGQrKXswLDJ9KSQvLnRlc3QodmFsdWUpIHx8IHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vJykgfHxcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcclxuICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgSElHSENIQVJUU19DRE5fVVJMLiBJdCBzaG91bGQgc3RhcnQgd2l0aCBodHRwOi8vIG9yIGh0dHBzOi8vLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTOiB2LmFycmF5KGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jb3JlU2NyaXB0cy52YWx1ZSksXHJcbiAgSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5tb2R1bGVTY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9JTkZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0lOU1RSOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9TVkc6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0JBVENIOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PVVRGSUxFOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9UWVBFOiB2LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLFxyXG4gIEVYUE9SVF9DT05TVFI6IHYuZW51bShbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddKSxcclxuICBFWFBPUlRfQjY0OiB2LmJvb2xlYW4oKSxcclxuICBFWFBPUlRfTk9fRE9XTkxPQUQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0dMT0JBTF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9USEVNRV9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19DVVNUT01fQ09ERTogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ0FMTEJBQ0s6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX1JFU09VUkNFUzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfTE9BRF9DT05GSUc6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUc6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlclxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1VQTE9BRF9MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgTE9HR0lOR19UT19GSUxFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gdWlcclxuICBVSV9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PREVfRU5WOiB2LmVudW0oWydkZXZlbG9wbWVudCcsICdwcm9kdWN0aW9uJywgJ3Rlc3QnXSksXHJcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0hBUkRfUkVTRVRfUEFHRTogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgTWFuYWdlcyBjb25maWd1cmF0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGJ5IGxvYWRpbmdcclxuICogYW5kIG1lcmdpbmcgb3B0aW9ucyBmcm9tIG11bHRpcGxlIHNvdXJjZXMsIHN1Y2ggYXMgZGVmYXVsdCBzZXR0aW5ncyxcclxuICogZW52aXJvbm1lbnQgdmFyaWFibGVzLCB1c2VyLXByb3ZpZGVkIG9wdGlvbnMsIGFuZCBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKiBFbnN1cmVzIHRoZSBnbG9iYWwgb3B0aW9ucyBhcmUgdXAtdG8tZGF0ZSB3aXRoIHRoZSBoaWdoZXN0IHByaW9yaXR5IHZhbHVlcy5cclxuICogUHJvdmlkZXMgZnVuY3Rpb25zIGZvciBhY2Nlc3NpbmcgYW5kIHVwZGF0aW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgaXNPYmplY3QsIGRlZXBDb3B5LCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuaW1wb3J0IHsgZGVmYXVsdENvbmZpZywgbmVzdGVkUHJvcHMsIGFic29sdXRlUHJvcHMgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0R2xvYmFsT3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zIG9mIHRoZSBzZXJ2ZXIgaW5zdGFuY2Ugb2JqZWN0XHJcbiAqIG9yIGl0cyBjb3B5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRSZWZlcmVuY2U9dHJ1ZV0gLSBPcHRpb25hbCBwYXJhbWV0ZXIgdG8gZGVjaWRlIHdoZXRoZXJcclxuICogdG8gcmV0dXJuIHRoZSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zIG9mIHRoZSBzZXJ2ZXIgaW5zdGFuY2Ugb2JqZWN0XHJcbiAqIG9yIHJldHVybiBhIGNvcHkgb2YgaXQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIHRydWUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zIG9mIHRoZSBzZXJ2ZXIgaW5zdGFuY2VcclxuICogb2JqZWN0IG9yIGl0cyBjb3B5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoZ2V0UmVmZXJlbmNlID0gdHJ1ZSkge1xyXG4gIHJldHVybiBnZXRSZWZlcmVuY2UgPyBnbG9iYWxPcHRpb25zIDogZGVlcENvcHkoZ2xvYmFsT3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBnbG9iYWwgb3B0aW9ucyBvZiB0aGUgZXhwb3J0IHNlcnZlciBpbnN0YW5jZSwga2VlcGluZyB0aGUgcHJpbmNpcGxlXHJcbiAqIG9mIHRoZSBvcHRpb25zIGxvYWQgcHJpb3JpdHkgZnJvbSBhbGwgYXZhaWxhYmxlIHNvdXJjZXMuIEl0IGFjY2VwdHMgb3B0aW9uYWxcclxuICogYGN1c3RvbU9wdGlvbnNgIG9iamVjdCBhbmQgYGNsaUFyZ3NgIGFycmF5IHdpdGggYXJndW1lbnRzIGZyb20gdGhlIENMSS4gVGhlc2VcclxuICogb3B0aW9ucyB3aWxsIGJlIHZhbGlkYXRlZCBhbmQgYXBwbGllZCBpZiBwcm92aWRlZC5cclxuICpcclxuICogVGhlIHByaW9yaXR5IG9yZGVyIG9mIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBPcHRpb25zIGZyb20gdGhlIGBsaWIvc2NoZW1hcy9jb25maWcuanNgIGZpbGUgKGRlZmF1bHQgdmFsdWVzKS5cclxuICogMi4gT3B0aW9ucyBmcm9tIGEgY3VzdG9tIEpTT04gZmlsZSAobG9hZGVkIGJ5IHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgKHRoZSBgLmVudmAgZmlsZSkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgZmlyc3QgcGFyYW1ldGVyIChieSBkZWZhdWx0IGFuIGVtcHR5IG9iamVjdCkuXHJcbiAqIDUuIE9wdGlvbnMgZnJvbSB0aGUgQ0xJLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2N1c3RvbU9wdGlvbnM9e31dIC0gT3B0aW9uYWwgY3VzdG9tIG9wdGlvbnMgZm9yIGFkZGl0aW9uYWxcclxuICogY29uZmlndXJhdGlvbi4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbY2xpQXJncz1bXV0gLSBPcHRpb25hbCBjb21tYW5kIGxpbmUgYXJndW1lbnRzXHJcbiAqIGZvciBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24uIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFttb2RpZnlHbG9iYWw9ZmFsc2VdIC0gT3B0aW9uYWwgcGFyYW1ldGVyIHRvIGRlY2lkZVxyXG4gKiB3aGV0aGVyIHRvIHVwZGF0ZSBhbmQgcmV0dXJuIHRoZSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9mIHRoZSBzZXJ2ZXIgaW5zdGFuY2Ugb2JqZWN0IG9yIHJldHVybiBhIGNvcHkgb2YgaXQuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGZhbHNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnZW5lcmFsIG9wdGlvbnMgb2JqZWN0LCByZWZsZWN0aW5nIHRoZSBtZXJnZWRcclxuICogY29uZmlndXJhdGlvbiBmcm9tIGFsbCBhdmFpbGFibGUgc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRPcHRpb25zKFxyXG4gIGN1c3RvbU9wdGlvbnMgPSB7fSxcclxuICBjbGlBcmdzID0gW10sXHJcbiAgbW9kaWZ5R2xvYmFsID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gT2JqZWN0IGZvciBvcHRpb25zIGxvYWRlZCB2aWEgdGhlIGBsb2FkQ29uZmlnYCBvcHRpb25cclxuICBsZXQgY29uZmlnT3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBPYmplY3QgZm9yIG9wdGlvbnMgZnJvbSB0aGUgQ0xJXHJcbiAgbGV0IGNsaU9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzLmxlbmd0aCkge1xyXG4gICAgLy8gR2V0IG9wdGlvbnMgZnJvbSB0aGUgY3VzdG9tIEpTT04gbG9hZGVkIHZpYSB0aGUgYGxvYWRDb25maWdgXHJcbiAgICBjb25maWdPcHRpb25zID0gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIENMSVxyXG4gICAgY2xpT3B0aW9ucyA9IF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncyk7XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgdGhlIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IG9yIGEgY29weSBvZiB0aGUgb2JqZWN0XHJcbiAgY29uc3QgZ2VuZXJhbE9wdGlvbnMgPSBnZXRPcHRpb25zKG1vZGlmeUdsb2JhbCk7XHJcblxyXG4gIC8vIFVwZGF0ZSB2YWx1ZXMgb2YgdGhlIGdlbmVyYWwgb3B0aW9ucyB3aXRoIHZhbHVlcyBmcm9tIGVhY2ggc291cmNlIHBvc3NpYmxlXHJcbiAgX3VwZGF0ZU9wdGlvbnMoXHJcbiAgICBkZWZhdWx0Q29uZmlnLFxyXG4gICAgZ2VuZXJhbE9wdGlvbnMsXHJcbiAgICBjb25maWdPcHRpb25zLFxyXG4gICAgY3VzdG9tT3B0aW9ucyxcclxuICAgIGNsaU9wdGlvbnNcclxuICApO1xyXG5cclxuICAvLyBSZXR1cm4gb3B0aW9uc1xyXG4gIHJldHVybiBnZW5lcmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGNvbnNpZGVyaW5nIGFic29sdXRlIHByb3BlcnRpZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtZXJnZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9yaWdpbmFsT3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTWVyZ2VkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBtZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zLCBuZXdPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGBuZXdPcHRpb25zYCBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG5ld09wdGlvbnMpKSB7XHJcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhuZXdPcHRpb25zKSkge1xyXG4gICAgICBvcmlnaW5hbE9wdGlvbnNba2V5XSA9XHJcbiAgICAgICAgaXNPYmplY3QodmFsdWUpICYmXHJcbiAgICAgICAgIWFic29sdXRlUHJvcHMuaW5jbHVkZXMoa2V5KSAmJlxyXG4gICAgICAgIG9yaWdpbmFsT3B0aW9uc1trZXldICE9PSB1bmRlZmluZWRcclxuICAgICAgICAgID8gbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9uc1trZXldLCB2YWx1ZSlcclxuICAgICAgICAgIDogdmFsdWUgIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICAgIDogb3JpZ2luYWxPcHRpb25zW2tleV07XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIHJlc3VsdCBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvbGRPcHRpb25zKSA9PT0gJ1tvYmplY3QgT2JqZWN0XScpIHtcclxuICAgIC8vIEl0ZXJhdGUgb3ZlciBlYWNoIGtleS12YWx1ZSBwYWlyIGluIHRoZSBvbGQtc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhvbGRPcHRpb25zKSkge1xyXG4gICAgICAvLyBJZiB0aGVyZSBpcyBhIG5lc3RlZCBtYXBwaW5nLCBzcGxpdCBpdCBpbnRvIGEgcHJvcGVydGllcyBjaGFpblxyXG4gICAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRQcm9wc1trZXldXHJcbiAgICAgICAgPyBuZXN0ZWRQcm9wc1trZXldLnNwbGl0KCcuJylcclxuICAgICAgICA6IFtdO1xyXG5cclxuICAgICAgLy8gSWYgaXQgaXMgdGhlIGxhc3QgcHJvcGVydHkgaW4gdGhlIGNoYWluLCBhc3NpZ24gdGhlIHZhbHVlLCBvdGhlcndpc2UsXHJcbiAgICAgIC8vIGNyZWF0ZSBvciByZXVzZSB0aGUgbmVzdGVkIG9iamVjdFxyXG4gICAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKFxyXG4gICAgICAgIChvYmosIHByb3AsIGluZGV4KSA9PlxyXG4gICAgICAgICAgKG9ialtwcm9wXSA9XHJcbiAgICAgICAgICAgIHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCA/IHZhbHVlIDogb2JqW3Byb3BdIHx8IHt9KSxcclxuICAgICAgICBuZXdPcHRpb25zXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIG5ldywgc3RydWN0dXJlZCBvcHRpb25zIG9iamVjdFxyXG4gIHJldHVybiBuZXdPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogVmFsaWRhdGVzLCBwYXJzZXMsIGFuZCBjaGVja3MgaWYgdGhlIHByb3ZpZGVkIGNvbmZpZyBpcyBhbGxvd2VkIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnXHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gY29uZmlnIC0gVGhlIGNvbmZpZyB0byBiZSB2YWxpZGF0ZWQgYW5kIHBhcnNlZCBhcyBhIHNldFxyXG4gKiBvZiBvcHRpb25zLiBNdXN0IGJlIGVpdGhlciBhbiBvYmplY3Qgb3IgYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3RvU3RyaW5nPWZhbHNlXSAtIFdoZXRoZXIgdG8gcmV0dXJuIGEgc3RyaW5naWZpZWQgdmVyc2lvblxyXG4gKiBvZiB0aGUgcGFyc2VkIGNvbmZpZy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgZmFsc2UuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2FsbG93RnVuY3Rpb25zPWZhbHNlXSAtIFdoZXRoZXIgdG8gYWxsb3cgZnVuY3Rpb25zXHJcbiAqIGluIHRoZSBwYXJzZWQgY29uZmlnLiBJZiB0cnVlLCBmdW5jdGlvbnMgYXJlIHByZXNlcnZlZC4gT3RoZXJ3aXNlLCB3aGVuXHJcbiAqIGEgZnVuY3Rpb24gaXMgZm91bmQsIG51bGwgaXMgcmV0dXJuZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGZhbHNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgZmFsc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRWZXJzaW9uKG5vTG9nbyA9IGZhbHNlKSB7XHJcbiAgLy8gR2V0IHBhY2thZ2UgdmVyc2lvbiBlaXRoZXIgZnJvbSBgLmVudmAgb3IgZnJvbSBgcGFja2FnZS5qc29uYFxyXG4gIGNvbnN0IHBhY2thZ2VWZXJzaW9uID0gSlNPTi5wYXJzZShcclxuICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ3BhY2thZ2UuanNvbicpKVxyXG4gICkudmVyc2lvbjtcclxuXHJcbiAgLy8gUHJpbnQgdGV4dCBvbmx5XHJcbiAgaWYgKG5vTG9nbykge1xyXG4gICAgY29uc29sZS5sb2coYEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciB2JHtwYWNrYWdlVmVyc2lvbn1gKTtcclxuICB9IGVsc2Uge1xyXG4gICAgLy8gUHJpbnQgdGhlIGxvZ29cclxuICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICdtc2cnLCAnc3RhcnR1cC5tc2cnKSkudG9TdHJpbmcoKS5ib2xkXHJcbiAgICAgICAgLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyBnbG9iYWwgb3B0aW9ucyBvYmplY3QgYmFzZWQgb24gcHJvdmlkZWRcclxuICogY29uZmlndXJhdGlvbiwgc2V0dGluZyB2YWx1ZXMgZnJvbSBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9pbml0R2xvYmFsT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvIGJlIHVzZWQgZm9yIGluaXRpYWxpemluZ1xyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBJbml0aWFsaXplZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmZ1bmN0aW9uIF9pbml0R2xvYmFsT3B0aW9ucyhjb25maWcpIHtcclxuICBjb25zdCBvcHRpb25zID0ge307XHJcblxyXG4gIC8vIFN0YXJ0IGluaXRpYWxpemluZyB0aGUgYG9wdGlvbnNgIG9iamVjdCByZWN1cnNpdmVseVxyXG4gIGZvciAoY29uc3QgW25hbWUsIGl0ZW1dIG9mIE9iamVjdC5lbnRyaWVzKGNvbmZpZykpIHtcclxuICAgIG9wdGlvbnNbbmFtZV0gPSBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoaXRlbSwgJ3ZhbHVlJylcclxuICAgICAgPyBpdGVtLnZhbHVlXHJcbiAgICAgIDogX2luaXRHbG9iYWxPcHRpb25zKGl0ZW0pO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBjcmVhdGVkIGBvcHRpb25zYCBvYmplY3RcclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgb3B0aW9ucyBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSB2YXJpb3VzIHNvdXJjZXMsIGZvbGxvd2luZyBhIHNwZWNpZmljXHJcbiAqIHByaW9yaXRpemF0aW9uIG9yZGVyLiBUaGUgZnVuY3Rpb24gY2hlY2tzIGZvciB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlclxyXG4gKiBvZiBwcmVjZWRlbmNlOiB0aGUgYGxvYWRDb25maWdgIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgZW52aXJvbm1lbnQgdmFyaWFibGVzLFxyXG4gKiBjdXN0b20gb3B0aW9ucywgYW5kIENMSSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCwgd2hpY2ggaW5jbHVkZXMgdGhlIGluaXRpYWxcclxuICogc2V0dGluZ3MgYW5kIG1ldGFkYXRhIGZvciBlYWNoIG9wdGlvbi4gVGhpcyBvYmplY3QgaXMgdXNlZCB0byBkZXRlcm1pbmVcclxuICogdGhlIHN0cnVjdHVyZSBhbmQgZGVmYXVsdCB2YWx1ZXMgZm9yIHRoZSBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0aGF0IHdpbGwgYmUgdXBkYXRlZCB3aXRoIHZhbHVlc1xyXG4gKiBmcm9tIG90aGVyIHNvdXJjZXMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWdPcHQgLSBUaGUgY29uZmlndXJhdGlvbiBvcHRpb25zIG9iamVjdCwgbG9hZGVkIHdpdGhcclxuICogdGhlIGBsb2FkQ29uZmlnYCBvcHRpb24sIHdoaWNoIG1heSBwcm92aWRlIHZhbHVlcyB0byBvdmVycmlkZSBkZWZhdWx0cy5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbU9wdCAtIFRoZSBjdXN0b20gb3B0aW9ucyBvYmplY3QsIHR5cGljYWxseSBjb250YWluaW5nXHJcbiAqIGFkZGl0aW9uYWwgYW5kIHVzZXItZGVmaW5lZCB2YWx1ZXMsIHdoaWNoIG1heSBvdmVycmlkZSBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjbGlPcHQgLSBUaGUgQ0xJIG9wdGlvbnMgb2JqZWN0LCB3aGljaCBtYXkgaW5jbHVkZSB2YWx1ZXNcclxuICogcHJvdmlkZWQgdGhyb3VnaCBjb21tYW5kLWxpbmUgYXJndW1lbnRzIGFuZCBoYXMgdGhlIGhpZ2hlc3QgcHJlY2VkZW5jZSBhbW9uZ1xyXG4gKiBvcHRpb25zLlxyXG4gKi9cclxuZnVuY3Rpb24gX3VwZGF0ZU9wdGlvbnMoY29uZmlnLCBvcHRpb25zLCBjb25maWdPcHQsIGN1c3RvbU9wdCwgY2xpT3B0KSB7XHJcbiAgT2JqZWN0LmtleXMoY29uZmlnKS5mb3JFYWNoKChrZXkpID0+IHtcclxuICAgIC8vIEdldCB0aGUgY29uZmlnIGVudHJ5IG9mIGEgc3BlY2lmaWMgb3B0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIEdhdGhlciB2YWx1ZXMgZm9yIHRoZSBvcHRpb25zIGZyb20gZXZlcnkgcG9zc2libGUgc291cmNlLCBpZiBleGlzdHNcclxuICAgIGNvbnN0IGNvbmZpZ1ZhbCA9IGNvbmZpZ09wdCAmJiBjb25maWdPcHRba2V5XTtcclxuICAgIGNvbnN0IGN1c3RvbVZhbCA9IGN1c3RvbU9wdCAmJiBjdXN0b21PcHRba2V5XTtcclxuICAgIGNvbnN0IGNsaVZhbCA9IGNsaU9wdCAmJiBjbGlPcHRba2V5XTtcclxuXHJcbiAgICAvLyBJZiB0aGUgdmFsdWUgbm90IGZvdW5kLCBuZWVkIHRvIGdvIGRlZXBlclxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgX3VwZGF0ZU9wdGlvbnMoZW50cnksIG9wdGlvbnNba2V5XSwgY29uZmlnVmFsLCBjdXN0b21WYWwsIGNsaVZhbCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gY3VzdG9tIEpTT04gb3B0aW9ucyBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY29uZmlnVmFsICE9PSB1bmRlZmluZWQgJiYgY29uZmlnVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY29uZmlnVmFsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzIGV4aXN0cywgaXQgdGFrZSBwcmVjZWRlbmNlXHJcbiAgICAgIGNvbnN0IGVudlZhbCA9IGVudnNbZW50cnkuZW52TGlua107XHJcbiAgICAgIGlmIChlbnRyeS5lbnZMaW5rIGluIGVudnMgJiYgZW52VmFsICE9PSB1bmRlZmluZWQgJiYgZW52VmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gZW52VmFsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gdXNlciBvcHRpb25zIGV4aXN0cywgaXQgdGFrZSBwcmVjZWRlbmNlXHJcbiAgICAgIGlmIChjdXN0b21WYWwgIT09IHVuZGVmaW5lZCAmJiBjdXN0b21WYWwgIT09IG51bGwpIHtcclxuICAgICAgICBvcHRpb25zW2tleV0gPSBjdXN0b21WYWw7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGEgdmFsdWUgZnJvbSBDTEkgb3B0aW9ucyBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY2xpVmFsICE9PSB1bmRlZmluZWQgJiYgY2xpVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY2xpVmFsO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmdcclxuICogd2l0aCB0aGUgb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy4gSW4gb3JkZXIgZm9yIGEgZnVuY3Rpb25cclxuICogdG8gYmUgcHJlc2VydmVkLCBpdCBuZWVkcyB0byBmb2xsb3cgdGhlIGZvcm1hdCBgZnVuY3Rpb24gKC4uLikgey4uLn1gLlxyXG4gKiBTdWNoIGEgZnVuY3Rpb24gY2FuIGFsc28gYmUgc3RyaW5naWZpZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWRcclxuICogaW4gdGhlIG91dHB1dC4gT3RoZXJ3aXNlIGFuIGVycm9yIGlzIHRocm93bi5cclxuICogQHBhcmFtIHtib29sZWFufSBzdHJpbmdpZnlGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBzYXZlZFxyXG4gKiBhcyBzdHJpbmdzLiBUaGUgYGFsbG93RnVuY3Rpb25zYCBtdXN0IGJlIHNldCB0byB0cnVlIGFzIHdlbGwgZm9yIHRoaXMgdG8gdGFrZVxyXG4gKiBhbiBlZmZlY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgd2hlbiBmdW5jdGlvbnMgYXJlIG5vdCBhbGxvd2VkIGJ1dCBhcmVcclxuICogZm91bmQgaW4gcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX29wdGlvbnNTdHJpbmdpZnkob3B0aW9ucywgYWxsb3dGdW5jdGlvbnMsIHN0cmluZ2lmeUZ1bmN0aW9ucykge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAoXywgdmFsdWUpID0+IHtcclxuICAgIC8vIFRyaW0gc3RyaW5nIHZhbHVlc1xyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgdmFsdWUgaXMgYSBmdW5jdGlvbiBvciBzdHJpbmdpZmllZCBmdW5jdGlvblxyXG4gICAgaWYgKFxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgfHxcclxuICAgICAgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpICYmXHJcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBJZiBhbGxvd0Z1bmN0aW9ucyBpcyBzZXQgdG8gdHJ1ZSwgcHJlc2VydmUgZnVuY3Rpb25zXHJcbiAgICAgIGlmIChhbGxvd0Z1bmN0aW9ucykge1xyXG4gICAgICAgIC8vIEJhc2VkIG9uIHRoZSBgc3RyaW5naWZ5RnVuY3Rpb25zYCBvcHRpb25zLCBzZXQgZnVuY3Rpb24gdmFsdWVzXHJcbiAgICAgICAgcmV0dXJuIHN0cmluZ2lmeUZ1bmN0aW9uc1xyXG4gICAgICAgICAgPyAvLyBBcyBzdHJpbmdpZmllZCBmdW5jdGlvbnNcclxuICAgICAgICAgICAgYFwiRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOXCJgXHJcbiAgICAgICAgICA6IC8vIEFzIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOYDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaHJvdyBhbiBlcnJvciBvdGhlcndpc2VcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgc2ltcGx5IHJldHVybiB0aGUgdmFsdWVcclxuICAgIHJldHVybiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIHN0cmluZ2lmeUZ1bmN0aW9ucyA/IC9cXFxcXCJFWFBfRlVOfEVYUF9GVU5cXFxcXCIvZyA6IC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHByb3ZpZGVkIHZpYVxyXG4gKiB0aGUgYGxvYWRDb25maWdgIG9wdGlvbiBpbiB0aGUgY29tbWFuZC1saW5lIGFyZ3VtZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2FkQ29uZmlnRmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjbGlBcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyB0byBzZWFyY2hcclxuICogZm9yIHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uIHdhcyB1c2VkXHJcbiAgY29uc3QgY29uZmlnSW5kZXggPSBjbGlBcmdzLmZpbmRJbmRleChcclxuICAgIChhcmcpID0+IGFyZy5yZXBsYWNlKC8tL2csICcnKSA9PT0gJ2xvYWRDb25maWcnXHJcbiAgKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uIHZhbHVlXHJcbiAgY29uc3QgY29uZmlnRmlsZU5hbWUgPSBjb25maWdJbmRleCA+IC0xICYmIGNsaUFyZ3NbY29uZmlnSW5kZXggKyAxXTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGBsb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBMb2FkIGFuIG9wdGlvbmFsIGN1c3RvbSBKU09OIGNvbmZpZyBmaWxlXHJcbiAgICAgIHJldHVybiBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpKSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgMixcclxuICAgICAgICBlcnJvcixcclxuICAgICAgICBgW2NvbmZpZ10gVW5hYmxlIHRvIGxvYWQgdGhlIGNvbmZpZ3VyYXRpb24gZnJvbSB0aGUgJHtjb25maWdGaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBObyBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gcmV0dXJuXHJcbiAgcmV0dXJuIHt9O1xyXG59XHJcblxyXG4vKipcclxuICogUGFyc2VzIGNvbW1hbmQtbGluZSBhcmd1bWVudHMgYW5kIHBhaXJzIGVhY2ggYXJndW1lbnQgd2l0aCBpdHMgY29ycmVzcG9uZGluZ1xyXG4gKiBvcHRpb24gaW4gdGhlIGNvbmZpZ3VyYXRpb24uIFRoZSB2YWx1ZXMgYXJlIHN0cnVjdHVyZWQgaW50byBhIG5lc3RlZCBvcHRpb25zXHJcbiAqIG9iamVjdCwgYmFzZWQgb24gcHJlZGVmaW5lZCBtYXBwaW5ncy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBuZXN0ZWRQcm9wcyAtIEFuIGFycmF5IG9mIG5lc3RpbmcgbGV2ZWwgZm9yIGFsbFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjbGlBcmdzIC0gQW4gYXJyYXkgb2YgY29tbWFuZC1saW5lIGFyZ3VtZW50c1xyXG4gKiBjb250YWluaW5nIG9wdGlvbnMgYW5kIHRoZWlyIGFzc29jaWF0ZWQgdmFsdWVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0IHdoZXJlIGVhY2ggb3B0aW9uIGZyb21cclxuICogdGhlIGNvbW1hbmQtbGluZSBpcyBwYWlyZWQgd2l0aCBpdHMgdmFsdWUsIHN0cnVjdHVyZWQgaW50byBuZXN0ZWQgb2JqZWN0c1xyXG4gKiBhcyBkZWZpbmVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX3BhaXJBcmd1bWVudFZhbHVlKG5lc3RlZFByb3BzLCBjbGlBcmdzKSB7XHJcbiAgLy8gQW4gZW1wdHkgb2JqZWN0IHRvIGNvbGxlY3QgYW5kIHN0cnVjdHVyaXplIGRhdGEgZnJvbSB0aGUgYXJnc1xyXG4gIGNvbnN0IGNsaU9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gQ3ljbGUgdGhyb3VnaCBhbGwgQ0xJIGFyZ3MgYW5kIGZpbHRlciB0aGVtXHJcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBjbGlBcmdzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICBjb25zdCBvcHRpb24gPSBjbGlBcmdzW2ldLnJlcGxhY2UoLy0vZywgJycpO1xyXG5cclxuICAgIC8vIEZpbmQgdGhlIHJpZ2h0IHBsYWNlIGZvciBwcm9wZXJ0eSdzIHZhbHVlXHJcbiAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRQcm9wc1tvcHRpb25dXHJcbiAgICAgID8gbmVzdGVkUHJvcHNbb3B0aW9uXS5zcGxpdCgnLicpXHJcbiAgICAgIDogW107XHJcblxyXG4gICAgLy8gQ3JlYXRlIG9wdGlvbnMgb2JqZWN0IHdpdGggdmFsdWVzIGZyb20gQ0xJIGZvciBsYXRlciBwYXJzaW5nIGFuZCBtZXJnaW5nXHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XHJcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcclxuICAgICAgICBjb25zdCB2YWx1ZSA9IGNsaUFyZ3NbKytpXTtcclxuICAgICAgICBpZiAoIXZhbHVlKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGBbY29uZmlnXSBNaXNzaW5nIHZhbHVlIGZvciB0aGUgQ0xJICctLSR7b3B0aW9ufScgYXJndW1lbnQuIFVzaW5nIHRoZSBkZWZhdWx0IHZhbHVlLmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIG9ialtwcm9wXSA9IHZhbHVlIHx8IG51bGw7XHJcbiAgICAgIH0gZWxzZSBpZiAob2JqW3Byb3BdID09PSB1bmRlZmluZWQpIHtcclxuICAgICAgICBvYmpbcHJvcF0gPSB7fTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xyXG4gICAgfSwgY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcGFyc2VkIENMSSBvcHRpb25zXHJcbiAgcmV0dXJuIGNsaU9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSB0cmF2ZXJzZXMgdGhlIG9wdGlvbnMgb2JqZWN0IHRvIHByaW50IHRoZSB1c2FnZSBpbmZvcm1hdGlvblxyXG4gKiBmb3IgZWFjaCBvcHRpb24gY2F0ZWdvcnkgYW5kIGluZGl2aWR1YWwgb3B0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2N5Y2xlQ2F0ZWdvcmllc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIENMSSBvcHRpb25zLlxyXG4gKiBJdCBtYXkgaW5jbHVkZSBuZXN0ZWQgY2F0ZWdvcmllcyBhbmQgaW5kaXZpZHVhbCBvcHRpb25zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb25zKSB7XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgb3B0aW9uXSBvZiBPYmplY3QuZW50cmllcyhvcHRpb25zKSkge1xyXG4gICAgLy8gSWYgdGhlIGN1cnJlbnQgZW50cnkgaXMgYSBjYXRlZ29yeSBhbmQgbm90IGEgbGVhZiBvcHRpb24sIHJlY3Vyc2UgaW50byBpdFxyXG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob3B0aW9uLCAndmFsdWUnKSkge1xyXG4gICAgICBfY3ljbGVDYXRlZ29yaWVzKG9wdGlvbik7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBQcmVwYXJlIGRlc2NyaXB0aW9uXHJcbiAgICAgIGNvbnN0IGRlc2NOYW1lID0gYCAtLSR7b3B0aW9uLmNsaU5hbWUgfHwgbmFtZX1gO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB2YWx1ZVxyXG4gICAgICBsZXQgb3B0aW9uVmFsdWUgPSBvcHRpb24udmFsdWU7XHJcblxyXG4gICAgICAvLyBQcmVwYXJlIHZhbHVlIGZvciBvcHRpb24gdGhhdCBpcyBub3QgbnVsbCBhbmQgaXMgYXJyYXkgb2Ygc3RyaW5nc1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmdbXScpKSB7XHJcbiAgICAgICAgb3B0aW9uVmFsdWUgPVxyXG4gICAgICAgICAgJ1snICsgb3B0aW9uVmFsdWUubWFwKChpdGVtKSA9PiBgJyR7aXRlbX0nYCkuam9pbignLCAnKSArICddJztcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGEgc3RyaW5nXHJcbiAgICAgIGlmIChvcHRpb25WYWx1ZSAhPT0gbnVsbCAmJiBvcHRpb24udHlwZXMuaW5jbHVkZXMoJ3N0cmluZycpKSB7XHJcbiAgICAgICAgb3B0aW9uVmFsdWUgPSBgJyR7b3B0aW9uVmFsdWV9J2A7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERpc3BsYXkgY29ycmVjdGx5IGFsaWduZWQgbWVzc2FnZXNcclxuICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgZGVzY05hbWUuZ3JlZW4sXHJcbiAgICAgICAgYCR7KCc8JyArIG9wdGlvbi50eXBlcy5qb2luKCd8JykgKyAnPicpLnllbGxvd31gLFxyXG4gICAgICAgIGAke1N0cmluZyhvcHRpb25WYWx1ZSkuYm9sZH1gLmJsdWUsXHJcbiAgICAgICAgYC0gJHtvcHRpb24uZGVzY3JpcHRpb259LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHNldE9wdGlvbnMsXHJcbiAgbWVyZ2VPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuICBpc0FsbG93ZWRDb25maWcsXHJcbiAgcHJpbnRMaWNlbnNlLFxyXG4gIHByaW50VXNhZ2UsXHJcbiAgcHJpbnRWZXJzaW9uXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBIVFRQIHV0aWxpdHkgbW9kdWxlIGZvciBmZXRjaGluZyBhbmQgcG9zdGluZyBkYXRhLiBTdXBwb3J0cyBib3RoXHJcbiAqIEhUVFAgYW5kIEhUVFBTIHByb3RvY29scywgcHJvdmlkaW5nIG1ldGhvZHMgdG8gbWFrZSBHRVQgYW5kIFBPU1QgcmVxdWVzdHNcclxuICogd2l0aCBjdXN0b21pemFibGUgb3B0aW9ucy4gSW5jbHVkZXMgcHJvdG9jb2wgZGV0ZXJtaW5hdGlvbiBiYXNlZCBvbiBVUkxcclxuICogYW5kIGF1Z21lbnRzIHJlc3BvbnNlIG9iamVjdHMgd2l0aCBhICd0ZXh0JyBwcm9wZXJ0eSBmb3IgZWFzaWVyIGRhdGEgYWNjZXNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgZGF0YSBmcm9tIHRoZSBzcGVjaWZpZWQgVVJMIHVzaW5nIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGZldGNoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGZldGNoIGRhdGEgZnJvbS5cclxuICogQHBhcmFtIHtPYmplY3R9IFtyZXF1ZXN0T3B0aW9ucz17fV0gLSBPcHRpb25zIGZvciB0aGUgSFRUUC9IVFRQUyByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBIVFRQL0hUVFBTIHJlc3BvbnNlXHJcbiAqIG9iamVjdCB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmZXRjaCh1cmwsIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgX2dldFByb3RvY29sTW9kdWxlKHVybClcclxuICAgICAgLmdldCh1cmwsIHJlcXVlc3RPcHRpb25zLCAocmVzcG9uc2UpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIXJlc3BvbnNlRGF0YSkge1xyXG4gICAgICAgICAgICByZWplY3QoJ05vdGhpbmcgd2FzIGZldGNoZWQgZnJvbSB0aGUgVVJMLicpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZW5kcyBhIFBPU1QgcmVxdWVzdCB0byB0aGUgc3BlY2lmaWVkIFVSTCB3aXRoIHRoZSBwcm92aWRlZCBKU09OIGJvZHkgdXNpbmdcclxuICogZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBzZW5kIHRoZSBQT1NUIHJlcXVlc3QgdG8uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbYm9keT17fV0gLSBUaGUgSlNPTiBib2R5IHRvIGluY2x1ZGUgaW4gdGhlIFBPU1QgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3QodXJsLCBib2R5ID0ge30sIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KGJvZHkpO1xyXG5cclxuICAgIC8vIFNldCBkZWZhdWx0IGhlYWRlcnMgYW5kIG1lcmdlIHdpdGggcmVxdWVzdE9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSBPYmplY3QuYXNzaWduKFxyXG4gICAgICB7XHJcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXHJcbiAgICAgICAgaGVhZGVyczoge1xyXG4gICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcclxuICAgICAgICAgICdDb250ZW50LUxlbmd0aCc6IGRhdGEubGVuZ3RoXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICByZXF1ZXN0T3B0aW9uc1xyXG4gICAgKTtcclxuXHJcbiAgICBjb25zdCByZXF1ZXN0ID0gX2dldFByb3RvY29sTW9kdWxlKHVybClcclxuICAgICAgLnJlcXVlc3QodXJsLCBvcHRpb25zLCAocmVzcG9uc2UpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAvLyBXcml0ZSB0aGUgcmVxdWVzdCBib2R5IGFuZCBlbmQgdGhlIHJlcXVlc3RcclxuICAgIHJlcXVlc3Qud3JpdGUoZGF0YSk7XHJcbiAgICByZXF1ZXN0LmVuZCgpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgYmFzZWQgb24gdGhlIHByb3ZpZGVkIFVSTC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9nZXRQcm90b2NvbE1vZHVsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBkZXRlcm1pbmUgdGhlIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgKGh0dHAgb3IgaHR0cHMpLlxyXG4gKi9cclxuZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlKHVybCkge1xyXG4gIHJldHVybiB1cmwuc3RhcnRzV2l0aCgnaHR0cHMnKSA/IGh0dHBzIDogaHR0cDtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGZldGNoLFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQSBjdXN0b20gZXJyb3IgY2xhc3MgZm9yIGhhbmRsaW5nIGV4cG9ydC1yZWxhdGVkIGVycm9ycy4gRXh0ZW5kcyB0aGUgbmF0aXZlXHJcbiAqIGBFcnJvcmAgY2xhc3MgdG8gaW5jbHVkZSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgbGlrZSBzdGF0dXMgY29kZSBhbmQgc3RhY2tcclxuICogdHJhY2UgZGV0YWlscy5cclxuICovXHJcbmNsYXNzIEV4cG9ydEVycm9yIGV4dGVuZHMgRXJyb3Ige1xyXG4gIC8qKlxyXG4gICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIFRoZSBlcnJvciBtZXNzYWdlIHRvIGJlIGRpc3BsYXllZC5cclxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzQ29kZSAtIE9wdGlvbmFsIEhUVFAgc3RhdHVzIGNvZGUgYXNzb2NpYXRlZFxyXG4gICAqIHdpdGggdGhlIGVycm9yIChlLmcuLCA0MDAsIDUwMCkuXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSwgc3RhdHVzQ29kZSkge1xyXG4gICAgc3VwZXIoKTtcclxuXHJcbiAgICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gICAgdGhpcy5zdGFja01lc3NhZ2UgPSBtZXNzYWdlO1xyXG5cclxuICAgIGlmIChzdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IHN0YXR1c0NvZGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXRzIGFkZGl0aW9uYWwgZXJyb3IgZGV0YWlscyBiYXNlZCBvbiBhbiBleGlzdGluZyBlcnJvciBvYmplY3QuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIEFuIGVycm9yIG9iamVjdCBjb250YWluaW5nIGRldGFpbHMgdG8gcG9wdWxhdGVcclxuICAgKiB0aGUgYEV4cG9ydEVycm9yYCBpbnN0YW5jZS5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtFeHBvcnRFcnJvcn0gVGhlIHVwZGF0ZWQgaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAgY2xhc3MuXHJcbiAgICovXHJcbiAgc2V0RXJyb3IoZXJyb3IpIHtcclxuICAgIHRoaXMuZXJyb3IgPSBlcnJvcjtcclxuXHJcbiAgICBpZiAoZXJyb3IubmFtZSkge1xyXG4gICAgICB0aGlzLm5hbWUgPSBlcnJvci5uYW1lO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChlcnJvci5zdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XHJcbiAgICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcclxuICAgICAgdGhpcy5zdGFjayA9IGVycm9yLnN0YWNrO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgRXhwb3J0RXJyb3I7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgY2FjaGUgbWFuYWdlciBpcyByZXNwb25zaWJsZSBmb3IgaGFuZGxpbmcgYW5kIG1hbmFnaW5nXHJcbiAqIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYWxvbmcgd2l0aCBpdHMgZGVwZW5kZW5jaWVzLiBJdCBlbnN1cmVzIHRoYXQgdGhlc2VcclxuICogcmVzb3VyY2VzIGFyZSBzdG9yZWQgYW5kIHJldHJpZXZlZCBlZmZpY2llbnRseSB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZVxyXG4gKiBhbmQgcmVkdWNlIHJlZHVuZGFudCBuZXR3b3JrIHJlcXVlc3RzLiBUaGUgY2FjaGUgaXMgc3RvcmVkIGluIHRoZSBgLmNhY2hlYFxyXG4gKiBkaXJlY3RvcnkgYnkgZGVmYXVsdCwgd2hpY2ggc2VydmVzIGFzIGEgZGVkaWNhdGVkIGZvbGRlciBmb3Iga2VlcGluZyBjYWNoZWRcclxuICogZmlsZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IEh0dHBzUHJveHlBZ2VudCB9IGZyb20gJ2h0dHBzLXByb3h5LWFnZW50JztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGZldGNoIH0gZnJvbSAnLi9mZXRjaC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gT2JqZWN0IGNvbnRhaW5pbmcgYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgc2VydmVyLnByb3h5YFxyXG4gKiBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGUoXHJcbiAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgc2VydmVyUHJveHlPcHRpb25zXHJcbikge1xyXG4gIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgLy8gR2V0IHRoZSBjYWNoZSBwYXRoXHJcbiAgY29uc3QgY2FjaGVQYXRoID0gZ2V0Q2FjaGVQYXRoKCk7XHJcblxyXG4gIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgY29uc3Qgc291cmNlUGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnc291cmNlcy5qcycpO1xyXG5cclxuICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICFleGlzdHNTeW5jKGNhY2hlUGF0aCkgJiYgbWtkaXJTeW5jKGNhY2hlUGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XHJcblxyXG4gIC8vIEZldGNoIGFsbCB0aGUgc2NyaXB0cyBlaXRoZXIgaWYgdGhlIGBtYW5pZmVzdC5qc29uYCBkb2VzIG5vdCBleGlzdFxyXG4gIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICBpZiAoIWV4aXN0c1N5bmMobWFuaWZlc3RQYXRoKSB8fCBoaWdoY2hhcnRzT3B0aW9ucy5mb3JjZUZldGNoKSB7XHJcbiAgICBsb2coMywgJ1tjYWNoZV0gRmV0Y2hpbmcgYW5kIGNhY2hpbmcgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMuJyk7XHJcbiAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICAgICAgc291cmNlUGF0aFxyXG4gICAgKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBSZWFkIHRoZSBtYW5pZmVzdCBKU09OXHJcbiAgICBjb25zdCBtYW5pZmVzdCA9IEpTT04ucGFyc2UocmVhZEZpbGVTeW5jKG1hbmlmZXN0UGF0aCkpO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZSBtb2R1bGVzIGlzIGFuIGFycmF5LCBpZiBzbywgd2UgcmV3cml0ZSBpdCB0byBhIG1hcCB0byBtYWtlXHJcbiAgICAvLyBpdCBlYXNpZXIgdG8gcmVzb2x2ZSBtb2R1bGVzLlxyXG4gICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICBjb25zdCBtb2R1bGVNYXAgPSB7fTtcclxuICAgICAgbWFuaWZlc3QubW9kdWxlcy5mb3JFYWNoKChtKSA9PiAobW9kdWxlTWFwW21dID0gMSkpO1xyXG4gICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgYWN0dWFsIG51bWJlciBvZiBzY3JpcHRzIHRvIGJlIGZldGNoZWRcclxuICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9IGhpZ2hjaGFydHNPcHRpb25zO1xyXG4gICAgY29uc3QgbnVtYmVyT2ZNb2R1bGVzID1cclxuICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgaGlnaGNoYXJ0cyBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAvLyBJZiB0aGVyZSBhcmUgY2hhbmdlcywgZmV0Y2ggcmVxdWVzdGVkIG1vZHVsZXMgYW5kIHByb2R1Y3RzLFxyXG4gICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgICdbY2FjaGVdIEEgSGlnaGNoYXJ0cyB2ZXJzaW9uIG1pc21hdGNoIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC4nXHJcbiAgICAgICk7XHJcbiAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgfSBlbHNlIGlmIChPYmplY3Qua2V5cyhtYW5pZmVzdC5tb2R1bGVzIHx8IHt9KS5sZW5ndGggIT09IG51bWJlck9mTW9kdWxlcykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICApO1xyXG4gICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENoZWNrIGVhY2ggbW9kdWxlLCBpZiBhbnl0aGluZyBpcyBtaXNzaW5nIHJlZmV0Y2ggZXZlcnl0aGluZ1xyXG4gICAgICByZXF1ZXN0VXBkYXRlID0gKG1vZHVsZVNjcmlwdHMgfHwgW10pLnNvbWUoKG1vZHVsZU5hbWUpID0+IHtcclxuICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgaWYgKHJlcXVlc3RVcGRhdGUpIHtcclxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCBfdXBkYXRlQ2FjaGUoXHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgIHNvdXJjZVBhdGhcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxvZygzLCAnW2NhY2hlXSBEZXBlbmRlbmN5IGNhY2hlIGlzIHVwIHRvIGRhdGUsIHByb2NlZWRpbmcuJyk7XHJcblxyXG4gICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgIGNhY2hlLnNvdXJjZXMgPSByZWFkRmlsZVN5bmMoc291cmNlUGF0aCwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgIC8vIEdldCBjdXJyZW50IG1vZHVsZXMgbWFwXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICAgIGNhY2hlLmhjVmVyc2lvbiA9IGV4dHJhY3RWZXJzaW9uKGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRmluYWxseSwgc2F2ZSB0aGUgbmV3IG1hbmlmZXN0LCB3aGljaCBpcyBiYXNpY2FsbHkgb3VyIGN1cnJlbnQgY29uZmlnXHJcbiAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgYXdhaXQgX3NhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHNPcHRpb25zLCBmZXRjaGVkTW9kdWxlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIEdldCB0aGUgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyB0byB1cGRhdGUgdG8gdGhlIG5ldyB2ZXJzaW9uXHJcbiAgY29uc3Qgb3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRvIHRoZSBuZXcgdmVyc2lvblxyXG4gIG9wdGlvbnMuaGlnaGNoYXJ0cy52ZXJzaW9uID0gbmV3VmVyc2lvbjtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgpOyAvLyAjNTYyXHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGEgc2luZ2xlIHNjcmlwdCBhbmQgdXBkYXRlcyB0aGUgYGZldGNoZWRNb2R1bGVzYCBhY2NvcmRpbmdseS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHQgLSBBIHBhdGggdG8gc2NyaXB0IHRvIGdldC5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gQWRkaXRpb25hbCBvcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnRcclxuICogdG8gdXNlIGZvciBhIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtzaG91bGRUaHJvd0Vycm9yPWZhbHNlXSAtIEEgZmxhZyB0byBpbmRpY2F0ZSBpZiB0aGUgZXJyb3JcclxuICogc2hvdWxkIGJlIHRocm93bi4gVGhpcyBzaG91bGQgYmUgdXNlZCBvbmx5IGZvciB0aGUgY29yZSBzY3JpcHRzLiBUaGUgZGVmYXVsdFxyXG4gKiB2YWx1ZSBpcyBmYWxzZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIHRleHQgcmVwcmVzZW50YXRpb25cclxuICogb2YgdGhlIGZldGNoZWQgc2NyaXB0LlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYSBwcm9ibGVtXHJcbiAqIHdpdGggZmV0Y2hpbmcgdGhlIHNjcmlwdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoXHJcbiAgc2NyaXB0LFxyXG4gIHJlcXVlc3RPcHRpb25zLFxyXG4gIGZldGNoZWRNb2R1bGVzLFxyXG4gIHNob3VsZFRocm93RXJyb3IgPSBmYWxzZVxyXG4pIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSAuanMgZnJvbSB0aGUgY3VzdG9tIHN0cmluZ3NcclxuICBpZiAoc2NyaXB0LmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgc2NyaXB0ID0gc2NyaXB0LnN1YnN0cmluZygwLCBzY3JpcHQubGVuZ3RoIC0gMyk7XHJcbiAgfVxyXG4gIGxvZyg0LCBgW2NhY2hlXSBGZXRjaGluZyBzY3JpcHQgLSAke3NjcmlwdH0uanNgKTtcclxuXHJcbiAgLy8gRmV0Y2ggdGhlIHNjcmlwdFxyXG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goYCR7c2NyaXB0fS5qc2AsIHJlcXVlc3RPcHRpb25zKTtcclxuXHJcbiAgLy8gSWYgT0ssIHJldHVybiBpdHMgdGV4dCByZXByZXNlbnRhdGlvblxyXG4gIGlmIChyZXNwb25zZS5zdGF0dXNDb2RlID09PSAyMDAgJiYgdHlwZW9mIHJlc3BvbnNlLnRleHQgPT0gJ3N0cmluZycpIHtcclxuICAgIGlmIChmZXRjaGVkTW9kdWxlcykge1xyXG4gICAgICBjb25zdCBtb2R1bGVOYW1lID0gZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0KTtcclxuICAgICAgZmV0Y2hlZE1vZHVsZXNbbW9kdWxlTmFtZV0gPSAxO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHJlc3BvbnNlLnRleHQ7XHJcbiAgfVxyXG5cclxuICAvLyBCYXNlZCBvbiB0aGUgYHNob3VsZFRocm93RXJyb3JgIGZsYWcsIGRlY2lkZSBob3cgdG8gc2VydmUgZXJyb3IgbWVzc2FnZVxyXG4gIGlmIChzaG91bGRUaHJvd0Vycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgIGBbY2FjaGVdIENvdWxkIG5vdCBmZXRjaCB0aGUgJHtzY3JpcHR9LmpzLiBUaGUgc2NyaXB0IG1pZ2h0IG5vdCBleGlzdCBpbiB0aGUgcmVxdWVzdGVkIHZlcnNpb24gKHN0YXR1cyBjb2RlOiAke3Jlc3BvbnNlLnN0YXR1c0NvZGV9KS5gLFxyXG4gICAgICA0MDRcclxuICAgICkuc2V0RXJyb3IocmVzcG9uc2UpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgIGBbY2FjaGVdIENvdWxkIG5vdCBmZXRjaCB0aGUgJHtzY3JpcHR9LmpzLiBUaGUgc2NyaXB0IG1pZ2h0IG5vdCBleGlzdCBpbiB0aGUgcmVxdWVzdGVkIHZlcnNpb24uYFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTYXZlcyB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgZmV0Y2hlZCBtb2R1bGVzIHRvIHRoZSBjYWNoZSBtYW5pZmVzdFxyXG4gKiBmaWxlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaGlnaGNoYXJ0c09wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgaGlnaGNoYXJ0c2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IFtmZXRjaGVkTW9kdWxlcz17fV0gLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHNcclxuICogbW9kdWxlcyBoYXZlIGJlZW4gZmV0Y2hlZC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIHdoaWxlXHJcbiAqIHdyaXRpbmcgdGhlIGNhY2hlIG1hbmlmZXN0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3NhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHNPcHRpb25zLCBmZXRjaGVkTW9kdWxlcyA9IHt9KSB7XHJcbiAgY29uc3QgbmV3TWFuaWZlc3QgPSB7XHJcbiAgICB2ZXJzaW9uOiBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uLFxyXG4gICAgbW9kdWxlczogZmV0Y2hlZE1vZHVsZXNcclxuICB9O1xyXG5cclxuICAvLyBVcGRhdGUgY2FjaGUgb2JqZWN0IHdpdGggdGhlIGN1cnJlbnQgbW9kdWxlc1xyXG4gIGNhY2hlLmFjdGl2ZU1hbmlmZXN0ID0gbmV3TWFuaWZlc3Q7XHJcblxyXG4gIGxvZygzLCAnW2NhY2hlXSBXcml0aW5nIGEgbmV3IG1hbmlmZXN0LicpO1xyXG4gIHRyeSB7XHJcbiAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICBqb2luKGdldENhY2hlUGF0aCgpLCAnbWFuaWZlc3QuanNvbicpLFxyXG4gICAgICBKU09OLnN0cmluZ2lmeShuZXdNYW5pZmVzdCksXHJcbiAgICAgICd1dGY4J1xyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBFcnJvciB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgSGlnaGNoYXJ0cyBgc2NyaXB0c2AgYW5kIGBjdXN0b21TY3JpcHRzYCBmcm9tIHRoZSBnaXZlbiBDRE5zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHNcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY29yZVNjcmlwdHMgLSBIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaC5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gbW9kdWxlU2NyaXB0cyAtIEhpZ2hjaGFydHMgbW9kdWxlcyB0byBmZXRjaC5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY3VzdG9tU2NyaXB0cyAtIEN1c3RvbSBzY3JpcHQgcGF0aHMgdG8gZmV0Y2ggKGZ1bGxcclxuICogVVJMcykuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgc2VydmVyLnByb3h5YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHNcclxuICogbW9kdWxlcyBoYXZlIGJlZW4gZmV0Y2hlZC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGZldGNoZWQgc2NyaXB0c1xyXG4gKiBjb250ZW50IGpvaW5lZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiBzZXR0aW5nIGFuIEhUVFAgQWdlbnQgZm9yIHByb3h5LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2ZldGNoU2NyaXB0cyhcclxuICBjb3JlU2NyaXB0cyxcclxuICBtb2R1bGVTY3JpcHRzLFxyXG4gIGN1c3RvbVNjcmlwdHMsXHJcbiAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gIGZldGNoZWRNb2R1bGVzXHJcbikge1xyXG4gIC8vIENvbmZpZ3VyZSBwcm94eSBpZiBleGlzdHNcclxuICBsZXQgcHJveHlBZ2VudDtcclxuICBjb25zdCBwcm94eUhvc3QgPSBzZXJ2ZXJQcm94eU9wdGlvbnMuaG9zdDtcclxuICBjb25zdCBwcm94eVBvcnQgPSBzZXJ2ZXJQcm94eU9wdGlvbnMucG9ydDtcclxuXHJcbiAgLy8gVHJ5IHRvIGNyZWF0ZSBhIFByb3h5IEFnZW50XHJcbiAgaWYgKHByb3h5SG9zdCAmJiBwcm94eVBvcnQpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIHByb3h5QWdlbnQgPSBuZXcgSHR0cHNQcm94eUFnZW50KHtcclxuICAgICAgICBob3N0OiBwcm94eUhvc3QsXHJcbiAgICAgICAgcG9ydDogcHJveHlQb3J0XHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2FjaGVdIENvdWxkIG5vdCBjcmVhdGUgYSBQcm94eSBBZ2VudC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIElmIGV4aXN0cywgYWRkIHByb3h5IGFnZW50IHRvIHJlcXVlc3Qgb3B0aW9uc1xyXG4gIGNvbnN0IHJlcXVlc3RPcHRpb25zID0gcHJveHlBZ2VudFxyXG4gICAgPyB7XHJcbiAgICAgICAgYWdlbnQ6IHByb3h5QWdlbnQsXHJcbiAgICAgICAgdGltZW91dDogc2VydmVyUHJveHlPcHRpb25zLnRpbWVvdXRcclxuICAgICAgfVxyXG4gICAgOiB7fTtcclxuXHJcbiAgY29uc3QgYWxsRmV0Y2hQcm9taXNlcyA9IFtcclxuICAgIC4uLmNvcmVTY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMsIHRydWUpXHJcbiAgICApLFxyXG4gICAgLi4ubW9kdWxlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzKVxyXG4gICAgKSxcclxuICAgIC4uLmN1c3RvbVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zKVxyXG4gICAgKVxyXG4gIF07XHJcblxyXG4gIGNvbnN0IGZldGNoZWRTY3JpcHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoYWxsRmV0Y2hQcm9taXNlcyk7XHJcbiAgcmV0dXJuIGZldGNoZWRTY3JpcHRzLmpvaW4oJztcXG4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGxvY2FsIGNhY2hlIHdpdGggSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCB0aGVpciB2ZXJzaW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfdXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gT2JqZWN0IGNvbnRhaW5pbmcgYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgc2VydmVyLnByb3h5YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc291cmNlUGF0aCAtIFRoZSBwYXRoIHRvIHRoZSBzb3VyY2UgZmlsZSBpbiB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCByZXByZXNlbnRpbmdcclxuICogdGhlIGZldGNoZWQgbW9kdWxlcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGFuIGlzc3VlIHVwZGF0aW5nXHJcbiAqIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3VwZGF0ZUNhY2hlKGhpZ2hjaGFydHNPcHRpb25zLCBzZXJ2ZXJQcm94eU9wdGlvbnMsIHNvdXJjZVBhdGgpIHtcclxuICAvLyBHZXQgSGlnaGNoYXJ0cyB2ZXJzaW9uIGZvciBzY3JpcHRzXHJcbiAgY29uc3QgaGNWZXJzaW9uID1cclxuICAgIGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24gPT09ICdsYXRlc3QnXHJcbiAgICAgID8gbnVsbFxyXG4gICAgICA6IGAke2hpZ2hjaGFydHNPcHRpb25zLnZlcnNpb259YDtcclxuXHJcbiAgLy8gR2V0IHRoZSBDRE4gdXJsIGZvciBzY3JpcHRzXHJcbiAgY29uc3QgY2RuVXJsID0gaGlnaGNoYXJ0c09wdGlvbnMuY2RuVXJsIHx8IGNhY2hlLmNkblVybDtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGNvbnN0IGZldGNoZWRNb2R1bGVzID0ge307XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW2NhY2hlXSBVcGRhdGluZyBjYWNoZSB2ZXJzaW9uIHRvIEhpZ2hjaGFydHM6ICR7aGNWZXJzaW9uIHx8ICdsYXRlc3QnfS5gXHJcbiAgICApO1xyXG5cclxuICAgIGNhY2hlLnNvdXJjZXMgPSBhd2FpdCBfZmV0Y2hTY3JpcHRzKFxyXG4gICAgICBbXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuY29yZVNjcmlwdHMubWFwKChjKSA9PlxyXG4gICAgICAgICAgaGNWZXJzaW9uID8gYCR7Y2RuVXJsfS8ke2hjVmVyc2lvbn0vJHtjfWAgOiBgJHtjZG5Vcmx9LyR7Y31gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBbXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMubW9kdWxlU2NyaXB0cy5tYXAoKG0pID0+XHJcbiAgICAgICAgICBtID09PSAnbWFwJ1xyXG4gICAgICAgICAgICA/IGhjVmVyc2lvblxyXG4gICAgICAgICAgICAgID8gYCR7Y2RuVXJsfS9tYXBzLyR7aGNWZXJzaW9ufS9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgICAgOiBgJHtjZG5Vcmx9L21hcHMvbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICA6IGhjVmVyc2lvblxyXG4gICAgICAgICAgICAgID8gYCR7Y2RuVXJsfS8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgKSxcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5pbmRpY2F0b3JTY3JpcHRzLm1hcCgoaSkgPT5cclxuICAgICAgICAgIGhjVmVyc2lvblxyXG4gICAgICAgICAgICA/IGAke2NkblVybH0vc3RvY2svJHtoY1ZlcnNpb259L2luZGljYXRvcnMvJHtpfWBcclxuICAgICAgICAgICAgOiBgJHtjZG5Vcmx9L3N0b2NrL2luZGljYXRvcnMvJHtpfWBcclxuICAgICAgICApXHJcbiAgICAgIF0sXHJcbiAgICAgIGhpZ2hjaGFydHNPcHRpb25zLmN1c3RvbVNjcmlwdHMsXHJcbiAgICAgIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICAgICAgZmV0Y2hlZE1vZHVsZXNcclxuICAgICk7XHJcblxyXG4gICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgIGNhY2hlLmhjVmVyc2lvbiA9IGV4dHJhY3RWZXJzaW9uKGNhY2hlLnNvdXJjZXMpO1xyXG5cclxuICAgIC8vIFNhdmUgdGhlIGZldGNoZWQgbW9kdWxlcyBpbnRvIGNhY2hlcycgc291cmNlIEpTT05cclxuICAgIHdyaXRlRmlsZVN5bmMoc291cmNlUGF0aCwgY2FjaGUuc291cmNlcyk7XHJcbiAgICByZXR1cm4gZmV0Y2hlZE1vZHVsZXM7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gVW5hYmxlIHRvIHVwZGF0ZSB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGNoZWNrQW5kVXBkYXRlQ2FjaGUsXHJcbiAgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24sXHJcbiAgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24sXHJcbiAgZXh0cmFjdFZlcnNpb24sXHJcbiAgZXh0cmFjdE1vZHVsZU5hbWUsXHJcbiAgZ2V0Q2FjaGUsXHJcbiAgZ2V0Q2FjaGVQYXRoXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtZXRob2RzIGZvciBpbml0aWFsaXppbmcgSGlnaGNoYXJ0cyB3aXRoIGN1c3RvbWl6ZWRcclxuICogYW5pbWF0aW9uIHNldHRpbmdzIGFuZCB0cmlnZ2VyaW5nIHRoZSBjcmVhdGlvbiBvZiBIaWdoY2hhcnRzIGNoYXJ0cyB3aXRoXHJcbiAqIGV4cG9ydC1zcGVjaWZpYyBjb25maWd1cmF0aW9ucyBpbiB0aGUgcGFnZSBjb250ZXh0LiBTdXBwb3J0cyBkeW5hbWljIG9wdGlvblxyXG4gKiBtZXJnaW5nLCBjdXN0b20gbG9naWMgaW5qZWN0aW9uLCBhbmQgY29udHJvbCBvdmVyIHJlbmRlcmluZyBiZWhhdmlvcnMuIFVzZWRcclxuICogYnkgdGhlIFB1cHBldGVlciBwYWdlLlxyXG4gKi9cclxuXHJcbi8qIGVzbGludC1kaXNhYmxlIG5vLXVuZGVmICovXHJcblxyXG4vKipcclxuICogU2V0dGluZyB0aGUgYEhpZ2hjaGFydHMuYW5pbU9iamVjdGAgZnVuY3Rpb24uIENhbGxlZCB3aGVuIGluaXRpbmcgdGhlIHBhZ2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXR1cEhpZ2hjaGFydHNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXR1cEhpZ2hjaGFydHMoKSB7XHJcbiAgSGlnaGNoYXJ0cy5hbmltT2JqZWN0ID0gZnVuY3Rpb24gKCkge1xyXG4gICAgcmV0dXJuIHsgZHVyYXRpb246IDAgfTtcclxuICB9O1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyB0aGUgYWN0dWFsIEhpZ2hjaGFydHMgY2hhcnQgb24gYSBwYWdlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUNoYXJ0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVDaGFydChvcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgbWVyZ2UsIHNldE9wdGlvbnMsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHVzZXJPcHRpb25zIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB1c2VyT3B0aW9ucyB3aGVuIGZvckV4cG9ydCBpcyB0cnVlLlxyXG4gICAgICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL2hpZ2hjaGFydHMvYmxvYi8zYWQ0MzBhMzUzYjgwNTZiOWU3NjRhYTRlNWNkNjgyOGFhNDc5ZGIyL2pzL3BhcnRzL0NoYXJ0LmpzI0wyNDFcclxuICAgICAgICAqL1xyXG4gICAgICB0b29sdGlwOiB7fVxyXG4gICAgfSk7XHJcblxyXG4gICAgKHVzZXJPcHRpb25zLnNlcmllcyB8fCBbXSkuZm9yRWFjaChmdW5jdGlvbiAoc2VyaWVzKSB7XHJcbiAgICAgIHNlcmllcy5hbmltYXRpb24gPSBmYWxzZTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEFkZCBmbGFnIHRvIGtub3cgaWYgY2hhcnQgcmVuZGVyIGhhcyBiZWVuIGNhbGxlZC5cclxuICAgIGlmICghd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlcikge1xyXG4gICAgICB3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyID0gSGlnaGNoYXJ0cy5hZGRFdmVudCh0aGlzLCAncmVuZGVyJywgKCkgPT4ge1xyXG4gICAgICAgIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gdHJ1ZTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbdXNlck9wdGlvbnMsIGNiXSk7XHJcbiAgfSk7XHJcblxyXG4gIHdyYXAoSGlnaGNoYXJ0cy5TZXJpZXMucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCBjaGFydCwgb3B0aW9ucykge1xyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbY2hhcnQsIG9wdGlvbnNdKTtcclxuICB9KTtcclxuXHJcbiAgLy8gU29tZSBtYW5kYXRvcnkgYWRkaXRpb25hbCBgY2hhcnRgIGFuZCBgZXhwb3J0aW5nYCBvcHRpb25zXHJcbiAgY29uc3QgYWRkaXRpb25hbE9wdGlvbnMgPSB7XHJcbiAgICBjaGFydDoge1xyXG4gICAgICAvLyBCeSBkZWZhdWx0IGFuaW1hdGlvbiBpcyBkaXNhYmxlZFxyXG4gICAgICBhbmltYXRpb246IGZhbHNlLFxyXG4gICAgICAvLyBHZXQgdGhlIHJpZ2h0IHNpemUgdmFsdWVzXHJcbiAgICAgIGhlaWdodDogb3B0aW9ucy5leHBvcnQuaGVpZ2h0LFxyXG4gICAgICB3aWR0aDogb3B0aW9ucy5leHBvcnQud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtvcHRpb25zLmV4cG9ydC5pbnN0cn1gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvblxyXG4gIGNvbnN0IHRoZW1lT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7b3B0aW9ucy5leHBvcnQudGhlbWVPcHRpb25zfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGdsb2JhbE9wdGlvbnNgIG9wdGlvblxyXG4gIGNvbnN0IGdsb2JhbE9wdGlvbnMgPSBuZXcgRnVuY3Rpb24oXHJcbiAgICBgcmV0dXJuICR7b3B0aW9ucy5leHBvcnQuZ2xvYmFsT3B0aW9uc31gXHJcbiAgKSgpO1xyXG5cclxuICAvLyBNZXJnZSB0aGUgZm9sbG93aW5nIG9wdGlvbnMgb2JqZWN0cyB0byBjcmVhdGUgZmluYWwgb3B0aW9uc1xyXG4gIGNvbnN0IGZpbmFsT3B0aW9ucyA9IG1lcmdlKFxyXG4gICAgZmFsc2UsXHJcbiAgICB0aGVtZU9wdGlvbnMsXHJcbiAgICB1c2VyT3B0aW9ucyxcclxuICAgIC8vIFBsYWNlZCBpdCBoZXJlIGluc3RlYWQgaW4gdGhlIGluaXQgYmVjYXVzZSBvZiB0aGUgc2l6ZSBpc3N1ZXNcclxuICAgIGFkZGl0aW9uYWxPcHRpb25zXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNhbGxiYWNrYCBvcHRpb25cclxuICBjb25zdCBmaW5hbENhbGxiYWNrID0gb3B0aW9ucy5jdXN0b21Mb2dpYy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke29wdGlvbnMuY3VzdG9tTG9naWMuY2FsbGJhY2t9YCkoKVxyXG4gICAgOiBudWxsO1xyXG5cclxuICAvLyBUcmlnZ2VyIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgaWYgKG9wdGlvbnMuY3VzdG9tTG9naWMuY3VzdG9tQ29kZSkge1xyXG4gICAgbmV3IEZ1bmN0aW9uKCdvcHRpb25zJywgb3B0aW9ucy5jdXN0b21Mb2dpYy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbb3B0aW9ucy5leHBvcnQuY29uc3RyXSgnY29udGFpbmVyJywgZmluYWxPcHRpb25zLCBmaW5hbENhbGxiYWNrKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBjdXJyZW50IGdsb2JhbCBvcHRpb25zXHJcbiAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIENsZWFyIGl0IGp1c3QgaW4gY2FzZSAoZS5nLiB0aGUgYHNldE9wdGlvbnNgIHdhcyB1c2VkIGluIHRoZSBgY3VzdG9tQ29kZWApXHJcbiAgZm9yIChjb25zdCBwcm9wIGluIGRlZmF1bHRPcHRpb25zKSB7XHJcbiAgICBpZiAodHlwZW9mIGRlZmF1bHRPcHRpb25zW3Byb3BdICE9PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgIGRlbGV0ZSBkZWZhdWx0T3B0aW9uc1twcm9wXTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgZGVmYXVsdCBvcHRpb25zIGJhY2tcclxuICBzZXRPcHRpb25zKEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaik7XHJcblxyXG4gIC8vIEVtcHR5IHRoZSBjdXN0b20gZ2xvYmFsIG9wdGlvbnMgb2JqZWN0XHJcbiAgSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqID0ge307XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzZXR1cEhpZ2hjaGFydHMsXHJcbiAgY3JlYXRlQ2hhcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgbWFuYWdpbmcgUHVwcGV0ZWVyIGJyb3dzZXJcclxuICogaW5zdGFuY2UsIGNyZWF0aW5nIGFuZCBjbGVhcmluZyBwYWdlcywgaW5qZWN0aW5nIGN1c3RvbSByZXNvdXJjZXMsXHJcbiAqIGFuZCBzZXR0aW5nIHVwIEhpZ2hjaGFydHMgZm9yIHNlcnZlci1zaWRlIHJlbmRlcmluZy4gVGhlIG1vZHVsZSBlbnN1cmVzXHJcbiAqIHRoYXQgcmVzb3VyY2VzIGFyZSBjb3JyZWN0bHkgbWFuYWdlZCBhbmQgY2FuIGhhbmRsZSBmYWlsdXJlcyBkdXJpbmdcclxuICogb3BlcmF0aW9ucyBsaWtlIGxhdW5jaGluZyB0aGUgYnJvd3NlciBvciBjcmVhdGluZyBuZXcgcGFnZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgcHVwcGV0ZWVyIGZyb20gJ3B1cHBldGVlcic7XHJcblxyXG5pbXBvcnQgeyBnZXRDYWNoZVBhdGggfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgc2V0dXBIaWdoY2hhcnRzIH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgcGFnZXNcclxuY29uc3QgdGVtcGxhdGUgPSByZWFkRmlsZVN5bmMoXHJcbiAgam9pbihfX2Rpcm5hbWUsICd0ZW1wbGF0ZXMnLCAndGVtcGxhdGUuaHRtbCcpLFxyXG4gICd1dGY4J1xyXG4pO1xyXG5cclxuLy8gVG8gc2F2ZSB0aGUgYnJvd3NlclxyXG5sZXQgYnJvd3NlciA9IG51bGw7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBleGlzdGluZyBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJyb3dzZXJcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEJyb3dzZXIoKSB7XHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBObyB2YWxpZCBicm93c2VyIGhhcyBiZWVuIGNyZWF0ZWQuJywgNTAwKTtcclxuICB9XHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVCcm93c2VyXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyXHJcbiAqIGxhdW5jaC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyXHJcbiAqIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBtYXggcmV0cmllcyB0byBvcGVuXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhcmUgcmVhY2hlZCwgb3IgaWYgbm8gYnJvd3NlciBpbnN0YW5jZSBpcyBmb3VuZCBhZnRlclxyXG4gKiByZXRyaWVzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncykge1xyXG4gIC8vIEdldCBgZGVidWdgIGFuZCBgb3RoZXJgIG9wdGlvbnNcclxuICBjb25zdCB7IGRlYnVnLCBvdGhlciB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZW5hYmxlOiBlbmFibGVkRGVidWcsIC4uLmRlYnVnT3B0aW9ucyB9ID0gZGVidWc7XHJcblxyXG4gIC8vIExhdW5jaCBvcHRpb25zIGZvciB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGNvbnN0IGxhdW5jaE9wdGlvbnMgPSB7XHJcbiAgICBoZWFkbGVzczogb3RoZXIuYnJvd3NlclNoZWxsTW9kZSA/ICdzaGVsbCcgOiB0cnVlLFxyXG4gICAgdXNlckRhdGFEaXI6ICd0bXAnLFxyXG4gICAgYXJnczogcHVwcGV0ZWVyQXJncyB8fCBbXSxcclxuICAgIGhhbmRsZVNJR0lOVDogZmFsc2UsXHJcbiAgICBoYW5kbGVTSUdURVJNOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR0hVUDogZmFsc2UsXHJcbiAgICB3YWl0Rm9ySW5pdGlhbFBhZ2U6IGZhbHNlLFxyXG4gICAgZGVmYXVsdFZpZXdwb3J0OiBudWxsLFxyXG4gICAgLi4uKGVuYWJsZWREZWJ1ZyAmJiBkZWJ1Z09wdGlvbnMpXHJcbiAgfTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlclxyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgLy8gQSBjb3VudGVyIGZvciB0aGUgYnJvd3NlcidzIGxhdW5jaCByZXRyaWVzXHJcbiAgICBsZXQgdHJ5Q291bnQgPSAwO1xyXG5cclxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbYnJvd3Nlcl0gQXR0ZW1wdGluZyB0byBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIExhdW5jaCB0aGUgYnJvd3NlclxyXG4gICAgICAgIGJyb3dzZXIgPSBhd2FpdCBwdXBwZXRlZXIubGF1bmNoKGxhdW5jaE9wdGlvbnMpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuXHJcbiAgICAgICAgICAvLyBXYWl0IGZvciBhIDQgc2Vjb25kcyBiZWZvcmUgdHJ5aW5nIGFnYWluXHJcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIDQwMDApKTtcclxuICAgICAgICAgIGF3YWl0IG9wZW4oKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIGF3YWl0IG9wZW4oKTtcclxuXHJcbiAgICAgIC8vIFNoZWxsIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChsYXVuY2hPcHRpb25zLmhlYWRsZXNzID09PSAnc2hlbGwnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBzaGVsbCBtb2RlLmApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEZWJ1ZyBtb2RlIGluZm9ybVxyXG4gICAgICBpZiAoZW5hYmxlZERlYnVnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1ticm93c2VyXSBNYXhpbXVtIHJldHJpZXMgdG8gb3BlbiBhIGJyb3dzZXIgaW5zdGFuY2UgcmVhY2hlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJywgNTAwKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGJyb3dzZXIgaW5zdGFuY2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyB0aGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgaWYgaXQgaXMgY29ubmVjdGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsb3NlQnJvd3NlclxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsb3NlQnJvd3NlcigpIHtcclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciB3aGVuIGNvbm5lY3RlZFxyXG4gIGlmIChicm93c2VyICYmIGJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgfVxyXG4gIGJyb3dzZXIgPSBudWxsO1xyXG4gIGxvZyg0LCAnW2Jyb3dzZXJdIENsb3NlZCB0aGUgYnJvd3Nlci4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBuZXcgUHVwcGV0ZWVyIHBhZ2Ugd2l0aGluIGFuIGV4aXN0aW5nIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqIFRoZSBmdW5jdGlvbiBjcmVhdGVzIGEgbmV3IHBhZ2UsIGRpc2FibGVzIGNhY2hpbmcsIHNldHMgY29udGVudCB1c2luZ1xyXG4gKiB0aGUgYF9zZXRQYWdlQ29udGVudCgpYCwgYW5kIHJldHVybnMgdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gbmV3UGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBgaWRgLFxyXG4gKiBgd29ya0NvdW50YCwgYW5kIGBwYWdlYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG5vIHZhbGlkIGJyb3dzZXJcclxuICogaGFzIGJlZW4gY29ubmVjdGVkIG9yIGlmIGEgcGFnZSBpcyBpbnZhbGlkIG9yIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBuZXdQYWdlKHBvb2xSZXNvdXJjZSkge1xyXG4gIC8vIEVycm9yIGluIGNhc2Ugb2Ygbm8gY29ubmVjdGVkIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIgfHwgIWJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFticm93c2VyXSBCcm93c2VyIGlzIG5vdCB5ZXQgY29ubmVjdGVkLmAsIDUwMCk7XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBwYWdlXHJcbiAgcG9vbFJlc291cmNlLnBhZ2UgPSBhd2FpdCBicm93c2VyLm5ld1BhZ2UoKTtcclxuXHJcbiAgLy8gRGlzYWJsZSBjYWNoZVxyXG4gIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IF9zZXRQYWdlQ29udGVudChwb29sUmVzb3VyY2UucGFnZSk7XHJcblxyXG4gIC8vIFNldCBwYWdlIGV2ZW50c1xyXG4gIF9zZXRQYWdlRXZlbnRzKHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIHBhZ2UgaXMgY29ycmVjdGx5IGNyZWF0ZWRcclxuICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlIHx8IHBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIFRoZSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLicsIDQwMCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLiBMb2dzXHJcbiAqIHRocm93biBlcnJvciBpZiBjbGVhcmluZyBvZiBhIHBhZ2UncyBjb250ZW50IGZhaWxzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBwYWdlIGFuZCBpZC5cclxuICogQHBhcmFtIHtib29sZWFufSBbaGFyZFJlc2V0PWZhbHNlXSAtIEEgZmxhZyBpbmRpY2F0aW5nIHRoZSB0eXBlIG9mIGNsZWFyaW5nXHJcbiAqIHRvIGJlIHBlcmZvcm1lZC4gSWYgdHJ1ZSwgbmF2aWdhdGVzIHRvIGBhYm91dDpibGFua2AgYW5kIHJlc2V0cyBjb250ZW50XHJcbiAqIGFuZCBzY3JpcHRzLiBJZiBmYWxzZSwgY2xlYXJzIHRoZSBib2R5IGNvbnRlbnQgYnkgc2V0dGluZyBhIHByZWRlZmluZWQgSFRNTFxyXG4gKiBzdHJ1Y3R1cmUuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGZhbHNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSB3aGVuIHBhZ2VcclxuICogaXMgY29ycmVjdGx5IGNsZWFyZWQgYW5kIGZhbHNlIHdoZW4gaXQgaXMgbm90LlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZShwb29sUmVzb3VyY2UsIGhhcmRSZXNldCA9IGZhbHNlKSB7XHJcbiAgdHJ5IHtcclxuICAgIGlmIChwb29sUmVzb3VyY2UucGFnZSAmJiAhcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICBpZiAoaGFyZFJlc2V0KSB7XHJcbiAgICAgICAgLy8gTmF2aWdhdGUgdG8gYGFib3V0OmJsYW5rYFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmdvdG8oJ2Fib3V0OmJsYW5rJywge1xyXG4gICAgICAgICAgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCdcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gU2V0IHRoZSBjb250ZW50IGFuZCBhbmQgc2NyaXB0cyBhZ2FpblxyXG4gICAgICAgIGF3YWl0IF9zZXRQYWdlQ29udGVudChwb29sUmVzb3VyY2UucGFnZSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2xlYXIgYm9keSBjb250ZW50XHJcbiAgICAgICAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5pbm5lckhUTUwgPVxyXG4gICAgICAgICAgICAnPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPjxkaXYgaWQ9XCJjb250YWluZXJcIj48L2Rpdj48L2Rpdj4nO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDIsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBDb250ZW50IG9mIHRoZSBwYWdlIGNvdWxkIG5vdCBiZSBjbGVhcmVkLmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgIHBvb2xSZXNvdXJjZS53b3JrQ291bnQgPSBnZXRPcHRpb25zKCkucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gIH1cclxuICByZXR1cm4gZmFsc2U7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIGN1c3RvbSBKUyBhbmQgQ1NTIHJlc291cmNlcyB0byBhIFB1cHBldGVlciBQYWdlIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBhZGRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBvYmplY3QgY29udGFpbmluZyBgY3VzdG9tTG9naWNgXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9ICFmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRydWUgOiBmYWxzZTtcclxuXHJcbiAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICBpbmplY3RlZEpzLnB1c2goXHJcbiAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgID8ge1xyXG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChmaWxlKSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZvciAoY29uc3QganNSZXNvdXJjZSBvZiBpbmplY3RlZEpzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhqc1Jlc291cmNlKSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIFRoZSBKUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpbmplY3RlZEpzLmxlbmd0aCA9IDA7XHJcblxyXG4gICAgLy8gTG9hZCBDU1NcclxuICAgIGNvbnN0IGluamVjdGVkQ3NzID0gW107XHJcbiAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgcGF0aDogam9pbihfX2Rpcm5hbWUsIGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoY3NzUmVzb3VyY2UpKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICAgYFticm93c2VyXSBUaGUgQ1NTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaW5qZWN0ZWRDc3MubGVuZ3RoID0gMDtcclxuICAgIH1cclxuICB9XHJcbiAgcmV0dXJuIGluamVjdGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIG91dCBhbGwgc3RhdGUgc2V0IG9uIHRoZSBwYWdlIHdpdGggYGFkZFNjcmlwdFRhZ2AgYW5kIGBhZGRTdHlsZVRhZ2AuXHJcbiAqIFJlbW92ZXMgaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS5cclxuICogQWRkaXRpb25hbGx5LCBpdCBkZXN0cm95cyBwcmV2aW91c2x5IGV4aXN0aW5nIGNoYXJ0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtBcnJheS48T2JqZWN0Pn0gaW5qZWN0ZWRSZXNvdXJjZXMgLSBBcnJheSBvZiBpbmplY3RlZCByZXNvdXJjZXNcclxuICogdG8gYmUgY2xlYXJlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICB0cnkge1xyXG4gICAgZm9yIChjb25zdCByZXNvdXJjZSBvZiBpbmplY3RlZFJlc291cmNlcykge1xyXG4gICAgICBhd2FpdCByZXNvdXJjZS5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzIGFmdGVyIGV4cG9ydCBpcyBkb25lIGFuZCByZXNldCBhbGwgQ1NTIGFuZCBzY3JpcHQgdGFnc1xyXG4gICAgYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCB3aGVuIGRvaW5nIFNWRyBleHBvcnRzXHJcbiAgICAgIGlmICh0eXBlb2YgSGlnaGNoYXJ0cyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcclxuXHJcbiAgICAgICAgLy8gQ2hlY2sgaW4gYW55IGFscmVhZHkgZXhpc3RpbmcgY2hhcnRzXHJcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XHJcbiAgICAgICAgICAvLyBEZXN0cm95IG9sZCBjaGFydHNcclxuICAgICAgICAgIGZvciAoY29uc3Qgb2xkQ2hhcnQgb2Ygb2xkQ2hhcnRzKSB7XHJcbiAgICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcclxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICAgIEhpZ2hjaGFydHMuY2hhcnRzLnNoaWZ0KCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLnNjcmlwdHNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLCAuLi5zdHlsZXNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc3R5bGUnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5saW5rc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdsaW5rJyk7XHJcblxyXG4gICAgICAvLyBSZW1vdmUgdGFnc1xyXG4gICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgW1xyXG4gICAgICAgIC4uLnNjcmlwdHNUb1JlbW92ZSxcclxuICAgICAgICAuLi5zdHlsZXNUb1JlbW92ZSxcclxuICAgICAgICAuLi5saW5rc1RvUmVtb3ZlXHJcbiAgICAgIF0pIHtcclxuICAgICAgICBlbGVtZW50LnJlbW92ZSgpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciBwYWdlJ3MgcmVzb3VyY2VzLmApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvbnRlbnQgZm9yIGEgUHVwcGV0ZWVyIFBhZ2UgdXNpbmcgYSBwcmVkZWZpbmVkIHRlbXBsYXRlXHJcbiAqIGFuZCBhZGRpdGlvbmFsIHNjcmlwdHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VDb250ZW50XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgY29udGVudFxyXG4gKiBpcyBiZWluZyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnQocGFnZSkge1xyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBwYWdlIGNvbnRlbnRcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gIC8vIEFkZCBhbGwgcmVnaXN0ZXJlZCBIaWdjaGFydHMgc2NyaXB0cywgcXVpdGUgZGVtYW5kaW5nXHJcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBqb2luKGdldENhY2hlUGF0aCgpLCAnc291cmNlcy5qcycpIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGluaXRpYWwgYGFuaW1PYmplY3RgIGZvciBIaWdoY2hhcnRzXHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IGV2ZW50cyAobGlrZSBgcGFnZWVycm9yYCBhbmQgYGNvbnNvbGVgKSBmb3IgYSBQdXBwZXRlZXIgUGFnZSBpbiBvcmRlclxyXG4gKiB0byBjYXRjaCBhbmQgZGlzcGxheSBlcnJvcnMgYW5kIGNvbnNvbGUgbG9ncyBmcm9tIHRoZSB3aW5kb3cgY29udGV4dC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgbGlzdGVuZXJzXHJcbiAqIGFyZSBiZWluZyBzZXQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc2V0UGFnZUV2ZW50cyhwYWdlKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgcGFnZWVycm9yYCBsaXN0ZW5lclxyXG4gIHBhZ2Uub24oJ3BhZ2VlcnJvcicsIGFzeW5jICgpID0+IHtcclxuICAgIC8vIEl0IHdvdWxkIHNlZW0gbGlrZSB0aGlzIG1heSBmaXJlIGF0IHRoZSBzYW1lIHRpbWUgb3Igc2hvcnRseSBiZWZvcmVcclxuICAgIC8vIGEgcGFnZSBpcyBjbG9zZWQuXHJcbiAgICBpZiAocGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgY29uc29sZWAgbGlzdGVuZXIsIGlmIG5lZWRlZFxyXG4gIGlmIChkZWJ1Zy5lbmFibGUgJiYgZGVidWcubGlzdGVuVG9Db25zb2xlKSB7XHJcbiAgICBwYWdlLm9uKCdjb25zb2xlJywgKG1lc3NhZ2UpID0+IHtcclxuICAgICAgY29uc29sZS5sb2coYFtkZWJ1Z10gJHttZXNzYWdlLnRleHQoKX1gKTtcclxuICAgIH0pO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldEJyb3dzZXIsXHJcbiAgY3JlYXRlQnJvd3NlcixcclxuICBjbG9zZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSxcclxuICBjbGVhclBhZ2UsXHJcbiAgYWRkUGFnZVJlc291cmNlcyxcclxuICBjbGVhclBhZ2VSZXNvdXJjZXNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogVGhlIENTUyB0byBiZSB1c2VkIG9uIHRoZSBleHBvcnRlZCBwYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQ1NTIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoKSA9PiBgXHJcblxyXG5odG1sLCBib2R5IHtcclxuICBtYXJnaW46IDA7XHJcbiAgcGFkZGluZzogMDtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG59XHJcblxyXG4jdGFibGUtZGl2LCAjc2xpZGVycywgI2RhdGF0YWJsZSwgI2NvbnRyb2xzLCAubGQtcm93IHtcclxuICBkaXNwbGF5OiBub25lO1xyXG4gIGhlaWdodDogMDtcclxufVxyXG5cclxuI2NoYXJ0LWNvbnRhaW5lciB7XHJcbiAgYm94LXNpemluZzogYm9yZGVyLWJveDtcclxuICBtYXJnaW46IDA7XHJcbiAgb3ZlcmZsb3c6IGF1dG87XHJcbiAgZm9udC1zaXplOiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyID4gZmlndXJlLCBkaXYge1xyXG4gIG1hcmdpbi10b3A6IDAgIWltcG9ydGFudDtcclxuICBtYXJnaW4tYm90dG9tOiAwICFpbXBvcnRhbnQ7XHJcbn1cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNzc1RlbXBsYXRlIGZyb20gJy4vY3NzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBUaGUgU1ZHIHRlbXBsYXRlIHRvIHVzZSB3aGVuIGxvYWRpbmcgU1ZHIGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIGlucHV0IGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBTVkcgdGVtcGxhdGUuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoc3ZnKSA9PiBgXHJcbjwhRE9DVFlQRSBodG1sPlxyXG48aHRtbCBsYW5nPSdlbi1VUyc+XHJcbiAgPGhlYWQ+XHJcbiAgICA8bWV0YSBodHRwLWVxdWl2PVwiQ29udGVudC1UeXBlXCIgY29udGVudD1cInRleHQvaHRtbDsgY2hhcnNldD11dGYtOFwiPlxyXG4gICAgPHRpdGxlPkhpZ2hjaGFydHMgRXhwb3J0PC90aXRsZT5cclxuICA8L2hlYWQ+XHJcbiAgPHN0eWxlPlxyXG4gICAgJHtjc3NUZW1wbGF0ZSgpfVxyXG4gIDwvc3R5bGU+XHJcbiAgPGJvZHk+XHJcbiAgICA8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+XHJcbiAgICAgICR7c3ZnfVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgaGFuZGxlcyBjaGFydCBleHBvcnQgZnVuY3Rpb25hbGl0eSB1c2luZyBQdXBwZXRlZXIuXHJcbiAqIEl0IHN1cHBvcnRzIGV4cG9ydGluZyBjaGFydHMgYXMgU1ZHLCBQTkcsIEpQRUcsIGFuZCBQREYgZm9ybWF0cy4gVGhlIG1vZHVsZVxyXG4gKiBtYW5hZ2VzIHBhZ2UgcmVzb3VyY2VzLCBzZXRzIHVwIHRoZSBleHBvcnQgZW52aXJvbm1lbnQsIGFuZCBwcm9jZXNzZXMgY2hhcnRcclxuICogY29uZmlndXJhdGlvbnMgb3IgU1ZHIGlucHV0cyBmb3IgcmVuZGVyaW5nLiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2VcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFkZFBhZ2VSZXNvdXJjZXMsIGNsZWFyUGFnZVJlc291cmNlcyB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IGNyZWF0ZUNoYXJ0IH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IHN2Z1RlbXBsYXRlIGZyb20gJy4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8KHN0cmluZ3xCdWZmZXJ8RXhwb3J0RXJyb3IpPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXNcclxuICogdG8gdGhlIGV4cG9ydGVkIGRhdGEgb3IgcmVqZWN0aW5nIHdpdGggYW4gYEV4cG9ydEVycm9yYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGV4cG9ydCB0byBhbiB1bnN1cHBvcnRlZFxyXG4gKiBvdXRwdXQgZm9ybWF0IG9jY3Vycy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnQocGFnZSwgb3B0aW9ucykge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheSAoYWRkaXRpb25hbCBKUyBhbmQgQ1NTKVxyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIGBleHBvcnRgIG9wdGlvbnNcclxuICAgIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICBsZXQgaXNTVkcgPSBmYWxzZTtcclxuICAgIGlmIChleHBvcnRPcHRpb25zLnN2Zykge1xyXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBgdHlwZWAgaXMgYWxzbyBTVkcsIHJldHVybiB0aGUgaW5wdXRcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgICAgICByZXR1cm4gZXhwb3J0T3B0aW9ucy5zdmc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIE1hcmsgYXMgU1ZHIGV4cG9ydCBmb3IgdGhlIGxhdGVyIHNpemUgY29ycmVjdGlvbnNcclxuICAgICAgaXNTVkcgPSB0cnVlO1xyXG5cclxuICAgICAgLy8gU1ZHIGV4cG9ydFxyXG4gICAgICBhd2FpdCBfc2V0QXNTdmcocGFnZSwgZXhwb3J0T3B0aW9ucy5zdmcpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBKU09OIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0XHJcbiAgICAgIGF3YWl0IF9zZXRBc09wdGlvbnMocGFnZSwgb3B0aW9ucyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXHJcbiAgICAvLyBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAuLi4oYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBvcHRpb25zLmN1c3RvbUxvZ2ljKSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZWFsIGNoYXJ0IHNpemUgYW5kIHNldCB0aGUgem9vbSBhY2NvcmRpbmdseVxyXG4gICAgY29uc3Qgc2l6ZSA9IGlzU1ZHXHJcbiAgICAgID8gYXdhaXQgcGFnZS5ldmFsdWF0ZSgoc2NhbGUpID0+IHtcclxuICAgICAgICAgIGNvbnN0IHN2Z0VsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFxyXG4gICAgICAgICAgICAnI2NoYXJ0LWNvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZSdcclxuICAgICAgICAgICk7XHJcblxyXG4gICAgICAgICAgLy8gR2V0IHRoZSB2YWx1ZXMgY29ycmVjdGx5IHNjYWxlZFxyXG4gICAgICAgICAgY29uc3QgY2hhcnRIZWlnaHQgPSBzdmdFbGVtZW50LmhlaWdodC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcbiAgICAgICAgICBjb25zdCBjaGFydFdpZHRoID0gc3ZnRWxlbWVudC53aWR0aC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gSW4gY2FzZSBvZiBTVkcgdGhlIHpvb20gbXVzdCBiZSBzZXQgZGlyZWN0bHkgZm9yIGJvZHkgYXMgc2NhbGVcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUubWFyZ2luID0gJzBweCc7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSwgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKSlcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG5cclxuICAgICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXNcclxuICAgICAgICAgIC8vIG9mIGV4cG9ydHMuIFJlc2V0IHRoZSB6b29tIGZvciBvdGhlciBleHBvcnRzIHRoYW4gdG8gU1ZHc1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY2xpcCByZWdpb24gZm9yIHRoZSBwYWdlXHJcbiAgICBjb25zdCB7IHgsIHkgfSA9IGF3YWl0IF9nZXRDbGlwUmVnaW9uKHBhZ2UpO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgaGVpZ2h0YCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0SGVpZ2h0ID0gTWF0aC5hYnMoXHJcbiAgICAgIE1hdGguY2VpbChzaXplLmNoYXJ0SGVpZ2h0IHx8IGV4cG9ydE9wdGlvbnMuaGVpZ2h0KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgZmluYWwgYHdpZHRoYCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0V2lkdGggPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRXaWR0aCB8fCBleHBvcnRPcHRpb25zLndpZHRoKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGZpbmFsIHZpZXdwb3J0IG5vdyB0aGF0IHdlIGhhdmUgdGhlIHJlYWwgaGVpZ2h0XHJcbiAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcclxuICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgIGRldmljZVNjYWxlRmFjdG9yOiBpc1NWRyA/IDEgOiBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpXHJcbiAgICB9KTtcclxuXHJcbiAgICBsZXQgcmVzdWx0O1xyXG4gICAgLy8gUmFzdGVyaXphdGlvbiBwcm9jZXNzXHJcbiAgICBzd2l0Y2ggKGV4cG9ydE9wdGlvbnMudHlwZSkge1xyXG4gICAgICBjYXNlICdzdmcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVTVkcocGFnZSk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BuZyc6XHJcbiAgICAgIGNhc2UgJ2pwZWcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVJbWFnZShcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnR5cGUsXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgICB4LFxyXG4gICAgICAgICAgICB5XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BkZic6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVBERihcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgZGVmYXVsdDpcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdDogJHtleHBvcnRPcHRpb25zLnR5cGV9LmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENsZWFyIHByZXZpb3VzbHkgaW5qZWN0ZWQgSlMgYW5kIENTUyByZXNvdXJjZXNcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIHNwZWNpZmllZCBwYWdlJ3MgY29udGVudCB3aXRoIHByb3ZpZGVkIGV4cG9ydCBpbnB1dCB3aXRoaW5cclxuICogdGhlIHdpbmRvdyBjb250ZXh0IHVzaW5nIHRoZSBgcGFnZS5zZXRDb250ZW50YCBmdW5jdGlvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0QXNTdmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIGlucHV0IGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgY29udGVudCBpcyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0QXNTdmcocGFnZSwgc3ZnKSB7XHJcbiAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKHN2ZyksIHtcclxuICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBvcHRpb25zIHdpdGggc3BlY2lmaWVkIGV4cG9ydCBpbnB1dCBhbmQgc2l6ZXMgYXMgY29uZmlndXJhdGlvbiBpbnRvXHJcbiAqIHRoZSBgY3JlYXRlQ2hhcnRgIGZ1bmN0aW9uIHdpdGhpbiB0aGUgd2luZG93IGNvbnRleHQgdXNpbmdcclxuICogdGhlIGBwYWdlLmV2YWx1YXRlYCBmdW5jdGlvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0QXNPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0IGNvbnRhaW5pbmcgY29tcGxldGUgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgY29uZmlndXJhdGlvblxyXG4gKiBpcyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0QXNPcHRpb25zKHBhZ2UsIG9wdGlvbnMpIHtcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKGNyZWF0ZUNoYXJ0LCBvcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50XHJcbiAqIHdpdGggdGhlICdjaGFydC1jb250YWluZXInIGlkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgeGAsIGB5YCwgYHdpZHRoYCwgYW5kIGBoZWlnaHRgIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZ2V0Q2xpcFJlZ2lvbihwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoJyNjaGFydC1jb250YWluZXInLCAoZWxlbWVudCkgPT4ge1xyXG4gICAgY29uc3QgeyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH0gPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgeCxcclxuICAgICAgeSxcclxuICAgICAgd2lkdGgsXHJcbiAgICAgIGhlaWdodDogTWF0aC50cnVuYyhoZWlnaHQgPiAxID8gaGVpZ2h0IDogNTAwKVxyXG4gICAgfTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gU1ZHIGJ5IGV2YWx1YXRpbmcgdGhlIGBvdXRlckhUTUxgIG9mIHRoZSBmaXJzdCAnc3ZnJyBlbGVtZW50XHJcbiAqIGluc2lkZSBhbiBlbGVtZW50IHdpdGggdGhlIGlkICdjb250YWluZXInLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVTVkdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBTVkcgc3RyaW5nLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVNWRyhwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoXHJcbiAgICAnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsXHJcbiAgICAoZWxlbWVudCkgPT4gZWxlbWVudC5vdXRlckhUTUxcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBzY3JlZW5zaG90YCBmdW5jdGlvbmFsaXR5IHdpdGhcclxuICogc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZUltYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEltYWdlIHR5cGUuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjbGlwIC0gQ2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGltYWdlIGJ1ZmZlclxyXG4gKiBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgIGZvciB0aW1lb3V0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZUltYWdlKHBhZ2UsIHR5cGUsIGNsaXAsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgcmV0dXJuIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xyXG4gICAgICB0eXBlLFxyXG4gICAgICBjbGlwLFxyXG4gICAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICAgIGZ1bGxQYWdlOiBmYWxzZSxcclxuICAgICAgb3B0aW1pemVGb3JTcGVlZDogdHJ1ZSxcclxuICAgICAgY2FwdHVyZUJleW9uZFZpZXdwb3J0OiB0cnVlLFxyXG4gICAgICAuLi4odHlwZSAhPT0gJ3BuZycgPyB7IHF1YWxpdHk6IDgwIH0gOiB7fSksXHJcbiAgICAgIC8vIEFsd2F5cyByZW5kZXIgb24gYSB0cmFuc3BhcmVudCBwYWdlIGlmIHRoZSBleHBlY3RlZCB0eXBlIGZvcm1hdCBpcyBQTkdcclxuICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZycgLy8gIzQ0NywgIzQ2M1xyXG4gICAgfSksXHJcbiAgICBuZXcgUHJvbWlzZSgoX3Jlc29sdmUsIHJlamVjdCkgPT5cclxuICAgICAgc2V0VGltZW91dChcclxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEV4cG9ydEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnLCA0MDgpKSxcclxuICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgICAgIClcclxuICAgIClcclxuICBdKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQREYgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBgcGRmYCBmdW5jdGlvbmFsaXR5IHdpdGggc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVBERlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtudW1iZXJ9IGhlaWdodCAtIFBERiBoZWlnaHQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aCAtIFBERiB3aWR0aC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBQREYgYnVmZmVyLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVBERihwYWdlLCBoZWlnaHQsIHdpZHRoLCByYXN0ZXJpemF0aW9uVGltZW91dCkge1xyXG4gIGF3YWl0IHBhZ2UuZW11bGF0ZU1lZGlhVHlwZSgnc2NyZWVuJyk7XHJcbiAgcmV0dXJuIHBhZ2UucGRmKHtcclxuICAgIC8vIFRoaXMgd2lsbCByZW1vdmUgYW4gZXh0cmEgZW1wdHkgcGFnZSBpbiBQREYgZXhwb3J0c1xyXG4gICAgaGVpZ2h0OiBoZWlnaHQgKyAxLFxyXG4gICAgd2lkdGgsXHJcbiAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICB0aW1lb3V0OiByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBwdXBwZXRlZXJFeHBvcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGEgd29ya2VyIHBvb2wgaW1wbGVtZW50YXRpb24gZm9yIG1hbmFnaW5nXHJcbiAqIHRoZSBicm93c2VyIGluc3RhbmNlIGFuZCBwYWdlcywgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIGZvciB1c2Ugd2l0aFxyXG4gKiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBvcHRpbWl6ZXMgcmVzb3VyY2VzIHVzYWdlIGFuZCBwZXJmb3JtYW5jZVxyXG4gKiBieSBtYWludGFpbmluZyBhIHBvb2wgb2Ygd29ya2VycyB0aGF0IGNhbiBoYW5kbGUgY29uY3VycmVudCBleHBvcnQgdGFza3NcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgY3JlYXRlQnJvd3NlciwgY2xvc2VCcm93c2VyLCBuZXdQYWdlLCBjbGVhclBhZ2UgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBwdXBwZXRlZXJFeHBvcnQgfSBmcm9tICcuL2V4cG9ydC5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXROZXdEYXRlVGltZSwgbWVhc3VyZVRpbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgcG9vbCBpbnN0YW5jZVxyXG5sZXQgcG9vbCA9IG51bGw7XHJcblxyXG4vLyBQb29sIHN0YXRpc3RpY3NcclxuY29uc3QgcG9vbFN0YXRzID0ge1xyXG4gIGV4cG9ydHNBdHRlbXB0ZWQ6IDAsXHJcbiAgZXhwb3J0c1BlcmZvcm1lZDogMCxcclxuICBleHBvcnRzRHJvcHBlZDogMCxcclxuICBleHBvcnRzRnJvbVN2ZzogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnM6IDAsXHJcbiAgZXhwb3J0c0Zyb21TdmdBdHRlbXB0czogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0czogMCxcclxuICB0aW1lU3BlbnQ6IDAsXHJcbiAgdGltZVNwZW50QXZlcmFnZTogMFxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNldHRpbmcgdXAgd29ya2VyIHJlc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0UG9vbFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3Bvb2xPcHRpb25zPWdldE9wdGlvbnMoKS5wb29sXSAtIE9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgZ2xvYmFsIHBvb2wgb3B0aW9ucyBvZiB0aGUgZXhwb3J0IHNlcnZlclxyXG4gKiBpbnN0YW5jZS5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gW3B1cHBldGVlckFyZ3M9W11dIC0gQWRkaXRpb25hbCBhcmd1bWVudHNcclxuICogZm9yIFB1cHBldGVlciBsYXVuY2guIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gZW5kaW5nIHRoZSBmdW5jdGlvblxyXG4gKiBleGVjdXRpb24gd2hlbiBhbiBhbHJlYWR5IGluaXRpYWxpemVkIHBvb2wgb2YgcmVzb3VyY2VzIGlzIGZvdW5kLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbFxyXG4gKiBvZiB3b3JrZXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRQb29sKFxyXG4gIHBvb2xPcHRpb25zID0gZ2V0T3B0aW9ucygpLnBvb2wsXHJcbiAgcHVwcGV0ZWVyQXJncyA9IFtdXHJcbikge1xyXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgcHVwcGV0ZWVyIGFyZ3VtZW50c1xyXG4gIGF3YWl0IGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncyk7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gSW5pdGlhbGl6aW5nIHBvb2wgd2l0aCB3b3JrZXJzOiBtaW4gJHtwb29sT3B0aW9ucy5taW5Xb3JrZXJzfSwgbWF4ICR7cG9vbE9wdGlvbnMubWF4V29ya2Vyc30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBpZiAocG9vbCkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICAnW3Bvb2xdIEFscmVhZHkgaW5pdGlhbGl6ZWQsIHBsZWFzZSBraWxsIGl0IGJlZm9yZSBjcmVhdGluZyBhIG5ldyBvbmUuJ1xyXG4gICAgICApO1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcCBhbiBleWUgb24gYSBjb3JyZWN0IG1pbiBhbmQgbWF4IHdvcmtlcnMgbnVtYmVyXHJcbiAgICBpZiAocG9vbE9wdGlvbnMubWluV29ya2VycyA+IHBvb2xPcHRpb25zLm1heFdvcmtlcnMpIHtcclxuICAgICAgcG9vbE9wdGlvbnMubWluV29ya2VycyA9IHBvb2xPcHRpb25zLm1heFdvcmtlcnM7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgcG9vbCBhbG9uZyB3aXRoIGEgbWluaW1hbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBwb29sID0gbmV3IFBvb2woe1xyXG4gICAgICAvLyBHZXQgdGhlIGBjcmVhdGVgLCBgdmFsaWRhdGVgLCBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9uc1xyXG4gICAgICAuLi5fZmFjdG9yeShwb29sT3B0aW9ucyksXHJcbiAgICAgIG1pbjogcG9vbE9wdGlvbnMubWluV29ya2VycyxcclxuICAgICAgbWF4OiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzLFxyXG4gICAgICBhY3F1aXJlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuYWNxdWlyZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmNyZWF0ZVRpbWVvdXQsXHJcbiAgICAgIGRlc3Ryb3lUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5kZXN0cm95VGltZW91dCxcclxuICAgICAgaWRsZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmlkbGVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVSZXRyeUludGVydmFsLFxyXG4gICAgICByZWFwSW50ZXJ2YWxNaWxsaXM6IHBvb2xPcHRpb25zLnJlYXBlckludGVydmFsLFxyXG4gICAgICBwcm9wYWdhdGVDcmVhdGVFcnJvcjogZmFsc2VcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFNldCBldmVudHNcclxuICAgIHBvb2wub24oJ3JlbGVhc2UnLCBhc3luYyAocmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gQ2xlYXIgcGFnZVxyXG4gICAgICBjb25zdCBjbGVhclN0YXR1cyA9IGF3YWl0IGNsZWFyUGFnZShyZXNvdXJjZSwgZmFsc2UpO1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cmVzb3VyY2UuaWR9XSAtIFJlbGVhc2luZyBhIHdvcmtlci4gQ2xlYXIgcGFnZSBzdGF0dXM6ICR7Y2xlYXJTdGF0dXN9LmBcclxuICAgICAgKTtcclxuICAgIH0pO1xyXG5cclxuICAgIHBvb2wub24oJ2Rlc3Ryb3lTdWNjZXNzJywgKF9ldmVudElkLCByZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cmVzb3VyY2UuaWR9XSAtIERlc3Ryb3llZCBhIHdvcmtlciBzdWNjZXNzZnVsbHkuYFxyXG4gICAgICApO1xyXG4gICAgICByZXNvdXJjZS5wYWdlID0gbnVsbDtcclxuICAgIH0pO1xyXG5cclxuICAgIGNvbnN0IGluaXRpYWxSZXNvdXJjZXMgPSBbXTtcclxuICAgIC8vIENyZWF0ZSBhbiBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcG9vbE9wdGlvbnMubWluV29ya2VyczsgaSsrKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG4gICAgICAgIGluaXRpYWxSZXNvdXJjZXMucHVzaChyZXNvdXJjZSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW3Bvb2xdIENvdWxkIG5vdCBjcmVhdGUgYW4gaW5pdGlhbCByZXNvdXJjZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlcyBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBpbml0aWFsUmVzb3VyY2VzLmZvckVhY2goKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIHBvb2wucmVsZWFzZShyZXNvdXJjZSk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gVGhlIHBvb2wgaXMgcmVhZHkke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RoID8gYCB3aXRoICR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGh9IGluaXRpYWwgcmVzb3VyY2VzIHdhaXRpbmcuYCA6ICcuJ31gXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcG9vbF0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgY3JlYXRlIHRoZSBwb29sIG9mIHdvcmtlcnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBLaWxscyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24ga2lsbFBvb2xcclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIGFmdGVyIHRoZSB3b3JrZXJzIGFyZVxyXG4gKiBraWxsZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBraWxsUG9vbCgpIHtcclxuICBsb2coMywgJ1twb29sXSBLaWxsaW5nIHBvb2wgd2l0aCBhbGwgd29ya2VycyBhbmQgY2xvc2luZyBicm93c2VyLicpO1xyXG5cclxuICAvLyBJZiBzdGlsbCBhbGl2ZSwgZGVzdHJveSB0aGUgcG9vbCBvZiBwYWdlcyBiZWZvcmUgY2xvc2luZyBhIGJyb3dzZXJcclxuICBpZiAocG9vbCkge1xyXG4gICAgLy8gRnJlZSB1cCBub3QgcmVsZWFzZWQgd29ya2Vyc1xyXG4gICAgZm9yIChjb25zdCB3b3JrZXIgb2YgcG9vbC51c2VkKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXIucmVzb3VyY2UpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgdGhlIHBvb2wgaWYgaXQgaXMgc3RpbGwgYXZhaWxhYmxlXHJcbiAgICBpZiAoIXBvb2wuZGVzdHJveWVkKSB7XHJcbiAgICAgIGF3YWl0IHBvb2wuZGVzdHJveSgpO1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBEZXN0cm95ZWQgdGhlIHBvb2wgb2YgcmVzb3VyY2VzLicpO1xyXG4gICAgfVxyXG4gICAgcG9vbCA9IG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGF3YWl0IGNsb3NlQnJvd3NlcigpO1xyXG59XHJcblxyXG4vKipcclxuICogUHJvY2Vzc2VzIHRoZSBleHBvcnQgd29yayB1c2luZyBhIHdvcmtlciBmcm9tIHRoZSBwb29sLiBBY3F1aXJlcyBhIHdvcmtlclxyXG4gKiBoYW5kbGUgZnJvbSB0aGUgcG9vbCwgcGVyZm9ybXMgdGhlIGV4cG9ydCB1c2luZyBwdXBwZXRlZXIsIGFuZCByZWxlYXNlc1xyXG4gKiB0aGUgd29ya2VyIGhhbmRsZSBiYWNrIHRvIHRoZSBwb29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHBvc3RXb3JrXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGV4cG9ydCByZXN1bHRcclxuICogYW5kIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0V29yayhvcHRpb25zKSB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIHN0YXJ0aW5nIHRvIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gQW4gZXhwb3J0IGF0dGVtcHQgY291bnRlZFxyXG4gICAgKytwb29sU3RhdHMuZXhwb3J0c0F0dGVtcHRlZDtcclxuXHJcbiAgICAvLyBEaXNwbGF5IHRoZSBwb29sIGluZm9ybWF0aW9uIGlmIG5lZWRlZFxyXG4gICAgaWYgKGdldE9wdGlvbnMoKS5wb29sLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGluIGNhc2Ugb2YgbGFja2luZyB0aGUgcG9vbCBpbnN0YW5jZVxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhlIGFjcXVpcmUgY291bnRlclxyXG4gICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFRyeSB0byBhY3F1aXJlIHRoZSB3b3JrZXIgYWxvbmcgd2l0aCB0aGUgaWQsIHdvcmtzIGNvdW50IGFuZCBwYWdlXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgICAgLy8gQWNxdWlyZSBhIHBvb2wgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxyXG4gICAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIG9wdGlvbnMuX3JlcXVlc3RJZFxyXG4gICAgICAgICAgICA/IGBbYmVuY2htYXJrXSBSZXF1ZXN0IFske29wdGlvbnMuX3JlcXVlc3RJZH1dIC0gYFxyXG4gICAgICAgICAgICA6ICdbYmVuY2htYXJrXSAnLFxyXG4gICAgICAgICAgYEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUgdG9vayAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdICcgK1xyXG4gICAgICAgICAgKG9wdGlvbnMuX3JlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMuX3JlcXVlc3RJZH1dIC0gYCA6ICcnKSArXHJcbiAgICAgICAgICBgRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFJlc29sdmVkIHdvcmtlciBwYWdlIGlzIGludmFsaWQ6IHRoZSBwb29sIHNldHVwIGlzIHdvbmt5LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgY29uc3Qgd29ya1N0YXJ0ID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHt3b3JrZXJIYW5kbGUuaWR9XSAtIFN0YXJ0aW5nIHdvcmsgb24gdGhpcyBwb29sIGVudHJ5LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnQgb24gYSBwdXBwZXRlZXIgbGV2ZWxcclxuICAgIGNvbnN0IGV4cG9ydENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KHdvcmtlckhhbmRsZS5wYWdlLCBvcHRpb25zKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiBpdCdzIGFuIGVycm9yXHJcbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgRXJyb3IpIHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSWYgdGhlcmUncyBhIHJhc3Rlcml6YXRpb24gdGltZW91dCwgd2Ugd2FudCBuZWVkIHRvIGZsdXNoIHRoZSBwYWdlLlxyXG4gICAgICAvLyBUaGlzIGlzIGJlY2F1c2UgdGhlIHBhZ2UgbWF5IGJlIGluIGEgc3RhdGUgd2hlcmUgaXQncyB3YWl0aW5nIGZvclxyXG4gICAgICAvLyB0aGUgc2NyZWVuc2hvdCB0byBmaW5pc2ggZXZlbiB0aG91Z2ggdGhlIHRpbWVvdXQgaGFzIG9jY3VyZWQuXHJcbiAgICAgIC8vIFdoaWNoIG9mIGNvdXJzZSBjYXVzZXMgYSBsb3Qgb2YgaXNzdWVzIHdpdGggdGhlIGV2ZW50IHN5c3RlbSxcclxuICAgICAgLy8gYW5kIHBhZ2UgY29uc2lzdGVuY3kuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIE9ubHkgcGFnZS5zY3JlZW5zaG90IHdpbGwgdGhyb3cgdGhpcywgdGltZW91dHMgZm9yIFBERidzIGFyZVxyXG4gICAgICAvLyBoYW5kbGVkIGJ5IHRoZSBwYWdlLnBkZiBmdW5jdGlvbiBpdHNlbGYuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIC4uLnllcywgdGhpcyBpcyB1Z2x5LlxyXG4gICAgICBpZiAocmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnKSB7XHJcbiAgICAgICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UgPSBudWxsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoXHJcbiAgICAgICAgcmVzdWx0Lm5hbWUgPT09ICdUaW1lb3V0RXJyb3InIHx8XHJcbiAgICAgICAgcmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnXHJcbiAgICAgICkge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbcG9vbF0gJyArXHJcbiAgICAgICAgICAgIChvcHRpb25zLl9yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLl9yZXF1ZXN0SWR9XSAtIGAgOiAnJykgK1xyXG4gICAgICAgICAgICAnUmFzdGVyaXphdGlvbiB0aW1lb3V0OiB5b3VyIGNoYXJ0IG1heSBiZSB0b28gY29tcGxleCBvciBsYXJnZSwgYW5kIGZhaWxlZCB0byByZW5kZXIgd2l0aGluIHRoZSBhbGxvdHRlZCB0aW1lLidcclxuICAgICAgICApLnNldEVycm9yKHJlc3VsdCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1twb29sXSAnICtcclxuICAgICAgICAgICAgKG9wdGlvbnMuX3JlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMuX3JlcXVlc3RJZH1dIC0gYCA6ICcnKSArXHJcbiAgICAgICAgICAgIGBFcnJvciBlbmNvdW50ZXJlZCBkdXJpbmcgZXhwb3J0OiAke2V4cG9ydENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgdGhlIFB1cHBldGVlciBleHBvcnQgdGltZVxyXG4gICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNSxcclxuICAgICAgICBvcHRpb25zLl9yZXF1ZXN0SWRcclxuICAgICAgICAgID8gYFtiZW5jaG1hcmtdIFJlcXVlc3QgWyR7b3B0aW9ucy5fcmVxdWVzdElkfV0gLSBgXHJcbiAgICAgICAgICA6ICdbYmVuY2htYXJrXSAnLFxyXG4gICAgICAgIGBFeHBvcnRpbmcgYSBjaGFydCBzdWNlc3NmdWxseSB0b29rICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXHJcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXHJcbiAgICBjb25zdCB3b3JrRW5kID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuICAgIGNvbnN0IGV4cG9ydFRpbWUgPSB3b3JrRW5kIC0gd29ya1N0YXJ0O1xyXG5cclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnQgKz0gZXhwb3J0VGltZTtcclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnRBdmVyYWdlID1cclxuICAgICAgcG9vbFN0YXRzLnRpbWVTcGVudCAvICsrcG9vbFN0YXRzLmV4cG9ydHNQZXJmb3JtZWQ7XHJcblxyXG4gICAgbG9nKDQsIGBbcG9vbF0gV29yayBjb21wbGV0ZWQgaW4gJHtleHBvcnRUaW1lfW1zLmApO1xyXG5cclxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gdGhlIHJlc3VsdFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzdWx0LFxyXG4gICAgICBvcHRpb25zXHJcbiAgICB9O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzRHJvcHBlZDtcclxuXHJcbiAgICBpZiAod29ya2VySGFuZGxlKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG4gICAgfVxyXG5cclxuICAgIHRocm93IGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZSBpZiBpbml0aWFsaXplZCwgb3IgbnVsbFxyXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sKCkge1xyXG4gIHJldHVybiBwb29sO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgc3RhdGlzdGljIG9mIGEgcG9vbCBpbnN0YWNlIGFib3V0IGV4cG9ydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sU3RhdHNcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGN1cnJlbnQgcG9vbCBzdGF0aXN0aWNzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xTdGF0cygpIHtcclxuICByZXR1cm4gcG9vbFN0YXRzO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXHJcbiAqIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZSByZXF1ZXN0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBQb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTigpIHtcclxuICByZXR1cm4ge1xyXG4gICAgbWluOiBwb29sLm1pbixcclxuICAgIG1heDogcG9vbC5tYXgsXHJcbiAgICB1c2VkOiBwb29sLm51bVVzZWQoKSxcclxuICAgIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXHJcbiAgICBhbGxDcmVhdGVkOiBwb29sLm51bVVzZWQoKSArIHBvb2wubnVtRnJlZSgpLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXM6IHBvb2wubnVtUGVuZGluZ0NyZWF0ZXMoKSxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9uczogcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSxcclxuICAgIHBlbmRpbmdEZXN0cm95czogcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoLFxyXG4gICAgYWJzb2x1dGVBbGw6XHJcbiAgICAgIHBvb2wubnVtVXNlZCgpICtcclxuICAgICAgcG9vbC5udW1GcmVlKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSArXHJcbiAgICAgIHBvb2wucGVuZGluZ0Rlc3Ryb3lzLmxlbmd0aFxyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cclxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXHJcbiAqIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mbygpIHtcclxuICBjb25zdCB7XHJcbiAgICBtaW4sXHJcbiAgICBtYXgsXHJcbiAgICB1c2VkLFxyXG4gICAgYXZhaWxhYmxlLFxyXG4gICAgYWxsQ3JlYXRlZCxcclxuICAgIHBlbmRpbmdBY3F1aXJlcyxcclxuICAgIHBlbmRpbmdDcmVhdGVzLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzLFxyXG4gICAgYWJzb2x1dGVBbGxcclxuICB9ID0gZ2V0UG9vbEluZm9KU09OKCk7XHJcblxyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21pbn0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWF4fS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHVzZWQgcmVzb3VyY2VzOiAke3VzZWR9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgZnJlZSByZXNvdXJjZXM6ICR7YXZhaWxhYmxlfS5gKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCBjcmVhdGVkICh1c2VkIGFuZCBmcmVlKSByZXNvdXJjZXM6ICR7YWxsQ3JlYXRlZH0uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBhY3F1aXJlZDogJHtwZW5kaW5nQWNxdWlyZXN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgY3JlYXRlZDogJHtwZW5kaW5nQ3JlYXRlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSB2YWxpZGF0ZWQ6ICR7cGVuZGluZ1ZhbGlkYXRpb25zfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGRlc3Ryb3llZDogJHtwZW5kaW5nRGVzdHJveXN9LmBcclxuICApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIHJlc291cmNlczogJHthYnNvbHV0ZUFsbH0uYCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGYWN0b3J5IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbiBvYmplY3Qgd2l0aCBgY3JlYXRlYCwgYHZhbGlkYXRlYCxcclxuICogYW5kIGBkZXN0cm95YCBmdW5jdGlvbnMgZm9yIHRoZSBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZhY3RvcnlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gT2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmFjdG9yeShwb29sT3B0aW9ucykge1xyXG4gIHJldHVybiB7XHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiBjcmVhdGVcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3RcclxuICAgICAqIGNvbnRhaW5pbmcgdGhlIHdvcmtlciBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIGluaXRpYWxcclxuICAgICAqIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGFuIGVycm9yIGR1cmluZ1xyXG4gICAgICogdGhlIGNyZWF0aW9uIG9mIHRoZSBuZXcgcGFnZS5cclxuICAgICAqL1xyXG4gICAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgIC8vIEluaXQgdGhlIHJlc291cmNlIHdpdGggdW5pcXVlIGlkIGFuZCB3b3JrIGNvdW50XHJcbiAgICAgIGNvbnN0IHBvb2xSZXNvdXJjZSA9IHtcclxuICAgICAgICBpZDogdXVpZCgpLFxyXG4gICAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sT3B0aW9ucy53b3JrTGltaXQgLyAyKSlcclxuICAgICAgfTtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gU3RhcnQgbWVhc3VyaW5nIGEgcGFnZSBjcmVhdGlvbiB0aW1lXHJcbiAgICAgICAgY29uc3Qgc3RhcnREYXRlID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICAgICAgLy8gQ3JlYXRlIGEgbmV3IHBhZ2VcclxuICAgICAgICBhd2FpdCBuZXdQYWdlKHBvb2xSZXNvdXJjZSk7XHJcblxyXG4gICAgICAgIC8vIE1lYXN1cmUgdGhlIHRpbWUgb2YgZnVsbCBjcmVhdGlvbiBhbmQgY29uZmlndXJhdGlvbiBvZiBhIHBhZ2VcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIsIHRvb2sgJHtcclxuICAgICAgICAgICAgZ2V0TmV3RGF0ZVRpbWUoKSAtIHN0YXJ0RGF0ZVxyXG4gICAgICAgICAgfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBSZXR1cm4gcmVhZHkgcG9vbCByZXNvdXJjZVxyXG4gICAgICAgIHJldHVybiBwb29sUmVzb3VyY2U7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIEVycm9yIGVuY291bnRlcmVkIHdoZW4gY3JlYXRpbmcgYSBuZXcgcGFnZS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIFZhbGlkYXRlcyBhIHdvcmtlciBwYWdlIGluIHRoZSBleHBvcnQgcG9vbCwgY2hlY2tpbmcgaWYgaXQgaGFzIGV4Y2VlZGVkXHJcbiAgICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiB2YWxpZGF0ZVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgaWYgdGhlIHdvcmtlclxyXG4gICAgICogaXMgdmFsaWQgYW5kIHdpdGhpbiB0aGUgd29yayBsaW1pdDsgb3RoZXJ3aXNlLCB0byBmYWxzZS5cclxuICAgICAqL1xyXG4gICAgdmFsaWRhdGU6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSW4gY2VydGFpbiBjYXNlcyBhY3F1aXJpbmcgdGhyb3dzIGEgVGFyZ2V0Q2xvc2VFcnJvciwgd2hpY2ggbWF5XHJcbiAgICAgIC8vIGJlIGNhdXNlZCBieSB0d28gdGhpbmdzOlxyXG4gICAgICAvLyAtIFRoZSBwYWdlIGlzIGNsb3NlZCBhbmQgYXR0ZW1wdGVkIHRvIGJlIHJldXNlZC5cclxuICAgICAgLy8gLSBMb3N0IGNvbnRhY3Qgd2l0aCB0aGUgYnJvd3Nlci5cclxuICAgICAgLy9cclxuICAgICAgLy8gV2hhdCB3ZSdyZSBzZWVpbmcgaW4gbG9ncyBpcyB0aGF0IHN1Y2Nlc3NpdmUgZXhwb3J0cyB0eXBpY2FsbHlcclxuICAgICAgLy8gc3VjY2VlZHMsIGFuZCB0aGUgc2VydmVyIHJlY292ZXJzLCBpbmRpY2F0aW5nIHRoYXQgaXQncyBsaWtlbHlcclxuICAgICAgLy8gdGhlIGZpcnN0IGNhc2UuIFRoaXMgaXMgYW4gYXR0ZW1wdCBhdCBhbGxpZXZhdGluZyB0aGUgaXNzdWUgYnlcclxuICAgICAgLy8gc2ltcGx5IG5vdCB2YWxpZGF0aW5nIHRoZSB3b3JrZXIgaWYgdGhlIHBhZ2UgaXMgbnVsbCBvciBjbG9zZWQuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIFRoZSBhY3R1YWwgcmVzdWx0IGZyb20gd2hlbiB0aGlzIGhhcHBlbmVkLCB3YXMgdGhhdCBhIHdvcmtlciB3b3VsZFxyXG4gICAgICAvLyBiZSBjb21wbGV0ZWx5IGxvY2tlZCwgc3RvcHBpbmcgaXQgZnJvbSBiZWluZyBhY3F1aXJlZCB1bnRpbFxyXG4gICAgICAvLyBpdHMgd29yayBjb3VudCByZWFjaGVkIHRoZSBsaW1pdC5cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgdmFsaWRcclxuICAgICAgaWYgKCFwb29sUmVzb3VyY2UucGFnZSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAobm8gdmFsaWQgcGFnZSBpcyBmb3VuZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHBhZ2VgIGlzIGNsb3NlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAocGFnZSBpcyBjbG9zZWQgb3IgaW52YWxpZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYG1haW5GcmFtZWAgaXMgZGV0YWNoZWRcclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlLm1haW5GcmFtZSgpLmRldGFjaGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlJ3MgZnJhbWUgaXMgZGV0YWNoZWQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGB3b3JrTGltaXRgIGlzIGV4Y2VlZGVkXHJcbiAgICAgIGlmIChcclxuICAgICAgICBwb29sT3B0aW9ucy53b3JrTGltaXQgJiZcclxuICAgICAgICArK3Bvb2xSZXNvdXJjZS53b3JrQ291bnQgPiBwb29sT3B0aW9ucy53b3JrTGltaXRcclxuICAgICAgKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChleGNlZWRlZCB0aGUgJHtwb29sT3B0aW9ucy53b3JrTGltaXR9IHdvcmtzIHBlciByZXNvdXJjZSBsaW1pdCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBUaGUgYHBvb2xSZXNvdXJjZWAgaXMgdmFsaWRhdGVkXHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIERlc3Ryb3lzIGEgd29ya2VyIGVudHJ5IGluIHRoZSBleHBvcnQgcG9vbCwgY2xvc2luZyBpdHMgYXNzb2NpYXRlZCBwYWdlLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGRlc3Ryb3lcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICAgKiB0aGUgd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAgICovXHJcbiAgICBkZXN0cm95OiBhc3luYyAocG9vbFJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIERlc3Ryb3lpbmcgYSB3b3JrZXIuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFJlbW92ZSBhbGwgYXR0YWNoZWQgZXZlbnQgbGlzdGVuZXJzIGZyb20gdGhlIHJlc291cmNlXHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3BhZ2VlcnJvcicpO1xyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdjb25zb2xlJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2ZyYW1lZGV0YWNoZWQnKTtcclxuXHJcbiAgICAgICAgICAvLyBXZSBuZWVkIHRvIHdhaXQgYXJvdW5kIGZvciB0aGlzXHJcbiAgICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5jbG9zZSgpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFBhZ2UgY291bGQgbm90IGJlIGNsb3NlZCB1cG9uIGRlc3Ryb3lpbmcuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH07XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBpbml0UG9vbCxcclxuICBraWxsUG9vbCxcclxuICBwb3N0V29yayxcclxuICBnZXRQb29sLFxyXG4gIGdldFBvb2xTdGF0cyxcclxuICBnZXRQb29sSW5mbyxcclxuICBnZXRQb29sSW5mb0pTT05cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFVzZWQgdG8gc2FuaXRpemUgdGhlIHN0cmluZ3MgY29taW5nIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVcclxuICogdG8gcHJldmVudCBYU1MgYXR0YWNrcyAod2l0aCB0aGUgRE9NUHVyaWZ5IGxpYnJhcnkpLlxyXG4gKi9cclxuXHJcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcclxuaW1wb3J0IHsgSlNET00gfSBmcm9tICdqc2RvbSc7XHJcblxyXG4vKipcclxuICogU2FuaXRpemVzIGEgZ2l2ZW4gSFRNTCBzdHJpbmcgYnkgcmVtb3ZpbmcgPHNjcmlwdD4gdGFncy4gVGhpcyBmdW5jdGlvbiB1c2VzXHJcbiAqIGEgcmVndWxhciBleHByZXNzaW9uIHRvIGZpbmQgYW5kIHJlbW92ZSBhbGwgb2NjdXJyZW5jZXMgb2YgPHNjcmlwdD48L3NjcmlwdD5cclxuICogdGFncyBhbmQgYW55IGNvbnRlbnQgd2l0aGluIHRoZW0uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzYW5pdGl6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgSFRNTCBzdHJpbmcgdG8gYmUgc2FuaXRpemVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2FuaXRpemVkIEhUTUwgc3RyaW5nLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplKGlucHV0KSB7XHJcbiAgLy8gR2V0IHRoZSB2aXJ0dWFsIERPTVxyXG4gIGNvbnN0IHdpbmRvdyA9IG5ldyBKU0RPTSgnJykud2luZG93O1xyXG5cclxuICAvLyBDcmVhdGUgYSBwdXJpZnlpbmcgaW5zdGFuY2VcclxuICBjb25zdCBwdXJpZnkgPSBET01QdXJpZnkod2luZG93KTtcclxuXHJcbiAgLy8gUmV0dXJuIHNhbml0aXplZCBpbnB1dFxyXG4gIHJldHVybiBwdXJpZnkuc2FuaXRpemUoaW5wdXQsIHsgQUREX1RBR1M6IFsnZm9yZWlnbk9iamVjdCddIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2FuaXRpemVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyB0byBwcmVwYXJlIGZvciB0aGUgZXhwb3J0aW5nIGNoYXJ0c1xyXG4gKiBpbnRvIHZhcmlvdXMgaW1hZ2Ugb3V0cHV0IGZvcm1hdHMgc3VjaCBhcyBKUEVHLCBQTkcsIFBERiwgYW5kIFNWR3MuXHJcbiAqIEl0IHN1cHBvcnRzIHNpbmdsZSBhbmQgYmF0Y2ggZXhwb3J0IG9wZXJhdGlvbnMgYW5kIGFsbG93cyBjdXN0b21pemF0aW9uXHJcbiAqIHRocm91Z2ggb3B0aW9ucyBwYXNzZWQgZnJvbSBjb25maWd1cmF0aW9ucyBvciBBUElzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIG1lcmdlT3B0aW9ucywgaXNBbGxvd2VkQ29uZmlnIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsga2lsbFBvb2wsIHBvc3RXb3JrLCBnZXRQb29sU3RhdHMgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHdyYXBBcm91bmRcclxufSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgZ2xvYmFsIGZsYWcgZm9yIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uXHJcbmxldCBhbGxvd0NvZGVFeGVjdXRpb24gPSBmYWxzZTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBvcHRpb25zIGFuZCBzYXZlc1xyXG4gKiB0aGUgaW1hZ2UgaW4gdGhlIHByb3ZpZGVkIG91dGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIG1heSBiZSBhIHBhcnRpYWxcclxuICogb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMuIEl0IG11c3QgY29udGFpbiBhdCBsZWFzdCBvbmUgb2YgdGhlIGZvbGxvd2luZ1xyXG4gKiBwcm9wZXJ0aWVzOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgc2luZ2xlIGV4cG9ydFxyXG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIHNpbmdsZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaW5nbGVFeHBvcnQob3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBleHBvcnQgbWFrZXMgc2Vuc2VcclxuICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLmV4cG9ydCkge1xyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnRcclxuICAgIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIGFzeW5jIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAvLyBFeGl0IHByb2Nlc3Mgd2hlbiBlcnJvciBleGlzdHNcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgIC8vIFNhdmUgdGhlIHJlc3VsdFxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgIC8vIEFzIGEgQmFzZTY0IHN0cmluZyB0byBhIHR4dCBmaWxlXHJcbiAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICBnZXRCYXNlNjQoZGF0YS5yZXN1bHQsIHR5cGUpXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAvLyBBcyBhIGNvcnJlY3QgaW1hZ2UgZm9ybWF0XHJcbiAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICBvdXRmaWxlIHx8IGBjaGFydC4ke3R5cGV9YCxcclxuICAgICAgICAgICAgdHlwZSAhPT0gJ3N2ZycgPyBCdWZmZXIuZnJvbShkYXRhLnJlc3VsdCwgJ2Jhc2U2NCcpIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEVycm9yIHdoaWxlIHNhdmluZyBhIGNoYXJ0LicsXHJcbiAgICAgICAgICA1MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gS2lsbCBwb29sIGFuZCBjbG9zZSBicm93c2VyIGFmdGVyIGZpbmlzaGluZyBzaW5nbGUgZXhwb3J0XHJcbiAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICB9KTtcclxuICB9IGVsc2Uge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NoYXJ0XSBObyBleHBlY3RlZCBgZXhwb3J0YCBvcHRpb25zIHdlcmUgZm91bmQuIFBsZWFzZSBwcm92aWRlIG9uZSBvZiB0aGUgZm9sbG93aW5nIG9wdGlvbnM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuJyxcclxuICAgICAgNDAwXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIGJhdGNoIGV4cG9ydCBwcm9jZXNzIGZvciBtdWx0aXBsZSBjaGFydHMgYmFzZWQgb24gdGhlIGluZm9ybWF0aW9uXHJcbiAqIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZyBmb3JtYXQ6XHJcbiAqIFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzIGFyZSBzYXZlZFxyXG4gKiBpbiBwcm92aWRlZCBvdXRmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBtYXkgYmUgYSBwYXJ0aWFsXHJcbiAqIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIHRvIGdlbmVyYXRlXHJcbiAqIHZhbGlkIGltYWdlcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGJhdGNoIGV4cG9ydFxyXG4gKiBwcm9jZXNzZXMgYXJlIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogYW55IG9mIHRoZSBiYXRjaCBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBiYXRjaEV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0ICYmIG9wdGlvbnMuZXhwb3J0LmJhdGNoKSB7XHJcbiAgICAvLyBBbiBhcnJheSBmb3IgY29sbGVjdGluZyBiYXRjaCBleHBvcnRzXHJcbiAgICBjb25zdCBiYXRjaEZ1bmN0aW9ucyA9IFtdO1xyXG5cclxuICAgIC8vIFNwbGl0IGFuZCBwYWlyIHRoZSBgYmF0Y2hgIGFyZ3VtZW50c1xyXG4gICAgZm9yIChsZXQgcGFpciBvZiBvcHRpb25zLmV4cG9ydC5iYXRjaC5zcGxpdCgnOycpIHx8IFtdKSB7XHJcbiAgICAgIHBhaXIgPSBwYWlyLnNwbGl0KCc9Jyk7XHJcbiAgICAgIGlmIChwYWlyLmxlbmd0aCA9PT0gMikge1xyXG4gICAgICAgIGJhdGNoRnVuY3Rpb25zLnB1c2goXHJcbiAgICAgICAgICBzdGFydEV4cG9ydChcclxuICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgIC4uLm9wdGlvbnMsXHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgY3VzdG9tT3B0aW9uc2AgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0XHJcbiAqIG1heSBiZSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zLiBUaGUgYGVuZENhbGxiYWNrYCBpcyBjYWxsZWQgd2hlblxyXG4gKiB0aGUgZXhwb3J0IGlzIGNvbXBsZXRlZCwgd2l0aCB0aGUgYGVycm9yYCBvYmplY3QgYXMgdGhlIGZpcnN0IGFyZ3VtZW50XHJcbiAqIGFuZCB0aGUgYGRhdGFgIG9iamVjdCBhcyB0aGUgc2Vjb25kLCB3aGljaCBjb250YWlucyB0aGUgQmFzZTY0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBjaGFydCBpbiB0aGUgYHJlc3VsdGAgcHJvcGVydHkgYW5kIHRoZSBmdWxsIHNldCBvZiBleHBvcnQgb3B0aW9uc1xyXG4gKiBpbiB0aGUgYG9wdGlvbnNgIHByb3BlcnR5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHN0YXJ0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21PcHRpb25zIC0gVGhlIGBjdXN0b21PcHRpb25zYCBvYmplY3QsIHdoaWNoIG1heVxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMuIElmIHRoZSBwcm92aWRlZCBvcHRpb25zIGFyZSBwYXJ0aWFsLFxyXG4gKiBtaXNzaW5nIHZhbHVlcyB3aWxsIGJlIG1lcmdlZCB3aXRoIHRoZSBkZWZhdWx0IGdlbmVyYWwgb3B0aW9ucywgcmV0cmlldmVkXHJcbiAqIHVzaW5nIHRoZSBgZ2V0T3B0aW9uc2AgZnVuY3Rpb24uXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHdvcmsgb3IgdXBvbiBlcnJvciBvY2N1cmFuY2Ugb2YgdGhlIGV4cG9ydGluZyBwcm9jZXNzLiBUaGUgZmlyc3RcclxuICogYXJndW1lbnQgaXMgYGVycm9yYCBvYmplY3QgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIHRoYXQgY29udGFpbnNcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5IGFuZCB0aGUgZnVsbFxyXG4gKiBzZXQgb2YgZXhwb3J0IG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGN1c3RvbU9wdGlvbnMsIGVuZENhbGxiYWNrKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gTWVyZ2UgdGhlIGN1c3RvbSBvcHRpb25zIGludG8gZGVmYXVsdCBvbmVzXHJcbiAgICBjb25zdCBvcHRpb25zID0gbWVyZ2VPcHRpb25zKGdldE9wdGlvbnMoZmFsc2UpLCBjdXN0b21PcHRpb25zKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGV4cG9ydCBvcHRpb25zXHJcbiAgICBjb25zdCBleHBvcnRPcHRpb25zID0gb3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSB2YWx1ZSB0byBiZSBhc3NpZ25lZCB0byB0aGUgZ2xvYmFsXHJcbiAqIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb24odmFsdWUpIHtcclxuICBhbGxvd0NvZGVFeGVjdXRpb24gPSB2YWx1ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEV4cG9ydHMgZnJvbSBhbiBTVkcgYmFzZWQgaW5wdXQgd2l0aCB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZXhwb3J0RnJvbVN2Z1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBTVkcgYmFzZWQgaW5wdXQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0IGNvbnRhaW5pbmcgY29tcGxldGUgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhIHJlc3VsdCBvZiB0aGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBub3QgYSBjb3JyZWN0XHJcbiAqIGNoYXJ0IG9wdGlvbnMgaW5wdXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZXhwb3J0RnJvbU9wdGlvbnMoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIGxvZyg0LCAnW2NoYXJ0XSBQYXJzaW5nIGlucHV0IGZyb20gb3B0aW9ucy4nKTtcclxuXHJcbiAgLy8gVHJ5IHRvIGNoZWNrLCB2YWxpZGF0ZSBhbmQgcGFyc2UgdG8gc3RyaW5naWZpZWQgb3B0aW9uc1xyXG4gIGNvbnN0IHN0cmluZ2lmaWVkT3B0aW9ucyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgIGlucHV0VG9FeHBvcnQsXHJcbiAgICB0cnVlLFxyXG4gICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICApO1xyXG5cclxuICAvLyBDaGVjayBpZiBhIGNvcnJlY3Qgc3RyaW5naWZpZWQgb3B0aW9uc1xyXG4gIGlmIChcclxuICAgIHN0cmluZ2lmaWVkT3B0aW9ucyA9PT0gbnVsbCB8fFxyXG4gICAgdHlwZW9mIHN0cmluZ2lmaWVkT3B0aW9ucyAhPT0gJ3N0cmluZycgfHxcclxuICAgICFzdHJpbmdpZmllZE9wdGlvbnMuc3RhcnRzV2l0aCgneycpIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLmVuZHNXaXRoKCd9JylcclxuICApIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gSW52YWxpZCBjb25maWd1cmF0aW9uIHByb3ZpZGVkIC0gT25seSBvcHRpb25zIGNvbmZpZ3VyYXRpb25zIGFuZCBTVkcgYXJlIGFsbG93ZWQgZm9yIHRoaXMgc2VydmVyLiBJZiB0aGlzIGlzIHlvdXIgc2VydmVyLCBKYXZhU2NyaXB0IGN1c3RvbSBjb2RlIGNhbiBiZSBlbmFibGVkIGJ5IHN0YXJ0aW5nIHRoZSBzZXJ2ZXIgd2l0aCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9ucyBzZXQgdG8gdHJ1ZS4nLFxyXG4gICAgICA0MDNcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGV4cG9ydCBpbnB1dCBhcyBhIHN0cmluZ2lmaWVkIGNoYXJ0IG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IHN0cmluZ2lmaWVkT3B0aW9ucztcclxuXHJcbiAgLy8gUmVzZXQgdGhlIHJlc3Qgb2YgdGhlIGV4cG9ydCBpbnB1dCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuc3ZnID0gbnVsbDtcclxuXHJcbiAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhIHN0cmluZ2lmaWVkIGNoYXJ0IG9wdGlvbnNcclxuICByZXR1cm4gX3ByZXBhcmVFeHBvcnQob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGdW5jdGlvbiBmb3IgZmluYWxpemluZyBvcHRpb25zIGFuZCBjb25maWd1cmF0aW9ucyBiZWZvcmUgZXhwb3J0LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9wcmVwYXJlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpIHtcclxuICBjb25zdCB7IGV4cG9ydDogZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWM6IGN1c3RvbUxvZ2ljT3B0aW9ucyB9ID0gb3B0aW9ucztcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYHR5cGVgIG9wdGlvblxyXG4gIGV4cG9ydE9wdGlvbnMudHlwZSA9IGZpeFR5cGUoZXhwb3J0T3B0aW9ucy50eXBlLCBleHBvcnRPcHRpb25zLm91dGZpbGUpO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5vdXRmaWxlID0gZml4T3V0ZmlsZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIE5vdGlmeSBhYm91dCB0aGUgY3VzdG9tIGxvZ2ljIHVzYWdlIHN0YXR1c1xyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW2NoYXJ0XSBUaGUgY3VzdG9tIGxvZ2ljIGlzICR7Y3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvbiA/ICdhbGxvd2VkJyA6ICdkaXNhbGxvd2VkJ30uYFxyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGN1c3RvbSBsb2dpYyBvcHRpb25zIChgY3VzdG9tQ29kZWAsIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgKVxyXG4gIF9oYW5kbGVDdXN0b21Mb2dpYyhjdXN0b21Mb2dpY09wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgZ2xvYmFsT3B0aW9uc2AgYW5kIGB0aGVtZU9wdGlvbnNgIG9wdGlvbnNcclxuICBfaGFuZGxlR2xvYmFsQW5kVGhlbWUoXHJcbiAgICBleHBvcnRPcHRpb25zLFxyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCwgYW5kIGBzY2FsZWAgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0ID0ge1xyXG4gICAgLi4uZXhwb3J0T3B0aW9ucyxcclxuICAgIC4uLl9maW5kQ2hhcnRTaXplKGV4cG9ydE9wdGlvbnMpXHJcbiAgfTtcclxuXHJcbiAgLy8gUG9zdCB0aGUgd29yayB0byB0aGUgcG9vbFxyXG4gIHJldHVybiBwb3N0V29yayhvcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgdGhlIGBoZWlnaHRgLCBgd2lkdGhgIGFuZCBgc2NhbGVgIGZvciBjaGFydCBleHBvcnRzIGJhc2VkXHJcbiAqIG9uIHRoZSBwcm92aWRlZCBleHBvcnQgb3B0aW9ucy5cclxuICpcclxuICogVGhlIGZ1bmN0aW9uIHByaW9yaXRpemVzIHZhbHVlcyBpbiB0aGUgZm9sbG93aW5nIG9yZGVyOlxyXG4gKiAxLiBUaGUgYGhlaWdodGAsIGB3aWR0aGAsIGBzY2FsZWAgZnJvbSB0aGUgYGV4cG9ydE9wdGlvbnNgLlxyXG4gKiAyLiBPcHRpb25zIGZyb20gdGhlIGNoYXJ0IGNvbmZpZ3VyYXRpb24gKGZyb20gYGV4cG9ydGluZ2AgYW5kIGBjaGFydGApLlxyXG4gKiAzLiBPcHRpb25zIGZyb20gdGhlIGdsb2JhbCBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogNC4gT3B0aW9ucyBmcm9tIHRoZSB0aGVtZSBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgIHNlY3Rpb25zKS5cclxuICogNS4gRmFsbGJhY2sgZGVmYXVsdCB2YWx1ZXMgKGBoZWlnaHQgPSA0MDBgLCBgd2lkdGggPSA2MDBgLCBgc2NhbGUgPSAxYCkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmluZENoYXJ0U2l6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBvYmplY3QgY29udGFpbmluZyBgY3VzdG9tTG9naWNgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY29kZSBleGVjdXRpb25cclxuICogaXMgbm90IGFsbG93ZWQgYnV0IGN1c3RvbSBsb2dpYyBvcHRpb25zIGFyZSBzdGlsbCBwcm92aWRlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVDdXN0b21Mb2dpYyhjdXN0b21Mb2dpY09wdGlvbnMsIGFsbG93Q29kZUV4ZWN1dGlvbikge1xyXG4gIC8vIEluIGNhc2Ugb2YgYWxsb3dpbmcgY29kZSBleGVjdXRpb25cclxuICBpZiAoYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgcmVzb3VyY2VzYCBvcHRpb25cclxuICAgIGlmICh0eXBlb2YgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gQ3VzdG9tIHN0cmluZ2lmaWVkIHJlc291cmNlc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmICghY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIExvYWQgdGhlIGRlZmF1bHQgb25lXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9IF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKCdyZXNvdXJjZXMuanNvbicpLCAndXRmOCcpLFxyXG4gICAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgIHRydWVcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZygyLCAnW2NoYXJ0XSBVbmFibGUgdG8gbG9hZCB0aGUgZGVmYXVsdCBgcmVzb3VyY2VzLmpzb25gIGZpbGUuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBUcnkgdG8gbG9hZCBjdXN0b20gY29kZSBhbmQgd3JhcCBhcm91bmQgaXQgaW4gYSBzZWxmIGludm9raW5nIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY3VzdG9tQ29kZWAgY2Fubm90IGJlIGxvYWRlZC4nKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUHJvY2VzcyB0aGUgYGNhbGxiYWNrYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGNhbGxiYWNrIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IHdyYXBBcm91bmQoXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW2NoYXJ0XSBUaGUgYGNhbGxiYWNrYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjdXN0b21Db2RlYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBjdXN0b21Db2RlYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjYWxsYmFja2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjaykpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGNhbGxiYWNrYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGByZXNvdXJjZXNgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgcmVzb3VyY2VzYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIElmIHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBmbGFnIGlzIHNldCB0byBmYWxzZSwgd2Ugc2hvdWxkIHJlZnVzZVxyXG4gICAgLy8gdGhlIHVzYWdlIG9mIHRoZSBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYCwgYW5kIGBjdXN0b21Db2RlYCBvcHRpb25zLlxyXG4gICAgLy8gQWRkaXRpb25hbGx5LCB0aGUgd29ya2VyIHdpbGwgcmVmdXNlIHRvIHJ1biBhcmJpdHJhcnkgSmF2YVNjcmlwdC5cclxuICAgIGlmIChcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGVcclxuICAgICkge1xyXG4gICAgICAvLyBSZXNldCBhbGwgY3VzdG9tIGNvZGUgb3B0aW9uc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gbnVsbDtcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBudWxsO1xyXG5cclxuICAgICAgLy8gU2VuZCBhIG1lc3NhZ2Ugc2F5aW5nIHRoYXQgdGhlIGV4cG9ydGVyIGRvZXMgbm90IHN1cHBvcnQgdGhlc2Ugc2V0dGluZ3NcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJywgYW5kICdjdXN0b21Db2RlJyBvcHRpb25zIGhhdmUgYmVlbiBkaXNhYmxlZCBmb3IgdGhpcyBzZXJ2ZXIuYCxcclxuICAgICAgICA0MDNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIGFuZCB2YWxpZGF0ZXMgcmVzb3VyY2VzIGZyb20gdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3IgZXhwb3J0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8c3RyaW5nfG51bGwpfSBbcmVzb3VyY2VzPW51bGxdIC0gVGhlIHJlc291cmNlcyB0byBiZSBoYW5kbGVkLlxyXG4gKiBDYW4gYmUgZWl0aGVyIGEgSlNPTiBvYmplY3QsIHN0cmluZ2lmaWVkIEpTT04sIGEgcGF0aCB0byBhIEpTT04gZmlsZSxcclxuICogb3IgbnVsbC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgbnVsbC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGxvYWRpbmdcclxuICogcmVzb3VyY2VzIGZyb20gZmlsZXMgaXMgYWxsb3dlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fG51bGwpfSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgbnVsbCBpZiBubyB2YWxpZCByZXNvdXJjZXNcclxuICogYXJlIGZvdW5kLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZVJlc291cmNlcyhcclxuICByZXNvdXJjZXMgPSBudWxsLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gTGlzdCBvZiBhbGxvd2VkIHNlY3Rpb25zIGluIHRoZSByZXNvdXJjZXMgSlNPTlxyXG4gIGNvbnN0IGFsbG93ZWRQcm9wcyA9IFsnanMnLCAnY3NzJywgJ2ZpbGVzJ107XHJcblxyXG4gIGxldCBoYW5kbGVkUmVzb3VyY2VzID0gcmVzb3VyY2VzO1xyXG4gIGxldCBjb3JyZWN0UmVzb3VyY2VzID0gZmFsc2U7XHJcblxyXG4gIC8vIFRyeSB0byBsb2FkIHJlc291cmNlcyBmcm9tIGEgZmlsZVxyXG4gIGlmIChhbGxvd0ZpbGVSZXNvdXJjZXMgJiYgcmVzb3VyY2VzLmVuZHNXaXRoKCcuanNvbicpKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBoYW5kbGVkUmVzb3VyY2VzID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgocmVzb3VyY2VzKSwgJ3V0ZjgnKSxcclxuICAgICAgICBmYWxzZSxcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2gge1xyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gVHJ5IHRvIGdldCBKU09OXHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzID0gaXNBbGxvd2VkQ29uZmlnKHJlc291cmNlcywgZmFsc2UsIGFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgZmlsZXMgc2VjdGlvblxyXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbHRlciBmcm9tIHVubmVjZXNzYXJ5IHByb3BlcnRpZXNcclxuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcclxuICAgIGlmICghYWxsb3dlZFByb3BzLmluY2x1ZGVzKHByb3BOYW1lKSkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlc1twcm9wTmFtZV07XHJcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICAgIGNvcnJlY3RSZXNvdXJjZXMgPSB0cnVlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIGFsbG93ZWQgcHJvcGVydGllcyBpcyBwcmVzZW50XHJcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcblxyXG4gIC8vIEhhbmRsZSBmaWxlcyBzZWN0aW9uXHJcbiAgaWYgKGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgPSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLm1hcCgoaXRlbSkgPT4gaXRlbS50cmltKCkpO1xyXG4gICAgaWYgKCFoYW5kbGVkUmVzb3VyY2VzLmZpbGVzIHx8IGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMubGVuZ3RoIDw9IDApIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcmVzb3VyY2VzXHJcbiAgcmV0dXJuIGhhbmRsZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBsb2FkaW5nIGFuZCB2YWxpZGF0aW9uIG9mIHRoZSBgZ2xvYmFsT3B0aW9uc2AgYW5kIGB0aGVtZU9wdGlvbnNgXHJcbiAqIGluIHRoZSBleHBvcnQgb3B0aW9ucy4gSWYgdGhlIG9wdGlvbiBpcyBhIHN0cmluZyBhbmQgcmVmZXJlbmNlcyBhIEpTT04gZmlsZVxyXG4gKiAod2hlbiB0aGUgYGFsbG93RmlsZVJlc291cmNlc2AgaXMgdHJ1ZSksIGl0IHJlYWRzIGFuZCBwYXJzZXMgdGhlIGZpbGUuXHJcbiAqIE90aGVyd2lzZSwgaXQgYXR0ZW1wdHMgdG8gcGFyc2UgdGhlIHN0cmluZyBvciBvYmplY3QgYXMgSlNPTi4gSWYgYW55IGVycm9yc1xyXG4gKiBvY2N1ciBkdXJpbmcgdGhpcyBwcm9jZXNzLCB0aGUgb3B0aW9uIGlzIHNldCB0byBudWxsLiBJZiB0aGVyZSBpcyBhbiBlcnJvclxyXG4gKiBsb2FkaW5nIG9yIHBhcnNpbmcgdGhlIGBnbG9iYWxPcHRpb25zYCBvciBgdGhlbWVPcHRpb25zYCwgdGhlIGVycm9yIGlzIGxvZ2dlZFxyXG4gKiBhbmQgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzdGFydEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgZW5hYmxpbmcgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc3BlY2lmaWVkIEV4cHJlc3MgYXBwLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IFtyYXRlTGltaXRpbmdPcHRpb25zPWdldE9wdGlvbnMoKS5zZXJ2ZXIucmF0ZUxpbWl0aW5nXSAtXHJcbiAqIE9iamVjdCBjb250YWluaW5nIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIHRoZSBnbG9iYWxcclxuICogcmF0ZSBsaW1pdGluZyBvcHRpb25zIG9mIHRoZSBleHBvcnQgc2VydmVyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0XHJcbiAqIHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiByYXRlTGltaXRpbmdNaWRkbGV3YXJlKFxyXG4gIGFwcCxcclxuICByYXRlTGltaXRpbmdPcHRpb25zID0gZ2V0T3B0aW9ucygpLnNlcnZlci5yYXRlTGltaXRpbmdcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWRcclxuICAgIGlmIChyYXRlTGltaXRpbmdPcHRpb25zLmVuYWJsZSkge1xyXG4gICAgICBjb25zdCBtc2cgPVxyXG4gICAgICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgICAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICAgICAgbWF4OiByYXRlTGltaXRpbmdPcHRpb25zLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgICAgIHdpbmRvdzogcmF0ZUxpbWl0aW5nT3B0aW9ucy53aW5kb3cgfHwgMSxcclxuICAgICAgICBkZWxheTogcmF0ZUxpbWl0aW5nT3B0aW9ucy5kZWxheSB8fCAwLFxyXG4gICAgICAgIHRydXN0UHJveHk6IHJhdGVMaW1pdGluZ09wdGlvbnMudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgICAgICBza2lwS2V5OiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBLZXkgfHwgZmFsc2UsXHJcbiAgICAgICAgc2tpcFRva2VuOiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBUb2tlbiB8fCBmYWxzZVxyXG4gICAgICB9O1xyXG5cclxuICAgICAgLy8gU2V0IGlmIGJlaGluZCBhIHByb3h5XHJcbiAgICAgIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICAgICAgYXBwLmVuYWJsZSgndHJ1c3QgcHJveHknKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gICAgICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcclxuICAgICAgICB3aW5kb3dNczogcmF0ZU9wdGlvbnMud2luZG93ICogNjAgKiAxMDAwLFxyXG4gICAgICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciB3aW5kb3dNc1xyXG4gICAgICAgIG1heDogcmF0ZU9wdGlvbnMubWF4LFxyXG4gICAgICAgIC8vIERpc2FibGUgZGVsYXlpbmcsIGZ1bGwgc3BlZWQgdW50aWwgdGhlIG1heCBsaW1pdCBpcyByZWFjaGVkXHJcbiAgICAgICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICAgICAgaGFuZGxlcjogKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5mb3JtYXQoe1xyXG4gICAgICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZCh7IG1lc3NhZ2U6IG1zZyB9KTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobXNnKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSk7XHJcbiAgICAgICAgfSxcclxuICAgICAgICBza2lwOiAocmVxdWVzdCkgPT4ge1xyXG4gICAgICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcclxuICAgICAgICAgIGlmIChcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcEtleSAhPT0gZmFsc2UgJiZcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBmYWxzZSAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmtleSA9PT0gcmF0ZU9wdGlvbnMuc2tpcEtleSAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmFjY2Vzc190b2tlbiA9PT0gcmF0ZU9wdGlvbnMuc2tpcFRva2VuXHJcbiAgICAgICAgICApIHtcclxuICAgICAgICAgICAgbG9nKDQsICdbcmF0ZSBsaW1pdGluZ10gU2tpcHBpbmcgcmF0ZSBsaW1pdGVyLicpO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgLy8gVXNlIGEgbGltaXRlciBhcyBhIG1pZGRsZXdhcmVcclxuICAgICAgYXBwLnVzZShsaW1pdGVyKTtcclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcmF0ZSBsaW1pdGluZ10gRW5hYmxlZCByYXRlIGxpbWl0aW5nIHdpdGggJHtyYXRlT3B0aW9ucy5tYXh9IHJlcXVlc3RzIHBlciAke3JhdGVPcHRpb25zLndpbmRvd30gbWludXRlIGZvciBlYWNoIElQLCB0cnVzdGluZyBwcm94eTogJHtyYXRlT3B0aW9ucy50cnVzdFByb3h5fS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tyYXRlIGxpbWl0aW5nXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBIVFRQIGVycm9yIGNsYXNzIHRoYXQgZXh0ZW5kcyB0aGUgYEV4cG9ydEVycm9yYC4gVXNlZCB0byBoYW5kbGVcclxuICogZXJyb3JzIHdpdGggSFRUUCBzdGF0dXMgY29kZXMuXHJcbiAqL1xyXG5jbGFzcyBIdHRwRXJyb3IgZXh0ZW5kcyBFeHBvcnRFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEh0dHBFcnJvcmAuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIFRoZSBlcnJvciBtZXNzYWdlIHRvIGJlIGRpc3BsYXllZC5cclxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzQ29kZSAtIE9wdGlvbmFsIEhUVFAgc3RhdHVzIGNvZGUgYXNzb2NpYXRlZFxyXG4gICAqIHdpdGggdGhlIGVycm9yIChlLmcuLCA0MDAsIDUwMCkuXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSwgc3RhdHVzQ29kZSkge1xyXG4gICAgc3VwZXIobWVzc2FnZSwgc3RhdHVzQ29kZSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXRzIG9yIHVwZGF0ZXMgdGhlIEhUVFAgc3RhdHVzIGNvZGUgZm9yIHRoZSBlcnJvci5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gVGhlIEhUVFAgc3RhdHVzIGNvZGUgdG8gYXNzaWduIHRvIHRoZSBlcnJvci5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtIdHRwRXJyb3J9IFRoZSB1cGRhdGVkIGluc3RhbmNlIG9mIHRoZSBgSHR0cEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IEh0dHBFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciB2YWxpZGF0aW5nIGluY29taW5nIEhUVFAgcmVxdWVzdHNcclxuICogaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi4gVGhpcyBtb2R1bGUgZW5zdXJlcyB0aGF0IHJlcXVlc3RzIGNvbnRhaW5cclxuICogYXBwcm9wcmlhdGUgY29udGVudCB0eXBlcyBhbmQgdmFsaWQgcmVxdWVzdCBib2RpZXMsIGluY2x1ZGluZyBwcm9wZXIgSlNPTlxyXG4gKiBzdHJ1Y3R1cmVzIGFuZCBjaGFydCBkYXRhIGZvciBleHBvcnRzLiBJdCBjaGVja3MgZm9yIHBvdGVudGlhbCBpc3N1ZXMgc3VjaFxyXG4gKiBhcyBtaXNzaW5nIG9yIG1hbGZvcm1lZCBkYXRhLCBwcml2YXRlIHJhbmdlIFVSTHMgaW4gU1ZHIHBheWxvYWRzLCBhbmQgYWxsb3dzXHJcbiAqIGZvciBmbGV4aWJsZSBvcHRpb25zIHZhbGlkYXRpb24uIFRoZSBtaWRkbGV3YXJlIGxvZ3MgZGV0YWlsZWQgaW5mb3JtYXRpb25cclxuICogYW5kIGhhbmRsZXMgZXJyb3JzIHJlbGF0ZWQgdG8gaW5jb3JyZWN0IHBheWxvYWRzLCBjaGFydCBkYXRhLCBhbmQgcHJpdmF0ZSBVUkxcclxuICogdXNhZ2UuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uIH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeENvbnN0cixcclxuICBmaXhUeXBlLFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZFxyXG59IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBIdHRwRXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0h0dHBFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgY29udGVudC10eXBlIGhlYWRlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7SHR0cEVycm9yfSBUaHJvd3MgYW4gYEh0dHBFcnJvcmAgaWYgdGhlIGNvbnRlbnQtdHlwZVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICovXHJcbmZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZShyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIGNvbnRlbnQgdHlwZSBoZWFkZXJcclxuICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gcmVxdWVzdC5oZWFkZXJzWydjb250ZW50LXR5cGUnXSB8fCAnJztcclxuXHJcbiAgICAvLyBBbGxvdyBvbmx5IEpTT04sIFVSTC1lbmNvZGVkIGFuZCBmb3JtIGRhdGEgd2l0aG91dCBmaWxlcyB0eXBlcyBvZiBkYXRhXHJcbiAgICBpZiAoXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24vanNvbicpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJykgJiZcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdtdWx0aXBhcnQvZm9ybS1kYXRhJylcclxuICAgICkge1xyXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICdbdmFsaWRhdGlvbl0gQ29udGVudC1UeXBlIG11c3QgYmUgYXBwbGljYXRpb24vanNvbiwgYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkLCBvciBtdWx0aXBhcnQvZm9ybS1kYXRhLicsXHJcbiAgICAgICAgNDE1XHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYHJlcXVlc3RCb2R5TWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIHJlcXVlc3QncyBib2R5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtIdHRwRXJyb3J9IFRocm93cyBhbiBgSHR0cEVycm9yYCBpZiB0aGUgYm9keSBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7SHR0cEVycm9yfSBUaHJvd3MgYW4gYEh0dHBFcnJvcmAgaWYgdGhlIGNoYXJ0IGRhdGEgZnJvbSB0aGUgYm9keVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7SHR0cEVycm9yfSBUaHJvd3MgYW4gYEh0dHBFcnJvcmAgaW4gY2FzZSBvZiB0aGUgcHJpdmF0ZSByYW5nZSB1cmxcclxuICogZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGJvZHlcclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHV1aWQoKS5yZXBsYWNlKC8tL2csICcnKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgZW1wdHkuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICBcIlt2YWxpZGF0aW9uXSBUaGUgcmVxdWVzdCBib2R5IGlzIHJlcXVpcmVkLiBQbGVhc2UgZW5zdXJlIHRoYXQgeW91ciBDb250ZW50LVR5cGUgaGVhZGVyIGlzIGNvcnJlY3QuIEFjY2VwdGVkIHR5cGVzIGFyZSAnYXBwbGljYXRpb24vanNvbicgYW5kICdtdWx0aXBhcnQvZm9ybS1kYXRhJy5cIixcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBvcHRpb24gZm9yIHRoZSBzZXJ2ZXJcclxuICAgIGNvbnN0IGFsbG93Q29kZUV4ZWN1dGlvbiA9IGdldEFsbG93Q29kZUV4ZWN1dGlvbigpO1xyXG5cclxuICAgIC8vIEZpbmQgYSBjb3JyZWN0IGNoYXJ0IG9wdGlvbnNcclxuICAgIGNvbnN0IGluc3RyID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAvLyBVc2Ugb25lIG9mIHRoZSBiZWxvd1xyXG4gICAgICBib2R5Lmluc3RyIHx8IGJvZHkub3B0aW9ucyB8fCBib2R5LmluZmlsZSB8fCBib2R5LmRhdGEsXHJcbiAgICAgIC8vIFN0cmluZ2lmeSBvcHRpb25zXHJcbiAgICAgIHRydWUsXHJcbiAgICAgIC8vIEFsbG93IG9yIGRpc2FsbG93IGZ1bmN0aW9uc1xyXG4gICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICk7XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgaXMgbm8gY29ycmVjdCBjaGFydCBkYXRhXHJcbiAgICBpZiAoaW5zdHIgPT09IG51bGwgJiYgIWJvZHkuc3ZnKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgbWlzc2luZyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm9yIGV4cG9ydDogJHtKU09OLnN0cmluZ2lmeShib2R5KX0uYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICBcIlt2YWxpZGF0aW9uXSBObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLlwiLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRlc3Qgb2YgeGxpbms6aHJlZiBlbGVtZW50cyBmcm9tIHBheWxvYWQncyBTVkcgZmFpbHNcclxuICAgIGlmIChib2R5LnN2ZyAmJiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKGJvZHkuc3ZnKSkge1xyXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgIFwiW3ZhbGlkYXRpb25dIFNWRyBwb3RlbnRpYWxseSBjb250YWluIGF0IGxlYXN0IG9uZSBmb3JiaWRkZW4gVVJMIGluICd4bGluazpocmVmJyBlbGVtZW50LiBQbGVhc2UgcmV2aWV3IHRoZSBTVkcgY29udGVudCBhbmQgZW5zdXJlIHRoYXQgYWxsIHJlZmVyZW5jZWQgVVJMcyBjb21wbHkgd2l0aCBzZWN1cml0eSBwb2xpY2llcy5cIixcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBib2R5IGFuZCBzdG9yZSBwYXJzZWQgc3RydWN0dXJlIGluIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnMgPSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY3JlYXRlZCBJRCBhcyBhIGBfcmVxdWVzdElkYCBwcm9wZXJ0eSBpbiB0aGUgdmFsaWRhdGVkIG9wdGlvbnNcclxuICAgICAgX3JlcXVlc3RJZDogcmVxdWVzdElkLFxyXG4gICAgICBleHBvcnQ6IHtcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICBzdmc6IGJvZHkuc3ZnLFxyXG4gICAgICAgIG91dGZpbGU6XHJcbiAgICAgICAgICBib2R5Lm91dGZpbGUgfHxcclxuICAgICAgICAgIGAke3JlcXVlc3QucGFyYW1zLmZpbGVuYW1lIHx8ICdjaGFydCd9LiR7Zml4VHlwZShib2R5LnR5cGUpfWAsXHJcbiAgICAgICAgdHlwZTogZml4VHlwZShib2R5LnR5cGUsIGJvZHkub3V0ZmlsZSksXHJcbiAgICAgICAgY29uc3RyOiBmaXhDb25zdHIoYm9keS5jb25zdHIpLFxyXG4gICAgICAgIGI2NDogYm9keS5iNjQsXHJcbiAgICAgICAgbm9Eb3dubG9hZDogYm9keS5ub0Rvd25sb2FkLFxyXG4gICAgICAgIGhlaWdodDogYm9keS5oZWlnaHQsXHJcbiAgICAgICAgd2lkdGg6IGJvZHkud2lkdGgsXHJcbiAgICAgICAgc2NhbGU6IGJvZHkuc2NhbGUsXHJcbiAgICAgICAgZ2xvYmFsT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS5nbG9iYWxPcHRpb25zLFxyXG4gICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICksXHJcbiAgICAgICAgdGhlbWVPcHRpb25zOiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICBib2R5LnRoZW1lT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApXHJcbiAgICAgIH0sXHJcbiAgICAgIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gICAgICAgIGFsbG93RmlsZVJlc291cmNlczogZmFsc2UsXHJcbiAgICAgICAgY3VzdG9tQ29kZTogYm9keS5jdXN0b21Db2RlLFxyXG4gICAgICAgIGNhbGxiYWNrOiBib2R5LmNhbGxiYWNrLFxyXG4gICAgICAgIHJlc291cmNlczogaXNBbGxvd2VkQ29uZmlnKGJvZHkucmVzb3VyY2VzLCB0cnVlLCBhbGxvd0NvZGVFeGVjdXRpb24pXHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgbmV4dCBtaWRkbGV3YXJlXHJcbiAgICByZXR1cm4gbmV4dCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlcyB0byB0aGUgcGFzc2VkIGV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCkge1xyXG4gIC8vIEFkZCBjb250ZW50IHR5cGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgYXBwLnBvc3QoWycvJywgJy86ZmlsZW5hbWUnXSwgY29udGVudFR5cGVNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHJlcXVlc3QgYm9keSByZXF1ZXN0IHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIHJlcXVlc3RCb2R5TWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgdGhlIGV4cG9ydCByb3V0ZXMgYW5kIGxvZ2ljIGZvciBoYW5kbGluZyBjaGFydCBleHBvcnRcclxuICogcmVxdWVzdHMgaW4gYW4gRXhwcmVzcyBzZXJ2ZXIuIFRoaXMgbW9kdWxlIHByb2Nlc3NlcyBpbmNvbWluZyByZXF1ZXN0c1xyXG4gKiB0byBleHBvcnQgY2hhcnRzIGluIHZhcmlvdXMgZm9ybWF0cyAoZS5nLiBKUEVHLCBQTkcsIFBERiwgU1ZHKS4gSXQgaW50ZWdyYXRlc1xyXG4gKiB3aXRoIEhpZ2hjaGFydHMnIGNvcmUgZnVuY3Rpb25hbGl0aWVzIGFuZCBzdXBwb3J0cyBib3RoIGltbWVkaWF0ZSBkb3dubG9hZFxyXG4gKiByZXNwb25zZXMgYW5kIEJhc2U2NC1lbmNvZGVkIGNvbnRlbnQgcmV0dXJucy4gVGhlIGNvZGUgYWxzbyBmZWF0dXJlc1xyXG4gKiBiZW5jaG1hcmtpbmcgZm9yIHBlcmZvcm1hbmNlIG1vbml0b3JpbmcuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgc3RhcnRFeHBvcnQgfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldEJhc2U2NCwgbWVhc3VyZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgSHR0cEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9IdHRwRXJyb3IuanMnO1xyXG5cclxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xyXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XHJcbiAgcG5nOiAnaW1hZ2UvcG5nJyxcclxuICBqcGVnOiAnaW1hZ2UvanBlZycsXHJcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcclxuICBwZGY6ICdhcHBsaWNhdGlvbi9wZGYnLFxyXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXHJcbn07XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgZXhwb3J0IHJlcXVlc3RzIGZyb20gdGhlIGNsaWVudC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0KHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0IGNvdW50aW5nIHRpbWUgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgcmVxdWVzdENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIEluIGNhc2UgdGhlIGNvbm5lY3Rpb24gaXMgY2xvc2VkLCBmb3JjZSB0byBhYm9ydCBmdXJ0aGVyIGFjdGlvbnNcclxuICAgIGxldCBjb25uZWN0aW9uQWJvcnRlZCA9IGZhbHNlO1xyXG4gICAgcmVxdWVzdC5zb2NrZXQub24oJ2Nsb3NlJywgKGhhZEVycm9ycykgPT4ge1xyXG4gICAgICBpZiAoaGFkRXJyb3JzKSB7XHJcbiAgICAgICAgY29ubmVjdGlvbkFib3J0ZWQgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIG9wdGlvbnMgcHJldmlvdXNseSB2YWxpZGF0ZWQgaW4gdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gICAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGlkXHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSByZXF1ZXN0T3B0aW9ucy5fcmVxdWVzdElkO1xyXG5cclxuICAgIC8vIEluZm8gYWJvdXQgYW4gaW5jb21pbmcgcmVxdWVzdCB3aXRoIGNvcnJlY3QgZGF0YVxyXG4gICAgbG9nKDQsIGBbZXhwb3J0XSBHb3QgYW4gaW5jb21pbmcgSFRUUCByZXF1ZXN0IHdpdGggSUQgJHtyZXF1ZXN0SWR9LmApO1xyXG5cclxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQocmVxdWVzdE9wdGlvbnMsIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAvLyBSZW1vdmUgdGhlIGNsb3NlIGV2ZW50IGZyb20gdGhlIHNvY2tldFxyXG4gICAgICByZXF1ZXN0LnNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2Nsb3NlJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkLCBkbyBub3RoaW5nXHJcbiAgICAgIGlmIChjb25uZWN0aW9uQWJvcnRlZCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSBjbGllbnQgY2xvc2VkIHRoZSBjb25uZWN0aW9uIGJlZm9yZSB0aGUgY2hhcnQgZmluaXNoZWQgcHJvY2Vzc2luZy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGVycm9yLCBsb2cgaXQgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGRhdGEgaXMgbWlzc2luZywgbG9nIHRoZSBtZXNzYWdlIGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmICghZGF0YSB8fCAhZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBSZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fFxyXG4gICAgICAgICAgICByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCByZXN1bHQgaXMgJHtkYXRhLnJlc3VsdH0uYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAnW2V4cG9ydF0gVW5leHBlY3RlZCByZXR1cm4gb2YgdGhlIGV4cG9ydCByZXN1bHQgZnJvbSB0aGUgY2hhcnQgZ2VuZXJhdGlvbi4gUGxlYXNlIGNoZWNrIHlvdXIgcmVxdWVzdCBkYXRhLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBSZXR1cm4gdGhlIHJlc3VsdCBpbiBhbiBhcHByb3ByaWF0ZSBmb3JtYXRcclxuICAgICAgaWYgKGRhdGEucmVzdWx0KSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzIHRvb2sgJHtyZXF1ZXN0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIGB0eXBlYCwgYGI2NGAsIGBub0Rvd25sb2FkYCwgYW5kIGBvdXRmaWxlYCBmcm9tIG9wdGlvbnNcclxuICAgICAgICBjb25zdCB7IHR5cGUsIGI2NCwgbm9Eb3dubG9hZCwgb3V0ZmlsZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgLy8gSWYgb25seSBCYXNlNjQgaXMgcmVxdWlyZWQsIHJldHVybiBpdFxyXG4gICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gU2V0IGNvcnJlY3QgY29udGVudCB0eXBlXHJcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVyKCdDb250ZW50LVR5cGUnLCByZXZlcnNlZE1pbWVbdHlwZV0gfHwgJ2ltYWdlL3BuZycpO1xyXG5cclxuICAgICAgICAvLyBEZWNpZGUgd2hldGhlciB0byBkb3dubG9hZCBvciBub3QgY2hhcnQgZmlsZVxyXG4gICAgICAgIGlmICghbm9Eb3dubG9hZCkge1xyXG4gICAgICAgICAgcmVzcG9uc2UuYXR0YWNobWVudChvdXRmaWxlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIElmIFNWRywgcmV0dXJuIHBsYWluIGNvbnRlbnQsIG90aGVyd2lzZSBhIGI2NCBzdHJpbmcgZnJvbSBhIGJ1ZmZlclxyXG4gICAgICAgIHJldHVybiB0eXBlID09PSAnc3ZnJ1xyXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGRhdGEucmVzdWx0KVxyXG4gICAgICAgICAgOiByZXNwb25zZS5zZW5kKEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JykpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBleHBvcnRgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4cG9ydFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGV4cG9ydFJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3RcclxuICAgKiBlbmRwb2ludC5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLycsIHJlcXVlc3RFeHBvcnQpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvOmZpbGVuYW1lJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgd2l0aFxyXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLzpmaWxlbmFtZScsIHJlcXVlc3RFeHBvcnQpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHNlcnZlciBoZWFsdGggbW9uaXRvcmluZywgaW5jbHVkaW5nXHJcbiAqIHVwdGltZSwgc3VjY2VzcyByYXRlcywgYW5kIG90aGVyIHNlcnZlciBzdGF0aXN0aWNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldFBvb2xTdGF0cywgZ2V0UG9vbEluZm9KU09OIH0gZnJvbSAnLi4vLi4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGFkZFRpbWVyIH0gZnJvbSAnLi4vLi4vdGltZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGdldE5ld0RhdGVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLy8gU2V0IHRoZSBzdGFydCBkYXRlIG9mIHRoZSBzZXJ2ZXJcclxuY29uc3Qgc2VydmVyU3RhcnRUaW1lID0gbmV3IERhdGUoKTtcclxuXHJcbi8vIEdldCB0aGUgYHBhY2thZ2UuanNvbmAgY29udGVudFxyXG5jb25zdCBwYWNrYWdlRmlsZSA9IEpTT04ucGFyc2UocmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpKTtcclxuXHJcbi8vIEFuIGFycmF5IGZvciBzdWNjZXNzIHJhdGUgcmF0aW9zXHJcbmNvbnN0IHN1Y2Nlc3NSYXRlcyA9IFtdO1xyXG5cclxuLy8gUmVjb3JkIGV2ZXJ5IG1pbnV0ZVxyXG5jb25zdCByZWNvcmRJbnRlcnZhbCA9IDYwICogMTAwMDtcclxuXHJcbi8vIDMwIG1pbnV0ZXNcclxuY29uc3Qgd2luZG93U2l6ZSA9IDMwO1xyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgbW92aW5nIGF2ZXJhZ2UgaW5kaWNhdG9yIGJhc2VkIG9uIHRoZSBkYXRhIGZyb20gdGhlIGBzdWNjZXNzUmF0ZXNgXHJcbiAqIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2VcclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gQSBtb3ZpbmcgYXZlcmFnZSBmb3Igc3VjY2VzcyByYXRpbyBvZiB0aGUgc2VydmVyIGV4cG9ydHMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpIHtcclxuICByZXR1cm4gc3VjY2Vzc1JhdGVzLnJlZHVjZSgoYSwgYikgPT4gYSArIGIsIDApIC8gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyB0aGUgaW50ZXJ2YWwgcmVzcG9uc2libGUgZm9yIGNhbGN1bGF0aW5nIGN1cnJlbnQgc3VjY2VzcyByYXRlIHJhdGlvXHJcbiAqIGFuZCBjb2xsZWN0cyByZWNvcmRzIHRvIHRoZSBgc3VjY2Vzc1JhdGVzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtOb2RlSlMuVGltZW91dH0gSWQgb2YgYW4gaW50ZXJ2YWwuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc3RhcnRTdWNjZXNzUmF0ZSgpIHtcclxuICByZXR1cm4gc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgIGNvbnN0IHN1Y2Nlc3NSYXRpbyA9XHJcbiAgICAgIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQgPT09IDBcclxuICAgICAgICA/IDFcclxuICAgICAgICA6IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDA7XHJcblxyXG4gICAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcclxuICAgIGlmIChzdWNjZXNzUmF0ZXMubGVuZ3RoID4gd2luZG93U2l6ZSkge1xyXG4gICAgICBzdWNjZXNzUmF0ZXMuc2hpZnQoKTtcclxuICAgIH1cclxuICB9LCByZWNvcmRJbnRlcnZhbCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgaGVhbHRoYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBoZWFsdGhSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBoZWFsdGhSb3V0ZXMoYXBwKSB7XHJcbiAgLy8gU3RhcnQgcHJvY2Vzc2luZyBzdWNjZXNzIHJhdGUgcmF0aW8gaW50ZXJ2YWwgYW5kIHNhdmUgaXRzIGlkIHRvIHRoZSBhcnJheVxyXG4gIC8vIGZvciB0aGUgZ3JhY2VmdWwgY2xlYXJpbmcgb24gc2h1dGRvd24gd2l0aCBpbmplY3RlZCBgYWRkVGltZXJgIGZ1bnRpb25cclxuICBhZGRUaW1lcihfc3RhcnRTdWNjZXNzUmF0ZSgpKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgR0VUICcvaGVhbHRoJyAtIEEgcm91dGUgZm9yIGdldHRpbmcgdGhlIGJhc2ljIHN0YXRzIG9mIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldCgnL2hlYWx0aCcsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbaGVhbHRoXSBSZXR1cm5pbmcgc2VydmVyIGhlYWx0aC4nKTtcclxuXHJcbiAgICAgIGNvbnN0IHN0YXRzID0gZ2V0UG9vbFN0YXRzKCk7XHJcbiAgICAgIGNvbnN0IHBlcmlvZCA9IHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbiAgICAgIGNvbnN0IG1vdmluZ0F2ZXJhZ2UgPSBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpO1xyXG5cclxuICAgICAgLy8gU2VuZCB0aGUgc2VydmVyJ3Mgc3RhdGlzdGljc1xyXG4gICAgICByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAvLyBTdGF0dXMgYW5kIHRpbWVzXHJcbiAgICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICAgIGJvb3RUaW1lOiBzZXJ2ZXJTdGFydFRpbWUsXHJcbiAgICAgICAgdXB0aW1lOiBgJHtNYXRoLmZsb29yKChnZXROZXdEYXRlVGltZSgpIC0gc2VydmVyU3RhcnRUaW1lLmdldFRpbWUoKSkgLyAxMDAwIC8gNjApfSBtaW51dGVzYCxcclxuXHJcbiAgICAgICAgLy8gVmVyc2lvbnNcclxuICAgICAgICBzZXJ2ZXJWZXJzaW9uOiBwYWNrYWdlRmlsZS52ZXJzaW9uLFxyXG4gICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG5cclxuICAgICAgICAvLyBFeHBvcnRzXHJcbiAgICAgICAgYXZlcmFnZUV4cG9ydFRpbWU6IHN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UsXHJcbiAgICAgICAgYXR0ZW1wdGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCxcclxuICAgICAgICBwZXJmb3JtZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzUGVyZm9ybWVkLFxyXG4gICAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNEcm9wcGVkLFxyXG4gICAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCAvIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQpICogMTAwLFxyXG5cclxuICAgICAgICAvLyBQb29sXHJcbiAgICAgICAgcG9vbDogZ2V0UG9vbEluZm9KU09OKCksXHJcblxyXG4gICAgICAgIC8vIE1vdmluZyBhdmVyYWdlXHJcbiAgICAgICAgcGVyaW9kLFxyXG4gICAgICAgIG1vdmluZ0F2ZXJhZ2UsXHJcbiAgICAgICAgbWVzc2FnZTpcclxuICAgICAgICAgIGlzTmFOKG1vdmluZ0F2ZXJhZ2UpIHx8ICFzdWNjZXNzUmF0ZXMubGVuZ3RoXHJcbiAgICAgICAgICAgID8gJ1RvbyBlYXJseSB0byByZXBvcnQuIE5vIGV4cG9ydHMgbWFkZSB5ZXQuIFBsZWFzZSBjaGVjayBiYWNrIHNvb24uJ1xyXG4gICAgICAgICAgICA6IGBMYXN0ICR7cGVyaW9kfSBtaW51dGVzIGhhZCBhIHN1Y2Nlc3MgcmF0ZSBvZiAke21vdmluZ0F2ZXJhZ2UudG9GaXhlZCgyKX0lLmAsXHJcblxyXG4gICAgICAgIC8vIFNWRyBhbmQgSlNPTiBleHBvcnRzXHJcbiAgICAgICAgc3ZnRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21TdmcsXHJcbiAgICAgICAganNvbkV4cG9ydHM6IHN0YXRzLmV4cG9ydHNGcm9tT3B0aW9ucyxcclxuICAgICAgICBzdmdFeHBvcnRzQXR0ZW1wdHM6IHN0YXRzLmV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMsXHJcbiAgICAgICAganNvbkV4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHNcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmluZyB0aGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyXHJcbiAqIHdoZW4gZW5hYmxlZC5cclxuICovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB1aWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdWlSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB1aVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy8nIC0gQSByb3V0ZSBmb3IgYSBVSSB3aGVuIGVuYWJsZWQgb24gdGhlIGV4cG9ydCBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldChnZXRPcHRpb25zKCkudWkucm91dGUgfHwgJy8nLCAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwge1xyXG4gICAgICAgIGFjY2VwdFJhbmdlczogZmFsc2VcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3IgdXBkYXRpbmcgdGhlIEhpZ2hjaGFydHMgdmVyc2lvblxyXG4gKiBvbiB0aGUgc2VydmVyLCB3aXRoIGF1dGhlbnRpY2F0aW9uIGFuZCB2YWxpZGF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLCBnZXRIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5cclxuaW1wb3J0IEh0dHBFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvSHR0cEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgdmVyc2lvbl9jaGFuZ2VgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIHZlcnNpb25DaGFuZ2VSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicgLSBBIHJvdXRlIGZvciBjaGFuZ2luZ1xyXG4gICAqIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gb24gdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnL3ZlcnNpb25fY2hhbmdlLzpuZXdWZXJzaW9uJywgYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBHZXQgdGhlIHRva2VuIGRpcmVjdGx5IGZyb20gZW52c1xyXG4gICAgICBjb25zdCBhZG1pblRva2VuID0gZW52cy5ISUdIQ0hBUlRTX0FETUlOX1RPS0VOO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGV4aXN0ZW5jZSBvZiB0aGUgdG9rZW5cclxuICAgICAgaWYgKCFhZG1pblRva2VuIHx8ICFhZG1pblRva2VuLmxlbmd0aCkge1xyXG4gICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIFRoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBmcm9tIHRoZSBoYy1hdXRoIGhlYWRlclxyXG4gICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIEludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdmVyc2lvbnNcclxuICAgICAgY29uc3QgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XHJcbiAgICAgIGlmIChuZXdWZXJzaW9uKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFVwZGF0ZSB2ZXJzaW9uXHJcbiAgICAgICAgICBhd2FpdCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAgICAgYFt2ZXJzaW9uXSBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgIDQwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXHJcbiAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcignW3ZlcnNpb25dIE5vIG5ldyB2ZXJzaW9uIHN1cHBsaWVkLicsIDQwMCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQSBtb2R1bGUgdGhhdCBzZXRzIHVwIGFuZCBtYW5hZ2VzIEhUVFAgYW5kIEhUVFBTIHNlcnZlcnNcclxuICogZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuIEl0IGhhbmRsZXMgc2VydmVyIGluaXRpYWxpemF0aW9uLFxyXG4gKiBjb25maWd1cmF0aW9uLCBlcnJvciBoYW5kbGluZywgbWlkZGxld2FyZSBzZXR1cCwgcm91dGUgZGVmaW5pdGlvbiwgYW5kIHJhdGVcclxuICogbGltaXRpbmcuIFRoZSBtb2R1bGUgZXhwb3J0cyBmdW5jdGlvbnMgdG8gc3RhcnQsIHN0b3AsIGFuZCBtYW5hZ2Ugc2VydmVyXHJcbiAqIGluc3RhbmNlcywgYXMgd2VsbCBhcyB1dGlsaXR5IGZ1bmN0aW9ucyBmb3IgZGVmaW5pbmcgcm91dGVzIGFuZCBhdHRhY2hpbmdcclxuICogbWlkZGxld2FyZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGUgfSBmcm9tICdmcy9wcm9taXNlcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBjb3JzIGZyb20gJ2NvcnMnO1xyXG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcbmltcG9ydCBtdWx0ZXIgZnJvbSAnbXVsdGVyJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IGVycm9yTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL2Vycm9yLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdGluZ01pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy9yYXRlTGltaXRpbmcuanMnO1xyXG5pbXBvcnQgdmFsaWRhdGlvbk1pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzJztcclxuXHJcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcclxuaW1wb3J0IGhlYWx0aFJvdXRlcyBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xyXG5pbXBvcnQgdmVyc2lvbkNoYW5nZVJvdXRlcyBmcm9tICcuL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcclxuY29uc3QgYWN0aXZlU2VydmVycyA9IG5ldyBNYXAoKTtcclxuXHJcbi8vIENyZWF0ZSBleHByZXNzIGFwcFxyXG5jb25zdCBhcHAgPSBleHByZXNzKCk7XHJcblxyXG4vKipcclxuICogU3RhcnRzIEhUVFAgb3IvYW5kIEhUVFBTIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi5cclxuICogVGhlIGBzZXJ2ZXJPcHRpb25zYCBvYmplY3QgY29udGFpbnMgYWxsIHNlcnZlciByZWxhdGVkIHByb3BlcnRpZXMgKHNlZVxyXG4gKiB0aGUgYHNlcnZlcmAgc2VjdGlvbiBpbiB0aGUgYGxpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgYSByZWZlcmVuY2UpLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHN0YXJ0U2VydmVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbc2VydmVyT3B0aW9ucz1nZXRPcHRpb25zKCkuc2VydmVyXSAtIE9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXJgIG9wdGlvbnMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIHRoZSBnbG9iYWwgc2VydmVyIG9wdGlvbnNcclxuICogb2YgdGhlIGV4cG9ydCBzZXJ2ZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBlbmRpbmcgdGhlIGZ1bmN0aW9uXHJcbiAqIGV4ZWN1dGlvbiB3aGVuIHRoZSBzZXJ2ZXIgc2hvdWxkIG5vdCBiZSBlbmFibGVkIG9yIHdoZW4gbm8gdmFsaWQgRXhwcmVzcyBhcHBcclxuICogaXMgZm91bmQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgc2VydmVyIGNhbm5vdFxyXG4gKiBiZSBjb25maWd1cmVkIGFuZCBzdGFydGVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0U2VydmVyKHNlcnZlck9wdGlvbnMgPSBnZXRPcHRpb25zKCkuc2VydmVyKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0b3AgaWYgbm90IGVuYWJsZWRcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5lbmFibGUgfHwgIWFwcCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tzZXJ2ZXJdIFNlcnZlciBjYW5ub3QgYmUgc3RhcnRlZCAobm90IGVuYWJsZWQgb3Igbm8gY29ycmVjdCBFeHByZXNzIGFwcCBmb3VuZCkuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUb28gYmlnIGxpbWl0cyBsZWFkIHRvIHRpbWVvdXRzIGluIHRoZSBleHBvcnQgcHJvY2VzcyB3aGVuXHJcbiAgICAvLyB0aGUgcmFzdGVyaXphdGlvbiB0aW1lb3V0IGlzIHNldCB0b28gbG93XHJcbiAgICBjb25zdCB1cGxvYWRMaW1pdEJ5dGVzID0gc2VydmVyT3B0aW9ucy51cGxvYWRMaW1pdCAqIDEwMjQgKiAxMDI0O1xyXG5cclxuICAgIC8vIE1lbW9yeSBzdG9yYWdlIGZvciBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XHJcblxyXG4gICAgLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3QgdXBsb2FkID0gbXVsdGVyKHtcclxuICAgICAgc3RvcmFnZSxcclxuICAgICAgbGltaXRzOiB7XHJcbiAgICAgICAgZmllbGRTaXplOiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIERpc2FibGUgdGhlIFgtUG93ZXJlZC1CeSBoZWFkZXJcclxuICAgIGFwcC5kaXNhYmxlKCd4LXBvd2VyZWQtYnknKTtcclxuXHJcbiAgICAvLyBFbmFibGUgQ09SUyBzdXBwb3J0XHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBjb3JzKHtcclxuICAgICAgICBtZXRob2RzOiBbJ1BPU1QnLCAnR0VUJywgJ09QVElPTlMnXVxyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXR0aW5nIGEgbG90IG9mIGBSYW5nZU5vdFNhdGlzZmlhYmxlRXJyb3JgIGV4Y2VwdGlvbnMgKGV2ZW4gdGhvdWdoIHRoaXNcclxuICAgIC8vIGlzIGEgZGVwcmVjYXRlZCBvcHRpb25zLCBsZXQncyB0cnkgdG8gc2V0IGl0IHRvIGZhbHNlKVxyXG4gICAgYXBwLnVzZSgocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgICAgcmVzcG9uc2Uuc2V0KCdBY2NlcHQtUmFuZ2VzJywgJ25vbmUnKTtcclxuICAgICAgbmV4dCgpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBKU09OIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MuanNvbih7XHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBVUkwtZW5jb2RlZCBmb3JtIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MudXJsZW5jb2RlZCh7XHJcbiAgICAgICAgZXh0ZW5kZWQ6IHRydWUsXHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gVXNlIG9ubHkgbm9uLWZpbGUgbXVsdGlwYXJ0IGZvcm0gZmllbGRzXHJcbiAgICBhcHAudXNlKHVwbG9hZC5ub25lKCkpO1xyXG5cclxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcclxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQIHNlcnZlclxyXG4gICAgaWYgKCFzZXJ2ZXJPcHRpb25zLnNzbC5mb3JjZSkge1xyXG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcCk7XHJcblxyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKGh0dHBTZXJ2ZXIpO1xyXG5cclxuICAgICAgLy8gTGlzdGVuXHJcbiAgICAgIGh0dHBTZXJ2ZXIubGlzdGVuKHNlcnZlck9wdGlvbnMucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFAgc2VydmVyXHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5zZXQoc2VydmVyT3B0aW9ucy5wb3J0LCBodHRwU2VydmVyKTtcclxuXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyT3B0aW9ucy5ob3N0fToke3NlcnZlck9wdGlvbnMucG9ydH0uYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQUyBzZXJ2ZXJcclxuICAgIGlmIChzZXJ2ZXJPcHRpb25zLnNzbC5lbmFibGUpIHtcclxuICAgICAgLy8gU2V0IHVwIGFuIFNTTCBzZXJ2ZXIgYWxzb1xyXG4gICAgICBsZXQga2V5LCBjZXJ0O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBrZXlcclxuICAgICAgICBrZXkgPSBhd2FpdCByZWFkRmlsZShcclxuICAgICAgICAgIGpvaW4oZ2V0QWJzb2x1dGVQYXRoKHNlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRoKSwgJ3NlcnZlci5rZXknKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgU1NMIGNlcnRpZmljYXRlXHJcbiAgICAgICAgY2VydCA9IGF3YWl0IHJlYWRGaWxlKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmNydCcpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFVuYWJsZSB0byBsb2FkIGtleS9jZXJ0aWZpY2F0ZSBmcm9tIHRoZSAnJHtzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChrZXkgJiYgY2VydCkge1xyXG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcclxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xyXG5cclxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgICAgX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAvLyBMaXN0ZW5cclxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyT3B0aW9ucy5zc2wucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUFMgc2VydmVyXHJcbiAgICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAzLFxyXG4gICAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQUyBzZXJ2ZXIgb24gJHtzZXJ2ZXJPcHRpb25zLmhvc3R9OiR7c2VydmVyT3B0aW9ucy5zc2wucG9ydH0uYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgc2VydmVyT3B0aW9ucy5yYXRlTGltaXRpbmcpO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgdmFsaWRhdGlvbiBoYW5kbGVyXHJcbiAgICB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGhlYWx0aFJvdXRlcyhhcHApO1xyXG4gICAgZXhwb3J0Um91dGVzKGFwcCk7XHJcbiAgICB1aVJvdXRlcyhhcHApO1xyXG4gICAgdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgY2VudHJhbGl6ZWQgZXJyb3IgaGFuZGxlclxyXG4gICAgZXJyb3JNaWRkbGV3YXJlKGFwcCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tzZXJ2ZXJdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHN0YXJ0IHRoZSBzZXJ2ZXIuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbG9zZXMgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzKCkge1xyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBzZXJ2ZXJzIHdvcmtpbmdcclxuICBpZiAoYWN0aXZlU2VydmVycy5zaXplID4gMCkge1xyXG4gICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zaW5nIGFsbCBzZXJ2ZXJzLmApO1xyXG5cclxuICAgIC8vIENsb3NlIGVhY2ggb25lIG9mIHNlcnZlcnNcclxuICAgIGZvciAoY29uc3QgW3BvcnQsIHNlcnZlcl0gb2YgYWN0aXZlU2VydmVycykge1xyXG4gICAgICBzZXJ2ZXIuY2xvc2UoKCkgPT4ge1xyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuZGVsZXRlKHBvcnQpO1xyXG4gICAgICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2VkIHNlcnZlciBvbiBwb3J0OiAke3BvcnR9LmApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0U2VydmVyc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPE9iamVjdD59IFNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFNlcnZlcnMoKSB7XHJcbiAgcmV0dXJuIGFjdGl2ZVNlcnZlcnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRFeHByZXNzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRFeHByZXNzKCkge1xyXG4gIHJldHVybiBleHByZXNzO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEFwcFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RXhwcmVzc30gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEFwcCgpIHtcclxuICByZXR1cm4gYXBwO1xyXG59XHJcblxyXG4vKipcclxuICogRW5hYmxlIHJhdGUgbGltaXRpbmcgZm9yIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVSYXRlTGltaXRpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHJhdGVMaW1pdGluZ09wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgcmF0ZUxpbWl0aW5nYFxyXG4gKiBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZyhyYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZShhcHAsIHJhdGVMaW1pdGluZ09wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogQXBwbHkgbWlkZGxld2FyZShzKSB0byBhIHNwZWNpZmljIHBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1c2VcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHVzZShwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC51c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBHRVQgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAuZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggUE9TVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLnBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoIGVycm9yIGhhbmRsZXJzIHRvIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVyc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhodHRwLlNlcnZlcnxodHRwcy5TZXJ2ZXIpfSBzZXJ2ZXIgLSBUaGUgSFRUUC9IVFRQUyBzZXJ2ZXIgaW5zdGFuY2UuXHJcbiAqL1xyXG5mdW5jdGlvbiBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhzZXJ2ZXIpIHtcclxuICBzZXJ2ZXIub24oJ2NsaWVudEVycm9yJywgKGVycm9yLCBzb2NrZXQpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgMSxcclxuICAgICAgZXJyb3IsXHJcbiAgICAgIGBbc2VydmVyXSBDbGllbnQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX0sIGRlc3Ryb3lpbmcgc29ja2V0LmBcclxuICAgICk7XHJcbiAgICBzb2NrZXQuZGVzdHJveSgpO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTZXJ2ZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdjb25uZWN0aW9uJywgKHNvY2tldCkgPT4ge1xyXG4gICAgc29ja2V0Lm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTb2NrZXQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc3RhcnRTZXJ2ZXIsXHJcbiAgY2xvc2VTZXJ2ZXJzLFxyXG4gIGdldFNlcnZlcnMsXHJcbiAgZ2V0RXhwcmVzcyxcclxuICBnZXRBcHAsXHJcbiAgZW5hYmxlUmF0ZUxpbWl0aW5nLFxyXG4gIHVzZSxcclxuICBnZXQsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgSGFuZGxlcyBncmFjZWZ1bCBzaHV0ZG93biBvZiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLCBlbnN1cmluZ1xyXG4gKiBwcm9wZXIgY2xlYW51cCBvZiByZXNvdXJjZXMgc3VjaCBhcyBicm93c2VyLCBwYWdlcywgc2VydmVycywgYW5kIHRpbWVycy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBraWxsUG9vbCB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGNsZWFyQWxsVGltZXJzIH0gZnJvbSAnLi90aW1lci5qcyc7XHJcbmltcG9ydCB7IGNsb3NlU2VydmVycyB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogQ2xlYW5zIHVwIGZ1bmN0aW9uIHRvIHRyaWdnZXIgYmVmb3JlIGVuZGluZyBwcm9jZXNzIGZvciB0aGUgZ3JhY2VmdWxcclxuICogc2h1dGRvd24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXBcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGV4aXRDb2RlIC0gQW4gZXhpdCBjb2RlIGZvciB0aGUgYHByb2Nlc3MuZXhpdCgpYCBmdW5jdGlvbi5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXAoZXhpdENvZGUpIHtcclxuICAvLyBBd2FpdCBmcmVlaW5nIGFsbCByZXNvdXJjZXNcclxuICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW1xyXG4gICAgLy8gQ2xlYXIgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzXHJcbiAgICBjbGVhckFsbFRpbWVycygpLFxyXG5cclxuICAgIC8vIEdldCBhdmFpbGFibGUgc2VydmVyIGluc3RhbmNlcyAoSFRUUC9IVFRQUykgYW5kIGNsb3NlIHRoZW1cclxuICAgIGNsb3NlU2VydmVycygpLFxyXG5cclxuICAgIC8vIENsb3NlIGFuIGFjdGl2ZSBwb29sIGFsb25nIHdpdGggaXRzIHdvcmtlcnMgYW5kIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgICBraWxsUG9vbCgpXHJcbiAgXSk7XHJcblxyXG4gIC8vIEV4aXQgcHJvY2VzcyB3aXRoIGEgY29ycmVjdCBjb2RlXHJcbiAgcHJvY2Vzcy5leGl0KGV4aXRDb2RlKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNodXRkb3duQ2xlYW5VcFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29yZSBtb2R1bGUgZm9yIGluaXRpYWxpemluZyBhbmQgbWFuYWdpbmcgdGhlIEhpZ2hjaGFydHMgRXhwb3J0XHJcbiAqIFNlcnZlci4gUHJvdmlkZXMgZnVuY3Rpb25hbGl0aWVzIGZvciBjb25maWd1cmluZyBleHBvcnRzLCBzZXR0aW5nIHVwIHNlcnZlclxyXG4gKiBvcGVyYXRpb25zLCBsb2dnaW5nLCBzY3JpcHRzIGNhY2hpbmcsIHJlc291cmNlIHBvb2xpbmcsIGFuZCBncmFjZWZ1bCBwcm9jZXNzXHJcbiAqIGNsZWFudXAuXHJcbiAqL1xyXG5cclxuaW1wb3J0ICdjb2xvcnMnO1xyXG5cclxuaW1wb3J0IHsgY2hlY2tBbmRVcGRhdGVDYWNoZSB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQge1xyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBiYXRjaEV4cG9ydCxcclxuICBzdGFydEV4cG9ydCxcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxufSBmcm9tICcuL2NoYXJ0LmpzJztcclxuaW1wb3J0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHNldE9wdGlvbnMsXHJcbiAgbWVyZ2VPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9uc1xyXG59IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHtcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIGluaXRMb2dnaW5nLFxyXG4gIHNldExvZ0xldmVsLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nXHJcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpbml0UG9vbCwga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzaHV0ZG93bkNsZWFuVXAgfSBmcm9tICcuL3Jlc291cmNlUmVsZWFzZS5qcyc7XHJcbmltcG9ydCBzZXJ2ZXIsIHsgc3RhcnRTZXJ2ZXIgfSBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiB0aGUgY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHJlc291cmNlIHBvb2wgb2NjdXIgZHVyaW5nIHRoaXNcclxuICogc3RhZ2UuIFRoaXMgZnVuY3Rpb24gbXVzdCBiZSBjYWxsZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXRcclxuICogdXAgYSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tT3B0aW9ucyAtIFRoZSBgY3VzdG9tT3B0aW9uc2Agb2JqZWN0LCB3aGljaCBtYXlcclxuICogYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zLiBJZiB0aGUgcHJvdmlkZWQgb3B0aW9ucyBhcmUgcGFydGlhbCxcclxuICogbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgZGVmYXVsdCBnZW5lcmFsIG9wdGlvbnMsIHJldHJpZXZlZFxyXG4gKiB1c2luZyB0aGUgYGdldE9wdGlvbnNgIGZ1bmN0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRFeHBvcnQoY3VzdG9tT3B0aW9ucykge1xyXG4gIC8vIEdldCB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IGNvcHkgYW5kIGV4dGVuZCBpdCB3aXRoIHRoZSBpbmNvbWluZyBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IG1lcmdlT3B0aW9ucyhnZXRPcHRpb25zKGZhbHNlKSwgY3VzdG9tT3B0aW9ucyk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcblxyXG4gIC8vIEluaXQgdGhlIHBvb2xcclxuICBhd2FpdCBpbml0UG9vbChvcHRpb25zLnBvb2wsIG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoZXMgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MsIGVuc3VyaW5nIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlc1xyXG4gKiBhbmQgdGVybWluYXRpb24gb24gZXhpdCBzaWduYWxzLiBIYW5kbGVzICdleGl0JywgJ1NJR0lOVCcsICdTSUdURVJNJ1xyXG4gKiBhbmQgJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnNcclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LmApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0lOVCdcclxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR1RFUk0nXHJcbiAgcHJvY2Vzcy5vbignU0lHVEVSTScsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoMCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoMCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAndW5jYXVnaHRFeGNlcHRpb24nXHJcbiAgcHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCBhc3luYyAoZXJyb3IsIG5hbWUpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIHNlcnZlcixcclxuICBzdGFydFNlcnZlcixcclxuXHJcbiAgLy8gT3B0aW9uc1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgc2V0T3B0aW9ucyxcclxuICBtZXJnZU9wdGlvbnMsXHJcbiAgbWFwVG9OZXdPcHRpb25zLFxyXG5cclxuICAvLyBFeHBvcnRpbmdcclxuICBpbml0RXhwb3J0LFxyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBiYXRjaEV4cG9ydCxcclxuICBzdGFydEV4cG9ydCxcclxuXHJcbiAgLy8gQ2FjaGVcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG5cclxuICAvLyBQb29sXHJcbiAgaW5pdFBvb2wsXHJcbiAga2lsbFBvb2wsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG5cclxuICAvLyBVdGlsc1xyXG4gIHNodXRkb3duQ2xlYW5VcFxyXG59O1xyXG4iXSwibmFtZXMiOlsiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJiYXNlVVJJIiwiZGVlcENvcHkiLCJvYmpBcnIiLCJvYmpBcnJDb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwiT2JqZWN0IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiZml4Q29uc3RyIiwiY29uc3RyIiwiZml4ZWRDb25zdHIiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJpbmNsdWRlcyIsImZpeE91dGZpbGUiLCJ0eXBlIiwib3V0ZmlsZSIsImdldEFic29sdXRlUGF0aCIsInNwbGl0Iiwic2hpZnQiLCJmaXhUeXBlIiwibWltZVR5cGVzIiwiZm9ybWF0cyIsInZhbHVlcyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsInBhdGgiLCJpc0Fic29sdXRlIiwiam9pbiIsImdldEJhc2U2NCIsImlucHV0IiwiQnVmZmVyIiwiZnJvbSIsInRvU3RyaW5nIiwiZ2V0TmV3RGF0ZSIsIkRhdGUiLCJ0cmltIiwiZ2V0TmV3RGF0ZVRpbWUiLCJnZXRUaW1lIiwiaXNPYmplY3QiLCJpdGVtIiwiaXNPYmplY3RFbXB0eSIsImtleXMiLCJsZW5ndGgiLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwic29tZSIsInBhdHRlcm4iLCJ0ZXN0IiwibWVhc3VyZVRpbWUiLCJzdGFydCIsInByb2Nlc3MiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJyb3VuZE51bWJlciIsInZhbHVlIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsIk1hdGgiLCJwb3ciLCJyb3VuZCIsIndyYXBBcm91bmQiLCJjdXN0b21Db2RlIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiaXNDYWxsYmFjayIsImVuZHNXaXRoIiwicmVhZEZpbGVTeW5jIiwic3RhcnRzV2l0aCIsImNvbG9ycyIsImxvZ2dpbmciLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsInBhdGhUb0xvZyIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibG9nIiwiYXJncyIsIm5ld0xldmVsIiwidGV4dHMiLCJsZXZlbCIsInByZWZpeCIsIl9sb2dUb0ZpbGUiLCJjb25zb2xlIiwiYXBwbHkiLCJ1bmRlZmluZWQiLCJjb25jYXQiLCJsb2dXaXRoU3RhY2siLCJlcnJvciIsImN1c3RvbU1lc3NhZ2UiLCJtYWluTWVzc2FnZSIsIm1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInB1c2giLCJpbml0TG9nZ2luZyIsImxvZ2dpbmdPcHRpb25zIiwiZGVzdCIsImZpbGUiLCJzZXRMb2dMZXZlbCIsImVuYWJsZUNvbnNvbGVMb2dnaW5nIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJleGlzdHNTeW5jIiwibWtkaXJTeW5jIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiX2luaXRHbG9iYWxPcHRpb25zIiwiZ2V0T3B0aW9ucyIsImdldFJlZmVyZW5jZSIsInNldE9wdGlvbnMiLCJjdXN0b21PcHRpb25zIiwiY2xpQXJncyIsIm1vZGlmeUdsb2JhbCIsImNvbmZpZ09wdGlvbnMiLCJjbGlPcHRpb25zIiwiX2xvYWRDb25maWdGaWxlIiwiX3BhaXJBcmd1bWVudFZhbHVlIiwiZ2VuZXJhbE9wdGlvbnMiLCJfdXBkYXRlT3B0aW9ucyIsIm1lcmdlT3B0aW9ucyIsIm9yaWdpbmFsT3B0aW9ucyIsIm5ld09wdGlvbnMiLCJlbnRyaWVzIiwibWFwVG9OZXdPcHRpb25zIiwib2xkT3B0aW9ucyIsInByb3BlcnRpZXNDaGFpbiIsInJlZHVjZSIsIm9iaiIsInByb3AiLCJpbmRleCIsImlzQWxsb3dlZENvbmZpZyIsImFsbG93RnVuY3Rpb25zIiwib2JqZWN0Q29uZmlnIiwiZXZhbCIsIkpTT04iLCJzdHJpbmdpZmllZE9wdGlvbnMiLCJfb3B0aW9uc1N0cmluZ2lmeSIsInBhcnNlZE9wdGlvbnMiLCJfIiwibmFtZSIsImNvbmZpZ09wdCIsImN1c3RvbU9wdCIsImNsaU9wdCIsImNvbmZpZ1ZhbCIsImN1c3RvbVZhbCIsImNsaVZhbCIsImVudlZhbCIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJFcnJvciIsImNvbmZpZ0luZGV4IiwiZmluZEluZGV4IiwiYXJnIiwiY29uZmlnRmlsZU5hbWUiLCJpIiwib3B0aW9uIiwiYXN5bmMiLCJmZXRjaCIsInVybCIsInJlcXVlc3RPcHRpb25zIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJfZ2V0UHJvdG9jb2xNb2R1bGUiLCJnZXQiLCJyZXNwb25zZSIsInJlc3BvbnNlRGF0YSIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiaHR0cHMiLCJodHRwIiwiRXhwb3J0RXJyb3IiLCJjb25zdHJ1Y3RvciIsInN0YXR1c0NvZGUiLCJzdXBlciIsInRoaXMiLCJzZXRFcnJvciIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsInNldHVwSGlnaGNoYXJ0cyIsIkhpZ2hjaGFydHMiLCJhbmltT2JqZWN0IiwiZHVyYXRpb24iLCJjcmVhdGVDaGFydCIsIm1lcmdlIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJpc1JlbmRlckNvbXBsZXRlIiwiQ2hhcnQiLCJwcm9jZWVkIiwidXNlck9wdGlvbnMiLCJjYiIsImV4cG9ydGluZyIsImVuYWJsZWQiLCJwbG90T3B0aW9ucyIsInNlcmllcyIsImxhYmVsIiwidG9vbHRpcCIsImFuaW1hdGlvbiIsIm9uSGlnaGNoYXJ0c1JlbmRlciIsImFkZEV2ZW50IiwiU2VyaWVzIiwiY2hhcnQiLCJhZGRpdGlvbmFsT3B0aW9ucyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImRlZmF1bHRPcHRpb25zIiwidGVtcGxhdGUiLCJicm93c2VyIiwiY3JlYXRlQnJvd3NlciIsInB1cHBldGVlckFyZ3MiLCJlbmFibGVkRGVidWciLCJkZWJ1Z09wdGlvbnMiLCJsYXVuY2hPcHRpb25zIiwidXNlckRhdGFEaXIiLCJoYW5kbGVTSUdJTlQiLCJoYW5kbGVTSUdURVJNIiwiaGFuZGxlU0lHSFVQIiwid2FpdEZvckluaXRpYWxQYWdlIiwiZGVmYXVsdFZpZXdwb3J0IiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwic2V0VGltZW91dCIsImNsb3NlQnJvd3NlciIsImNvbm5lY3RlZCIsImNsb3NlIiwibmV3UGFnZSIsInBvb2xSZXNvdXJjZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJfc2V0UGFnZUNvbnRlbnQiLCJfc2V0UGFnZUV2ZW50cyIsImlzQ2xvc2VkIiwiY2xlYXJQYWdlIiwiaGFyZFJlc2V0IiwiZ290byIsIndhaXRVbnRpbCIsImV2YWx1YXRlIiwiYm9keSIsImlubmVySFRNTCIsImlkIiwid29ya0NvdW50IiwiYWRkUGFnZVJlc291cmNlcyIsImN1c3RvbUxvZ2ljT3B0aW9ucyIsImluamVjdGVkUmVzb3VyY2VzIiwiaW5qZWN0ZWRKcyIsImpzIiwiY29udGVudCIsImZpbGVzIiwiaXNMb2NhbCIsImpzUmVzb3VyY2UiLCJhZGRTY3JpcHRUYWciLCJpbmplY3RlZENzcyIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJjc3NSZXNvdXJjZSIsImFkZFN0eWxlVGFnIiwiY2xlYXJQYWdlUmVzb3VyY2VzIiwicmVzb3VyY2UiLCJkaXNwb3NlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJlbGVtZW50IiwicmVtb3ZlIiwic2V0Q29udGVudCIsImNzc1RlbXBsYXRlIiwic3ZnVGVtcGxhdGUiLCJwdXBwZXRlZXJFeHBvcnQiLCJleHBvcnRPcHRpb25zIiwiaXNTVkciLCJfc2V0QXNTdmciLCJfc2V0QXNPcHRpb25zIiwic2l6ZSIsInN2Z0VsZW1lbnQiLCJxdWVyeVNlbGVjdG9yIiwiY2hhcnRIZWlnaHQiLCJiYXNlVmFsIiwiY2hhcnRXaWR0aCIsInN0eWxlIiwiem9vbSIsIm1hcmdpbiIsIngiLCJ5IiwiX2dldENsaXBSZWdpb24iLCJ2aWV3cG9ydEhlaWdodCIsImFicyIsImNlaWwiLCJ2aWV3cG9ydFdpZHRoIiwicmVzdWx0Iiwic2V0Vmlld3BvcnQiLCJkZXZpY2VTY2FsZUZhY3RvciIsIl9jcmVhdGVTVkciLCJfY3JlYXRlSW1hZ2UiLCJfY3JlYXRlUERGIiwiJGV2YWwiLCJnZXRCb3VuZGluZ0NsaWVudFJlY3QiLCJ0cnVuYyIsIm91dGVySFRNTCIsImNsaXAiLCJyYWNlIiwic2NyZWVuc2hvdCIsImVuY29kaW5nIiwiZnVsbFBhZ2UiLCJvcHRpbWl6ZUZvclNwZWVkIiwiY2FwdHVyZUJleW9uZFZpZXdwb3J0IiwicXVhbGl0eSIsIm9taXRCYWNrZ3JvdW5kIiwiX3Jlc29sdmUiLCJlbXVsYXRlTWVkaWFUeXBlIiwicGRmIiwicG9vbFN0YXRzIiwiZXhwb3J0c0F0dGVtcHRlZCIsImV4cG9ydHNQZXJmb3JtZWQiLCJleHBvcnRzRHJvcHBlZCIsImV4cG9ydHNGcm9tU3ZnIiwiZXhwb3J0c0Zyb21PcHRpb25zIiwiZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyIsImV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzIiwidGltZVNwZW50IiwidGltZVNwZW50QXZlcmFnZSIsImluaXRQb29sIiwicG9vbE9wdGlvbnMiLCJQb29sIiwiX2ZhY3RvcnkiLCJhY3F1aXJlVGltZW91dE1pbGxpcyIsImNyZWF0ZVRpbWVvdXRNaWxsaXMiLCJkZXN0cm95VGltZW91dE1pbGxpcyIsImlkbGVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpcyIsInJlYXBJbnRlcnZhbE1pbGxpcyIsInByb3BhZ2F0ZUNyZWF0ZUVycm9yIiwiY2xlYXJTdGF0dXMiLCJfZXZlbnRJZCIsImluaXRpYWxSZXNvdXJjZXMiLCJhY3F1aXJlIiwicHJvbWlzZSIsInJlbGVhc2UiLCJraWxsUG9vbCIsIndvcmtlciIsInVzZWQiLCJkZXN0cm95ZWQiLCJwb3N0V29yayIsIndvcmtlckhhbmRsZSIsImdldFBvb2xJbmZvIiwiYWNxdWlyZUNvdW50ZXIiLCJfcmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsImV4cG9ydFRpbWUiLCJnZXRQb29sU3RhdHMiLCJnZXRQb29sSW5mb0pTT04iLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwibnVtRnJlZSIsImFsbENyZWF0ZWQiLCJwZW5kaW5nQWNxdWlyZXMiLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwZW5kaW5nQ3JlYXRlcyIsIm51bVBlbmRpbmdDcmVhdGVzIiwicGVuZGluZ1ZhbGlkYXRpb25zIiwibnVtUGVuZGluZ1ZhbGlkYXRpb25zIiwicGVuZGluZ0Rlc3Ryb3lzIiwiYWJzb2x1dGVBbGwiLCJjcmVhdGUiLCJ1dWlkIiwicmFuZG9tIiwic3RhcnREYXRlIiwidmFsaWRhdGUiLCJtYWluRnJhbWUiLCJkZXRhY2hlZCIsInJlbW92ZUFsbExpc3RlbmVycyIsInNhbml0aXplIiwiSlNET00iLCJET01QdXJpZnkiLCJBRERfVEFHUyIsInNpbmdsZUV4cG9ydCIsInN0YXJ0RXhwb3J0IiwiZGF0YSIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiYmF0Y2hSZXN1bHRzIiwiYWxsU2V0dGxlZCIsInJlYXNvbiIsImVuZENhbGxiYWNrIiwiZmlsZUNvbnRlbnQiLCJfZXhwb3J0RnJvbVN2ZyIsIl9leHBvcnRGcm9tT3B0aW9ucyIsImdldEFsbG93Q29kZUV4ZWN1dGlvbiIsInNldEFsbG93Q29kZUV4ZWN1dGlvbiIsImlucHV0VG9FeHBvcnQiLCJfcHJlcGFyZUV4cG9ydCIsIl9oYW5kbGVDdXN0b21Mb2dpYyIsIl9oYW5kbGVHbG9iYWxBbmRUaGVtZSIsIl9maW5kQ2hhcnRTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidGltZXJJZHMiLCJhZGRUaW1lciIsImNsZWFyQWxsVGltZXJzIiwiY2xlYXJJbnRlcnZhbCIsImNsZWFyVGltZW91dCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcXVlc3QiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RhdHVzIiwianNvbiIsImVycm9yTWlkZGxld2FyZSIsImFwcCIsInVzZSIsInJhdGVMaW1pdGluZ01pZGRsZXdhcmUiLCJyYXRlTGltaXRpbmdPcHRpb25zIiwibXNnIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwicmF0ZUxpbWl0Iiwid2luZG93TXMiLCJkZWxheU1zIiwiaGFuZGxlciIsImZvcm1hdCIsInNlbmQiLCJkZWZhdWx0Iiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwiSHR0cEVycm9yIiwic2V0U3RhdHVzIiwiY29udGVudFR5cGVNaWRkbGV3YXJlIiwiY29udGVudFR5cGUiLCJoZWFkZXJzIiwicmVxdWVzdEJvZHlNaWRkbGV3YXJlIiwicmVxdWVzdElkIiwiY29ubmVjdGlvbiIsInJlbW90ZUFkZHJlc3MiLCJ2YWxpZGF0ZWRPcHRpb25zIiwicGFyYW1zIiwiZmlsZW5hbWUiLCJ2YWxpZGF0aW9uTWlkZGxld2FyZSIsInBvc3QiLCJyZXZlcnNlZE1pbWUiLCJwbmciLCJqcGVnIiwiZ2lmIiwicmVxdWVzdEV4cG9ydCIsInJlcXVlc3RDb3VudGVyIiwiY29ubmVjdGlvbkFib3J0ZWQiLCJzb2NrZXQiLCJoYWRFcnJvcnMiLCJoZWFkZXIiLCJhdHRhY2htZW50IiwiZXhwb3J0Um91dGVzIiwic2VydmVyU3RhcnRUaW1lIiwicGFja2FnZUZpbGUiLCJzdWNjZXNzUmF0ZXMiLCJyZWNvcmRJbnRlcnZhbCIsIndpbmRvd1NpemUiLCJfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSIsImEiLCJiIiwiX3N0YXJ0U3VjY2Vzc1JhdGUiLCJzZXRJbnRlcnZhbCIsInN0YXRzIiwic3VjY2Vzc1JhdGlvIiwiaGVhbHRoUm91dGVzIiwicGVyaW9kIiwibW92aW5nQXZlcmFnZSIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJzZXJ2ZXJWZXJzaW9uIiwiaGlnaGNoYXJ0c1ZlcnNpb24iLCJhdmVyYWdlRXhwb3J0VGltZSIsImF0dGVtcHRlZEV4cG9ydHMiLCJwZXJmb3JtZWRFeHBvcnRzIiwiZmFpbGVkRXhwb3J0cyIsInN1Y2Vzc1JhdGlvIiwidG9GaXhlZCIsInN2Z0V4cG9ydHMiLCJqc29uRXhwb3J0cyIsInN2Z0V4cG9ydHNBdHRlbXB0cyIsImpzb25FeHBvcnRzQXR0ZW1wdHMiLCJ1aVJvdXRlcyIsInNlbmRGaWxlIiwiYWNjZXB0UmFuZ2VzIiwidmVyc2lvbkNoYW5nZVJvdXRlcyIsImFkbWluVG9rZW4iLCJ0b2tlbiIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJPcHRpb25zIiwidXBsb2FkTGltaXRCeXRlcyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRTaXplIiwiZGlzYWJsZSIsImNvcnMiLCJtZXRob2RzIiwic2V0IiwibGltaXQiLCJ1cmxlbmNvZGVkIiwiZXh0ZW5kZWQiLCJub25lIiwic3RhdGljIiwiaHR0cFNlcnZlciIsImNyZWF0ZVNlcnZlciIsIl9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwibGlzdGVuIiwiY2VydCIsInJlYWRGaWxlIiwiaHR0cHNTZXJ2ZXIiLCJjbG9zZVNlcnZlcnMiLCJkZWxldGUiLCJnZXRTZXJ2ZXJzIiwiZ2V0RXhwcmVzcyIsImdldEFwcCIsImVuYWJsZVJhdGVMaW1pdGluZyIsIm1pZGRsZXdhcmVzIiwic2h1dGRvd25DbGVhblVwIiwiZXhpdENvZGUiLCJleGl0IiwiaW5pdEV4cG9ydCIsIl9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyIsImNvZGUiXSwibWFwcGluZ3MiOiJ3bkJBMkJPLE1BQU1BLFlBQVlDLElBQWFBLGNBQUMsSUFBSUMsSUFBSSxPQUFRLG9CQUFBQyxTQUFBQyxRQUFBLE9BQUFDLGNBQUFDLFlBQUFDLEtBQUFDLHdCQUFBLFdBQUFBLHVCQUFBQyxRQUFBQyxlQUFBRix1QkFBQUcsS0FBQSxJQUFBVCxJQUFBLFlBQUFDLFNBQUFTLFNBQUFMLE9BK0JoRCxTQUFTTSxTQUFTQyxHQUV2QixHQUFlLE9BQVhBLEdBQXFDLGlCQUFYQSxFQUM1QixPQUFPQSxFQUlULE1BQU1DLEVBQWFDLE1BQU1DLFFBQVFILEdBQVUsR0FBSyxHQUdoRCxJQUFLLE1BQU1JLEtBQU9KLEVBQ1pLLE9BQU9DLFVBQVVDLGVBQWVDLEtBQUtSLEVBQVFJLEtBQy9DSCxFQUFXRyxHQUFPTCxTQUFTQyxFQUFPSSxLQUt0QyxPQUFPSCxDQUNULENBMkRPLFNBQVNRLFVBQVVDLEdBQ3hCLElBRUUsTUFBTUMsRUFBYyxHQUFHRCxFQUFPRSxjQUFjQyxRQUFRLFFBQVMsV0FRN0QsTUFMb0IsVUFBaEJGLEdBQ0ZBLEVBQVlDLGNBSVAsQ0FBQyxRQUFTLGFBQWMsV0FBWSxjQUFjRSxTQUN2REgsR0FFRUEsRUFDQSxPQUNSLENBQUksTUFFQSxNQUFPLE9BQ1IsQ0FDSCxDQVlPLFNBQVNJLFdBQVdDLEVBQU1DLEdBTy9CLE1BQU8sR0FMVUMsZ0JBQWdCRCxHQUFXLFNBQ3pDRSxNQUFNLEtBQ05DLFdBR21CSixHQUN4QixDQWFPLFNBQVNLLFFBQVFMLEVBQU1DLEVBQVUsTUFFdEMsTUFBTUssRUFBWSxDQUNoQixZQUFhLE1BQ2IsYUFBYyxPQUNkLGtCQUFtQixNQUNuQixnQkFBaUIsT0FJYkMsRUFBVWxCLE9BQU9tQixPQUFPRixHQUc5QixHQUFJTCxFQUFTLENBQ1gsTUFBTVEsRUFBVVIsRUFBUUUsTUFBTSxLQUFLTyxNQUduQixRQUFaRCxFQUNGVCxFQUFPLE9BQ0VPLEVBQVFULFNBQVNXLElBQVlULElBQVNTLElBQy9DVCxFQUFPUyxFQUVWLENBR0QsT0FBT0gsRUFBVU4sSUFBU08sRUFBUUksTUFBTUMsR0FBTUEsSUFBTVosS0FBUyxLQUMvRCxDQVlPLFNBQVNFLGdCQUFnQlcsR0FDOUIsT0FBT0MsS0FBQUEsV0FBV0QsR0FBUUEsRUFBT0UsS0FBQUEsS0FBSzdDLFlBQVcyQyxFQUNuRCxDQVlPLFNBQVNHLFVBQVVDLEVBQU9qQixHQUUvQixNQUFhLFFBQVRBLEdBQTBCLE9BQVJBLEVBQ2JrQixPQUFPQyxLQUFLRixFQUFPLFFBQVFHLFNBQVMsVUFJdENILENBQ1QsQ0FPTyxTQUFTSSxhQUVkLE9BQU8sSUFBSUMsTUFBT0YsV0FBV2pCLE1BQU0sS0FBSyxHQUFHb0IsTUFDN0MsQ0FPTyxTQUFTQyxpQkFDZCxPQUFPLElBQUlGLE1BQU9HLFNBQ3BCLENBV08sU0FBU0MsU0FBU0MsR0FDdkIsTUFBZ0Qsb0JBQXpDdEMsT0FBT0MsVUFBVThCLFNBQVM1QixLQUFLbUMsRUFDeEMsQ0FXTyxTQUFTQyxjQUFjRCxHQUM1QixNQUNrQixpQkFBVEEsSUFDTnpDLE1BQU1DLFFBQVF3QyxJQUNOLE9BQVRBLEdBQzZCLElBQTdCdEMsT0FBT3dDLEtBQUtGLEdBQU1HLE1BRXRCLENBV08sU0FBU0MsdUJBQXVCSixHQVNyQyxNQVJzQixDQUNwQixtREFDQSx1RUFDQSx3RUFDQSx1RkFDQSxxRUFHbUJLLE1BQU1DLEdBQVlBLEVBQVFDLEtBQUtQLElBQ3RELENBU08sU0FBU1EsY0FDZCxNQUFNQyxFQUFRQyxRQUFRQyxPQUFPQyxTQUM3QixNQUFPLElBQU1DLE9BQU9ILFFBQVFDLE9BQU9DLFNBQVdILEdBQVMsR0FDekQsQ0FZTyxTQUFTSyxZQUFZQyxFQUFPQyxFQUFZLEdBQzdDLE1BQU1DLEVBQWFDLEtBQUtDLElBQUksR0FBSUgsR0FBYSxHQUM3QyxPQUFPRSxLQUFLRSxPQUFPTCxFQUFRRSxHQUFjQSxDQUMzQyxDQTZCTyxTQUFTSSxXQUFXQyxFQUFZQyxFQUFvQkMsR0FBYSxHQUN0RSxHQUFJRixHQUFvQyxpQkFBZkEsRUFHdkIsT0FGQUEsRUFBYUEsRUFBVzFCLFFBRVQ2QixTQUFTLE9BRWZGLEVBQ0hGLFdBQ0VLLEdBQUFBLGFBQWFuRCxnQkFBZ0IrQyxHQUFhLFFBQzFDQyxFQUNBQyxHQUVGLE1BRUhBLElBQ0FGLEVBQVdLLFdBQVcsZUFDckJMLEVBQVdLLFdBQVcsZ0JBQ3RCTCxFQUFXSyxXQUFXLFNBQ3RCTCxFQUFXSyxXQUFXLFVBR2pCLElBQUlMLE9BSU5BLEVBQVdwRCxRQUFRLEtBQU0sR0FFcEMsQ0N2WEEsTUFBTTBELE9BQVMsQ0FBQyxNQUFPLFNBQVUsT0FBUSxPQUFRLFNBRzNDQyxRQUFVLENBRWRDLFdBQVcsRUFDWEMsUUFBUSxFQUNSQyxhQUFhLEVBRWJDLFVBQVcsR0FFWEMsV0FBWSxDQUNWLENBQ0VDLE1BQU8sUUFDUEMsTUFBT1IsT0FBTyxJQUVoQixDQUNFTyxNQUFPLFVBQ1BDLE1BQU9SLE9BQU8sSUFFaEIsQ0FDRU8sTUFBTyxTQUNQQyxNQUFPUixPQUFPLElBRWhCLENBQ0VPLE1BQU8sVUFDUEMsTUFBT1IsT0FBTyxJQUVoQixDQUNFTyxNQUFPLFlBQ1BDLE1BQU9SLE9BQU8sTUFrQmIsU0FBU1MsT0FBT0MsR0FDckIsTUFBT0MsS0FBYUMsR0FBU0YsR0FHdkJKLFdBQUVBLEVBQVVPLE1BQUVBLEdBQVVaLFFBRzlCLEdBQ2UsSUFBYlUsSUFDYyxJQUFiQSxHQUFrQkEsRUFBV0UsR0FBU0EsRUFBUVAsRUFBVy9CLFFBRTFELE9BSUYsTUFBTXVDLEVBQVMsR0FBR2hELGlCQUFpQndDLEVBQVdLLEVBQVcsR0FBR0osV0FHeEROLFFBQVFFLFFBQ1ZZLFdBQVdILEVBQU9FLEdBSWhCYixRQUFRQyxXQUNWYyxRQUFRUCxJQUFJUSxXQUNWQyxFQUNBLENBQUNKLEVBQU9qRCxXQUFXb0MsUUFBUUssV0FBV0ssRUFBVyxHQUFHSCxRQUFRVyxPQUFPUCxHQUd6RSxDQWdCTyxTQUFTUSxhQUFhVCxFQUFVVSxFQUFPQyxHQUU1QyxNQUFNQyxFQUFjRCxHQUFpQkQsRUFBTUcsU0FHckNYLE1BQUVBLEVBQUtQLFdBQUVBLEdBQWVMLFFBRzlCLEdBQWlCLElBQWJVLEdBQWtCQSxFQUFXRSxHQUFTQSxFQUFRUCxFQUFXL0IsT0FDM0QsT0FJRixNQUFNdUMsRUFBUyxHQUFHaEQsaUJBQWlCd0MsRUFBV0ssRUFBVyxHQUFHSixXQUd0RGtCLEVBQWVKLEVBQU1LLE1BR3JCZCxFQUFRLENBQUNXLEdBQ1hFLEdBQ0ZiLEVBQU1lLEtBQUssS0FBTUYsR0FJZnhCLFFBQVFFLFFBQ1ZZLFdBQVdILEVBQU9FLEdBSWhCYixRQUFRQyxXQUNWYyxRQUFRUCxJQUFJUSxXQUNWQyxFQUNBLENBQUNKLEVBQU9qRCxXQUFXb0MsUUFBUUssV0FBV0ssRUFBVyxHQUFHSCxRQUFRVyxPQUFPLENBQ2pFUCxFQUFNL0QsUUFBUW1ELE9BQU9XLEVBQVcsT0FDN0JDLElBSVgsQ0FTTyxTQUFTZ0IsWUFBWUMsR0FFMUIsTUFBTWhCLE1BQUVBLEVBQUtpQixLQUFFQSxFQUFJQyxLQUFFQSxFQUFJN0IsVUFBRUEsRUFBU0MsT0FBRUEsR0FBVzBCLEVBR2pERyxZQUFZbkIsR0FHWm9CLHFCQUFxQi9CLEdBR3JCZ0Msa0JBQWtCSixFQUFNQyxFQUFNNUIsRUFDaEMsQ0FVTyxTQUFTNkIsWUFBWW5CLEdBQ3RCQSxHQUFTLEdBQUtBLEdBQVNaLFFBQVFLLFdBQVcvQixTQUM1QzBCLFFBQVFZLE1BQVFBLEVBRXBCLENBU08sU0FBU29CLHFCQUFxQi9CLEdBRW5DRCxRQUFRQyxVQUFZQSxDQUN0QixDQVdPLFNBQVNnQyxrQkFBa0JKLEVBQU1DLEVBQU01QixHQUU1Q0YsUUFBUUUsT0FBU0EsRUFHYkEsSUFDRkYsUUFBUTZCLEtBQU9BLEVBQ2Y3QixRQUFROEIsS0FBT0EsRUFFbkIsQ0FZQSxTQUFTaEIsV0FBV0gsRUFBT0UsR0FDcEJiLFFBQVFHLGVBRVYrQixjQUFXeEYsZ0JBQWdCc0QsUUFBUTZCLFFBQ2xDTSxHQUFBQSxVQUFVekYsZ0JBQWdCc0QsUUFBUTZCLE9BR3BDN0IsUUFBUUksVUFBWTFELGdCQUFnQmEsS0FBSUEsS0FBQ3lDLFFBQVE2QixLQUFNN0IsUUFBUThCLE9BSS9EOUIsUUFBUUcsYUFBYyxHQUl4QmlDLEdBQVVBLFdBQ1JwQyxRQUFRSSxVQUNSLENBQUNTLEdBQVFLLE9BQU9QLEdBQU9wRCxLQUFLLEtBQU8sTUFDbEM2RCxJQUNLQSxHQUFTcEIsUUFBUUUsUUFBVUYsUUFBUUcsY0FDckNILFFBQVFFLFFBQVMsRUFDakJGLFFBQVFHLGFBQWMsRUFDdEJnQixhQUFhLEVBQUdDLEVBQU8seUNBQ3hCLEdBR1AsQ0NyT08sTUFBTWlCLGNBQWdCLENBQzNCQyxVQUFXLENBQ1Q3QixLQUFNLENBQ0p2QixNQUFPLENBQ0wsbUNBQ0Esa0JBQ0EsMENBQ0EsMkJBQ0Esa0NBQ0Esa0NBQ0Esd0NBQ0EsMkNBQ0EscUJBQ0EsNEJBQ0EsMkNBQ0EsdURBQ0EsNkJBQ0EseUJBQ0EsMEJBQ0EsK0JBQ0EsdUJBQ0EsdUZBQ0EseUJBQ0Esb0NBQ0Esb0JBQ0EsMEJBQ0EsOENBQ0EsMkJBQ0EsMEJBQ0EsNkJBQ0EsbUNBQ0Esd0NBQ0EsbUNBQ0EsMkJBQ0Esa0NBQ0EsdUJBQ0EsaUJBQ0EseUJBQ0EsOEJBQ0Esb0JBQ0EsMkJBQ0EsZUFDQSw2QkFDQSxpQkFDQSxhQUNBLGVBQ0Esc0JBQ0EsY0FDQSx5QkFDQSxvQkFDQSx1QkFFRnFELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLGlCQUNUQyxRQUFTLGdCQUNUQyxZQUFhLCtCQUNiQyxjQUFlLENBQ2JuRyxLQUFNLE9BQ05vRyxVQUFXLE9BSWpCQyxXQUFZLENBQ1ZDLFFBQVMsQ0FDUDVELE1BQU8sU0FDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHFCQUNURSxZQUFhLHFCQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFNBR1Z1RyxPQUFRLENBQ043RCxNQUFPLDhCQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMscUJBQ1RFLFlBQWEsaUNBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sU0FHVndHLFdBQVksQ0FDVjlELE9BQU8sRUFDUHFELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLHlCQUNURSxZQUFhLGtEQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1Z5RyxVQUFXLENBQ1QvRCxNQUFPLFNBQ1BxRCxNQUFPLENBQUMsVUFDUkMsUUFBUyx3QkFDVEUsWUFBYSwrQ0FDYkMsY0FBZSxDQUNibkcsS0FBTSxTQUdWMEcsWUFBYSxDQUNYaEUsTUFBTyxDQUFDLGFBQWMsa0JBQW1CLGlCQUN6Q3FELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLDBCQUNURSxZQUFhLG1DQUNiQyxjQUFlLENBQ2JuRyxLQUFNLGNBQ04yRyxhQUFjLDBEQUdsQkMsY0FBZSxDQUNibEUsTUFBTyxDQUNMLFFBQ0EsTUFDQSxRQUNBLFlBQ0EsdUJBQ0EsZ0JBRUEsZUFDQSxRQUNBLE9BQ0EsYUFDQSxtQkFDQSxlQUNBLGNBQ0EsVUFDQSxVQUNBLGNBQ0EsV0FDQSxVQUNBLFlBQ0EsY0FDQSxZQUNBLHNCQUNBLFNBQ0EsU0FDQSxXQUNBLGFBQ0EsWUFDQSxlQUNBLHlCQUNBLFNBQ0EsZUFDQSxZQUNBLGtCQUNBLFNBQ0EsY0FDQSxtQkFDQSxlQUNBLGtCQUNBLGNBQ0EsZUFFQSxjQUNBLFdBQ0EsZUFDQSxXQUNBLFNBQ0EsT0FDQSxXQUNBLFlBQ0EsU0FDQSxxQkFDQSxhQUNBLFdBQ0EsV0FDQSxXQUNBLFdBQ0EsZUFDQSxVQUNBLGtCQUNBLG9CQUNBLGFBQ0EsVUFDQSxjQUNBLFlBQ0EsWUFFRnFELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLDRCQUNURSxZQUFhLHFDQUNiQyxjQUFlLENBQ2JuRyxLQUFNLGNBQ04yRyxhQUFjLDBEQUdsQkUsaUJBQWtCLENBQ2hCbkUsTUFBTyxDQUFDLGtCQUNScUQsTUFBTyxDQUFDLFlBQ1JDLFFBQVMsK0JBQ1RFLFlBQWEsd0NBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sY0FDTjJHLGFBQWMsMERBR2xCRyxjQUFlLENBQ2JwRSxNQUFPLENBQ0wsd0VBQ0Esa0dBRUZxRCxNQUFPLENBQUMsWUFDUkMsUUFBUyw0QkFDVEUsWUFBYSxxREFDYkMsY0FBZSxDQUNibkcsS0FBTSxPQUNOb0csVUFBVyxPQUlqQlcsT0FBUSxDQUNOQyxPQUFRLENBQ050RSxNQUFPLEtBQ1BxRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQkFDVEUsWUFDRSwrREFDRkMsY0FBZSxDQUNibkcsS0FBTSxTQUdWaUgsTUFBTyxDQUNMdkUsTUFBTyxLQUNQcUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsZUFDVEUsWUFDRSxtRUFDRkMsY0FBZSxDQUNibkcsS0FBTSxTQUdWa0gsUUFBUyxDQUNQeEUsTUFBTyxLQUNQcUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsaUJBQ1RFLFlBQWEsK0JBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sU0FHVm1ILElBQUssQ0FDSHpFLE1BQU8sS0FDUHFELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLGFBQ1RFLFlBQWEsbURBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sU0FHVm9ILE1BQU8sQ0FDTDFFLE1BQU8sS0FDUHFELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLGVBQ1RFLFlBQ0UsZ0VBQ0ZDLGNBQWUsQ0FDYm5HLEtBQU0sU0FHVkMsUUFBUyxDQUNQeUMsTUFBTyxLQUNQcUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsaUJBQ1RFLFlBQ0UscUZBQ0ZDLGNBQWUsQ0FDYm5HLEtBQU0sU0FHVkEsS0FBTSxDQUNKMEMsTUFBTyxNQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsY0FDVEUsWUFBYSxvREFDYkMsY0FBZSxDQUNibkcsS0FBTSxTQUNOcUgsS0FBTSxlQUNOQyxRQUFTLENBQUMsTUFBTyxPQUFRLE1BQU8sU0FHcEM1SCxPQUFRLENBQ05nRCxNQUFPLFFBQ1BxRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxnQkFDVEUsWUFDRSx1RUFDRkMsY0FBZSxDQUNibkcsS0FBTSxTQUNOcUgsS0FBTSxpQkFDTkMsUUFBUyxDQUFDLFFBQVMsYUFBYyxXQUFZLGdCQUdqREMsSUFBSyxDQUNIN0UsT0FBTyxFQUNQcUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsYUFDVEUsWUFDRSxvRkFDRkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWd0gsV0FBWSxDQUNWOUUsT0FBTyxFQUNQcUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMscUJBQ1RFLFlBQ0UsMEVBQ0ZDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVnlILE9BQVEsQ0FDTi9FLE1BQU8sS0FDUHFELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLGdCQUNURSxZQUFhLHlEQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1YwSCxNQUFPLENBQ0xoRixNQUFPLEtBQ1BxRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxlQUNURSxZQUFhLHdEQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1YySCxNQUFPLENBQ0xqRixNQUFPLEtBQ1BxRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxlQUNURSxZQUNFLGdGQUNGQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1Y0SCxjQUFlLENBQ2JsRixNQUFPLElBQ1BxRCxNQUFPLENBQUMsVUFDUkMsUUFBUyx3QkFDVEUsWUFBYSxrREFDYkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWNkgsYUFBYyxDQUNabkYsTUFBTyxJQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsaURBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVjhILGFBQWMsQ0FDWnBGLE1BQU8sRUFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHVCQUNURSxZQUNFLHlFQUNGQyxjQUFlLENBQ2JuRyxLQUFNLFNBQ04rSCxJQUFLLEdBQ0xDLElBQUssSUFHVEMsY0FBZSxDQUNidkYsTUFBTyxLQUNQcUQsTUFBTyxDQUFDLFNBQVUsU0FBVSxRQUM1QkMsUUFBUyx3QkFDVEUsWUFDRSxtRkFDRkMsY0FBZSxDQUNibkcsS0FBTSxTQUdWa0ksYUFBYyxDQUNaeEYsTUFBTyxLQUNQcUQsTUFBTyxDQUFDLFNBQVUsU0FBVSxRQUM1QkMsUUFBUyx1QkFDVEUsWUFDRSxrRkFDRkMsY0FBZSxDQUNibkcsS0FBTSxTQUdWbUkscUJBQXNCLENBQ3BCekYsTUFBTyxLQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsK0JBQ1RFLFlBQWEsNkNBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sWUFJWm9JLFlBQWEsQ0FDWEMsbUJBQW9CLENBQ2xCM0YsT0FBTyxFQUNQcUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsb0NBQ1RFLFlBQ0UsbUVBQ0ZDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVmtELG1CQUFvQixDQUNsQlIsT0FBTyxFQUNQcUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsb0NBQ1RFLFlBQ0Usa0ZBQ0ZDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVmlELFdBQVksQ0FDVlAsTUFBTyxLQUNQcUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsMkJBQ1RFLFlBQ0UsdUhBQ0ZDLGNBQWUsQ0FDYm5HLEtBQU0sU0FHVnNJLFNBQVUsQ0FDUjVGLE1BQU8sS0FDUHFELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLHdCQUNURSxZQUNFLGtGQUNGQyxjQUFlLENBQ2JuRyxLQUFNLFNBR1Z1SSxVQUFXLENBQ1Q3RixNQUFPLEtBQ1BxRCxNQUFPLENBQUMsU0FBVSxTQUFVLFFBQzVCQyxRQUFTLHlCQUNURSxZQUNFLHNHQUNGQyxjQUFlLENBQ2JuRyxLQUFNLFNBR1Z3SSxXQUFZLENBQ1Y5RixNQUFPLEtBQ1BxRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUywyQkFDVHlDLFdBQVksV0FDWnZDLFlBQWEsK0NBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sU0FHVjBJLGFBQWMsQ0FDWmhHLE1BQU8sS0FDUHFELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLDZCQUNURSxZQUNFLCtEQUNGQyxjQUFlLENBQ2JuRyxLQUFNLFVBSVoySSxPQUFRLENBQ05DLE9BQVEsQ0FDTmxHLE9BQU8sRUFDUHFELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGdCQUNUQyxRQUFTLGVBQ1RDLFlBQWEsOEJBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVjZJLEtBQU0sQ0FDSm5HLE1BQU8sVUFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGNBQ1RFLFlBQWEseUJBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sU0FHVjhJLEtBQU0sQ0FDSnBHLE1BQU8sS0FDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGNBQ1RFLFlBQWEsNkJBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVitJLFlBQWEsQ0FDWHJHLE1BQU8sRUFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHNCQUNURSxZQUFhLGtDQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1ZnSixhQUFjLENBQ1p0RyxPQUFPLEVBQ1BxRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxzQkFDVEMsUUFBUyxxQkFDVEMsWUFDRSwwRUFDRkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWaUosTUFBTyxDQUNMSixLQUFNLENBQ0puRyxNQUFPLEtBQ1BxRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxvQkFDVEMsUUFBUyxZQUNUQyxZQUFhLDBDQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFNBR1Y4SSxLQUFNLENBQ0pwRyxNQUFPLEtBQ1BxRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxvQkFDVEMsUUFBUyxZQUNUQyxZQUFhLDBDQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1ZrSixRQUFTLENBQ1B4RyxNQUFPLElBQ1BxRCxNQUFPLENBQUMsVUFDUkMsUUFBUyx1QkFDVEMsUUFBUyxlQUNUQyxZQUNFLDhEQUNGQyxjQUFlLENBQ2JuRyxLQUFNLFlBSVptSixhQUFjLENBQ1pQLE9BQVEsQ0FDTmxHLE9BQU8sRUFDUHFELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLDhCQUNUQyxRQUFTLHFCQUNUQyxZQUFhLGtEQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1ZvSixZQUFhLENBQ1gxRyxNQUFPLEdBQ1BxRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxvQ0FDVHlDLFdBQVksWUFDWnZDLFlBQWEsZ0RBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVnFKLE9BQVEsQ0FDTjNHLE1BQU8sRUFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLDhCQUNURSxZQUFhLDJDQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1ZzSixNQUFPLENBQ0w1RyxNQUFPLEVBQ1BxRCxNQUFPLENBQUMsVUFDUkMsUUFBUyw2QkFDVEUsWUFDRSx1RUFDRkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWdUosV0FBWSxDQUNWN0csT0FBTyxFQUNQcUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsbUNBQ1RFLFlBQWEsc0RBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVndKLFFBQVMsQ0FDUDlHLE1BQU8sS0FDUHFELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLGdDQUNURSxZQUFhLHdEQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFNBR1Z5SixVQUFXLENBQ1QvRyxNQUFPLEtBQ1BxRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxrQ0FDVEUsWUFBYSx3REFDYkMsY0FBZSxDQUNibkcsS0FBTSxVQUlaMEosSUFBSyxDQUNIZCxPQUFRLENBQ05sRyxPQUFPLEVBQ1BxRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxvQkFDVEMsUUFBUyxZQUNUQyxZQUFhLG1DQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1YySixNQUFPLENBQ0xqSCxPQUFPLEVBQ1BxRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxtQkFDVEMsUUFBUyxXQUNUd0MsV0FBWSxVQUNadkMsWUFBYSxnREFDYkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWOEksS0FBTSxDQUNKcEcsTUFBTyxJQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsa0JBQ1RDLFFBQVMsVUFDVEMsWUFBYSwwQkFDYkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWNEosU0FBVSxDQUNSbEgsTUFBTyxLQUNQcUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsdUJBQ1RDLFFBQVMsY0FDVHdDLFdBQVksVUFDWnZDLFlBQWEsdUNBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FLZDZKLEtBQU0sQ0FDSkMsV0FBWSxDQUNWcEgsTUFBTyxFQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsbUJBQ1RFLFlBQWEsc0RBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVitKLFdBQVksQ0FDVnJILE1BQU8sRUFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLG1CQUNUeUMsV0FBWSxVQUNadkMsWUFBYSwwQ0FDYkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWZ0ssVUFBVyxDQUNUdEgsTUFBTyxHQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsa0JBQ1RFLFlBQWEsd0RBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVmlLLGVBQWdCLENBQ2R2SCxNQUFPLElBQ1BxRCxNQUFPLENBQUMsVUFDUkMsUUFBUyx1QkFDVEUsWUFBYSxtREFDYkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWa0ssY0FBZSxDQUNieEgsTUFBTyxJQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsc0JBQ1RFLFlBQWEsa0RBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVm1LLGVBQWdCLENBQ2R6SCxNQUFPLElBQ1BxRCxNQUFPLENBQUMsVUFDUkMsUUFBUyx1QkFDVEUsWUFBYSxvREFDYkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWb0ssWUFBYSxDQUNYMUgsTUFBTyxJQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsb0JBQ1RFLFlBQWEsd0RBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVnFLLG9CQUFxQixDQUNuQjNILE1BQU8sSUFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLDZCQUNURSxZQUNFLHdFQUNGQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1ZzSyxlQUFnQixDQUNkNUgsTUFBTyxJQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQ0UsK0RBQ0ZDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVmdKLGFBQWMsQ0FDWnRHLE9BQU8sRUFDUHFELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG9CQUNUQyxRQUFTLG1CQUNUQyxZQUFhLDZDQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFlBSVp3RCxRQUFTLENBQ1BZLE1BQU8sQ0FDTDFCLE1BQU8sRUFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGdCQUNUQyxRQUFTLFdBQ1RDLFlBQWEsMEJBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sU0FDTitDLE1BQU8sRUFDUGdGLElBQUssRUFDTEMsSUFBSyxJQUdUMUMsS0FBTSxDQUNKNUMsTUFBTywrQkFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGVBQ1RDLFFBQVMsVUFDVEMsWUFDRSw4REFDRkMsY0FBZSxDQUNibkcsS0FBTSxTQUdWcUYsS0FBTSxDQUNKM0MsTUFBTyxNQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsZUFDVEMsUUFBUyxVQUNUQyxZQUFhLDBEQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFNBR1Z5RCxVQUFXLENBQ1RmLE9BQU8sRUFDUHFELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLHFCQUNUQyxRQUFTLGVBQ1RDLFlBQWEsc0NBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVjBELE9BQVEsQ0FDTmhCLE9BQU8sRUFDUHFELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGtCQUNUQyxRQUFTLFlBQ1RDLFlBQWEsd0NBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sWUFJWnVLLEdBQUksQ0FDRjNCLE9BQVEsQ0FDTmxHLE9BQU8sRUFDUHFELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLFlBQ1RDLFFBQVMsV0FDVEMsWUFBYSxtREFDYkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWd0ssTUFBTyxDQUNMOUgsTUFBTyxJQUNQcUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsV0FDVEMsUUFBUyxVQUNUQyxZQUFhLGdDQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFVBSVp5SyxNQUFPLENBQ0xDLFFBQVMsQ0FDUGhJLE1BQU8sYUFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGlCQUNURSxZQUFhLCtCQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFNBR1YySyxxQkFBc0IsQ0FDcEJqSSxPQUFPLEVBQ1BxRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxnQ0FDVEUsWUFBYSxpREFDYkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWNEssT0FBUSxDQUNObEksT0FBTyxFQUNQcUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsZ0JBQ1RFLFlBQWEsK0NBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVjZLLGNBQWUsQ0FDYm5JLE9BQU8sRUFDUHFELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLHdCQUNURSxZQUFhLG9EQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1Y4SyxpQkFBa0IsQ0FDaEJwSSxPQUFPLEVBQ1BxRCxNQUFPLENBQUMsV0FDUkMsUUFBUywyQkFDVEUsWUFBYSx5REFDYkMsY0FBZSxDQUNibkcsS0FBTSxZQUlaK0ssTUFBTyxDQUNMbkMsT0FBUSxDQUNObEcsT0FBTyxFQUNQcUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsZUFDVEMsUUFBUyxjQUNUQyxZQUFhLDREQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1ZnTCxTQUFVLENBQ1J0SSxPQUFPLEVBQ1BxRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxpQkFDVEUsWUFDRSw2RUFDRkMsY0FBZSxDQUNibkcsS0FBTSxXQUdWaUwsU0FBVSxDQUNSdkksT0FBTyxFQUNQcUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsaUJBQ1RFLFlBQWEsK0NBQ2JDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVmtMLGdCQUFpQixDQUNmeEksT0FBTyxFQUNQcUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsMEJBQ1RFLFlBQ0UscUVBQ0ZDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVm1MLE9BQVEsQ0FDTnpJLE9BQU8sRUFDUHFELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGVBQ1RFLFlBQ0Usa0ZBQ0ZDLGNBQWUsQ0FDYm5HLEtBQU0sV0FHVm9MLE9BQVEsQ0FDTjFJLE1BQU8sRUFDUHFELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGdCQUNURSxZQUFhLDREQUNiQyxjQUFlLENBQ2JuRyxLQUFNLFdBR1ZxTCxjQUFlLENBQ2IzSSxNQUFPLEtBQ1BxRCxNQUFPLENBQUMsVUFDUkMsUUFBUyx1QkFDVEUsWUFBYSwwQkFDYkMsY0FBZSxDQUNibkcsS0FBTSxhQU9Ec0wsWUFBY0MsbUJBQW1CMUYsZUFHakMyRixjQUFnQkMscUJBQXFCNUYsZUFvQmxELFNBQVMwRixtQkFBbUJHLEVBQVFKLEVBQWMsQ0FBQSxFQUFJSyxFQUFZLElBcUJoRSxPQXBCQXRNLE9BQU93QyxLQUFLNkosR0FBUUUsU0FBU3hNLElBRTNCLE1BQU15TSxFQUFRSCxFQUFPdE0sUUFHTSxJQUFoQnlNLEVBQU1uSixNQUVmNkksbUJBQW1CTSxFQUFPUCxFQUFhLEdBQUdLLEtBQWF2TSxNQUd2RGtNLEVBQVlPLEVBQU01RixTQUFXN0csR0FBTyxHQUFHdU0sS0FBYXZNLElBQU0wTSxVQUFVLFFBRzNDckgsSUFBckJvSCxFQUFNcEQsYUFDUjZDLEVBQVlPLEVBQU1wRCxZQUFjLEdBQUdrRCxLQUFhdk0sSUFBTTBNLFVBQVUsSUFFbkUsSUFJSVIsQ0FDVCxDQWlCQSxTQUFTRyxxQkFBcUJDLEVBQVFGLEVBQWdCLElBa0JwRCxPQWpCQW5NLE9BQU93QyxLQUFLNkosR0FBUUUsU0FBU3hNLElBRTNCLE1BQU15TSxFQUFRSCxFQUFPdE0sUUFHTSxJQUFoQnlNLEVBQU05RixNQUVmMEYscUJBQXFCSSxFQUFPTCxHQUd4QkssRUFBTTlGLE1BQU1qRyxTQUFTLFdBQ3ZCMEwsRUFBY3RHLEtBQUs5RixFQUV0QixJQUlJb00sQ0FDVCxDQ3JoQ0FPLE9BQU9MLFNBSVAsTUFBTU0sRUFBSSxDQUdSQyxNQUFRQyxHQUNOQyxJQUFDQSxFQUNFQyxTQUNBQyxXQUFXM0osR0FDVkEsRUFDR3ZDLE1BQU0sS0FDTm1NLEtBQUs1SixHQUFVQSxFQUFNbkIsU0FDckJnTCxRQUFRN0osR0FBVXdKLEVBQVlwTSxTQUFTNEMsT0FFM0MySixXQUFXM0osR0FBV0EsRUFBTVosT0FBU1ksT0FBUStCLElBSWxEK0gsUUFBUyxJQUNQTCxJQUFDQSxFQUNFTSxLQUFLLENBQUMsT0FBUSxRQUFTLEtBQ3ZCSixXQUFXM0osR0FBcUIsS0FBVkEsRUFBeUIsU0FBVkEsT0FBbUIrQixJQUk3RGdJLEtBQU9qTSxHQUNMMkwsSUFBQ0EsRUFDRU0sS0FBSyxJQUFJak0sRUFBUSxLQUNqQjZMLFdBQVczSixHQUFxQixLQUFWQSxFQUFlQSxPQUFRK0IsSUFJbEQySCxPQUFRLElBQ05ELElBQUNBLEVBQ0VDLFNBQ0E3SyxPQUNBbUwsUUFDRWhLLElBQ0UsQ0FBQyxRQUFTLFlBQWEsT0FBUSxPQUFPNUMsU0FBUzRDLElBQ3RDLEtBQVZBLElBQ0RBLElBQVcsQ0FDVnFDLFFBQVMsbURBQW1EckMsU0FHL0QySixXQUFXM0osR0FBcUIsS0FBVkEsRUFBZUEsT0FBUStCLElBSWxEa0ksWUFBYSxJQUNYUixJQUFDQSxFQUNFQyxTQUNBN0ssT0FDQW1MLFFBQ0VoSyxHQUNXLEtBQVZBLElBQWtCa0ssTUFBTUMsV0FBV25LLEtBQVdtSyxXQUFXbkssR0FBUyxJQUNuRUEsSUFBVyxDQUNWcUMsUUFBUyxxREFBcURyQyxTQUdqRTJKLFdBQVczSixHQUFxQixLQUFWQSxFQUFlbUssV0FBV25LLFFBQVMrQixJQUk5RHFJLGVBQWdCLElBQ2RYLElBQUNBLEVBQ0VDLFNBQ0E3SyxPQUNBbUwsUUFDRWhLLEdBQ1csS0FBVkEsSUFBa0JrSyxNQUFNQyxXQUFXbkssS0FBV21LLFdBQVduSyxJQUFVLElBQ3BFQSxJQUFXLENBQ1ZxQyxRQUFTLHlEQUF5RHJDLFNBR3JFMkosV0FBVzNKLEdBQXFCLEtBQVZBLEVBQWVtSyxXQUFXbkssUUFBUytCLEtBR25Ec0ksT0FBU1osSUFBQ0EsRUFBQ2EsT0FBTyxDQUU3QkMsZUFBZ0JqQixFQUFFSSxTQUdsQmMsbUJBQW9CZixJQUFDQSxFQUNsQkMsU0FDQTdLLE9BQ0FtTCxRQUNFaEssR0FBVSw2QkFBNkJSLEtBQUtRLElBQW9CLEtBQVZBLElBQ3REQSxJQUFXLENBQ1ZxQyxRQUFTLDRGQUE0RnJDLFNBR3hHMkosV0FBVzNKLEdBQXFCLEtBQVZBLEVBQWVBLE9BQVErQixJQUNoRDBJLG1CQUFvQmhCLElBQUNBLEVBQ2xCQyxTQUNBN0ssT0FDQW1MLFFBQ0VoSyxHQUNDQSxFQUFNWSxXQUFXLGFBQ2pCWixFQUFNWSxXQUFXLFlBQ1AsS0FBVlosSUFDREEsSUFBVyxDQUNWcUMsUUFBUyw2RkFBNkZyQyxTQUd6RzJKLFdBQVczSixHQUFxQixLQUFWQSxFQUFlQSxPQUFRK0IsSUFDaEQySSx1QkFBd0JwQixFQUFFUSxVQUMxQmEsc0JBQXVCckIsRUFBRUksU0FDekJrQix1QkFBd0J0QixFQUFFSSxTQUMxQm1CLHdCQUF5QnZCLEVBQUVDLE1BQU1wRyxjQUFjUSxXQUFXSyxZQUFZaEUsT0FDdEU4SywwQkFBMkJ4QixFQUFFQyxNQUMzQnBHLGNBQWNRLFdBQVdPLGNBQWNsRSxPQUV6QytLLDZCQUE4QnpCLEVBQUVDLE1BQzlCcEcsY0FBY1EsV0FBV1EsaUJBQWlCbkUsT0FFNUNnTCwwQkFBMkIxQixFQUFFQyxNQUMzQnBHLGNBQWNRLFdBQVdTLGNBQWNwRSxPQUl6Q2lMLGNBQWUzQixFQUFFSSxTQUNqQndCLGFBQWM1QixFQUFFSSxTQUNoQnlCLGVBQWdCN0IsRUFBRUksU0FDbEIwQixXQUFZOUIsRUFBRUksU0FDZDJCLGFBQWMvQixFQUFFSSxTQUNoQjRCLGVBQWdCaEMsRUFBRUksU0FDbEI2QixZQUFhakMsRUFBRVMsS0FBSyxDQUFDLE9BQVEsTUFBTyxNQUFPLFFBQzNDeUIsY0FBZWxDLEVBQUVTLEtBQUssQ0FBQyxRQUFTLGFBQWMsV0FBWSxlQUMxRDBCLFdBQVluQyxFQUFFUSxVQUNkNEIsbUJBQW9CcEMsRUFBRVEsVUFDdEI2QixjQUFlckMsRUFBRVcsY0FDakIyQixhQUFjdEMsRUFBRVcsY0FDaEI0QixhQUFjdkMsRUFBRVcsY0FDaEI2QixzQkFBdUJ4QyxFQUFFVyxjQUN6QjhCLHFCQUFzQnpDLEVBQUVXLGNBQ3hCK0IscUJBQXNCMUMsRUFBRVcsY0FDeEJnQyxzQkFBdUIzQyxFQUFFSSxTQUN6QndDLHFCQUFzQjVDLEVBQUVJLFNBQ3hCeUMsNkJBQThCN0MsRUFBRWMsaUJBR2hDZ0Msa0NBQW1DOUMsRUFBRVEsVUFDckN1QyxrQ0FBbUMvQyxFQUFFUSxVQUNyQ3dDLHlCQUEwQmhELEVBQUVJLFNBQzVCNkMsc0JBQXVCakQsRUFBRUksU0FDekI4Qyx1QkFBd0JsRCxFQUFFSSxTQUMxQitDLHlCQUEwQm5ELEVBQUVJLFNBQzVCZ0QsMkJBQTRCcEQsRUFBRUksU0FHOUJpRCxjQUFlckQsRUFBRVEsVUFDakI4QyxZQUFhdEQsRUFBRUksU0FDZm1ELFlBQWF2RCxFQUFFVyxjQUNmNkMsb0JBQXFCeEQsRUFBRVcsY0FDdkI4QyxvQkFBcUJ6RCxFQUFFUSxVQUd2QmtELGtCQUFtQjFELEVBQUVJLFNBQ3JCdUQsa0JBQW1CM0QsRUFBRVcsY0FDckJpRCxxQkFBc0I1RCxFQUFFYyxpQkFHeEIrQyw0QkFBNkI3RCxFQUFFUSxVQUMvQnNELGtDQUFtQzlELEVBQUVjLGlCQUNyQ2lELDRCQUE2Qi9ELEVBQUVjLGlCQUMvQmtELDJCQUE0QmhFLEVBQUVjLGlCQUM5Qm1ELGlDQUFrQ2pFLEVBQUVRLFVBQ3BDMEQsOEJBQStCbEUsRUFBRUksU0FDakMrRCxnQ0FBaUNuRSxFQUFFSSxTQUduQ2dFLGtCQUFtQnBFLEVBQUVRLFVBQ3JCNkQsaUJBQWtCckUsRUFBRVEsVUFDcEI4RCxnQkFBaUJ0RSxFQUFFVyxjQUNuQjRELHFCQUFzQnZFLEVBQUVJLFNBR3hCb0UsaUJBQWtCeEUsRUFBRWMsaUJBQ3BCMkQsaUJBQWtCekUsRUFBRWMsaUJBQ3BCNEQsZ0JBQWlCMUUsRUFBRVcsY0FDbkJnRSxxQkFBc0IzRSxFQUFFYyxpQkFDeEI4RCxvQkFBcUI1RSxFQUFFYyxpQkFDdkIrRCxxQkFBc0I3RSxFQUFFYyxpQkFDeEJnRSxrQkFBbUI5RSxFQUFFYyxpQkFDckJpRSwyQkFBNEIvRSxFQUFFYyxpQkFDOUJrRSxxQkFBc0JoRixFQUFFYyxpQkFDeEJtRSxrQkFBbUJqRixFQUFFUSxVQUdyQjBFLGNBQWUvRSxJQUFDQSxFQUNiQyxTQUNBN0ssT0FDQW1MLFFBQ0VoSyxHQUNXLEtBQVZBLElBQ0VrSyxNQUFNQyxXQUFXbkssS0FDakJtSyxXQUFXbkssSUFBVSxHQUNyQm1LLFdBQVduSyxJQUFVLElBQ3hCQSxJQUFXLENBQ1ZxQyxRQUFTLG1HQUFtR3JDLFNBRy9HMkosV0FBVzNKLEdBQXFCLEtBQVZBLEVBQWVtSyxXQUFXbkssUUFBUytCLElBQzVEME0sYUFBY25GLEVBQUVJLFNBQ2hCZ0YsYUFBY3BGLEVBQUVJLFNBQ2hCaUYsbUJBQW9CckYsRUFBRVEsVUFDdEI4RSxnQkFBaUJ0RixFQUFFUSxVQUduQitFLFVBQVd2RixFQUFFUSxVQUNiZ0YsU0FBVXhGLEVBQUVJLFNBR1pxRixlQUFnQnpGLEVBQUVTLEtBQUssQ0FBQyxjQUFlLGFBQWMsU0FDckRpRiw4QkFBK0IxRixFQUFFUSxVQUNqQ21GLGNBQWUzRixFQUFFUSxVQUNqQm9GLHNCQUF1QjVGLEVBQUVRLFVBQ3pCcUYseUJBQTBCN0YsRUFBRVEsVUFHNUJzRixhQUFjOUYsRUFBRVEsVUFDaEJ1RixlQUFnQi9GLEVBQUVRLFVBQ2xCd0YsZUFBZ0JoRyxFQUFFUSxVQUNsQnlGLHdCQUF5QmpHLEVBQUVRLFVBQzNCMEYsYUFBY2xHLEVBQUVRLFVBQ2hCMkYsY0FBZW5HLEVBQUVjLGlCQUNqQnNGLHFCQUFzQnBHLEVBQUVXLGdCQUdiMEYsS0FBT3RGLE9BQU91RixVQUFVQyxNQUFNbFEsUUFBUW1RLEtDdk83Q3ZLLGNBQWdCd0ssbUJBQW1CNU0sZUFlbEMsU0FBUzZNLFdBQVdDLEdBQWUsR0FDeEMsT0FBT0EsRUFBZTFLLGNBQWdCbEosU0FBU2tKLGNBQ2pELENBOEJPLFNBQVMySyxXQUNkQyxFQUFnQixDQUFFLEVBQ2xCQyxFQUFVLEdBQ1ZDLEdBQWUsR0FHZixJQUFJQyxFQUFnQixDQUFBLEVBR2hCQyxFQUFhLENBQUEsRUFHYkgsRUFBUWhSLFNBRVZrUixFQUFnQkUsZ0JBQWdCSixHQUdoQ0csRUFBYUUsbUJBQW1CN0gsWUFBYXdILElBSS9DLE1BQU1NLEVBQWlCVixXQUFXSyxHQVlsQyxPQVRBTSxlQUNFeE4sY0FDQXVOLEVBQ0FKLEVBQ0FILEVBQ0FJLEdBSUtHLENBQ1QsQ0FZTyxTQUFTRSxhQUFhQyxFQUFpQkMsR0FFNUMsR0FBSTlSLFNBQVM4UixHQUNYLElBQUssTUFBT3BVLEVBQUtzRCxLQUFVckQsT0FBT29VLFFBQVFELEdBQ3hDRCxFQUFnQm5VLEdBQ2RzQyxTQUFTZ0IsS0FDUjhJLGNBQWMxTCxTQUFTVixTQUNDcUYsSUFBekI4TyxFQUFnQm5VLEdBQ1prVSxhQUFhQyxFQUFnQm5VLEdBQU1zRCxRQUN6QitCLElBQVYvQixFQUNFQSxFQUNBNlEsRUFBZ0JuVSxHQUs1QixPQUFPbVUsQ0FDVCxDQWtCTyxTQUFTRyxnQkFBZ0JDLEdBRTlCLE1BQU1ILEVBQWEsQ0FBQSxFQUduQixHQUFtRCxvQkFBL0NuVSxPQUFPQyxVQUFVOEIsU0FBUzVCLEtBQUttVSxHQUVqQyxJQUFLLE1BQU92VSxFQUFLc0QsS0FBVXJELE9BQU9vVSxRQUFRRSxHQUFhLENBRXJELE1BQU1DLEVBQWtCdEksWUFBWWxNLEdBQ2hDa00sWUFBWWxNLEdBQUtlLE1BQU0sS0FDdkIsR0FJSnlULEVBQWdCQyxRQUNkLENBQUNDLEVBQUtDLEVBQU1DLElBQ1RGLEVBQUlDLEdBQ0hILEVBQWdCOVIsT0FBUyxJQUFNa1MsRUFBUXRSLEVBQVFvUixFQUFJQyxJQUFTLElBQ2hFUCxFQUVILENBSUgsT0FBT0EsQ0FDVCxDQW9CTyxTQUFTUyxnQkFDZHZJLE9BQ0F0SyxVQUFXLEVBQ1g4UyxnQkFBaUIsR0FFakIsSUFFRSxJQUFLeFMsU0FBU2dLLFNBQTZCLGlCQUFYQSxPQUU5QixPQUFPLEtBSVQsTUFBTXlJLGFBQ2MsaUJBQVh6SSxPQUNId0ksZUFDRUUsS0FBSyxJQUFJMUksV0FDVDJJLEtBQUs5QixNQUFNN0csUUFDYkEsT0FHQTRJLG1CQUFxQkMsa0JBQ3pCSixhQUNBRCxnQkFDQSxHQUlJTSxjQUFnQk4sZUFDbEJHLEtBQUs5QixNQUNIZ0Msa0JBQWtCSixhQUFjRCxnQkFBZ0IsSUFDaEQsQ0FBQ08sRUFBRy9SLFFBQ2UsaUJBQVZBLE9BQXNCQSxNQUFNWSxXQUFXLFlBQzFDOFEsS0FBSyxJQUFJMVIsVUFDVEEsUUFFUjJSLEtBQUs5QixNQUFNK0Isb0JBR2YsT0FBT2xULFNBQVdrVCxtQkFBcUJFLGFBQ3hDLENBQUMsTUFBTzVQLEdBRVAsT0FBTyxJQUNSLENBQ0gsQ0FzRkEsU0FBUzZOLG1CQUFtQi9HLEdBQzFCLE1BQU14RSxFQUFVLENBQUEsRUFHaEIsSUFBSyxNQUFPd04sRUFBTS9TLEtBQVN0QyxPQUFPb1UsUUFBUS9ILEdBQ3hDeEUsRUFBUXdOLEdBQVFyVixPQUFPQyxVQUFVQyxlQUFlQyxLQUFLbUMsRUFBTSxTQUN2REEsRUFBS2UsTUFDTCtQLG1CQUFtQjlRLEdBSXpCLE9BQU91RixDQUNULENBdUJBLFNBQVNtTSxlQUFlM0gsRUFBUXhFLEVBQVN5TixFQUFXQyxFQUFXQyxHQUM3RHhWLE9BQU93QyxLQUFLNkosR0FBUUUsU0FBU3hNLElBRTNCLE1BQU15TSxFQUFRSCxFQUFPdE0sR0FHZjBWLEVBQVlILEdBQWFBLEVBQVV2VixHQUNuQzJWLEVBQVlILEdBQWFBLEVBQVV4VixHQUNuQzRWLEVBQVNILEdBQVVBLEVBQU96VixHQUdoQyxRQUEyQixJQUFoQnlNLEVBQU1uSixNQUNmMlEsZUFBZXhILEVBQU8zRSxFQUFROUgsR0FBTTBWLEVBQVdDLEVBQVdDLE9BQ3JELENBRURGLFVBQ0Y1TixFQUFROUgsR0FBTzBWLEdBSWpCLE1BQU1HLEVBQVM1QyxLQUFLeEcsRUFBTTdGLFNBQ3RCNkYsRUFBTTdGLFdBQVdxTSxNQUFqQnhHLE1BQXlCb0osSUFDM0IvTixFQUFROUgsR0FBTzZWLEdBSWJGLFVBQ0Y3TixFQUFROUgsR0FBTzJWLEdBSWJDLFVBQ0Y5TixFQUFROUgsR0FBTzRWLEVBRWxCLElBRUwsQ0FzQk8sU0FBU1Qsa0JBQWtCck4sRUFBU2dOLEVBQWdCZ0IsR0FpQ3pELE9BQU9iLEtBQUtjLFVBQVVqTyxHQWhDRyxDQUFDdU4sRUFBRy9SLEtBTzNCLEdBTHFCLGlCQUFWQSxJQUNUQSxFQUFRQSxFQUFNbkIsUUFLRyxtQkFBVm1CLEdBQ1csaUJBQVZBLEdBQ05BLEVBQU1ZLFdBQVcsYUFDakJaLEVBQU1VLFNBQVMsS0FDakIsQ0FFQSxHQUFJOFEsRUFFRixPQUFPZ0IsRUFFSCxZQUFZeFMsRUFBUSxJQUFJMFMsV0FBVyxPQUFRLGVBRTNDLFdBQVcxUyxFQUFRLElBQUkwUyxXQUFXLE9BQVEsY0FHOUMsTUFBTSxJQUFJQyxLQUViLENBR0QsT0FBTzNTLENBQUssSUFJbUMwUyxXQUMvQ0YsRUFBcUIseUJBQTJCLHFCQUNoRCxHQUVKLENBZUEsU0FBU2hDLGdCQUFnQkosR0FFdkIsTUFBTXdDLEVBQWN4QyxFQUFReUMsV0FDekJDLEdBQWtDLGVBQTFCQSxFQUFJM1YsUUFBUSxLQUFNLE1BSXZCNFYsRUFBaUJILEdBQWUsR0FBS3hDLEVBQVF3QyxFQUFjLEdBR2pFLEdBQUlHLEVBQ0YsSUFFRSxPQUFPcEIsS0FBSzlCLE1BQU1sUCxHQUFBQSxhQUFhbkQsZ0JBQWdCdVYsSUFDaEQsQ0FBQyxNQUFPN1EsR0FDUEQsYUFDRSxFQUNBQyxFQUNBLHNEQUFzRDZRLFVBRXpELENBSUgsTUFBTyxFQUNULENBa0JBLFNBQVN0QyxtQkFBbUI3SCxFQUFhd0gsR0FFdkMsTUFBTUcsRUFBYSxDQUFBLEVBR25CLElBQUssSUFBSXlDLEVBQUksRUFBR0EsRUFBSTVDLEVBQVFoUixPQUFRNFQsSUFBSyxDQUN2QyxNQUFNQyxFQUFTN0MsRUFBUTRDLEdBQUc3VixRQUFRLEtBQU0sSUFHbEMrVCxFQUFrQnRJLEVBQVlxSyxHQUNoQ3JLLEVBQVlxSyxHQUFReFYsTUFBTSxLQUMxQixHQUdKeVQsRUFBZ0JDLFFBQU8sQ0FBQ0MsRUFBS0MsRUFBTUMsS0FDakMsR0FBSUosRUFBZ0I5UixPQUFTLElBQU1rUyxFQUFPLENBQ3hDLE1BQU10UixFQUFRb1EsSUFBVTRDLEdBQ25CaFQsR0FDSHNCLElBQ0UsRUFDQSx5Q0FBeUMyUix5Q0FHN0M3QixFQUFJQyxHQUFRclIsR0FBUyxJQUN0QixXQUF3QitCLElBQWRxUCxFQUFJQyxLQUNiRCxFQUFJQyxHQUFRLElBRWQsT0FBT0QsRUFBSUMsRUFBSyxHQUNmZCxFQUNKLENBR0QsT0FBT0EsQ0FDVCxDQ3ZnQk8yQyxlQUFlQyxNQUFNQyxFQUFLQyxFQUFpQixJQUNoRCxPQUFPLElBQUlDLFNBQVEsQ0FBQ0MsRUFBU0MsS0FDM0JDLG1CQUFtQkwsR0FDaEJNLElBQUlOLEVBQUtDLEdBQWlCTSxJQUN6QixJQUFJQyxFQUFlLEdBR25CRCxFQUFTRSxHQUFHLFFBQVNDLElBQ25CRixHQUFnQkUsQ0FBSyxJQUl2QkgsRUFBU0UsR0FBRyxPQUFPLEtBQ1pELEdBQ0hKLEVBQU8scUNBRVRHLEVBQVNJLEtBQU9ILEVBQ2hCTCxFQUFRSSxFQUFTLEdBQ2pCLElBRUhFLEdBQUcsU0FBVTNSLElBQ1pzUixFQUFPdFIsRUFBTSxHQUNiLEdBRVIsQ0F3RUEsU0FBU3VSLG1CQUFtQkwsR0FDMUIsT0FBT0EsRUFBSXhTLFdBQVcsU0FBV29ULE1BQVFDLElBQzNDLENDcEhBLE1BQU1DLG9CQUFvQnZCLE1BUXhCLFdBQUF3QixDQUFZOVIsRUFBUytSLEdBQ25CQyxRQUVBQyxLQUFLalMsUUFBVUEsRUFDZmlTLEtBQUtoUyxhQUFlRCxFQUVoQitSLElBQ0ZFLEtBQUtGLFdBQWFBLEVBRXJCLENBVUQsUUFBQUcsQ0FBU3JTLEdBZ0JQLE9BZkFvUyxLQUFLcFMsTUFBUUEsRUFFVEEsRUFBTThQLE9BQ1JzQyxLQUFLdEMsS0FBTzlQLEVBQU04UCxNQUdoQjlQLEVBQU1rUyxhQUNSRSxLQUFLRixXQUFhbFMsRUFBTWtTLFlBR3RCbFMsRUFBTUssUUFDUitSLEtBQUtoUyxhQUFlSixFQUFNRyxRQUMxQmlTLEtBQUsvUixNQUFRTCxFQUFNSyxPQUdkK1IsSUFDUixFQzNCSCxNQUFNRSxNQUFRLENBQ1ozUSxPQUFRLDhCQUNSNFEsZUFBZ0IsQ0FBRSxFQUNsQkMsUUFBUyxHQUNUQyxVQUFXLElBY056QixlQUFlMEIsb0JBQ3BCQyxFQUNBQyxHQUVBLElBQUlDLEVBR0osTUFBTWhSLEVBQVlpUixlQUdaQyxFQUFlNVcsS0FBQUEsS0FBSzBGLEVBQVcsaUJBQy9CbVIsRUFBYTdXLEtBQUFBLEtBQUswRixFQUFXLGNBT25DLElBSkNmLEdBQVVBLFdBQUNlLElBQWNkLEdBQVNBLFVBQUNjLEVBQVcsQ0FBRW9SLFdBQVcsS0FJdkRuUyxHQUFBQSxXQUFXaVMsSUFBaUJKLEVBQWtCL1EsV0FDakR4QyxJQUFJLEVBQUcseURBQ1B5VCxRQUF1QkssYUFDckJQLEVBQ0FDLEVBQ0FJLE9BRUcsQ0FDTCxJQUFJRyxHQUFnQixFQUdwQixNQUFNQyxFQUFXM0QsS0FBSzlCLE1BQU1sUCxHQUFBQSxhQUFhc1UsSUFJekMsR0FBSUssRUFBU0MsU0FBVy9ZLE1BQU1DLFFBQVE2WSxFQUFTQyxTQUFVLENBQ3ZELE1BQU1DLEVBQVksQ0FBQSxFQUNsQkYsRUFBU0MsUUFBUXJNLFNBQVN1TSxHQUFPRCxFQUFVQyxHQUFLLElBQ2hESCxFQUFTQyxRQUFVQyxDQUNwQixDQUdELE1BQU14UixZQUFFQSxFQUFXRSxjQUFFQSxFQUFhQyxpQkFBRUEsR0FBcUIwUSxFQUNuRGEsRUFDSjFSLEVBQVk1RSxPQUFTOEUsRUFBYzlFLE9BQVMrRSxFQUFpQi9FLE9BSzNEa1csRUFBUzFSLFVBQVlpUixFQUFrQmpSLFNBQ3pDdEMsSUFDRSxFQUNBLHlFQUVGK1QsR0FBZ0IsR0FDUDFZLE9BQU93QyxLQUFLbVcsRUFBU0MsU0FBVyxJQUFJblcsU0FBV3NXLEdBQ3hEcFUsSUFDRSxFQUNBLCtFQUVGK1QsR0FBZ0IsR0FHaEJBLEdBQWlCblIsR0FBaUIsSUFBSTVFLE1BQU1xVyxJQUMxQyxJQUFLTCxFQUFTQyxRQUFRSSxHQUtwQixPQUpBclUsSUFDRSxFQUNBLGVBQWVxVSxpREFFVixDQUNSLElBS0ROLEVBQ0ZOLFFBQXVCSyxhQUNyQlAsRUFDQUMsRUFDQUksSUFHRjVULElBQUksRUFBRyx1REFHUGtULE1BQU1FLFFBQVUvVCxHQUFBQSxhQUFhdVUsRUFBWSxRQUd6Q0gsRUFBaUJPLEVBQVNDLFFBRzFCZixNQUFNRyxVQUFZaUIsZUFBZXBCLE1BQU1FLFNBRTFDLE9BSUttQixzQkFBc0JoQixFQUFtQkUsRUFDakQsQ0FTTyxTQUFTZSx1QkFDZCxPQUFPdEIsTUFBTUcsU0FDZixDQVdPekIsZUFBZTZDLHdCQUF3QkMsR0FFNUMsTUFBTXhSLEVBQVV3TCxhQUdoQnhMLEVBQVFiLFdBQVdDLFFBQVVvUyxRQUd2QnBCLG9CQUFvQnBRLEVBQVFiLFdBQVlhLEVBQVF5QixPQUFPTSxNQUMvRCxDQVdPLFNBQVNxUCxlQUFlSyxHQUM3QixPQUFPQSxFQUNKN00sVUFBVSxFQUFHNk0sRUFBYUMsUUFBUSxPQUNsQy9ZLFFBQVEsS0FBTSxJQUNkQSxRQUFRLEtBQU0sSUFDZEEsUUFBUSxNQUFPLElBQ2YwQixNQUNMLENBWU8sU0FBU3NYLGtCQUFrQkMsR0FDaEMsT0FBT0EsRUFBV2paLFFBQ2hCLHFFQUNBLEdBRUosQ0FvQk8sU0FBUzZYLGVBQ2QsT0FBT3hYLGdCQUFnQndTLGFBQWFyTSxXQUFXSSxVQUNqRCxDQXVCQW1QLGVBQWVtRCx1QkFDYkMsRUFDQWpELEVBQ0EwQixFQUNBd0IsR0FBbUIsR0FHZkQsRUFBTzVWLFNBQVMsU0FDbEI0VixFQUFTQSxFQUFPbE4sVUFBVSxFQUFHa04sRUFBT2xYLE9BQVMsSUFFL0NrQyxJQUFJLEVBQUcsNkJBQTZCZ1YsUUFHcEMsTUFBTTNDLFFBQWlCUixNQUFNLEdBQUdtRCxPQUFhakQsR0FHN0MsR0FBNEIsTUFBeEJNLEVBQVNTLFlBQThDLGlCQUFqQlQsRUFBU0ksS0FBa0IsQ0FDbkUsR0FBSWdCLEVBQWdCLENBRWxCQSxFQURtQm9CLGtCQUFrQkcsSUFDUixDQUM5QixDQUNELE9BQU8zQyxFQUFTSSxJQUNqQixDQUdELEdBQUl3QyxFQUNGLE1BQU0sSUFBSXJDLFlBQ1IsK0JBQStCb0MsMkVBQWdGM0MsRUFBU1MsZUFDeEgsS0FDQUcsU0FBU1osR0FFWHJTLElBQ0UsRUFDQSwrQkFBK0JnViw2REFHckMsQ0FnQkFwRCxlQUFlMkMsc0JBQXNCaEIsRUFBbUJFLEVBQWlCLElBQ3ZFLE1BQU15QixFQUFjLENBQ2xCNVMsUUFBU2lSLEVBQWtCalIsUUFDM0IyUixRQUFTUixHQUlYUCxNQUFNQyxlQUFpQitCLEVBRXZCbFYsSUFBSSxFQUFHLG1DQUNQLElBQ0VtVixHQUFhQSxjQUNYcFksVUFBSzJXLGVBQWdCLGlCQUNyQnJELEtBQUtjLFVBQVUrRCxHQUNmLE9BRUgsQ0FBQyxNQUFPdFUsR0FDUCxNQUFNLElBQUlnUyxZQUNSLDRDQUNBLEtBQ0FLLFNBQVNyUyxFQUNaLENBQ0gsQ0F1QkFnUixlQUFld0QsY0FDYjFTLEVBQ0FFLEVBQ0FFLEVBQ0EwUSxFQUNBQyxHQUdBLElBQUk0QixFQUNKLE1BQU1DLEVBQVk5QixFQUFtQjNPLEtBQy9CMFEsRUFBWS9CLEVBQW1CMU8sS0FHckMsR0FBSXdRLEdBQWFDLEVBQ2YsSUFDRUYsRUFBYSxJQUFJRyxnQkFBQUEsZ0JBQWdCLENBQy9CM1EsS0FBTXlRLEVBQ054USxLQUFNeVEsR0FFVCxDQUFDLE1BQU8zVSxHQUNQLE1BQU0sSUFBSWdTLFlBQ1IsMENBQ0EsS0FDQUssU0FBU3JTLEVBQ1osQ0FJSCxNQUFNbVIsRUFBaUJzRCxFQUNuQixDQUNFSSxNQUFPSixFQUNQblEsUUFBU3NPLEVBQW1CdE8sU0FFOUIsR0FFRXdRLEVBQW1CLElBQ3BCaFQsRUFBWTRGLEtBQUswTSxHQUNsQkQsdUJBQXVCLEdBQUdDLElBQVVqRCxFQUFnQjBCLEdBQWdCLFFBRW5FN1EsRUFBYzBGLEtBQUswTSxHQUNwQkQsdUJBQXVCLEdBQUdDLElBQVVqRCxFQUFnQjBCLFFBRW5EM1EsRUFBY3dGLEtBQUswTSxHQUNwQkQsdUJBQXVCLEdBQUdDLElBQVVqRCxNQUt4QyxhQUQ2QkMsUUFBUTJELElBQUlELElBQ25CM1ksS0FBSyxNQUM3QixDQW1CQTZVLGVBQWVrQyxhQUFhUCxFQUFtQkMsRUFBb0JJLEdBRWpFLE1BQU1QLEVBQzBCLFdBQTlCRSxFQUFrQmpSLFFBQ2QsS0FDQSxHQUFHaVIsRUFBa0JqUixVQUdyQkMsRUFBU2dSLEVBQWtCaFIsUUFBVTJRLE1BQU0zUSxPQUVqRCxJQUNFLE1BQU1rUixFQUFpQixDQUFBLEVBdUN2QixPQXJDQXpULElBQ0UsRUFDQSxpREFBaURxVCxHQUFhLGFBR2hFSCxNQUFNRSxjQUFnQmdDLGNBQ3BCLElBQ0s3QixFQUFrQjdRLFlBQVk0RixLQUFLc04sR0FDcEN2QyxFQUFZLEdBQUc5USxLQUFVOFEsS0FBYXVDLElBQU0sR0FBR3JULEtBQVVxVCxPQUc3RCxJQUNLckMsRUFBa0IzUSxjQUFjMEYsS0FBSzZMLEdBQ2hDLFFBQU5BLEVBQ0lkLEVBQ0UsR0FBRzlRLFVBQWU4USxhQUFxQmMsSUFDdkMsR0FBRzVSLGtCQUF1QjRSLElBQzVCZCxFQUNFLEdBQUc5USxLQUFVOFEsYUFBcUJjLElBQ2xDLEdBQUc1UixhQUFrQjRSLFNBRTFCWixFQUFrQjFRLGlCQUFpQnlGLEtBQUtvSixHQUN6QzJCLEVBQ0ksR0FBRzlRLFdBQWdCOFEsZ0JBQXdCM0IsSUFDM0MsR0FBR25QLHNCQUEyQm1QLE9BR3RDNkIsRUFBa0J6USxjQUNsQjBRLEVBQ0FDLEdBSUZQLE1BQU1HLFVBQVlpQixlQUFlcEIsTUFBTUUsU0FHdkMrQixHQUFBQSxjQUFjdkIsRUFBWVYsTUFBTUUsU0FDekJLLENBQ1IsQ0FBQyxNQUFPN1MsR0FDUCxNQUFNLElBQUlnUyxZQUNSLHVEQUNBLEtBQ0FLLFNBQVNyUyxFQUNaLENBQ0gsQ0N0Y08sU0FBU2lWLGtCQUNkQyxXQUFXQyxXQUFhLFdBQ3RCLE1BQU8sQ0FBRUMsU0FBVSxFQUN2QixDQUNBLENBV09wRSxlQUFlcUUsWUFBWS9TLEdBRWhDLE1BQU13TCxXQUFFQSxFQUFVd0gsTUFBRUEsRUFBS3RILFdBQUVBLEVBQVV1SCxLQUFFQSxHQUFTTCxXQUloREEsV0FBV00sY0FBZ0JGLEdBQU0sRUFBTyxDQUFFLEVBQUV4SCxLQUc1Q3JKLE9BQU9nUixrQkFBbUIsRUFDMUJGLEVBQUtMLFdBQVdRLE1BQU1oYixVQUFXLFFBQVEsU0FBVWliLEVBQVNDLEVBQWFDLEtBRXZFRCxFQUFjTixFQUFNTSxFQUFhLENBQy9CRSxVQUFXLENBQ1RDLFNBQVMsR0FFWEMsWUFBYSxDQUNYQyxPQUFRLENBQ05DLE1BQU8sQ0FDTEgsU0FBUyxLQU9mSSxRQUFTLENBQUUsS0FHQUYsUUFBVSxJQUFJalAsU0FBUSxTQUFVaVAsR0FDM0NBLEVBQU9HLFdBQVksQ0FDekIsSUFHUzNSLE9BQU80UixxQkFDVjVSLE9BQU80UixtQkFBcUJuQixXQUFXb0IsU0FBU2xFLEtBQU0sVUFBVSxLQUM5RDNOLE9BQU9nUixrQkFBbUIsQ0FBSSxLQUlsQ0UsRUFBUS9WLE1BQU13UyxLQUFNLENBQUN3RCxFQUFhQyxHQUN0QyxJQUVFTixFQUFLTCxXQUFXcUIsT0FBTzdiLFVBQVcsUUFBUSxTQUFVaWIsRUFBU2EsRUFBT2xVLEdBQ2xFcVQsRUFBUS9WLE1BQU13UyxLQUFNLENBQUNvRSxFQUFPbFUsR0FDaEMsSUFHRSxNQUFNbVUsRUFBb0IsQ0FDeEJELE1BQU8sQ0FFTEosV0FBVyxFQUVYdlQsT0FBUVAsRUFBUUgsT0FBT1UsT0FDdkJDLE1BQU9SLEVBQVFILE9BQU9XLE9BRXhCZ1QsVUFBVyxDQUVUQyxTQUFTLElBS1BILEVBQWMsSUFBSWMsU0FBUyxVQUFVcFUsRUFBUUgsT0FBT0UsUUFBdEMsR0FHZGlCLEVBQWUsSUFBSW9ULFNBQVMsVUFBVXBVLEVBQVFILE9BQU9tQixlQUF0QyxHQUdmRCxFQUFnQixJQUFJcVQsU0FDeEIsVUFBVXBVLEVBQVFILE9BQU9rQixnQkFETCxHQUtoQnNULEVBQWVyQixHQUNuQixFQUNBaFMsRUFDQXNTLEVBRUFhLEdBSUlHLEVBQWdCdFUsRUFBUWtCLFlBQVlFLFNBQ3RDLElBQUlnVCxTQUFTLFVBQVVwVSxFQUFRa0IsWUFBWUUsV0FBM0MsR0FDQSxLQUdBcEIsRUFBUWtCLFlBQVluRixZQUN0QixJQUFJcVksU0FBUyxVQUFXcFUsRUFBUWtCLFlBQVluRixXQUE1QyxDQUF3RHVYLEdBSXREdlMsR0FDRjJLLEVBQVczSyxHQUliNlIsV0FBVzVTLEVBQVFILE9BQU9ySCxRQUFRLFlBQWE2YixFQUFjQyxHQUc3RCxNQUFNQyxFQUFpQi9JLElBR3ZCLElBQUssTUFBTXFCLEtBQVEwSCxFQUNtQixtQkFBekJBLEVBQWUxSCxXQUNqQjBILEVBQWUxSCxHQUsxQm5CLEVBQVdrSCxXQUFXTSxlQUd0Qk4sV0FBV00sY0FBZ0IsRUFDN0IsQ0MzSEEsTUFBTXNCLFNBQVdyWSxHQUFZQSxhQUMzQnRDLFVBQUs3QyxZQUFXLFlBQWEsaUJBQzdCLFFBSUYsSUFBSXlkLFFBQVUsS0FtQ1AvRixlQUFlZ0csY0FBY0MsR0FFbEMsTUFBTTlRLE1BQUVBLEVBQUtOLE1BQUVBLEdBQVVpSSxjQUdqQjlKLE9BQVFrVCxLQUFpQkMsR0FBaUJoUixFQUc1Q2lSLEVBQWdCLENBQ3BCaFIsVUFBVVAsRUFBTUssa0JBQW1CLFFBQ25DbVIsWUFBYSxNQUNiaFksS0FBTTRYLEdBQWlCLEdBQ3ZCSyxjQUFjLEVBQ2RDLGVBQWUsRUFDZkMsY0FBYyxFQUNkQyxvQkFBb0IsRUFDcEJDLGdCQUFpQixRQUNiUixHQUFnQkMsR0FJdEIsSUFBS0osUUFBUyxDQUVaLElBQUlZLEVBQVcsRUFFZixNQUFNQyxFQUFPNUcsVUFDWCxJQUNFNVIsSUFDRSxFQUNBLHlEQUF5RHVZLE9BSTNEWixjQUFnQjdWLFVBQVUyVyxPQUFPVCxFQUNsQyxDQUFDLE1BQU9wWCxHQVFQLEdBUEFELGFBQ0UsRUFDQUMsRUFDQSxvREFJRTJYLEVBQVcsSUFPYixNQUFNM1gsRUFOTlosSUFBSSxFQUFHLHNDQUFzQ3VZLHVCQUd2QyxJQUFJdkcsU0FBU0ssR0FBYXFHLFdBQVdyRyxFQUFVLGFBQy9DbUcsR0FJVCxHQUdILFVBQ1FBLElBR3lCLFVBQTNCUixFQUFjaFIsVUFDaEJoSCxJQUFJLEVBQUcsNkNBSUw4WCxHQUNGOVgsSUFBSSxFQUFHLDRDQUVWLENBQUMsTUFBT1ksR0FDUCxNQUFNLElBQUlnUyxZQUNSLGdFQUNBLEtBQ0FLLFNBQVNyUyxFQUNaLENBRUQsSUFBSytXLFFBQ0gsTUFBTSxJQUFJL0UsWUFBWSwyQ0FBNEMsSUFFckUsQ0FHRCxPQUFPK0UsT0FDVCxDQVFPL0YsZUFBZStHLGVBRWhCaEIsU0FBV0EsUUFBUWlCLGlCQUNmakIsUUFBUWtCLFFBRWhCbEIsUUFBVSxLQUNWM1gsSUFBSSxFQUFHLGdDQUNULENBZ0JPNFIsZUFBZWtILFFBQVFDLEdBRTVCLElBQUtwQixVQUFZQSxRQUFRaUIsVUFDdkIsTUFBTSxJQUFJaEcsWUFBWSwwQ0FBMkMsS0FnQm5FLEdBWkFtRyxFQUFhQyxXQUFhckIsUUFBUW1CLGdCQUc1QkMsRUFBYUMsS0FBS0MsaUJBQWdCLFNBR2xDQyxnQkFBZ0JILEVBQWFDLE1BR25DRyxlQUFlSixFQUFhQyxPQUd2QkQsRUFBYUMsTUFBUUQsRUFBYUMsS0FBS0ksV0FDMUMsTUFBTSxJQUFJeEcsWUFBWSwyQ0FBNEMsSUFFdEUsQ0FrQk9oQixlQUFleUgsVUFBVU4sRUFBY08sR0FBWSxHQUN4RCxJQUNFLEdBQUlQLEVBQWFDLE9BQVNELEVBQWFDLEtBQUtJLFdBZ0IxQyxPQWZJRSxTQUVJUCxFQUFhQyxLQUFLTyxLQUFLLGNBQWUsQ0FDMUNDLFVBQVcsMkJBSVBOLGdCQUFnQkgsRUFBYUMsYUFHN0JELEVBQWFDLEtBQUtTLFVBQVMsS0FDL0JwZixTQUFTcWYsS0FBS0MsVUFDWiw0REFBNEQsS0FHM0QsQ0FFVixDQUFDLE1BQU8vWSxHQUNQRCxhQUNFLEVBQ0FDLEVBQ0EseUJBQXlCbVksRUFBYWEsbURBSXhDYixFQUFhYyxVQUFZbkwsYUFBYTdJLEtBQUtHLFVBQVksQ0FDeEQsQ0FDRCxPQUFPLENBQ1QsQ0FpQk80TCxlQUFla0ksaUJBQWlCZCxFQUFNZSxHQUUzQyxNQUFNQyxFQUFvQixHQUdwQnpWLEVBQVl3VixFQUFtQnhWLFVBQ3JDLEdBQUlBLEVBQVcsQ0FDYixNQUFNMFYsRUFBYSxHQVVuQixHQVBJMVYsRUFBVTJWLElBQ1pELEVBQVcvWSxLQUFLLENBQ2RpWixRQUFTNVYsRUFBVTJWLEtBS25CM1YsRUFBVTZWLE1BQ1osSUFBSyxNQUFNOVksS0FBUWlELEVBQVU2VixNQUFPLENBQ2xDLE1BQU1DLEdBQVcvWSxFQUFLaEMsV0FBVyxRQUdqQzJhLEVBQVcvWSxLQUNUbVosRUFDSSxDQUNFRixRQUFTOWEsR0FBQUEsYUFBYW5ELGdCQUFnQm9GLEdBQU8sU0FFL0MsQ0FDRXdRLElBQUt4USxHQUdkLENBR0gsSUFBSyxNQUFNZ1osS0FBY0wsRUFDdkIsSUFDRUQsRUFBa0I5WSxXQUFXOFgsRUFBS3VCLGFBQWFELEdBQ2hELENBQUMsTUFBTzFaLEdBQ1BELGFBQWEsRUFBR0MsRUFBTyw4Q0FDeEIsQ0FFSHFaLEVBQVduYyxPQUFTLEVBR3BCLE1BQU0wYyxFQUFjLEdBQ3BCLEdBQUlqVyxFQUFVa1csSUFBSyxDQUNqQixJQUFJQyxFQUFhblcsRUFBVWtXLElBQUlFLE1BQU0sdUJBQ3JDLEdBQUlELEVBRUYsSUFBSyxJQUFJRSxLQUFpQkYsRUFDcEJFLElBQ0ZBLEVBQWdCQSxFQUNiL2UsUUFBUSxPQUFRLElBQ2hCQSxRQUFRLFVBQVcsSUFDbkJBLFFBQVEsS0FBTSxJQUNkQSxRQUFRLEtBQU0sSUFDZEEsUUFBUSxJQUFLLElBQ2JBLFFBQVEsTUFBTyxJQUNmMEIsT0FHQ3FkLEVBQWN0YixXQUFXLFFBQzNCa2IsRUFBWXRaLEtBQUssQ0FDZjRRLElBQUs4SSxJQUVFYixFQUFtQjdhLG9CQUM1QnNiLEVBQVl0WixLQUFLLENBQ2ZyRSxLQUFNRSxLQUFBQSxLQUFLN0MsWUFBVzBnQixNQVFoQ0osRUFBWXRaLEtBQUssQ0FDZmlaLFFBQVM1VixFQUFVa1csSUFBSTVlLFFBQVEsc0JBQXVCLEtBQU8sTUFHL0QsSUFBSyxNQUFNZ2YsS0FBZUwsRUFDeEIsSUFDRVIsRUFBa0I5WSxXQUFXOFgsRUFBSzhCLFlBQVlELEdBQy9DLENBQUMsTUFBT2phLEdBQ1BELGFBQ0UsRUFDQUMsRUFDQSwrQ0FFSCxDQUVINFosRUFBWTFjLE9BQVMsQ0FDdEIsQ0FDRixDQUNELE9BQU9rYyxDQUNULENBZU9wSSxlQUFlbUosbUJBQW1CL0IsRUFBTWdCLEdBQzdDLElBQ0UsSUFBSyxNQUFNZ0IsS0FBWWhCLFFBQ2ZnQixFQUFTQyxnQkFJWGpDLEVBQUtTLFVBQVMsS0FFbEIsR0FBMEIsb0JBQWYzRCxXQUE0QixDQUVyQyxNQUFNb0YsRUFBWXBGLFdBQVdxRixPQUc3QixHQUFJamdCLE1BQU1DLFFBQVErZixJQUFjQSxFQUFVcGQsT0FFeEMsSUFBSyxNQUFNc2QsS0FBWUYsRUFDckJFLEdBQVlBLEVBQVNDLFVBRXJCdkYsV0FBV3FGLE9BQU8vZSxPQUd2QixDQUdELFNBQVVrZixHQUFtQmpoQixTQUFTa2hCLHFCQUFxQixXQUVyRCxJQUFNQyxHQUFrQm5oQixTQUFTa2hCLHFCQUFxQixhQUVsREUsR0FBaUJwaEIsU0FBU2toQixxQkFBcUIsUUFHekQsSUFBSyxNQUFNRyxJQUFXLElBQ2pCSixLQUNBRSxLQUNBQyxHQUVIQyxFQUFRQyxRQUNULEdBRUosQ0FBQyxNQUFPL2EsR0FDUEQsYUFBYSxFQUFHQyxFQUFPLDhDQUN4QixDQUNILENBWUFnUixlQUFlc0gsZ0JBQWdCRixTQUV2QkEsRUFBSzRDLFdBQVdsRSxTQUFVLENBQUU4QixVQUFXLDJCQUd2Q1IsRUFBS3VCLGFBQWEsQ0FBRTFkLEtBQU1FLEtBQUlBLEtBQUMyVyxlQUFnQixzQkFHL0NzRixFQUFLUyxTQUFTNUQsZ0JBQ3RCLENBV0EsU0FBU3NELGVBQWVILEdBRXRCLE1BQU1qUyxNQUFFQSxHQUFVMkgsYUFHbEJzSyxFQUFLekcsR0FBRyxhQUFhWCxVQUdmb0gsRUFBS0ksVUFFUixJQUlDclMsRUFBTW5DLFFBQVVtQyxFQUFNRyxpQkFDeEI4UixFQUFLekcsR0FBRyxXQUFZeFIsSUFDbEJSLFFBQVFQLElBQUksV0FBV2UsRUFBUTBSLFNBQVMsR0FHOUMsQ0M1Y0EsSUFBQW9KLFlBQWUsSUFBTSx5WENJTkMsWUFBQzNZLEdBQVEsOExBUWxCMFksOEVBSUUxWSx3Q0NXRHlPLGVBQWVtSyxnQkFBZ0IvQyxFQUFNOVYsR0FFMUMsTUFBTThXLEVBQW9CLEdBRTFCLElBRUUsTUFBTWdDLEVBQWdCOVksRUFBUUgsT0FFOUIsSUFBSWtaLEdBQVEsRUFDWixHQUFJRCxFQUFjN1ksSUFBSyxDQUlyQixHQUhBbkQsSUFBSSxFQUFHLG1DQUdvQixRQUF2QmdjLEVBQWNoZ0IsS0FDaEIsT0FBT2dnQixFQUFjN1ksSUFJdkI4WSxHQUFRLFFBR0ZDLFVBQVVsRCxFQUFNZ0QsRUFBYzdZLElBQzFDLE1BQ01uRCxJQUFJLEVBQUcsMkNBR0RtYyxjQUFjbkQsRUFBTTlWLEdBTTVCOFcsRUFBa0I5WSxjQUNONFksaUJBQWlCZCxFQUFNOVYsRUFBUWtCLGNBSTNDLE1BQU1nWSxFQUFPSCxRQUNIakQsRUFBS1MsVUFBVTlWLElBQ25CLE1BQU0wWSxFQUFhaGlCLFNBQVNpaUIsY0FDMUIsc0NBSUlDLEVBQWNGLEVBQVc1WSxPQUFPK1ksUUFBUTlkLE1BQVFpRixFQUNoRDhZLEVBQWFKLEVBQVczWSxNQUFNOFksUUFBUTlkLE1BQVFpRixFQVVwRCxPQU5BdEosU0FBU3FmLEtBQUtnRCxNQUFNQyxLQUFPaFosRUFJM0J0SixTQUFTcWYsS0FBS2dELE1BQU1FLE9BQVMsTUFFdEIsQ0FDTEwsY0FDQUUsYUFDRCxHQUNBNVQsV0FBV21ULEVBQWNyWSxjQUN0QnFWLEVBQUtTLFVBQVMsS0FFbEIsTUFBTThDLFlBQUVBLEVBQVdFLFdBQUVBLEdBQWVwWCxPQUFPeVEsV0FBV3FGLE9BQU8sR0FPN0QsT0FGQTlnQixTQUFTcWYsS0FBS2dELE1BQU1DLEtBQU8sRUFFcEIsQ0FDTEosY0FDQUUsYUFDRCxLQUlESSxFQUFFQSxFQUFDQyxFQUFFQSxTQUFZQyxlQUFlL0QsR0FHaENnRSxFQUFpQm5lLEtBQUtvZSxJQUMxQnBlLEtBQUtxZSxLQUFLZCxFQUFLRyxhQUFlUCxFQUFjdlksU0FJeEMwWixFQUFnQnRlLEtBQUtvZSxJQUN6QnBlLEtBQUtxZSxLQUFLZCxFQUFLSyxZQUFjVCxFQUFjdFksUUFVN0MsSUFBSTBaLEVBRUosYUFSTXBFLEVBQUtxRSxZQUFZLENBQ3JCNVosT0FBUXVaLEVBQ1J0WixNQUFPeVosRUFDUEcsa0JBQW1CckIsRUFBUSxFQUFJcFQsV0FBV21ULEVBQWNyWSxTQUtsRHFZLEVBQWNoZ0IsTUFDcEIsSUFBSyxNQUNIb2hCLFFBQWVHLFdBQVd2RSxHQUMxQixNQUNGLElBQUssTUFDTCxJQUFLLE9BQ0hvRSxRQUFlSSxhQUNieEUsRUFDQWdELEVBQWNoZ0IsS0FDZCxDQUNFMEgsTUFBT3laLEVBQ1AxWixPQUFRdVosRUFDUkgsSUFDQUMsS0FFRmQsRUFBYzdYLHNCQUVoQixNQUNGLElBQUssTUFDSGlaLFFBQWVLLFdBQ2J6RSxFQUNBZ0UsRUFDQUcsRUFDQW5CLEVBQWM3WCxzQkFFaEIsTUFDRixRQUNFLE1BQU0sSUFBSXlPLFlBQ1IsdUNBQXVDb0osRUFBY2hnQixRQUNyRCxLQU1OLGFBRE0rZSxtQkFBbUIvQixFQUFNZ0IsR0FDeEJvRCxDQUNSLENBQUMsTUFBT3hjLEdBRVAsYUFETW1hLG1CQUFtQi9CLEVBQU1nQixHQUN4QnBaLENBQ1IsQ0FDSCxDQWNBZ1IsZUFBZXNLLFVBQVVsRCxFQUFNN1YsU0FDdkI2VixFQUFLNEMsV0FBV0UsWUFBWTNZLEdBQU0sQ0FDdENxVyxVQUFXLG9CQUVmLENBaUJBNUgsZUFBZXVLLGNBQWNuRCxFQUFNOVYsU0FDM0I4VixFQUFLUyxTQUFTeEQsWUFBYS9TLEVBQ25DLENBY0EwTyxlQUFlbUwsZUFBZS9ELEdBQzVCLE9BQU9BLEVBQUswRSxNQUFNLG9CQUFxQmhDLElBQ3JDLE1BQU1tQixFQUFFQSxFQUFDQyxFQUFFQSxFQUFDcFosTUFBRUEsRUFBS0QsT0FBRUEsR0FBV2lZLEVBQVFpQyx3QkFDeEMsTUFBTyxDQUNMZCxJQUNBQyxJQUNBcFosUUFDQUQsT0FBUTVFLEtBQUsrZSxNQUFNbmEsRUFBUyxFQUFJQSxFQUFTLEtBQzFDLEdBRUwsQ0FhQW1PLGVBQWUyTCxXQUFXdkUsR0FDeEIsT0FBT0EsRUFBSzBFLE1BQ1YsZ0NBQ0NoQyxHQUFZQSxFQUFRbUMsV0FFekIsQ0FrQkFqTSxlQUFlNEwsYUFBYXhFLEVBQU1oZCxFQUFNOGhCLEVBQU0zWixHQUM1QyxPQUFPNk4sUUFBUStMLEtBQUssQ0FDbEIvRSxFQUFLZ0YsV0FBVyxDQUNkaGlCLE9BQ0E4aEIsT0FDQUcsU0FBVSxTQUNWQyxVQUFVLEVBQ1ZDLGtCQUFrQixFQUNsQkMsdUJBQXVCLEtBQ1YsUUFBVHBpQixFQUFpQixDQUFFcWlCLFFBQVMsSUFBTyxDQUFBLEVBRXZDQyxlQUF3QixPQUFSdGlCLElBRWxCLElBQUlnVyxTQUFRLENBQUN1TSxFQUFVck0sSUFDckJ3RyxZQUNFLElBQU14RyxFQUFPLElBQUlVLFlBQVksd0JBQXlCLE9BQ3REek8sR0FBd0IsU0FJaEMsQ0FpQkF5TixlQUFlNkwsV0FBV3pFLEVBQU12VixFQUFRQyxFQUFPUyxHQUU3QyxhQURNNlUsRUFBS3dGLGlCQUFpQixVQUNyQnhGLEVBQUt5RixJQUFJLENBRWRoYixPQUFRQSxFQUFTLEVBQ2pCQyxRQUNBdWEsU0FBVSxTQUNWL1ksUUFBU2YsR0FBd0IsTUFFckMsQ0NwU0EsSUFBSTBCLEtBQU8sS0FHWCxNQUFNNlksVUFBWSxDQUNoQkMsaUJBQWtCLEVBQ2xCQyxpQkFBa0IsRUFDbEJDLGVBQWdCLEVBQ2hCQyxlQUFnQixFQUNoQkMsbUJBQW9CLEVBQ3BCQyx1QkFBd0IsRUFDeEJDLDJCQUE0QixFQUM1QkMsVUFBVyxFQUNYQyxpQkFBa0IsR0FzQmJ2TixlQUFld04sU0FDcEJDLEVBQWMzUSxhQUFhN0ksS0FDM0JnUyxFQUFnQixVQUdWRCxjQUFjQyxHQUVwQixJQU1FLEdBTEE3WCxJQUNFLEVBQ0EsOENBQThDcWYsRUFBWXZaLG1CQUFtQnVaLEVBQVl0WixlQUd2RkYsS0FLRixZQUpBN0YsSUFDRSxFQUNBLHlFQU1BcWYsRUFBWXZaLFdBQWF1WixFQUFZdFosYUFDdkNzWixFQUFZdlosV0FBYXVaLEVBQVl0WixZQUl2Q0YsS0FBTyxJQUFJeVosS0FBQUEsS0FBSyxJQUVYQyxTQUFTRixHQUNadGIsSUFBS3NiLEVBQVl2WixXQUNqQjlCLElBQUtxYixFQUFZdFosV0FDakJ5WixxQkFBc0JILEVBQVlwWixlQUNsQ3daLG9CQUFxQkosRUFBWW5aLGNBQ2pDd1oscUJBQXNCTCxFQUFZbFosZUFDbEN3WixrQkFBbUJOLEVBQVlqWixZQUMvQndaLDBCQUEyQlAsRUFBWWhaLG9CQUN2Q3daLG1CQUFvQlIsRUFBWS9ZLGVBQ2hDd1osc0JBQXNCLElBSXhCamEsS0FBSzBNLEdBQUcsV0FBV1gsTUFBT29KLElBRXhCLE1BQU0rRSxRQUFvQjFHLFVBQVUyQixHQUFVLEdBQzlDaGIsSUFDRSxFQUNBLHlCQUF5QmdiLEVBQVNwQixnREFBZ0RtRyxLQUNuRixJQUdIbGEsS0FBSzBNLEdBQUcsa0JBQWtCLENBQUN5TixFQUFVaEYsS0FDbkNoYixJQUNFLEVBQ0EseUJBQXlCZ2IsRUFBU3BCLDBDQUVwQ29CLEVBQVNoQyxLQUFPLElBQUksSUFHdEIsTUFBTWlILEVBQW1CLEdBRXpCLElBQUssSUFBSXZPLEVBQUksRUFBR0EsRUFBSTJOLEVBQVl2WixXQUFZNEwsSUFDMUMsSUFDRSxNQUFNc0osUUFBaUJuVixLQUFLcWEsVUFBVUMsUUFDdENGLEVBQWlCL2UsS0FBSzhaLEVBQ3ZCLENBQUMsTUFBT3BhLEdBQ1BELGFBQWEsRUFBR0MsRUFBTywrQ0FDeEIsQ0FJSHFmLEVBQWlCclksU0FBU29ULElBQ3hCblYsS0FBS3VhLFFBQVFwRixFQUFTLElBR3hCaGIsSUFDRSxFQUNBLDRCQUEyQmlnQixFQUFpQm5pQixPQUFTLFNBQVNtaUIsRUFBaUJuaUIsb0NBQXNDLEtBRXhILENBQUMsTUFBTzhDLEdBQ1AsTUFBTSxJQUFJZ1MsWUFDUiw2REFDQSxLQUNBSyxTQUFTclMsRUFDWixDQUNILENBWU9nUixlQUFleU8sV0FJcEIsR0FIQXJnQixJQUFJLEVBQUcsNkRBR0g2RixLQUFNLENBRVIsSUFBSyxNQUFNeWEsS0FBVXphLEtBQUswYSxLQUN4QjFhLEtBQUt1YSxRQUFRRSxFQUFPdEYsVUFJakJuVixLQUFLMmEsa0JBQ0YzYSxLQUFLd1YsVUFDWHJiLElBQUksRUFBRyw0Q0FFVDZGLEtBQU8sSUFDUixPQUdLOFMsY0FDUixDQW1CTy9HLGVBQWU2TyxTQUFTdmQsR0FDN0IsSUFBSXdkLEVBRUosSUFZRSxHQVhBMWdCLElBQUksRUFBRyxnREFHTDBlLFVBQVVDLGlCQUdSalEsYUFBYTdJLEtBQUtiLGNBQ3BCMmIsZUFJRzlhLEtBQ0gsTUFBTSxJQUFJK00sWUFDUix1REFDQSxLQUtKLE1BQU1nTyxFQUFpQnppQixjQUd2QixJQUNFNkIsSUFBSSxFQUFHLHFDQUdQMGdCLFFBQXFCN2EsS0FBS3FhLFVBQVVDLFFBR2hDamQsRUFBUXlCLE9BQU9LLGNBQ2pCaEYsSUFDRSxFQUNBa0QsRUFBUTJkLFdBQ0osd0JBQXdCM2QsRUFBUTJkLGlCQUNoQyxlQUNKLGtDQUFrQ0QsU0FHdkMsQ0FBQyxNQUFPaGdCLEdBQ1AsTUFBTSxJQUFJZ1MsWUFDUixXQUNHMVAsRUFBUTJkLFdBQWEsWUFBWTNkLEVBQVEyZCxpQkFBbUIsSUFDN0Qsd0RBQXdERCxTQUMxRCxLQUNBM04sU0FBU3JTLEVBQ1osQ0FHRCxHQUZBWixJQUFJLEVBQUcscUNBRUYwZ0IsRUFBYTFILEtBR2hCLE1BREEwSCxFQUFhN0csVUFBWTNXLEVBQVEyQyxLQUFLRyxVQUFZLEVBQzVDLElBQUk0TSxZQUNSLG1FQUNBLEtBS0osTUFBTWtPLEVBQVl0akIsaUJBRWxCd0MsSUFDRSxFQUNBLHlCQUF5QjBnQixFQUFhOUcsMkNBSXhDLE1BQU1tSCxFQUFnQjVpQixjQUNoQmlmLFFBQWVyQixnQkFBZ0IyRSxFQUFhMUgsS0FBTTlWLEdBR3hELEdBQUlrYSxhQUFrQi9MLE1BbUJwQixLQU51QiwwQkFBbkIrTCxFQUFPcmMsVUFFVDJmLEVBQWE3RyxVQUFZM1csRUFBUTJDLEtBQUtHLFVBQVksRUFDbEQwYSxFQUFhMUgsS0FBTyxNQUlKLGlCQUFoQm9FLEVBQU8xTSxNQUNZLDBCQUFuQjBNLEVBQU9yYyxRQUVELElBQUk2UixZQUNSLFdBQ0cxUCxFQUFRMmQsV0FBYSxZQUFZM2QsRUFBUTJkLGlCQUFtQixJQUM3RCxpSEFDRjVOLFNBQVNtSyxHQUVMLElBQUl4SyxZQUNSLFdBQ0cxUCxFQUFRMmQsV0FBYSxZQUFZM2QsRUFBUTJkLGlCQUFtQixJQUM3RCxvQ0FBb0NFLFVBQ3RDOU4sU0FBU21LLEdBS1hsYSxFQUFReUIsT0FBT0ssY0FDakJoRixJQUNFLEVBQ0FrRCxFQUFRMmQsV0FDSix3QkFBd0IzZCxFQUFRMmQsaUJBQ2hDLGVBQ0osc0NBQXNDRSxVQUsxQ2xiLEtBQUt1YSxRQUFRTSxHQUliLE1BQ01NLEVBRFV4akIsaUJBQ2FzakIsRUFTN0IsT0FQQXBDLFVBQVVRLFdBQWE4QixFQUN2QnRDLFVBQVVTLGlCQUNSVCxVQUFVUSxZQUFjUixVQUFVRSxpQkFFcEM1ZSxJQUFJLEVBQUcsNEJBQTRCZ2hCLFFBRzVCLENBQ0w1RCxTQUNBbGEsVUFFSCxDQUFDLE1BQU90QyxHQU9QLE9BTkU4ZCxVQUFVRyxlQUVSNkIsR0FDRjdhLEtBQUt1YSxRQUFRTSxHQUdUOWYsQ0FDUCxDQUNILENBcUJPLFNBQVNxZ0IsZUFDZCxPQUFPdkMsU0FDVCxDQVVPLFNBQVN3QyxrQkFDZCxNQUFPLENBQ0xuZCxJQUFLOEIsS0FBSzlCLElBQ1ZDLElBQUs2QixLQUFLN0IsSUFDVnVjLEtBQU0xYSxLQUFLc2IsVUFDWEMsVUFBV3ZiLEtBQUt3YixVQUNoQkMsV0FBWXpiLEtBQUtzYixVQUFZdGIsS0FBS3diLFVBQ2xDRSxnQkFBaUIxYixLQUFLMmIscUJBQ3RCQyxlQUFnQjViLEtBQUs2YixvQkFDckJDLG1CQUFvQjliLEtBQUsrYix3QkFDekJDLGdCQUFpQmhjLEtBQUtnYyxnQkFBZ0IvakIsT0FDdENna0IsWUFDRWpjLEtBQUtzYixVQUNMdGIsS0FBS3diLFVBQ0x4YixLQUFLMmIscUJBQ0wzYixLQUFLNmIsb0JBQ0w3YixLQUFLK2Isd0JBQ0wvYixLQUFLZ2MsZ0JBQWdCL2pCLE9BRTNCLENBU08sU0FBUzZpQixjQUNkLE1BQU01YyxJQUNKQSxFQUFHQyxJQUNIQSxFQUFHdWMsS0FDSEEsRUFBSWEsVUFDSkEsRUFBU0UsV0FDVEEsRUFBVUMsZ0JBQ1ZBLEVBQWVFLGVBQ2ZBLEVBQWNFLG1CQUNkQSxFQUFrQkUsZ0JBQ2xCQSxFQUFlQyxZQUNmQSxHQUNFWixrQkFFSmxoQixJQUFJLEVBQUcsMkRBQTJEK0QsTUFDbEUvRCxJQUFJLEVBQUcsMkRBQTJEZ0UsTUFDbEVoRSxJQUFJLEVBQUcsd0NBQXdDdWdCLE1BQy9DdmdCLElBQUksRUFBRyx3Q0FBd0NvaEIsTUFDL0NwaEIsSUFDRSxFQUNBLCtEQUErRHNoQixNQUVqRXRoQixJQUNFLEVBQ0EsMERBQTBEdWhCLE1BRTVEdmhCLElBQ0UsRUFDQSx5REFBeUR5aEIsTUFFM0R6aEIsSUFDRSxFQUNBLDJEQUEyRDJoQixNQUU3RDNoQixJQUNFLEVBQ0EsMkRBQTJENmhCLE1BRTdEN2hCLElBQUksRUFBRyx1Q0FBdUM4aEIsS0FDaEQsQ0FVQSxTQUFTdkMsU0FBU0YsR0FDaEIsTUFBTyxDQWNMMEMsT0FBUW5RLFVBRU4sTUFBTW1ILEVBQWUsQ0FDbkJhLEdBQUlvSSxLQUFBQSxLQUVKbkksVUFBV2hiLEtBQUtFLE1BQU1GLEtBQUtvakIsVUFBWTVDLEVBQVlyWixVQUFZLEtBR2pFLElBRUUsTUFBTWtjLEVBQVkxa0IsaUJBY2xCLGFBWE1zYixRQUFRQyxHQUdkL1ksSUFDRSxFQUNBLHlCQUF5QitZLEVBQWFhLDZDQUNwQ3BjLGlCQUFtQjBrQixRQUtoQm5KLENBQ1IsQ0FBQyxNQUFPblksR0FLUCxNQUpBWixJQUNFLEVBQ0EseUJBQXlCK1ksRUFBYWEscURBRWxDaFosQ0FDUCxHQWdCSHVoQixTQUFVdlEsTUFBT21ILEdBaUJWQSxFQUFhQyxLQVNkRCxFQUFhQyxLQUFLSSxZQUNwQnBaLElBQ0UsRUFDQSx5QkFBeUIrWSxFQUFhYSx5REFFakMsR0FJTGIsRUFBYUMsS0FBS29KLFlBQVlDLFVBQ2hDcmlCLElBQ0UsRUFDQSx5QkFBeUIrWSxFQUFhYSx3REFFakMsS0FLUHlGLEVBQVlyWixhQUNWK1MsRUFBYWMsVUFBWXdGLEVBQVlyWixhQUV2Q2hHLElBQ0UsRUFDQSx5QkFBeUIrWSxFQUFhYSx5Q0FBeUN5RixFQUFZcloseUNBRXRGLElBbENQaEcsSUFDRSxFQUNBLHlCQUF5QitZLEVBQWFhLHNEQUVqQyxHQThDWHlCLFFBQVN6SixNQUFPbUgsSUFNZCxHQUxBL1ksSUFDRSxFQUNBLHlCQUF5QitZLEVBQWFhLDhCQUdwQ2IsRUFBYUMsT0FBU0QsRUFBYUMsS0FBS0ksV0FDMUMsSUFFRUwsRUFBYUMsS0FBS3NKLG1CQUFtQixhQUNyQ3ZKLEVBQWFDLEtBQUtzSixtQkFBbUIsV0FDckN2SixFQUFhQyxLQUFLc0osbUJBQW1CLHVCQUcvQnZKLEVBQWFDLEtBQUtILE9BQ3pCLENBQUMsTUFBT2pZLEdBS1AsTUFKQVosSUFDRSxFQUNBLHlCQUF5QitZLEVBQWFhLG1EQUVsQ2haLENBQ1AsQ0FDRixFQUdQLENDMWtCTyxTQUFTMmhCLFNBQVN0bEIsR0FFdkIsTUFBTW9JLEVBQVMsSUFBSW1kLE1BQUFBLE1BQU0sSUFBSW5kLE9BTTdCLE9BSGVvZCxVQUFVcGQsR0FHWGtkLFNBQVN0bEIsRUFBTyxDQUFFeWxCLFNBQVUsQ0FBQyxrQkFDN0MsQ0NIQSxJQUFJcmUsb0JBQXFCLEVBbUJsQnVOLGVBQWUrUSxhQUFhemYsR0FFakMsSUFBSUEsSUFBV0EsRUFBUUgsT0FxQ3JCLE1BQU0sSUFBSTZQLFlBQ1Isa0tBQ0EsV0FyQ0lnUSxZQUFZMWYsR0FBUzBPLE1BQU9oUixFQUFPaWlCLEtBRXZDLEdBQUlqaUIsRUFDRixNQUFNQSxFQUlSLE1BQU0yQyxJQUFFQSxFQUFHdEgsUUFBRUEsRUFBT0QsS0FBRUEsR0FBUzZtQixFQUFLM2YsUUFBUUgsT0FHNUMsSUFDTVEsRUFFRjRSLEdBQWFBLGNBQ1gsR0FBR2xaLEVBQVFFLE1BQU0sS0FBS0MsU0FBVyxjQUNqQ1ksVUFBVTZsQixFQUFLekYsT0FBUXBoQixJQUl6Qm1aLEdBQWFBLGNBQ1hsWixHQUFXLFNBQVNELElBQ1gsUUFBVEEsRUFBaUJrQixPQUFPQyxLQUFLMGxCLEVBQUt6RixPQUFRLFVBQVl5RixFQUFLekYsT0FHaEUsQ0FBQyxNQUFPeGMsR0FDUCxNQUFNLElBQUlnUyxZQUNSLHNDQUNBLEtBQ0FLLFNBQVNyUyxFQUNaLE9BR0t5ZixVQUFVLEdBUXRCLENBcUJPek8sZUFBZWtSLFlBQVk1ZixHQUVoQyxLQUFJQSxHQUFXQSxFQUFRSCxRQUFVRyxFQUFRSCxPQUFPSyxPQTRFOUMsTUFBTSxJQUFJd1AsWUFDUiwrR0FDQSxLQTlFbUQsQ0FFckQsTUFBTW1RLEVBQWlCLEdBR3ZCLElBQUssSUFBSUMsS0FBUTlmLEVBQVFILE9BQU9LLE1BQU1qSCxNQUFNLE1BQVEsR0FDbEQ2bUIsRUFBT0EsRUFBSzdtQixNQUFNLEtBQ0UsSUFBaEI2bUIsRUFBS2xsQixPQUNQaWxCLEVBQWU3aEIsS0FDYjBoQixZQUNFLElBQ0sxZixFQUNISCxPQUFRLElBQ0hHLEVBQVFILE9BQ1hDLE9BQVFnZ0IsRUFBSyxHQUNiL21CLFFBQVMrbUIsRUFBSyxNQUdsQixDQUFDcGlCLEVBQU9paUIsS0FFTixHQUFJamlCLEVBQ0YsTUFBTUEsRUFJUixNQUFNMkMsSUFBRUEsRUFBR3RILFFBQUVBLEVBQU9ELEtBQUVBLEdBQVM2bUIsRUFBSzNmLFFBQVFILE9BRzVDLElBQ01RLEVBRUY0UixHQUFhQSxjQUNYLEdBQUdsWixFQUFRRSxNQUFNLEtBQUtDLFNBQVcsY0FDakNZLFVBQVU2bEIsRUFBS3pGLE9BQVFwaEIsSUFJekJtWixHQUFhQSxjQUNYbFosRUFDUyxRQUFURCxFQUNJa0IsT0FBT0MsS0FBSzBsQixFQUFLekYsT0FBUSxVQUN6QnlGLEVBQUt6RixPQUdkLENBQUMsTUFBT3hjLEdBQ1AsTUFBTSxJQUFJZ1MsWUFDUixzQ0FDQSxLQUNBSyxTQUFTclMsRUFDWixNQUtQWixJQUFJLEVBQUcsdURBS1gsTUFBTWlqQixRQUFxQmpSLFFBQVFrUixXQUFXSCxTQUd4QzFDLFdBR040QyxFQUFhcmIsU0FBUSxDQUFDd1YsRUFBUXBOLEtBRXhCb04sRUFBTytGLFFBQ1R4aUIsYUFDRSxFQUNBeWMsRUFBTytGLE9BQ1AsK0JBQStCblQsRUFBUSxzQ0FFMUMsR0FFUCxDQU1BLENBOEJPNEIsZUFBZWdSLFlBQVkvVCxFQUFldVUsR0FDL0MsSUFFRXBqQixJQUFJLEVBQUcsMkNBR1AsTUFBTWtELEVBQVVvTSxhQUFhWixZQUFXLEdBQVFHLEdBRzFDbU4sRUFBZ0I5WSxFQUFRSCxPQUc5QixHQUE2QixPQUF6QmlaLEVBQWNoWixPQUFpQixDQUdqQyxJQUFJcWdCLEVBRkpyakIsSUFBSSxFQUFHLG1EQUdQLElBRUVxakIsRUFBY2hrQixHQUFZQSxhQUN4Qm5ELGdCQUFnQjhmLEVBQWNoWixRQUM5QixPQUVILENBQUMsTUFBT3BDLEdBQ1AsTUFBTSxJQUFJZ1MsWUFDUixtREFDQSxLQUNBSyxTQUFTclMsRUFDWixDQUdELEdBQUlvYixFQUFjaFosT0FBTzVELFNBQVMsUUFFaEM0YyxFQUFjN1ksSUFBTWtnQixNQUNmLEtBQUlySCxFQUFjaFosT0FBTzVELFNBQVMsU0FJdkMsTUFBTSxJQUFJd1QsWUFDUixrREFDQSxLQUpGb0osRUFBYy9ZLE1BQVFvZ0IsQ0FNdkIsQ0FDRixDQUdELEdBQTBCLE9BQXRCckgsRUFBYzdZLElBQWMsQ0FDOUJuRCxJQUFJLEVBQUcscURBR0xpaEIsZUFBZWpDLHVCQUdqQixNQUFNNUIsUUFBZWtHLGVBQ25CZixTQUFTdkcsRUFBYzdZLEtBQ3ZCRCxHQU9GLFFBSEUrZCxlQUFlbkMsZUFHVnNFLEVBQVksS0FBTWhHLEVBQzFCLENBR0QsR0FBNEIsT0FBeEJwQixFQUFjL1ksT0FBNEMsT0FBMUIrWSxFQUFjOVksUUFBa0IsQ0FDbEVsRCxJQUFJLEVBQUcsc0RBR0xpaEIsZUFBZWhDLDJCQUdqQixNQUFNN0IsUUFBZW1HLG1CQUNuQnZILEVBQWMvWSxPQUFTK1ksRUFBYzlZLFFBQ3JDQSxHQU9GLFFBSEUrZCxlQUFlbEMsbUJBR1ZxRSxFQUFZLEtBQU1oRyxFQUMxQixDQUdELE9BQU9nRyxFQUNMLElBQUl4USxZQUNGLGdKQUNBLEtBR0wsQ0FBQyxNQUFPaFMsR0FDUCxPQUFPd2lCLEVBQVl4aUIsRUFDcEIsQ0FDSCxDQVNPLFNBQVM0aUIsd0JBQ2QsT0FBT25mLGtCQUNULENBVU8sU0FBU29mLHNCQUFzQi9rQixHQUNwQzJGLG1CQUFxQjNGLENBQ3ZCLENBa0JBa1QsZUFBZTBSLGVBQWVJLEVBQWV4Z0IsR0FFM0MsR0FDMkIsaUJBQWxCd2dCLElBQ05BLEVBQWM5TyxRQUFRLFNBQVcsR0FBSzhPLEVBQWM5TyxRQUFRLFVBQVksR0FZekUsT0FWQTVVLElBQUksRUFBRyxpQ0FHUGtELEVBQVFILE9BQU9JLElBQU11Z0IsRUFHckJ4Z0IsRUFBUUgsT0FBT0UsTUFBUSxLQUN2QkMsRUFBUUgsT0FBT0csUUFBVSxLQUdsQnlnQixlQUFlemdCLEdBRXRCLE1BQU0sSUFBSTBQLFlBQVksbUNBQW9DLElBRTlELENBa0JBaEIsZUFBZTJSLG1CQUFtQkcsRUFBZXhnQixHQUMvQ2xELElBQUksRUFBRyx1Q0FHUCxNQUFNc1EsRUFBcUJMLGdCQUN6QnlULEdBQ0EsRUFDQXhnQixFQUFRa0IsWUFBWUMsb0JBSXRCLEdBQ3lCLE9BQXZCaU0sR0FDOEIsaUJBQXZCQSxJQUNOQSxFQUFtQmhSLFdBQVcsT0FDOUJnUixFQUFtQmxSLFNBQVMsS0FFN0IsTUFBTSxJQUFJd1QsWUFDUixvUEFDQSxLQVdKLE9BTkExUCxFQUFRSCxPQUFPRSxNQUFRcU4sRUFHdkJwTixFQUFRSCxPQUFPSSxJQUFNLEtBR2R3Z0IsZUFBZXpnQixFQUN4QixDQWNBME8sZUFBZStSLGVBQWV6Z0IsR0FDNUIsTUFBUUgsT0FBUWlaLEVBQWU1WCxZQUFhMlYsR0FBdUI3VyxFQStCbkUsT0E1QkE4WSxFQUFjaGdCLEtBQU9LLFFBQVEyZixFQUFjaGdCLEtBQU1nZ0IsRUFBYy9mLFNBRy9EK2YsRUFBYy9mLFFBQVVGLFdBQVdpZ0IsRUFBY2hnQixLQUFNZ2dCLEVBQWMvZixTQUdyRStELElBQ0UsRUFDQSwrQkFBK0IrWixFQUFtQjFWLG1CQUFxQixVQUFZLGlCQUlyRnVmLG1CQUFtQjdKLEVBQW9CQSxFQUFtQjFWLG9CQUcxRHdmLHNCQUNFN0gsRUFDQWpDLEVBQW1CN2EsbUJBQ25CNmEsRUFBbUIxVixvQkFJckJuQixFQUFRSCxPQUFTLElBQ1ppWixLQUNBOEgsZUFBZTlILElBSWJ5RSxTQUFTdmQsRUFDbEIsQ0FvQkEsU0FBUzRnQixlQUFlOUgsR0FFdEIsTUFBUTVFLE1BQU8yTSxFQUFjck4sVUFBV3NOLEdBQ3RDaEksRUFBYzlZLFNBQVcrTSxnQkFBZ0IrTCxFQUFjL1ksU0FBVSxHQUczRG1VLE1BQU82TSxFQUFvQnZOLFVBQVd3TixHQUM1Q2pVLGdCQUFnQitMLEVBQWMvWCxpQkFBa0IsR0FHMUNtVCxNQUFPK00sRUFBbUJ6TixVQUFXME4sR0FDM0NuVSxnQkFBZ0IrTCxFQUFjOVgsZ0JBQWlCLEVBTTNDUCxFQUFRbEYsWUFDWkksS0FBS21GLElBQ0gsR0FDQW5GLEtBQUtrRixJQUNIaVksRUFBY3JZLE9BQ1pxZ0IsR0FBa0JyZ0IsT0FDbEJ1Z0IsR0FBd0J2Z0IsT0FDeEJ5Z0IsR0FBdUJ6Z0IsT0FDdkJxWSxFQUFjbFksY0FDZCxFQUNGLElBR0osR0E0QklzWSxFQUFPLENBQUUzWSxPQXZCYnVZLEVBQWN2WSxRQUNkdWdCLEdBQWtCSyxjQUNsQk4sR0FBY3RnQixRQUNkeWdCLEdBQXdCRyxjQUN4QkosR0FBb0J4Z0IsUUFDcEIyZ0IsR0FBdUJDLGNBQ3ZCRixHQUFtQjFnQixRQUNuQnVZLEVBQWNwWSxlQUNkLElBZXFCRixNQVhyQnNZLEVBQWN0WSxPQUNkc2dCLEdBQWtCTSxhQUNsQlAsR0FBY3JnQixPQUNkd2dCLEdBQXdCSSxhQUN4QkwsR0FBb0J2Z0IsT0FDcEIwZ0IsR0FBdUJFLGFBQ3ZCSCxHQUFtQnpnQixPQUNuQnNZLEVBQWNuWSxjQUNkLElBRzRCRixTQUc5QixJQUFLLElBQUs0Z0IsRUFBTzdsQixLQUFVckQsT0FBT29VLFFBQVEyTSxHQUN4Q0EsRUFBS21JLEdBQ2MsaUJBQVY3bEIsR0FBc0JBLEVBQU03QyxRQUFRLFNBQVUsSUFBTTZDLEVBSS9ELE9BQU8wZCxDQUNULENBa0JBLFNBQVN3SCxtQkFBbUI3SixFQUFvQjFWLEdBRTlDLEdBQUlBLEVBQW9CLENBRXRCLEdBQTRDLGlCQUFqQzBWLEVBQW1CeFYsVUFFNUJ3VixFQUFtQnhWLFVBQVlpZ0IsaUJBQzdCekssRUFBbUJ4VixVQUNuQndWLEVBQW1CN2Esb0JBQ25CLFFBRUcsSUFBSzZhLEVBQW1CeFYsVUFDN0IsSUFFRXdWLEVBQW1CeFYsVUFBWWlnQixpQkFDN0JubEIsR0FBQUEsYUFBYW5ELGdCQUFnQixrQkFBbUIsUUFDaEQ2ZCxFQUFtQjdhLG9CQUNuQixFQUVILENBQUMsTUFBTzBCLEdBQ1BaLElBQUksRUFBRyw0REFDUixDQUlILElBRUUrWixFQUFtQjlhLFdBQWFELFdBQzlCK2EsRUFBbUI5YSxXQUNuQjhhLEVBQW1CN2EsbUJBRXRCLENBQUMsTUFBTzBCLEdBQ1BELGFBQWEsRUFBR0MsRUFBTyw4Q0FHdkJtWixFQUFtQjlhLFdBQWEsSUFDakMsQ0FHRCxJQUVFOGEsRUFBbUJ6VixTQUFXdEYsV0FDNUIrYSxFQUFtQnpWLFNBQ25CeVYsRUFBbUI3YSxvQkFDbkIsRUFFSCxDQUFDLE1BQU8wQixHQUNQRCxhQUFhLEVBQUdDLEVBQU8sNENBR3ZCbVosRUFBbUJ6VixTQUFXLElBQy9CLENBR0csQ0FBQyxVQUFNN0QsR0FBVzNFLFNBQVNpZSxFQUFtQjlhLGFBQ2hEZSxJQUFJLEVBQUcsdURBSUwsQ0FBQyxVQUFNUyxHQUFXM0UsU0FBU2llLEVBQW1CelYsV0FDaER0RSxJQUFJLEVBQUcscURBSUwsQ0FBQyxVQUFNUyxHQUFXM0UsU0FBU2llLEVBQW1CeFYsWUFDaER2RSxJQUFJLEVBQUcscURBRWIsTUFJSSxHQUNFK1osRUFBbUJ6VixVQUNuQnlWLEVBQW1CeFYsV0FDbkJ3VixFQUFtQjlhLFdBUW5CLE1BTEE4YSxFQUFtQnpWLFNBQVcsS0FDOUJ5VixFQUFtQnhWLFVBQVksS0FDL0J3VixFQUFtQjlhLFdBQWEsS0FHMUIsSUFBSTJULFlBQ1Isb0dBQ0EsSUFJUixDQWtCQSxTQUFTNFIsaUJBQ1BqZ0IsRUFBWSxLQUNackYsRUFDQW1GLEdBR0EsTUFBTW9nQixFQUFlLENBQUMsS0FBTSxNQUFPLFNBRW5DLElBQUlDLEVBQW1CbmdCLEVBQ25Cb2dCLEdBQW1CLEVBR3ZCLEdBQUl6bEIsR0FBc0JxRixFQUFVbkYsU0FBUyxTQUMzQyxJQUNFc2xCLEVBQW1CelUsZ0JBQ2pCNVEsR0FBQUEsYUFBYW5ELGdCQUFnQnFJLEdBQVksU0FDekMsRUFDQUYsRUFFUixDQUFNLE1BQ0EsT0FBTyxJQUNSLE1BR0RxZ0IsRUFBbUJ6VSxnQkFBZ0IxTCxHQUFXLEVBQU9GLEdBR2pEcWdCLElBQXFCeGxCLFVBQ2hCd2xCLEVBQWlCdEssTUFLNUIsSUFBSyxNQUFNd0ssS0FBWUYsRUFDaEJELEVBQWEzb0IsU0FBUzhvQixHQUVmRCxJQUNWQSxHQUFtQixVQUZaRCxFQUFpQkUsR0FPNUIsT0FBS0QsR0FLREQsRUFBaUJ0SyxRQUNuQnNLLEVBQWlCdEssTUFBUXNLLEVBQWlCdEssTUFBTTlSLEtBQUszSyxHQUFTQSxFQUFLSixXQUM5RG1uQixFQUFpQnRLLE9BQVNzSyxFQUFpQnRLLE1BQU10YyxRQUFVLFdBQ3ZENG1CLEVBQWlCdEssT0FLckJzSyxHQVpFLElBYVgsQ0FtQkEsU0FBU2Isc0JBQ1A3SCxFQUNBOWMsRUFDQW1GLEdBR0EsQ0FBQyxnQkFBaUIsZ0JBQWdCdUQsU0FBU2lkLElBQ3pDLElBRU03SSxFQUFjNkksS0FHZDNsQixHQUNzQyxpQkFBL0I4YyxFQUFjNkksSUFDckI3SSxFQUFjNkksR0FBYXpsQixTQUFTLFNBR3BDNGMsRUFBYzZJLEdBQWU1VSxnQkFDM0I1USxHQUFBQSxhQUFhbkQsZ0JBQWdCOGYsRUFBYzZJLElBQWUsU0FDMUQsRUFDQXhnQixHQUlGMlgsRUFBYzZJLEdBQWU1VSxnQkFDM0IrTCxFQUFjNkksSUFDZCxFQUNBeGdCLEdBSVAsQ0FBQyxNQUFPekQsR0FDUEQsYUFDRSxFQUNBQyxFQUNBLGlCQUFpQmlrQix5QkFJbkI3SSxFQUFjNkksR0FBZSxJQUM5QixLQUlDLENBQUMsVUFBTXBrQixHQUFXM0UsU0FBU2tnQixFQUFjL1gsZ0JBQzNDakUsSUFBSSxFQUFHLDBEQUlMLENBQUMsVUFBTVMsR0FBVzNFLFNBQVNrZ0IsRUFBYzlYLGVBQzNDbEUsSUFBSSxFQUFHLHdEQUVYLENDanlCQSxNQUFNOGtCLFNBQVcsR0FTVixTQUFTQyxTQUFTbkwsR0FDdkJrTCxTQUFTNWpCLEtBQUswWSxFQUNoQixDQVFPLFNBQVNvTCxpQkFDZGhsQixJQUFJLEVBQUcsMkRBQ1AsSUFBSyxNQUFNNFosS0FBTWtMLFNBQ2ZHLGNBQWNyTCxHQUNkc0wsYUFBYXRMLEVBRWpCLENDZkEsU0FBU3VMLG1CQUFtQnZrQixFQUFPd2tCLEVBQVMvUyxFQUFVZ1QsR0FVcEQsT0FSQTFrQixhQUFhLEVBQUdDLEdBR21CLGdCQUEvQjhOLGFBQWFqSSxNQUFNQyxnQkFDZDlGLEVBQU1LLE1BSVJva0IsRUFBS3prQixFQUNkLENBWUEsU0FBUzBrQixzQkFBc0Ixa0IsRUFBT3drQixFQUFTL1MsRUFBVWdULEdBRXZELE1BQU10a0IsUUFBRUEsRUFBT0UsTUFBRUEsR0FBVUwsRUFHckJrUyxFQUFhbFMsRUFBTWtTLFlBQWMsSUFHdkNULEVBQVNrVCxPQUFPelMsR0FBWTBTLEtBQUssQ0FBRTFTLGFBQVkvUixVQUFTRSxTQUMxRCxDQU9lLFNBQVN3a0IsZ0JBQWdCQyxHQUV0Q0EsRUFBSUMsSUFBSVIsb0JBR1JPLEVBQUlDLElBQUlMLHNCQUNWLENDMUNlLFNBQVNNLHVCQUN0QkYsRUFDQUcsRUFBc0JuWCxhQUFhL0osT0FBT1EsY0FFMUMsSUFFRSxHQUFJMGdCLEVBQW9CamhCLE9BQVEsQ0FDOUIsTUFBTWtoQixFQUNKLHlFQUdJQyxFQUFjLENBQ2xCL2hCLElBQUs2aEIsRUFBb0J6Z0IsYUFBZSxHQUN4Q0MsT0FBUXdnQixFQUFvQnhnQixRQUFVLEVBQ3RDQyxNQUFPdWdCLEVBQW9CdmdCLE9BQVMsRUFDcENDLFdBQVlzZ0IsRUFBb0J0Z0IsYUFBYyxFQUM5Q0MsUUFBU3FnQixFQUFvQnJnQixVQUFXLEVBQ3hDQyxVQUFXb2dCLEVBQW9CcGdCLFlBQWEsR0FJMUNzZ0IsRUFBWXhnQixZQUNkbWdCLEVBQUk5Z0IsT0FBTyxlQUliLE1BQU1vaEIsRUFBVUMsVUFBVSxDQUN4QkMsU0FBK0IsR0FBckJILEVBQVkxZ0IsT0FBYyxJQUVwQ3JCLElBQUsraEIsRUFBWS9oQixJQUVqQm1pQixRQUFTSixFQUFZemdCLE1BQ3JCOGdCLFFBQVMsQ0FBQ2hCLEVBQVMvUyxLQUNqQkEsRUFBU2dVLE9BQU8sQ0FDZGIsS0FBTSxLQUNKblQsRUFBU2tULE9BQU8sS0FBS2UsS0FBSyxDQUFFdmxCLFFBQVMra0IsR0FBTSxFQUU3Q1MsUUFBUyxLQUNQbFUsRUFBU2tULE9BQU8sS0FBS2UsS0FBS1IsRUFBSSxHQUVoQyxFQUVKVSxLQUFPcEIsSUFHcUIsSUFBeEJXLEVBQVl2Z0IsVUFDYyxJQUExQnVnQixFQUFZdGdCLFdBQ1oyZixFQUFRcUIsTUFBTXJyQixNQUFRMnFCLEVBQVl2Z0IsU0FDbEM0ZixFQUFRcUIsTUFBTUMsZUFBaUJYLEVBQVl0Z0IsWUFFM0N6RixJQUFJLEVBQUcsMkNBQ0EsS0FPYjBsQixFQUFJQyxJQUFJSyxHQUVSaG1CLElBQ0UsRUFDQSw4Q0FBOEMrbEIsRUFBWS9oQixvQkFBb0IraEIsRUFBWTFnQiw4Q0FBOEMwZ0IsRUFBWXhnQixjQUV2SixDQUNGLENBQUMsTUFBTzNFLEdBQ1AsTUFBTSxJQUFJZ1MsWUFDUix5RUFDQSxLQUNBSyxTQUFTclMsRUFDWixDQUNILENDekZBLE1BQU0rbEIsa0JBQWtCL1QsWUFRdEIsV0FBQUMsQ0FBWTlSLEVBQVMrUixHQUNuQkMsTUFBTWhTLEVBQVMrUixFQUNoQixDQVNELFNBQUE4VCxDQUFVOVQsR0FHUixPQUZBRSxLQUFLRixXQUFhQSxFQUVYRSxJQUNSLEVDVUgsU0FBUzZULHNCQUFzQnpCLEVBQVMvUyxFQUFVZ1QsR0FDaEQsSUFFRSxNQUFNeUIsRUFBYzFCLEVBQVEyQixRQUFRLGlCQUFtQixHQUd2RCxJQUNHRCxFQUFZaHJCLFNBQVMsc0JBQ3JCZ3JCLEVBQVlockIsU0FBUyx1Q0FDckJnckIsRUFBWWhyQixTQUFTLHVCQUV0QixNQUFNLElBQUk2cUIsVUFDUixpSEFDQSxLQUtKLE9BQU90QixHQUNSLENBQUMsTUFBT3prQixHQUNQLE9BQU95a0IsRUFBS3prQixFQUNiLENBQ0gsQ0FtQkEsU0FBU29tQixzQkFBc0I1QixFQUFTL1MsRUFBVWdULEdBQ2hELElBRUUsTUFBTTNMLEVBQU8wTCxFQUFRMUwsS0FHZnVOLEVBQVlqRixLQUFBQSxLQUFPbm1CLFFBQVEsS0FBTSxJQUd2QyxJQUFLNmQsR0FBUTliLGNBQWM4YixHQVF6QixNQVBBMVosSUFDRSxFQUNBLHlCQUF5QmluQix5QkFDdkI3QixFQUFRMkIsUUFBUSxvQkFBc0IzQixFQUFROEIsV0FBV0MsMkRBSXZELElBQUlSLFVBQ1Isc0tBQ0EsS0FLSixNQUFNdGlCLEVBQXFCbWYsd0JBR3JCdmdCLEVBQVFnTixnQkFFWnlKLEVBQUt6VyxPQUFTeVcsRUFBS3hXLFNBQVd3VyxFQUFLMVcsUUFBVTBXLEVBQUttSixNQUVsRCxFQUVBeGUsR0FJRixHQUFjLE9BQVZwQixJQUFtQnlXLEVBQUt2VyxJQVExQixNQVBBbkQsSUFDRSxFQUNBLHlCQUF5QmluQix5QkFDdkI3QixFQUFRMkIsUUFBUSxvQkFBc0IzQixFQUFROEIsV0FBV0MsMkZBQ21COVcsS0FBS2MsVUFBVXVJLE9BR3pGLElBQUlpTixVQUNSLGlSQUNBLEtBS0osR0FBSWpOLEVBQUt2VyxLQUFPcEYsdUJBQXVCMmIsRUFBS3ZXLEtBQzFDLE1BQU0sSUFBSXdqQixVQUNSLDRMQUNBLEtBMENKLE9BckNBdkIsRUFBUWdDLGlCQUFtQixDQUV6QnZHLFdBQVlvRyxFQUNabGtCLE9BQVEsQ0FDTkUsUUFDQUUsSUFBS3VXLEVBQUt2VyxJQUNWbEgsUUFDRXlkLEVBQUt6ZCxTQUNMLEdBQUdtcEIsRUFBUWlDLE9BQU9DLFVBQVksV0FBV2pyQixRQUFRcWQsRUFBSzFkLFFBQ3hEQSxLQUFNSyxRQUFRcWQsRUFBSzFkLEtBQU0wZCxFQUFLemQsU0FDOUJQLE9BQVFELFVBQVVpZSxFQUFLaGUsUUFDdkI2SCxJQUFLbVcsRUFBS25XLElBQ1ZDLFdBQVlrVyxFQUFLbFcsV0FDakJDLE9BQVFpVyxFQUFLalcsT0FDYkMsTUFBT2dXLEVBQUtoVyxNQUNaQyxNQUFPK1YsRUFBSy9WLE1BQ1pNLGNBQWVnTSxnQkFDYnlKLEVBQUt6VixlQUNMLEVBQ0FJLEdBRUZILGFBQWMrTCxnQkFDWnlKLEVBQUt4VixjQUNMLEVBQ0FHLElBR0pELFlBQWEsQ0FDWEMscUJBQ0FuRixvQkFBb0IsRUFDcEJELFdBQVl5YSxFQUFLemEsV0FDakJxRixTQUFVb1YsRUFBS3BWLFNBQ2ZDLFVBQVcwTCxnQkFBZ0J5SixFQUFLblYsV0FBVyxFQUFNRixLQUs5Q2doQixHQUNSLENBQUMsTUFBT3prQixHQUNQLE9BQU95a0IsRUFBS3prQixFQUNiLENBQ0gsQ0FPZSxTQUFTMm1CLHFCQUFxQjdCLEdBRTNDQSxFQUFJOEIsS0FBSyxDQUFDLElBQUssY0FBZVgsdUJBRzlCbkIsRUFBSThCLEtBQUssQ0FBQyxJQUFLLGNBQWVSLHNCQUNoQyxDQ2xMQSxNQUFNUyxhQUFlLENBQ25CQyxJQUFLLFlBQ0xDLEtBQU0sYUFDTkMsSUFBSyxZQUNMbkosSUFBSyxrQkFDTHRiLElBQUssaUJBZ0JQeU8sZUFBZWlXLGNBQWN6QyxFQUFTL1MsRUFBVWdULEdBQzlDLElBRUUsTUFBTXlDLEVBQWlCM3BCLGNBR3ZCLElBQUk0cEIsR0FBb0IsRUFDeEIzQyxFQUFRNEMsT0FBT3pWLEdBQUcsU0FBVTBWLElBQ3RCQSxJQUNGRixHQUFvQixFQUNyQixJQUlILE1BQU1oVyxFQUFpQnFULEVBQVFnQyxpQkFHekJILEVBQVlsVixFQUFlOE8sV0FHakM3Z0IsSUFBSSxFQUFHLGlEQUFpRGluQixZQUdsRHJFLFlBQVk3USxHQUFnQixDQUFDblIsRUFBT2lpQixLQUt4QyxHQUhBdUMsRUFBUTRDLE9BQU8xRixtQkFBbUIsU0FHOUJ5RixFQUNGL25CLElBQ0UsRUFDQSxxQkFBcUJpbkIsbUZBSHpCLENBU0EsR0FBSXJtQixFQUNGLE1BQU1BLEVBSVIsSUFBS2lpQixJQUFTQSxFQUFLekYsT0FTakIsTUFSQXBkLElBQ0UsRUFDQSxxQkFBcUJpbkIscUJBQ25CN0IsRUFBUTJCLFFBQVEsb0JBQ2hCM0IsRUFBUThCLFdBQVdDLG1EQUNpQnRFLEVBQUt6RixXQUd2QyxJQUFJdUosVUFDUiw2R0FDQSxLQUtKLEdBQUk5RCxFQUFLekYsT0FBUSxDQUNmcGQsSUFDRSxFQUNBLHFCQUFxQmluQix5Q0FBaURhLFVBSXhFLE1BQU05ckIsS0FBRUEsRUFBSXVILElBQUVBLEVBQUdDLFdBQUVBLEVBQVV2SCxRQUFFQSxHQUFZNG1CLEVBQUszZixRQUFRSCxPQUd4RCxPQUFJUSxFQUNLOE8sRUFBU2lVLEtBQUt0cEIsVUFBVTZsQixFQUFLekYsT0FBUXBoQixLQUk5Q3FXLEVBQVM2VixPQUFPLGVBQWdCVCxhQUFhenJCLElBQVMsYUFHakR3SCxHQUNINk8sRUFBUzhWLFdBQVdsc0IsR0FJTixRQUFURCxFQUNIcVcsRUFBU2lVLEtBQUt6RCxFQUFLekYsUUFDbkIvSyxFQUFTaVUsS0FBS3BwQixPQUFPQyxLQUFLMGxCLEVBQUt6RixPQUFRLFdBQzVDLENBbERBLENBa0RBLEdBRUosQ0FBQyxNQUFPeGMsR0FDUCxPQUFPeWtCLEVBQUt6a0IsRUFDYixDQUNILENBU2UsU0FBU3duQixhQUFhMUMsR0FLbkNBLEVBQUk4QixLQUFLLElBQUtLLGVBTWRuQyxFQUFJOEIsS0FBSyxhQUFjSyxjQUN6QixDQ3BJQSxNQUFNUSxnQkFBa0IsSUFBSS9xQixLQUd0QmdyQixZQUFjalksS0FBSzlCLE1BQU1sUCxHQUFZQSxhQUFDdEMsS0FBSUEsS0FBQzdDLFlBQVcsa0JBR3REcXVCLGFBQWUsR0FHZkMsZUFBaUIsSUFHakJDLFdBQWEsR0FVbkIsU0FBU0MsMEJBQ1AsT0FBT0gsYUFBYTFZLFFBQU8sQ0FBQzhZLEVBQUdDLElBQU1ELEVBQUlDLEdBQUcsR0FBS0wsYUFBYXpxQixNQUNoRSxDQVVBLFNBQVMrcUIsb0JBQ1AsT0FBT0MsYUFBWSxLQUNqQixNQUFNQyxFQUFROUgsZUFDUitILEVBQ3VCLElBQTNCRCxFQUFNcEssaUJBQ0YsRUFDQ29LLEVBQU1uSyxpQkFBbUJtSyxFQUFNcEssaUJBQW9CLElBRTFENEosYUFBYXJuQixLQUFLOG5CLEdBQ2RULGFBQWF6cUIsT0FBUzJxQixZQUN4QkYsYUFBYW5zQixPQUNkLEdBQ0Fvc0IsZUFDTCxDQVNlLFNBQVNTLGFBQWF2RCxHQUduQ1gsU0FBUzhELHFCQUtUbkQsRUFBSXRULElBQUksV0FBVyxDQUFDZ1QsRUFBUy9TLEVBQVVnVCxLQUNyQyxJQUNFcmxCLElBQUksRUFBRyxxQ0FFUCxNQUFNK29CLEVBQVE5SCxlQUNSaUksRUFBU1gsYUFBYXpxQixPQUN0QnFyQixFQUFnQlQsMEJBR3RCclcsRUFBU2lVLEtBQUssQ0FFWmYsT0FBUSxLQUNSNkQsU0FBVWYsZ0JBQ1ZnQixPQUFRLEdBQUd4cUIsS0FBS3lxQixPQUFPOXJCLGlCQUFtQjZxQixnQkFBZ0I1cUIsV0FBYSxJQUFPLGNBRzlFOHJCLGNBQWVqQixZQUFZaG1CLFFBQzNCa25CLGtCQUFtQmhWLHVCQUduQmlWLGtCQUFtQlYsRUFBTTVKLGlCQUN6QnVLLGlCQUFrQlgsRUFBTXBLLGlCQUN4QmdMLGlCQUFrQlosRUFBTW5LLGlCQUN4QmdMLGNBQWViLEVBQU1sSyxlQUNyQmdMLFlBQWNkLEVBQU1uSyxpQkFBbUJtSyxFQUFNcEssaUJBQW9CLElBR2pFOVksS0FBTXFiLGtCQUdOZ0ksU0FDQUMsZ0JBQ0Fwb0IsUUFDRTZILE1BQU11Z0IsS0FBbUJaLGFBQWF6cUIsT0FDbEMsb0VBQ0EsUUFBUW9yQixtQ0FBd0NDLEVBQWNXLFFBQVEsT0FHNUVDLFdBQVloQixFQUFNakssZUFDbEJrTCxZQUFhakIsRUFBTWhLLG1CQUNuQmtMLG1CQUFvQmxCLEVBQU0vSix1QkFDMUJrTCxvQkFBcUJuQixFQUFNOUosNEJBRTlCLENBQUMsTUFBT3JlLEdBQ1AsT0FBT3lrQixFQUFLemtCLEVBQ2IsSUFFTCxDQzdHZSxTQUFTdXBCLFNBQVN6RSxHQUkvQkEsRUFBSXRULElBQUkxRCxhQUFhbkksR0FBR0MsT0FBUyxLQUFLLENBQUM0ZSxFQUFTL1MsRUFBVWdULEtBQ3hELElBQ0VoVCxFQUFTK1gsU0FBU3J0QixLQUFJQSxLQUFDN0MsWUFBVyxTQUFVLGNBQWUsQ0FDekRtd0IsY0FBYyxHQUVqQixDQUFDLE1BQU96cEIsR0FDUCxPQUFPeWtCLEVBQUt6a0IsRUFDYixJQUVMLENDYmUsU0FBUzBwQixvQkFBb0I1RSxHQUsxQ0EsRUFBSThCLEtBQUssK0JBQStCNVYsTUFBT3dULEVBQVMvUyxFQUFVZ1QsS0FDaEUsSUFFRSxNQUFNa0YsRUFBYWxjLEtBQUsvRSx1QkFHeEIsSUFBS2loQixJQUFlQSxFQUFXenNCLE9BQzdCLE1BQU0sSUFBSTZvQixVQUNSLGlIQUNBLEtBS0osTUFBTTZELEVBQVFwRixFQUFRaFQsSUFBSSxXQUcxQixJQUFLb1ksR0FBU0EsSUFBVUQsRUFDdEIsTUFBTSxJQUFJNUQsVUFDUiwyRUFDQSxLQUtKLE1BQU1qUyxFQUFhMFEsRUFBUWlDLE9BQU8zUyxXQUNsQyxJQUFJQSxFQW1CRixNQUFNLElBQUlpUyxVQUFVLHFDQUFzQyxLQWxCMUQsVUFFUWxTLHdCQUF3QkMsRUFDL0IsQ0FBQyxNQUFPOVQsR0FDUCxNQUFNLElBQUkrbEIsVUFDUiw2QkFBNkIvbEIsRUFBTUcsVUFDbkMsS0FDQWtTLFNBQVNyUyxFQUNaLENBR0R5UixFQUFTa1QsT0FBTyxLQUFLZSxLQUFLLENBQ3hCeFQsV0FBWSxJQUNaMFcsa0JBQW1CaFYsdUJBQ25CelQsUUFBUywrQ0FBK0MyVCxNQU03RCxDQUFDLE1BQU85VCxHQUNQLE9BQU95a0IsRUFBS3prQixFQUNiLElBRUwsQ0N2Q0EsTUFBTTZwQixjQUFnQixJQUFJQyxJQUdwQmhGLElBQU1pRixVQXFCTC9ZLGVBQWVnWixZQUFZQyxFQUFnQm5jLGFBQWEvSixRQUM3RCxJQUVFLElBQUtrbUIsRUFBY2ptQixTQUFXOGdCLElBQzVCLE1BQU0sSUFBSTlTLFlBQ1IsbUZBQ0EsS0FNSixNQUFNa1ksRUFBK0MsS0FBNUJELEVBQWM5bEIsWUFBcUIsS0FHdERnbUIsRUFBVUMsT0FBT0MsZ0JBR2pCQyxFQUFTRixPQUFPLENBQ3BCRCxVQUNBSSxPQUFRLENBQ05DLFVBQVdOLEtBMkNmLEdBdENBcEYsSUFBSTJGLFFBQVEsZ0JBR1ozRixJQUFJQyxJQUNGMkYsS0FBSyxDQUNIQyxRQUFTLENBQUMsT0FBUSxNQUFPLGNBTTdCN0YsSUFBSUMsS0FBSSxDQUFDUCxFQUFTL1MsRUFBVWdULEtBQzFCaFQsRUFBU21aLElBQUksZ0JBQWlCLFFBQzlCbkcsR0FBTSxJQUlSSyxJQUFJQyxJQUNGZ0YsUUFBUW5GLEtBQUssQ0FDWGlHLE1BQU9YLEtBS1hwRixJQUFJQyxJQUNGZ0YsUUFBUWUsV0FBVyxDQUNqQkMsVUFBVSxFQUNWRixNQUFPWCxLQUtYcEYsSUFBSUMsSUFBSXVGLEVBQU9VLFFBR2ZsRyxJQUFJQyxJQUFJZ0YsUUFBUWtCLE9BQU85dUIsS0FBSUEsS0FBQzdDLFlBQVcsYUFHbEMyd0IsRUFBY25sQixJQUFJQyxNQUFPLENBRTVCLE1BQU1tbUIsRUFBYW5aLEtBQUtvWixhQUFhckcsS0FHckNzRywyQkFBMkJGLEdBRzNCQSxFQUFXRyxPQUFPcEIsRUFBYy9sQixLQUFNK2xCLEVBQWNobUIsTUFBTSxLQUV4RDRsQixjQUFjZSxJQUFJWCxFQUFjL2xCLEtBQU1nbkIsR0FFdEM5ckIsSUFDRSxFQUNBLG1DQUFtQzZxQixFQUFjaG1CLFFBQVFnbUIsRUFBYy9sQixRQUN4RSxHQUVKLENBR0QsR0FBSStsQixFQUFjbmxCLElBQUlkLE9BQVEsQ0FFNUIsSUFBSXhKLEVBQUs4d0IsRUFFVCxJQUVFOXdCLFFBQVkrd0IsU0FBUUEsU0FDbEJwdkIsS0FBSUEsS0FBQ2IsZ0JBQWdCMnVCLEVBQWNubEIsSUFBSUUsVUFBVyxjQUNsRCxRQUlGc21CLFFBQWFDLFNBQVFBLFNBQ25CcHZCLEtBQUlBLEtBQUNiLGdCQUFnQjJ1QixFQUFjbmxCLElBQUlFLFVBQVcsY0FDbEQsT0FFSCxDQUFDLE1BQU9oRixHQUNQWixJQUNFLEVBQ0EscURBQXFENnFCLEVBQWNubEIsSUFBSUUsc0RBRTFFLENBRUQsR0FBSXhLLEdBQU84d0IsRUFBTSxDQUVmLE1BQU1FLEVBQWMxWixNQUFNcVosYUFBYSxDQUFFM3dCLE1BQUs4d0IsUUFBUXhHLEtBR3REc0csMkJBQTJCSSxHQUczQkEsRUFBWUgsT0FBT3BCLEVBQWNubEIsSUFBSVosS0FBTStsQixFQUFjaG1CLE1BQU0sS0FFN0Q0bEIsY0FBY2UsSUFBSVgsRUFBY25sQixJQUFJWixLQUFNc25CLEdBRTFDcHNCLElBQ0UsRUFDQSxvQ0FBb0M2cUIsRUFBY2htQixRQUFRZ21CLEVBQWNubEIsSUFBSVosUUFDN0UsR0FFSixDQUNGLENBR0Q4Z0IsdUJBQXVCRixJQUFLbUYsRUFBYzFsQixjQUcxQ29pQixxQkFBcUI3QixLQUdyQnVELGFBQWF2RCxLQUNiMEMsYUFBYTFDLEtBQ2J5RSxTQUFTekUsS0FDVDRFLG9CQUFvQjVFLEtBR3BCRCxnQkFBZ0JDLElBQ2pCLENBQUMsTUFBTzlrQixHQUNQLE1BQU0sSUFBSWdTLFlBQ1IscURBQ0EsS0FDQUssU0FBU3JTLEVBQ1osQ0FDSCxDQU9PLFNBQVN5ckIsZUFFZCxHQUFJNUIsY0FBY3JPLEtBQU8sRUFBRyxDQUMxQnBjLElBQUksRUFBRyxpQ0FHUCxJQUFLLE1BQU84RSxFQUFNSCxLQUFXOGxCLGNBQzNCOWxCLEVBQU9rVSxPQUFNLEtBQ1g0UixjQUFjNkIsT0FBT3huQixHQUNyQjlFLElBQUksRUFBRyxtQ0FBbUM4RSxLQUFRLEdBR3ZELENBQ0gsQ0FTTyxTQUFTeW5CLGFBQ2QsT0FBTzlCLGFBQ1QsQ0FTTyxTQUFTK0IsYUFDZCxPQUFPN0IsT0FDVCxDQVNPLFNBQVM4QixTQUNkLE9BQU8vRyxHQUNULENBVU8sU0FBU2dILG1CQUFtQjdHLEdBQ2pDRCx1QkFBdUJGLElBQUtHLEVBQzlCLENBVU8sU0FBU0YsSUFBSTlvQixLQUFTOHZCLEdBQzNCakgsSUFBSUMsSUFBSTlvQixLQUFTOHZCLEVBQ25CLENBVU8sU0FBU3ZhLElBQUl2VixLQUFTOHZCLEdBQzNCakgsSUFBSXRULElBQUl2VixLQUFTOHZCLEVBQ25CLENBVU8sU0FBU25GLEtBQUszcUIsS0FBUzh2QixHQUM1QmpILElBQUk4QixLQUFLM3FCLEtBQVM4dkIsRUFDcEIsQ0FTQSxTQUFTWCwyQkFBMkJybkIsR0FDbENBLEVBQU80TixHQUFHLGVBQWUsQ0FBQzNSLEVBQU9vbkIsS0FDL0JybkIsYUFDRSxFQUNBQyxFQUNBLDBCQUEwQkEsRUFBTUcsK0JBRWxDaW5CLEVBQU8zTSxTQUFTLElBR2xCMVcsRUFBTzROLEdBQUcsU0FBVTNSLElBQ2xCRCxhQUFhLEVBQUdDLEVBQU8sMEJBQTBCQSxFQUFNRyxVQUFVLElBR25FNEQsRUFBTzROLEdBQUcsY0FBZXlWLElBQ3ZCQSxFQUFPelYsR0FBRyxTQUFVM1IsSUFDbEJELGFBQWEsRUFBR0MsRUFBTywwQkFBMEJBLEVBQU1HLFVBQVUsR0FDakUsR0FFTixDQUVBLElBQWU0RCxPQUFBLENBQ2JpbUIsd0JBQ0F5QiwwQkFDQUUsc0JBQ0FDLHNCQUNBQyxjQUNBQyxzQ0FDQS9HLFFBQ0F2VCxRQUNBb1YsV0N4VUs1VixlQUFlZ2IsZ0JBQWdCQyxTQUU5QjdhLFFBQVFrUixXQUFXLENBRXZCOEIsaUJBR0FxSCxlQUdBaE0sYUFJRmhpQixRQUFReXVCLEtBQUtELEVBQ2YsQ0NnQk9qYixlQUFlbWIsV0FBV2xlLEdBRS9CLE1BQU0zTCxFQUFVb00sYUFBYVosWUFBVyxHQUFRRyxHQUdoRDRVLHNCQUFzQnZnQixFQUFRa0IsWUFBWUMsb0JBRzFDbEQsWUFBWStCLEVBQVExRCxTQUdoQjBELEVBQVF1RCxNQUFNRSxzQkFDaEJxbUIsb0NBSUkxWixvQkFBb0JwUSxFQUFRYixXQUFZYSxFQUFReUIsT0FBT00sYUFHdkRtYSxTQUFTbGMsRUFBUTJDLEtBQU0zQyxFQUFRcEIsVUFBVTdCLEtBQ2pELENBU0EsU0FBUytzQiw4QkFDUGh0QixJQUFJLEVBQUcsc0RBR1AzQixRQUFRa1UsR0FBRyxRQUFTMGEsSUFDbEJqdEIsSUFBSSxFQUFHLHNDQUFzQ2l0QixLQUFRLElBSXZENXVCLFFBQVFrVSxHQUFHLFVBQVVYLE1BQU9sQixFQUFNdWMsS0FDaENqdEIsSUFBSSxFQUFHLGlCQUFpQjBRLHNCQUF5QnVjLFlBQzNDTCxnQkFBZ0IsRUFBRSxJQUkxQnZ1QixRQUFRa1UsR0FBRyxXQUFXWCxNQUFPbEIsRUFBTXVjLEtBQ2pDanRCLElBQUksRUFBRyxpQkFBaUIwUSxzQkFBeUJ1YyxZQUMzQ0wsZ0JBQWdCLEVBQUUsSUFJMUJ2dUIsUUFBUWtVLEdBQUcsVUFBVVgsTUFBT2xCLEVBQU11YyxLQUNoQ2p0QixJQUFJLEVBQUcsaUJBQWlCMFEsc0JBQXlCdWMsWUFDM0NMLGdCQUFnQixFQUFFLElBSTFCdnVCLFFBQVFrVSxHQUFHLHFCQUFxQlgsTUFBT2hSLEVBQU84UCxLQUM1Qy9QLGFBQWEsRUFBR0MsRUFBTyxpQkFBaUI4UCxrQkFDbENrYyxnQkFBZ0IsRUFBRSxHQUU1QixDQUVBLElBQWU1YyxNQUFBLENBRWJyTCxjQUNBaW1CLHdCQUdBbGMsc0JBQ0FFLHNCQUNBVSwwQkFDQUksZ0NBR0FxZCxzQkFDQXBLLDBCQUNBRyx3QkFDQUYsd0JBR0F0UCx3Q0FHQThMLGtCQUNBaUIsa0JBR0FyZ0IsUUFDQVcsMEJBQ0FZLHdCQUNBQywwQ0FDQUMsb0NBR0FtckIifQ== diff --git a/dist/index.esm.js b/dist/index.esm.js index 1801a237..4d431f23 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 o,readFileSync as r,promises as i,writeFileSync as s}from"fs";import n,{join as a,posix as l}from"path";import{HttpsProxyAgent as c}from"https-proxy-agent";import p from"prompts";import h from"dotenv";import{z as u}from"zod";import{fileURLToPath as d}from"url";import g from"http";import m from"https";import{Pool as f}from"tarn";import{v4 as v}from"uuid";import y from"puppeteer";import{JSDOM as b}from"jsdom";import w from"dompurify";import E from"cors";import T from"express";import S from"multer";import x from"express-rate-limit";const O={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","overlapping-datalabels","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"]},R={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{value:O.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:O.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:O.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{value:O.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:{enable:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},delay:{value:0,type:"number",envLink:"SERVER_RATE_LIMITING_DELAY",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The `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."}}},L={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:R.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:R.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:R.highcharts.cdnURL.value},{type:"multiselect",name:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:R.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:R.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:R.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:R.highcharts.customScripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:R.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:R.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${R.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${R.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:R.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:R.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:R.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:R.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:R.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:R.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:R.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:R.server.host.value},{type:"number",name:"port",message:"Server port",initial:R.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:R.server.benchmarking.value},{type:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:R.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:R.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:R.server.proxy.timeout.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:R.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:R.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:R.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:R.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:R.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:R.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:R.server.rateLimiting.skipToken.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:R.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:R.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:R.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:R.server.ssl.certPath.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:R.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:R.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:R.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:R.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:R.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:R.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:R.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:R.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:R.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:R.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:R.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:R.logging.file.value},{type:"text",name:"dest",message:"The path to a log file when the file logging is enabled",initial:R.logging.dest.value},{type:"toggle",name:"toConsole",message:"Enable logging to the console",initial:R.logging.toConsole.value},{type:"toggle",name:"toFile",message:"Enables logging to a file",initial:R.logging.toFile.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:R.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:R.ui.route.value}],other:[{type:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:R.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:R.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:R.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:R.other.hardResetPage.value},{type:"toggle",name:"browserShellMode",message:"Decides if the browser runs in the shell mode",initial:R.other.browserShellMode.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:R.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:R.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:R.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:R.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:R.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:R.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:R.debug.debuggingPort.value}]},_=["options","globalOptions","themeOptions","resources","payload"],k={},I=(e,t="")=>{Object.keys(e).forEach((o=>{if(!["puppeteer","highcharts"].includes(o)){const r=e[o];void 0===r.value?I(r,`${t}.${o}`):(k[r.cliName||o]=`${t}.${o}`.substring(1),void 0!==r.legacyName&&(k[r.legacyName]=`${t}.${o}`.substring(1)))}}))};I(R),h.config();const C=e=>u.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),N=()=>u.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),A=e=>u.enum([...e,""]).transform((e=>""!==e?e:void 0)),P=()=>u.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),(e=>({message:`The string contains forbidden values, received '${e}'`}))).transform((e=>""!==e?e:void 0)),H=()=>u.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),(e=>({message:`The value must be numeric and positive, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),$=()=>u.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),(e=>({message:`The value must be numeric and non-negative, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),G=u.object({HIGHCHARTS_VERSION:u.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),(e=>({message:`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:u.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),(e=>({message:`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CORE_SCRIPTS:C(O.core),HIGHCHARTS_MODULE_SCRIPTS:C(O.modules),HIGHCHARTS_INDICATOR_SCRIPTS:C(O.indicators),HIGHCHARTS_FORCE_FETCH:N(),HIGHCHARTS_CACHE_PATH:P(),HIGHCHARTS_ADMIN_TOKEN:P(),EXPORT_TYPE:A(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:A(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:H(),EXPORT_DEFAULT_WIDTH:H(),EXPORT_DEFAULT_SCALE:H(),EXPORT_RASTERIZATION_TIMEOUT:$(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:N(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:N(),SERVER_ENABLE:N(),SERVER_HOST:P(),SERVER_PORT:H(),SERVER_BENCHMARKING:N(),SERVER_PROXY_HOST:P(),SERVER_PROXY_PORT:H(),SERVER_PROXY_TIMEOUT:$(),SERVER_RATE_LIMITING_ENABLE:N(),SERVER_RATE_LIMITING_MAX_REQUESTS:$(),SERVER_RATE_LIMITING_WINDOW:$(),SERVER_RATE_LIMITING_DELAY:$(),SERVER_RATE_LIMITING_TRUST_PROXY:N(),SERVER_RATE_LIMITING_SKIP_KEY:P(),SERVER_RATE_LIMITING_SKIP_TOKEN:P(),SERVER_SSL_ENABLE:N(),SERVER_SSL_FORCE:N(),SERVER_SSL_PORT:H(),SERVER_SSL_CERT_PATH:P(),POOL_MIN_WORKERS:$(),POOL_MAX_WORKERS:$(),POOL_WORK_LIMIT:H(),POOL_ACQUIRE_TIMEOUT:$(),POOL_CREATE_TIMEOUT:$(),POOL_DESTROY_TIMEOUT:$(),POOL_IDLE_TIMEOUT:$(),POOL_CREATE_RETRY_INTERVAL:$(),POOL_REAPER_INTERVAL:$(),POOL_BENCHMARKING:N(),LOGGING_LEVEL:u.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),(e=>({message:`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:P(),LOGGING_DEST:P(),LOGGING_TO_CONSOLE:N(),LOGGING_TO_FILE:N(),UI_ENABLE:N(),UI_ROUTE:P(),OTHER_NODE_ENV:A(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:N(),OTHER_NO_LOGO:N(),OTHER_HARD_RESET_PAGE:N(),OTHER_BROWSER_SHELL_MODE:N(),DEBUG_ENABLE:N(),DEBUG_HEADLESS:N(),DEBUG_DEVTOOLS:N(),DEBUG_LISTEN_TO_CONSOLE:N(),DEBUG_DUMPIO:N(),DEBUG_SLOW_MO:$(),DEBUG_DEBUGGING_PORT:H()}).partial().parse(process.env),D=["red","yellow","blue","gray","green"];let U={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:D[0]},{title:"warning",color:D[1]},{title:"notice",color:D[2]},{title:"verbose",color:D[3]},{title:"benchmark",color:D[4]}],listeners:[]};const j=(r,i)=>{U.pathCreated||(!e(U.dest)&&t(U.dest),U.pathCreated=!0),o(`${U.dest}${U.file}`,[i].concat(r).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),U.toFile=!1)}))},M=(...e)=>{const[t,...o]=e,{levelsDesc:r,level:i}=U;if(5!==t&&(0===t||t>i||i>r.length))return;const s=`${(new Date).toString().split("(")[0].trim()} [${r[t-1].title}] -`;U.listeners.forEach((e=>{e(s,o.join(" "))})),U.toConsole&&console.log.apply(void 0,[s.toString()[U.levelsDesc[t-1].color]].concat(o)),U.toFile&&j(o,s)},F=(e,t,o)=>{const r=o||t.message,{level:i,levelsDesc:s}=U;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=[r,"\n",a];U.toConsole&&console.log.apply(void 0,[n.toString()[U.levelsDesc[e-1].color]].concat([r[D[e-1]],"\n",a])),U.listeners.forEach((e=>{e(n,l.join(" "))})),U.toFile&&j(l,n)},W=e=>{e>=0&&e<=U.levelsDesc.length&&(U.level=e)},V=(e,t)=>{if(U={...U,dest:e||U.dest,file:t||U.file,toFile:!0},0===U.dest.length)return M(1,"[logger] File logging initialization: no path supplied.");U.dest.endsWith("/")||(U.dest+="/")},q=d(new URL("../.",import.meta.url)),B=(e,t)=>{const o=["png","jpeg","pdf","svg"];if(t){const r=t.split(".").pop();"jpg"===r?e="jpeg":o.includes(r)&&e!==r&&(e=r)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||o.find((t=>t===e))||"png"},X=(e=!1,t)=>{const o=["js","css","files"];let i=e,s=!1;if(t&&e.endsWith(".json"))try{i=K(r(e,"utf8"))}catch(e){return F(2,e,"[cli] No resources found.")}else i=K(e),i&&!t&&delete i.files;for(const e in i)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):M(3,"[cli] No resources found.")};function K(e,t){try{const o=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof o&&t?JSON.stringify(o):o}catch{return!1}}const J=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=J(e[o]));return t},z=(e,t)=>JSON.stringify(e,((e,o)=>("string"==typeof o&&((o=o.trim()).startsWith("function(")||o.startsWith("function ("))&&o.endsWith("}")&&(o=t?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof o?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:o))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function Y(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[o,r]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(r,"value")){let e=` --${r.cliName||o} ${("<"+r.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,r.description,`[Default: ${r.value.toString().bold}]`.blue)}else e(r)};Object.keys(R).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(R[t]))})),console.log("\n")}const Q=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,Z=(e,t)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!t&&Z(r(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")},ee=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let te={};const oe=()=>te,re=(e,t,o=[])=>{const r=J(e);for(const[e,s]of Object.entries(t))r[e]="object"!=typeof(i=s)||Array.isArray(i)||null===i||o.includes(e)||void 0===r[e]?void 0!==s?s:r[e]:re(r[e],s,o);var i;return r};function ie(e,t={},o=""){Object.keys(e).forEach((r=>{const i=e[r],s=t&&t[r];void 0===i.value?ie(i,s,`${o}.${r}`):(void 0!==s&&(i.value=s),i.envLink in G&&void 0!==G[i.envLink]&&(i.value=G[i.envLink]))}))}function se(e){let t={};for(const[o,r]of Object.entries(e))t[o]=Object.prototype.hasOwnProperty.call(r,"value")?r.value:se(r);return t}function ne(e,t,o){for(;t.length>1;){const r=t.shift();return Object.prototype.hasOwnProperty.call(e,r)||(e[r]={}),e[r]=ne(Object.assign({},e[r]),t,o),e}return e[t[0]]=o,e}async function ae(e,t={}){return new Promise(((o,r)=>{const i=(e=>e.startsWith("https")?m:g)(e);i.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}class le extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}}const ce={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},pe=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),he=async(e,t,o,r=!1)=>{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),M(4,`[cache] Fetching script - ${e}.js`);const i=await ae(`${e}.js`,t);if(200===i.statusCode&&"string"==typeof i.text){if(o){o[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return i.text}if(r)throw new le(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${i.statusCode}).`).setError(i);return M(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},ue=async(e,t,o)=>{const r=e.version,i="latest"!==r&&r?`${r}/`:"",n=e.cdnURL||ce.cdnURL;M(3,`[cache] Updating cache version to Highcharts: ${i||"latest"}.`);const a={};try{return ce.sources=await(async(e,t,o,r,i)=>{let s;const n=r.host,a=r.port;if(n&&a)try{s=new c({host:n,port:a})}catch(e){throw new le("[cache] Could not create a Proxy Agent.").setError(e)}const l=s?{agent:s,timeout:G.SERVER_PROXY_TIMEOUT}:{},p=[...e.map((e=>he(`${e}`,l,i,!0))),...t.map((e=>he(`${e}`,l,i))),...o.map((e=>he(`${e}`,l)))];return(await Promise.all(p)).join(";\n")})([...e.coreScripts.map((e=>`${n}${i}${e}`))],[...e.moduleScripts.map((e=>"map"===e?`${n}maps/${i}modules/${e}`:`${n}${i}modules/${e}`)),...e.indicatorScripts.map((e=>`${n}stock/${i}indicators/${e}`))],e.customScripts,t,a),ce.hcVersion=pe(ce),s(o,ce.sources),a}catch(e){throw new le("[cache] Unable to update the local Highcharts cache.").setError(e)}},de=async o=>{const{highcharts:i,server:n}=o,l=a(q,i.cachePath);let c;const p=a(l,"manifest.json"),h=a(l,"sources.js");if(!e(l)&&t(l),!e(p)||i.forceFetch)M(3,"[cache] Fetching and caching Highcharts dependencies."),c=await ue(i,n.proxy,h);else{let e=!1;const t=JSON.parse(r(p));if(t.modules&&Array.isArray(t.modules)){const e={};t.modules.forEach((t=>e[t]=1)),t.modules=e}const{coreScripts:o,moduleScripts:s,indicatorScripts:a}=i,l=o.length+s.length+a.length;t.version!==i.version?(M(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),e=!0):Object.keys(t.modules||{}).length!==l?(M(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),e=!0):e=(s||[]).some((e=>{if(!t.modules[e])return M(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),e?c=await ue(i,n.proxy,h):(M(3,"[cache] Dependency cache is up to date, proceeding."),ce.sources=r(h,"utf8"),c=t.modules,ce.hcVersion=pe(ce))}await(async(e,t)=>{const o={version:e.version,modules:t||{}};ce.activeManifest=o,M(3,"[cache] Writing a new manifest.");try{s(a(q,e.cachePath,"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new le("[cache] Error writing the cache manifest.").setError(e)}})(i,c)},ge=()=>a(q,oe().highcharts.cachePath),me=()=>ce.hcVersion;function fe(){Highcharts.animObject=function(){return{duration:0}}}async function ve(e,t,o){window._displayErrors=o;const{getOptions:r,merge:i,setOptions:s,wrap:n}=Highcharts;Highcharts.setOptionsObj=i(!1,{},r()),t.customLogic.customCode&&new Function(t.customLogic.customCode)();const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,o){((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,o])})),n(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e,c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,h=JSON.parse(t.export.globalOptions);h&&s(h),Highcharts[t.export.constr||"chart"]("container",c,p);const u=r();for(const e in u)"function"!=typeof u[e]&&delete u[e];s(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const ye=r(q+"/templates/template.html","utf8");let be;async function we(){if(!be)return!1;const e=await be.newPage();return await e.setCacheEnabled(!1),await Te(e),function(e){const{debug:t}=oe();t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}));e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)}))}(e),e}async function Ee(e,t){for(const e of t)await e.dispose();await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}async function Te(e){await e.setContent(ye,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${ge()}/sources.js`}),await e.evaluate(fe)}const Se=async(e,t,o,r)=>e.evaluate(ve,t,o,r);var xe=async(e,t,o)=>{let i=[];try{M(4,"[export] Determining export path.");const s=o.export,a=s?.options?.chart?.displayErrors&&ce.activeManifest.modules.debugger;let l;if(t.indexOf&&(t.indexOf("=0||t.indexOf("=0)){if(M(4,"[export] Treating as SVG."),"svg"===s.type)return t;l=!0,await e.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(t),{waitUntil:"domcontentloaded"})}else M(4,"[export] Treating as config."),s.strInj?await Se(e,{chart:{height:s.height,width:s.width}},o,a):(t.chart.height=s.height,t.chart.width=s.width,await Se(e,t,o,a));i=await async function(e,t){const o=[],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:r(e,"utf8")}:{url:e})}for(const t of s)try{o.push(await e.addScriptTag(t))}catch(e){F(2,e,"[export] The JS resource cannot be loaded.")}s.length=0;const a=[];if(i.css){let r=i.css.match(/@import\s*([^;]*);/g);if(r)for(let e of r)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?a.push({url:e}):t.customLogic.allowFileResources&&a.push({path:n.join(q,e)}));a.push({content:i.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of a)try{o.push(await e.addStyleTag(t))}catch(e){F(2,e,"[export] The CSS resource cannot be loaded.")}a.length=0}}return o}(e,o);const c=l?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(s.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),p=Math.ceil(c.chartHeight||s.height),h=Math.ceil(c.chartWidth||s.width),{x:u,y:d}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:i}=e.getBoundingClientRect();return{x:t,y:o,width:r,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,o,r,i)=>Promise.race([e.screenshot({type:t,encoding:o,clip:r,captureBeyondViewport:!0,fullPage:!1,optimizeForSpeed:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new le("Rasterization timeout"))),i||1500)))]))(e,s.type,"base64",{width:h,height:p,x:u,y:d},s.rasterizationTimeout);else{if("pdf"!==s.type)throw new le(`[export] Unsupported output format ${s.type}.`);g=await(async(e,t,o,r,i)=>(await e.emulateMediaType("screen"),Promise.race([e.pdf({height:t+1,width:o,encoding:r}),new Promise(((e,t)=>setTimeout((()=>t(new le("Rasterization timeout"))),i||1500)))])))(e,p,h,"base64",s.rasterizationTimeout)}return await Ee(e,i),g}catch(t){return await Ee(e,i),t}};let Oe=!1;const Re={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let Le={};const _e={create:async()=>{let e=!1;const t=v(),o=(new Date).getTime();try{if(e=await we(),!e||e.isClosed())throw new le("The page is invalid or closed.");M(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-o} ms.`)}catch(e){throw new le("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(Le.workLimit/2))}},validate:async e=>!(Le.workLimit&&++e.workCount>Le.workLimit)||(M(3,`[pool] Worker failed validation: exceeded work limit (limit is ${Le.workLimit}).`),!1),destroy:async e=>{M(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&await e.page.close()}},ke=async e=>{if(Le=e&&e.pool?{...e.pool}:{},await async function(e){const{debug:t,other:o}=oe(),{enable:r,...i}=t,s={headless:!o.browserShellMode||"shell",userDataDir:"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&i};if(!be){let e=0;const t=async()=>{try{M(3,`[browser] Attempting to get a browser instance (try ${++e}).`),be=await y.launch(s)}catch(o){if(F(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;M(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===s.headless&&M(3,"[browser] Launched browser in shell mode."),r&&M(3,"[browser] Launched browser in debug mode.")}catch(e){throw new le("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!be)throw new le("[browser] Cannot find a browser to open.")}return be}(e.puppeteerArgs),M(3,`[pool] Initializing pool with workers: min ${Le.minWorkers}, max ${Le.maxWorkers}.`),Oe)return M(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(Le.minWorkers)>parseInt(Le.maxWorkers)&&(Le.minWorkers=Le.maxWorkers);try{Oe=new f({..._e,min:parseInt(Le.minWorkers),max:parseInt(Le.maxWorkers),acquireTimeoutMillis:Le.acquireTimeout,createTimeoutMillis:Le.createTimeout,destroyTimeoutMillis:Le.destroyTimeout,idleTimeoutMillis:Le.idleTimeout,createRetryIntervalMillis:Le.createRetryInterval,reapIntervalMillis:Le.reaperInterval,propagateCreateError:!1}),Oe.on("release",(async e=>{await async function(e,t=!1){try{e.isClosed()||(t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await Te(e)):await e.evaluate((()=>{document.body.innerHTML='
'})))}catch(e){F(2,e,"[browser] Could not clear the content of the page.")}}(e.page,!1),M(4,`[pool] Releasing a worker with ID ${e.id}.`)})),Oe.on("destroySuccess",((e,t)=>{M(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t{Oe.release(e)})),M(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new le("[pool] Could not create the pool of workers.").setError(e)}};async function Ie(){if(M(3,"[pool] Killing pool with all workers and closing browser."),Oe){for(const e of Oe.used)Oe.release(e.resource);Oe.destroyed||(await Oe.destroy(),M(4,"[browser] Destroyed the pool of resources."))}await async function(){be?.connected&&await be.close(),M(4,"[browser] Closed the browser.")}()}const Ce=async(e,t)=>{let o;try{if(M(4,"[pool] Work received, starting to process."),++Re.exportAttempts,Le.benchmarking&&Ae(),!Oe)throw new le("Work received, but pool has not been started.");const r=ee();try{M(4,"[pool] Acquiring a worker handle."),o=await Oe.acquire().promise,t.server.benchmarking&&M(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${r()}ms.`)}catch(e){throw new le((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${r()}ms.`).setError(e)}if(M(4,"[pool] Acquired a worker handle."),!o.page)throw new le("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();M(4,`[pool] Starting work on pool entry with ID ${o.id}.`);const s=ee(),n=await xe(o.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(o.page.close(),o.page=await we()),new le((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${s()}ms.`).setError(n);t.server.benchmarking&&M(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${s()}ms.`),Oe.release(o);const a=(new Date).getTime()-i;return Re.timeSpent+=a,Re.spentAverage=Re.timeSpent/++Re.performedExports,M(4,`[pool] Work completed in ${a} ms.`),{result:n,options:t}}catch(e){throw++Re.droppedExports,o&&Oe.release(o),new le(`[pool] In pool.postWork: ${e.message}`).setError(e)}},Ne=()=>({min:Oe.min,max:Oe.max,all:Oe.numFree()+Oe.numUsed(),available:Oe.numFree(),used:Oe.numUsed(),pending:Oe.numPendingAcquires()});function Ae(){const{min:e,max:t,all:o,available:r,used:i,pending:s}=Ne();M(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),M(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),M(5,`[pool] The number of all created resources: ${o}.`),M(5,`[pool] The number of available resources: ${r}.`),M(5,`[pool] The number of acquired resources: ${i}.`),M(5,`[pool] The number of resources waiting to be acquired: ${s}.`)}var Pe=Ne,He=()=>Re;let $e=!1;const Ge=async(e,t)=>{M(4,"[chart] Starting the exporting process.");const o=((e,t={})=>{let o={};return e.svg?(o=J(t),o.export.type=e.type||e.export.type,o.export.scale=e.scale||e.export.scale,o.export.outfile=e.outfile||e.export.outfile,o.payload={svg:e.svg}):o=re(t,e,_),o.export.outfile=o.export?.outfile||`chart.${o.export?.type||"png"}`,o})(e,oe()),i=o.export;if(o.payload?.svg&&""!==o.payload.svg)try{M(4,"[chart] Attempting to export from a SVG input.");const e=Me(function(e){const t=new b("").window;return w(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}(o.payload.svg),o,t);return++Re.exportFromSvgAttempts,e}catch(e){return t(new le("[chart] Error loading SVG input.").setError(e))}if(i.infile&&i.infile.length)try{return M(4,"[chart] Attempting to export from an input file."),o.export.instr=r(i.infile,"utf8"),Me(o.export.instr.trim(),o,t)}catch(e){return t(new le("[chart] Error loading input file.").setError(e))}if(i.instr&&""!==i.instr||i.options&&""!==i.options)try{return M(4,"[chart] Attempting to export from a raw input."),Q(o.customLogic?.allowCodeExecution)?je(o,t):"string"==typeof i.instr?Me(i.instr.trim(),o,t):Ue(o,i.instr||i.options,t)}catch(e){return t(new le("[chart] Error loading raw input.").setError(e))}return t(new le("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},De=e=>{const{chart:t,exporting:o}=e.export?.options||K(e.export?.instr),r=K(e.export?.globalOptions);let i=e.export?.scale||o?.scale||r?.exporting?.scale||e.export?.defaultScale||1;i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const o=Math.pow(10,t||0);return Math.round(+e*o)/o})(i,2);const s={height:e.export?.height||o?.sourceHeight||t?.height||r?.exporting?.sourceHeight||r?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||o?.sourceWidth||t?.width||r?.exporting?.sourceWidth||r?.chart?.width||e.export?.defaultWidth||600,scale:i};for(let[e,t]of Object.entries(s))s[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return s},Ue=async(e,t,o,i)=>{let{export:s,customLogic:n}=e;const a="boolean"==typeof n.allowCodeExecution?n.allowCodeExecution:$e;if(n){if(a)if("string"==typeof e.customLogic.resources)e.customLogic.resources=X(e.customLogic.resources,Q(e.customLogic.allowFileResources));else if(!e.customLogic.resources)try{const t=r("resources.json","utf8");e.customLogic.resources=X(t,Q(e.customLogic.allowFileResources))}catch(e){F(2,e,"[chart] Unable to load the default resources.json file.")}}else n=e.customLogic={};if(!a&&n){if(n.callback||n.resources||n.customCode)return o(new le("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));n.callback=!1,n.resources=!1,n.customCode=!1}if(t&&(t.chart=t.chart||{},t.exporting=t.exporting||{},t.exporting.enabled=!1),s.constr=s.constr||"chart",s.type=B(s.type,s.outfile),"svg"===s.type&&(s.width=!1),["globalOptions","themeOptions"].forEach((e=>{try{s&&s[e]&&("string"==typeof s[e]&&s[e].endsWith(".json")?s[e]=K(r(s[e],"utf8"),!0):s[e]=K(s[e],!0))}catch(t){s[e]={},F(2,t,`[chart] The '${e}' cannot be loaded.`)}})),n.allowCodeExecution)try{n.customCode=Z(n.customCode,n.allowFileResources)}catch(e){F(2,e,"[chart] The 'customCode' cannot be loaded.")}if(n&&n.callback&&n.callback?.indexOf("{")<0)if(n.allowFileResources)try{n.callback=r(n.callback,"utf8")}catch(e){n.callback=!1,F(2,e,"[chart] The 'callback' cannot be loaded.")}else n.callback=!1;e.export={...e.export,...De(e)};try{return o(!1,await Ce(s.strInj||t||i,e))}catch(e){return o(e)}},je=(e,t)=>{try{let o,r=e.export.instr||e.export.options;return"string"!=typeof r&&(o=r=z(r,e.customLogic?.allowCodeExecution)),o=r.replaceAll(/\t|\n|\r/g,"").trim(),";"===o[o.length-1]&&(o=o.substring(0,o.length-1)),e.export.strInj=o,Ue(e,!1,t)}catch(o){return t(new le(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(o))}},Me=(e,t,o)=>{const{allowCodeExecution:r}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return M(4,"[chart] Parsing input as SVG."),Ue(t,!1,o,e);try{const r=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return Ue(t,r,o)}catch(e){return Q(r)?je(t,o):o(new le("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},Fe=[],We=()=>{M(4,"[server] Clearing all registered intervals.");for(const e of Fe)clearInterval(e)},Ve=(e,t,o,r)=>{F(1,e),"development"!==G.OTHER_NODE_ENV&&delete e.stack,r(e)},qe=(e,t,o,r)=>{const{statusCode:i,status:s,message:n,stack:a}=e,l=i||s||500;o.status(l).json({statusCode:l,message:n,stack:a})};var Be=(e,t)=>{const o="Too many requests, you have been rate limited. Please try again later.",r={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};r.trustProxy&&e.enable("trust proxy");const i=x({windowMs:60*r.window*1e3,max:r.max,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>!1!==r.skipKey&&!1!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(M(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),M(3,`[rate limiting] Enabled rate limiting with ${r.max} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)};class Xe extends le{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}var Ke=e=>!!e&&e.post("/version/change/:newVersion",(async(e,t,o)=>{try{const o=G.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new Xe("The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new Xe("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i)throw new Xe("No new version supplied.",400);try{await(async e=>{const t=oe();t?.highcharts&&(t.highcharts.version=e),await de(t)})(i)}catch(e){throw new Xe(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:me(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){o(e)}}));const Je={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let ze=0;const Ye=[],Qe=[],Ze=(e,t,o,r)=>{let i=!0;const{id:s,uniqueId:n,type:a,body:l}=r;return e.some((e=>{if(e){let r=e(t,o,s,n,a,l);return void 0!==r&&!0!==r&&(i=r),!0}})),i},et=async(e,t,o)=>{try{const o=ee(),i=v().replace(/-/g,""),s=oe(),n=e.body,a=++ze;let l=B(n.type);if(!n||"object"==typeof(r=n)&&!Array.isArray(r)&&null!==r&&0===Object.keys(r).length)throw new Xe("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400);let c=K(n.infile||n.options||n.data);if(!c&&!n.svg)throw M(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Payload received: ${JSON.stringify(n)}.`),new Xe("No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.",400);let p=!1;if(p=Ze(Ye,e,t,{id:a,uniqueId:i,type:l,body:n}),!0!==p)return t.send(p);let h=!1;e.socket.on("close",(()=>{h=!0})),M(4,`[export] Got an incoming HTTP request with ID ${i}.`),n.constr="string"==typeof n.constr&&n.constr||"chart";const u={export:{instr:c,type:l,constr:n.constr[0].toLowerCase()+n.constr.substr(1),height:n.height,width:n.width,scale:n.scale||s.export.scale,globalOptions:K(n.globalOptions,!0),themeOptions:K(n.themeOptions,!0)},customLogic:{allowCodeExecution:$e,allowFileResources:!1,resources:K(n.resources,!0),callback:n.callback,customCode:n.customCode}};c&&(u.export.instr=z(c,u.customLogic.allowCodeExecution));const d=re(s,u);if(d.export.options=c,d.payload={svg:n.svg||!1,b64:n.b64||!1,noDownload:n.noDownload||!1,requestId:i},n.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(d.payload.svg))throw new Xe("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400);await Ge(d,((r,c)=>{if(e.socket.removeAllListeners("close"),s.server.benchmarking&&M(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${o()}ms.`),h)return M(3,"[export] The client closed the connection before the chart finished processing.");if(r)throw r;if(!c||!c.result)throw new Xe(`Unexpected return from chart generation. Please check your request data. For the request with ID ${i}, the result is ${c.result}.`,400);return l=c.options.export.type,Ze(Qe,e,t,{id:a,body:c.result}),c.result?n.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",Je[l]||"image/png"),n.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){o(e)}var r};const tt=JSON.parse(r(a(q,"package.json"))),ot=new Date,rt=[];function it(e){if(!e)return!1;var t;t=setInterval((()=>{const e=He(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;rt.push(t),rt.length>30&&rt.shift()}),6e4),Fe.push(t),e.get("/health",((e,t)=>{const o=He(),r=rt.length,i=rt.reduce(((e,t)=>e+t),0)/rt.length;M(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:ot,uptime:Math.floor(((new Date).getTime()-ot.getTime())/1e3/60)+" minutes",version:tt.version,highchartsVersion:me(),averageProcessingTime:o.spentAverage,performedExports:o.performedExports,failedExports:o.droppedExports,exportAttempts:o.exportAttempts,sucessRatio:o.performedExports/o.exportAttempts*100,pool:Pe(),period:r,movingAverage:i,message:isNaN(i)||!rt.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${r} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:o.exportFromSvgAttempts,jsonExportAttempts:o.performedExports-o.exportFromSvgAttempts})}))}const st=new Map,nt=T();nt.disable("x-powered-by"),nt.use(E());const at=S.memoryStorage(),lt=S({storage:at,limits:{fieldSize:52428800}});nt.use(T.json({limit:52428800})),nt.use(T.urlencoded({extended:!0,limit:52428800})),nt.use(lt.none());const ct=e=>{e.on("clientError",(e=>{F(1,e,`[server] Client error: ${e.message}`)})),e.on("error",(e=>{F(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{F(1,e,`[server] Socket error: ${e.message}`)}))}))},pt=async e=>{try{if(!e.enable)return!1;if(!e.ssl.force){const t=g.createServer(nt);ct(t),t.listen(e.port,e.host),st.set(e.port,t),M(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let t,o;try{t=await i.readFile(l.join(e.ssl.certPath,"server.key"),"utf8"),o=await i.readFile(l.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){M(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=m.createServer({key:t,cert:o},nt);ct(r),r.listen(e.ssl.port,e.host),st.set(e.ssl.port,r),M(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}}e.rateLimiting&&e.rateLimiting.enable&&![0,NaN].includes(e.rateLimiting.maxRequests)&&Be(nt,e.rateLimiting),nt.use(T.static(l.join(q,"public"))),it(nt),(e=>{e.post("/",et),e.post("/:filename",et)})(nt),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(a(q,"public","index.html"))}))})(nt),Ke(nt),(e=>{e.use(Ve),e.use(qe)})(nt)}catch(e){throw new le("[server] Could not configure and start the server.").setError(e)}},ht=()=>{M(4,"[server] Closing all servers.");for(const[e,t]of st)t.close((()=>{st.delete(e),M(4,`[server] Closed server on port: ${e}.`)}))};var ut={startServer:pt,closeServers:ht,getServers:()=>st,enableRateLimiting:e=>Be(nt,e),getExpress:()=>T,getApp:()=>nt,use:(e,...t)=>{nt.use(e,...t)},get:(e,...t)=>{nt.get(e,...t)},post:(e,...t)=>{nt.post(e,...t)}};const dt=async e=>{await Promise.allSettled([We(),ht(),Ie()]),process.exit(e)};var gt={server:ut,startServer:pt,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,$e=Q(t),(e=>{for(const[t,o]of Object.entries(e))U[t]=o;W(e&&parseInt(e.level)),e&&e.dest&&e.toFile&&V(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(M(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{M(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{M(4,`The ${e} event with code: ${t}.`),await dt(0)})),process.on("SIGTERM",(async(e,t)=>{M(4,`The ${e} event with code: ${t}.`),await dt(0)})),process.on("SIGHUP",(async(e,t)=>{M(4,`The ${e} event with code: ${t}.`),await dt(0)})),process.on("uncaughtException",(async(e,t)=>{F(1,e,`The ${t} error.`),await dt(1)}))),await de(e),await ke({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer.args||[]}),e},singleExport:async e=>{e.export.instr=e.export.instr||e.export.options,await Ge(e,(async(e,t)=>{if(e)throw e;const{outfile:o,type:r}=t.options.export;s(o||`chart.${r}`,"svg"!==r?Buffer.from(t.result,"base64"):t.result),await Ie()}))},batchExport:async e=>{const t=[];for(let o of e.export.batch.split(";"))o=o.split("="),2===o.length&&t.push(Ge({...e,export:{...e.export,infile:o[0],outfile:o[1]}},((e,t)=>{if(e)throw e;s(t.options.export.outfile,"svg"!==t.options.export.type?Buffer.from(t.result,"base64"):t.result)})));try{await Promise.all(t),await Ie()}catch(e){throw new le("[chart] Error encountered during batch export.").setError(e)}},startExport:Ge,initPool:ke,killPool:Ie,setOptions:(e,t)=>(t?.length&&(te=function(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(t>-1&&e[t+1]){const o=e[t+1];try{if(o&&o.endsWith(".json"))return JSON.parse(r(o))}catch(e){F(2,e,`[config] Unable to load the configuration from the ${o} file.`)}}return{}}(t)),ie(R,te),te=se(R),e&&(te=re(te,e,_)),t?.length&&(te=function(e,t,o){let r=!1;for(let i=0;i(n.length-1===o&&(a=e[t].type),e[t])),o),n.reduce(((e,o,l)=>(n.length-1===l&&void 0!==e[o]&&(t[++i]?"boolean"===a?e[o]=Q(t[i]):"number"===a?e[o]=+t[i]:a.indexOf("]")>=0?e[o]=t[i].split(","):e[o]=t[i]:(M(2,`[config] Missing value for the '${s}' argument. Using the default value.`),r=!0)),e[o])),e)}r&&Y();return e}(te,t,R)),te),shutdownCleanUp:dt,log:M,logWithStack:F,setLogLevel:W,enableFileLogging:V,mapToNewConfig:e=>{const t={};for(const[o,r]of Object.entries(e)){const e=k[o]?k[o].split("."):[];e.reduce(((t,o,i)=>t[o]=e.length-1===i?r:t[o]||{}),t)}return t},manualConfig:async t=>{let o={};e(t)&&(o=JSON.parse(r(t,"utf8")));const s=Object.keys(L).map((e=>({title:`${e} options`,value:e})));return p({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:s},{onSubmit:async(e,r)=>{let s=0,n=[];for(const e of r)L[e]=L[e].map((t=>({...t,section:e}))),n=[...n,...L[e]];return await p(n,{onSubmit:async(e,r)=>{if("moduleScripts"===e.name?(r=r.length?r.map((t=>e.choices[t])):e.choices,o[e.section][e.name]=r):o[e.section]=ne(Object.assign({},o[e.section]||{}),e.name.split("."),e.choices?e.choices[r]:r),++s===n.length){try{await i.writeFile(t,JSON.stringify(o,null,2),"utf8")}catch(e){F(1,e,`[config] An error occurred while creating the ${t} file.`)}return!0}}}),!0}})},printLogo:e=>{const t=JSON.parse(r(a(q,"package.json"))).version;e?console.log(`Starting Highcharts Export Server v${t}...`):console.log(r(q+"/msg/startup.msg").toString().bold.yellow,`v${t}\n`.bold)},printUsage:Y};export{gt as default}; +import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import{readFile}from"fs/promises";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?e:join(__dirname,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t.message,{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=e}function enableFileLogging(e,t,o){logging.toFile=o,o&&(logging.dest=e,logging.file=t)}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initGlobalOptions(defaultConfig);function getOptions(e=!0){return e?globalOptions:deepCopy(globalOptions)}function setOptions(e={},t=[],o=!1){let r={},n={};t.length&&(r=_loadConfigFile(t),n=_pairArgumentValue(nestedProps,t));const i=getOptions(o);return _updateOptions(defaultConfig,i,r,e,n),i}function mergeOptions(e,t){if(isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?mergeOptions(e[o],r):void 0!==r?r:e[o];return e}function mapToNewOptions(e){const t={};if("[object Object]"===Object.prototype.toString.call(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initGlobalOptions(e){const t={};for(const[o,r]of Object.entries(e))t[o]=Object.prototype.hasOwnProperty.call(r,"value")?r.value:_initGlobalOptions(r);return t}function _updateOptions(e,t,o,r,n){Object.keys(e).forEach((i=>{const s=e[i],a=o&&o[i],l=r&&r[i],c=n&&n[i];if(void 0===s.value)_updateOptions(s,t[i],a,l,c);else{null!=a&&(t[i]=a);const e=envs[s.envLink];s.envLink in envs&&null!=e&&(t[i]=e),null!=l&&(t[i]=l),null!=c&&(t[i]=c)}}))}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _loadConfigFile(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,""))),o=t>-1&&e[t+1];if(o)try{return JSON.parse(readFileSync(getAbsolutePath(o)))}catch(e){logWithStack(2,e,`[config] Unable to load the configuration from the ${o} file.`)}return{}}function _pairArgumentValue(e,t){const o={};for(let r=0;r{if(i.length-1===s){const i=t[++r];i||log(2,`[config] Missing value for the CLI '--${n}' argument. Using the default value.`),e[o]=i||null}else void 0===e[o]&&(e[o]={});return e[o]}),o)}return o}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n));if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=getOptions();t.highcharts.version=e,await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e){const{getOptions:t,merge:o,setOptions:r,wrap:n}=Highcharts;Highcharts.setOptionsObj=o(!1,{},t()),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=o(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,o){e.apply(this,[t,o])}));const i={chart:{animation:!1,height:e.export.height,width:e.export.width},exporting:{enabled:!1}},s=new Function(`return ${e.export.instr}`)(),a=new Function(`return ${e.export.themeOptions}`)(),l=new Function(`return ${e.export.globalOptions}`)(),c=o(!1,a,s,i),p=e.customLogic.callback?new Function(`return ${e.customLogic.callback}`)():null;e.customLogic.customCode&&new Function("options",e.customLogic.customCode)(s),l&&r(l),Highcharts[e.export.constr]("container",c,p);const u=t();for(const e in u)"function"!=typeof u[e]&&delete u[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:join(__dirname,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t){const o=[];try{const r=t.export;let n=!1;if(r.svg){if(log(4,"[export] Treating as SVG input."),"svg"===r.type)return r.svg;n=!0,await _setAsSvg(e,r.svg)}else log(4,"[export] Treating as JSON config."),await _setAsOptions(e,t);o.push(...await addPageResources(e,t.customLogic));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(r.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||r.height)),c=Math.abs(Math.ceil(i.chartWidth||r.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(r.scale)}),r.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,r.type,{width:c,height:l,x:s,y:a},r.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,r.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${r.type}.`,400)}return await clearPageResources(e,o),p}catch(t){return await clearPageResources(e,o),t}}async function _setAsSvg(e,t){await e.setContent(svgTemplate(t),{waitUntil:"domcontentloaded"})}async function _setAsOptions(e,t){await e.evaluate(createChart,t)}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e=getOptions().pool,t=[]){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,getOptions().pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,e._requestId?`[benchmark] Request [${e._requestId}] - `:"[benchmark] ",`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+"Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.").setError(i):new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+`Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,e._requestId?`[benchmark] Request [${e._requestId}] - `:"[benchmark] ",`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport(e,(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({...e,export:{...e.export,infile:o[0],outfile:o[1]}},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{log(4,"[chart] Starting the exporting process.");const o=mergeOptions(getOptions(!1),e),r=o.export;if(null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t=getOptions().server.rateLimiting){try{if(t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,max:r.max,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>!1!==r.skipKey&&!1!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.max} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}class HttpError extends ExportError{constructor(e,t){super(e,t)}setStatus(e){return this.statusCode=e,this}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new HttpError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4().replace(/-/g,"");if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new HttpError("[validation] The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.",400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new HttpError("[validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new HttpError("[validation] 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);return e.validatedOptions={_requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${fixType(t.type)}`,type:fixType(t.type,t.outfile),constr:fixConstr(t.constr),b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n._requestId;log(4,`[export] Got an incoming HTTP request with ID ${i}.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new HttpError("[export] Unexpected return of the export result from the chart generation. Please check your request data.",400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"))),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new HttpError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new HttpError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new HttpError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new HttpError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e=getOptions().server){try{if(!e.enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const t=1024*e.uploadLimit*1024,o=multer.memoryStorage(),r=multer({storage:o,limits:{fieldSize:t}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:t})),app.use(express.urlencoded({extended:!0,limit:t})),app.use(r.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),healthRoutes(app),exportRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){rateLimitingMiddleware(app,e)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=mergeOptions(getOptions(!1),e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={server:server,startServer:startServer,getOptions:getOptions,setOptions:setOptions,mergeOptions:mergeOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,checkAndUpdateCache:checkAndUpdateCache,initPool:initPool,killPool:killPool,log:log,logWithStack:logWithStack,setLogLevel:setLogLevel,enableConsoleLogging:enableConsoleLogging,enableFileLogging:enableFileLogging,shutdownCleanUp:shutdownCleanUp};export{index as default,initExport}; //# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index 3423b1a2..6ba656dd 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":["/*******************************************************************************\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 '--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 },\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 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 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 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 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 delay: {\n value: 0,\n type: 'number',\n envLink: 'SERVER_RATE_LIMITING_DELAY',\n description:\n 'The delay duration for each successive request before reaching the maximum limit.'\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 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 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: '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: 'number',\n name: 'rateLimiting.delay',\n message:\n 'The delay for each successive request before reaching the maximum',\n initial: defaultConfig.server.rateLimiting.delay.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: '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\ndotenv.config();\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 (value) => ({\n message: `The string contains forbidden values, received '${value}'`\n })\n )\n .transform((value) => (value !== '' ? value : undefined)),\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 (value) => ({\n message: `The value must be numeric and positive, received '${value}'`\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 (value) => ({\n message: `The value must be numeric and non-negative, received '${value}'`\n })\n )\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\n};\n\nexport const Config = z.object({\n // highcharts\n HIGHCHARTS_VERSION: z\n .string()\n .trim()\n .refine(\n (value) => /^(latest|\\d+(\\.\\d+){0,2})$/.test(value) || value === '',\n (value) => ({\n message: `HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${value}'`\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 (value) => ({\n message: `Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${value}'`\n })\n )\n .transform((value) => (value !== '' ? value : undefined)),\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_BENCHMARKING: v.boolean(),\n\n // server proxy\n SERVER_PROXY_HOST: v.string(),\n SERVER_PROXY_PORT: v.positiveNum(),\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_DELAY: 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_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 (value) => ({\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\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_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\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 { 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\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 __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(url, requestOptions, (res) => {\n let data = '';\n\n // A chunk of data has been received.\n res.on('data', (chunk) => {\n data += chunk;\n });\n\n // The whole response has been received.\n res.on('end', () => {\n if (!data) {\n reject('Nothing was fetched from the URL.');\n }\n\n res.text = data;\n resolve(res);\n });\n })\n .on('error', (error) => {\n reject(error);\n });\n });\n}\n\n/**\n * Sends a POST request to the specified URL with the 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 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 if (error.stack) {\n this.stackMessage = error.message;\n this.stack = error.stack;\n }\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 } 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 } 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 */\nexport const extractModuleName = (scriptPath) => {\n return scriptPath.replace(\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\n ''\n );\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} shouldThrowError - A flag to indicate if the error should be\n * 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 shouldThrowError = false\n) => {\n // Get rid of the .js from the custom strings\n if (script.endsWith('.js')) {\n script = script.substring(0, script.length - 3);\n }\n\n log(4, `[cache] Fetching script - ${script}.js`);\n\n // Fetch the script\n const response = await fetch(`${script}.js`, requestOptions);\n\n // If OK, return its text representation\n if (response.statusCode === 200 && typeof response.text == 'string') {\n if (fetchedModules) {\n const moduleName = extractModuleName(script);\n fetchedModules[moduleName] = 1;\n }\n\n return response.text;\n }\n\n if (shouldThrowError) {\n throw new ExportError(\n `Could not fetch the ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`\n ).setError(response);\n } else {\n log(\n 2,\n `[cache] Could not fetch the ${script}.js. 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 {string} coreScripts - Array of Highcharts core scripts to fetch.\n * @param {string} moduleScripts - Array of Highcharts modules to fetch.\n * @param {string} customScripts - Array of custom script paths to fetch\n * (full URLs).\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 coreScripts,\n moduleScripts,\n customScripts,\n proxyOptions,\n fetchedModules\n) => {\n // Configure proxy if exists\n let proxyAgent;\n const proxyHost = proxyOptions.host;\n const proxyPort = proxyOptions.port;\n\n // Try to create a Proxy Agent\n if (proxyHost && proxyPort) {\n try {\n proxyAgent = new HttpsProxyAgent({\n host: proxyHost,\n port: proxyPort\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 allFetchPromises = [\n ...coreScripts.map((script) =>\n fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\n ),\n ...moduleScripts.map((script) =>\n fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\n ),\n ...customScripts.map((script) =>\n fetchAndProcessScript(`${script}`, requestOptions)\n )\n ];\n\n const fetchedScripts = await Promise.all(allFetchPromises);\n return fetchedScripts.join(';\\n');\n};\n\n/**\n * Updates the local cache with Highcharts scripts and their versions.\n *\n * @param {Object} options - Object containing all options.\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 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 const fetchedModules = {};\n try {\n cache.sources = await fetchScripts(\n [\n ...highchartsOptions.coreScripts.map((c) => `${cdnURL}${hcVersion}${c}`)\n ],\n [\n ...highchartsOptions.moduleScripts.map((m) =>\n m === 'map'\n ? `${cdnURL}maps/${hcVersion}modules/${m}`\n : `${cdnURL}${hcVersion}modules/${m}`\n ),\n ...highchartsOptions.indicatorScripts.map(\n (i) => `${cdnURL}stock/${hcVersion}indicators/${i}`\n )\n ],\n highchartsOptions.customScripts,\n proxyOptions,\n fetchedModules\n );\n\n cache.hcVersion = extractVersion(cache);\n\n // Save the fetched modules into caches' source JSON\n writeFileSync(sourcePath, cache.sources);\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 // Trigger custom code\n if (options.customLogic.customCode) {\n new Function(options.customLogic.customCode)();\n }\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 // 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 Highcharts[options.export.constr || 'chart'](\n 'container',\n finalOptions,\n finalCallback\n );\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/**\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 * 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 // Get debug and other options\n const { 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: './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 // Create a browser\n if (!browser) {\n let tryCount = 0;\n\n const open = async () => {\n try {\n log(\n 3,\n `[browser] Attempting to get a browser instance (try ${++tryCount}).`\n );\n browser = await puppeteer.launch(launchOptions);\n } catch (error) {\n logWithStack(\n 1,\n error,\n '[browser] Failed to launch a browser instance.'\n );\n\n // Retry to launch browser until reaching max attempts\n if (tryCount < 25) {\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\n await new Promise((response) => setTimeout(response, 4000));\n await open();\n } else {\n throw error;\n }\n }\n };\n\n try {\n await open();\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 } catch (error) {\n throw new ExportError(\n '[browser] Maximum retries to open a browser instance reached.'\n ).setError(error);\n }\n\n if (!browser) {\n throw new ExportError('[browser] Cannot find a browser to open.');\n }\n }\n\n // Return a browser promise\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 // Close the browser when connnected\n if (browser?.connected) {\n await browser.close();\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 * @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 if (!browser) {\n return false;\n }\n\n // Create a page\n const 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}\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.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 }\n } catch (error) {\n logWithStack(\n 2,\n error,\n '[browser] Could not clear the content of the page.'\n );\n }\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 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 // eslint-disable-next-line no-undef\n const [...scriptsToRemove] = document.getElementsByTagName('script');\n // eslint-disable-next-line no-undef\n const [, ...stylesToRemove] = document.getElementsByTagName('style');\n // eslint-disable-next-line no-undef\n const [...linksToRemove] = document.getElementsByTagName('link');\n\n // Remove tags\n for (const element of [\n ...scriptsToRemove,\n ...stylesToRemove,\n ...linksToRemove\n ]) {\n element.remove();\n }\n });\n}\n\n/**\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 // 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 // eslint-disable-next-line no-undef\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 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 = (page, type, encoding, clip, rasterizationTimeout) =>\n 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 setTimeout(\n () => reject(new ExportError('Rasterization timeout')),\n rasterizationTimeout || 1500\n )\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 return Promise.race([\n page.pdf({\n // This will remove an extra empty page in PDF exports\n height: height + 1,\n width,\n encoding\n }),\n new Promise((_resolve, reject) =>\n setTimeout(\n () => reject(new ExportError('Rasterization timeout')),\n rasterizationTimeout || 1500\n )\n )\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 page.evaluate(triggerExport, chart, options, displayErrors);\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 // eslint-disable-next-line no-undef\n document.body.style.zoom = scale;\n\n // Set the margin to 0px\n // eslint-disable-next-line no-undef\n document.body.style.margin = '0px';\n\n return {\n chartHeight,\n chartWidth\n };\n }, parseFloat(exportOptions.scale))\n : await page.evaluate(() => {\n // eslint-disable-next-line no-undef\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 // eslint-disable-next-line no-undef\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.ceil(size.chartHeight || exportOptions.height);\n const viewportWidth = Math.ceil(size.chartWidth || exportOptions.width);\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\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} 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';\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};\n\nlet poolConfig = {};\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 log(\n 3,\n `[pool] Successfully created a worker ${id} - took ${\n new Date().getTime() - startDate\n } ms.`\n );\n } catch (error) {\n throw new ExportError(\n 'Error encountered when creating a new page.'\n ).setError(error);\n }\n\n return {\n id,\n page,\n // Try to distribute the initial work count\n workCount: Math.round(Math.random() * (poolConfig.workLimit / 2))\n };\n },\n\n /**\n * Validates a worker 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 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) {\n // We don't really need to wait around for this\n await workerHandle.page.close();\n }\n }\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 // 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', async (resource) => {\n // Clear page\n await clearPage(resource.page, false);\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 });\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 // 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 ).setError(error);\n }\n log(4, '[pool] Acquired a worker handle.');\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 // TODO: If the export failed because puppeteer timed out, we need to force kill the worker so we get a new page. That needs to be handled better than this hack.\n if (result.message === 'Rasterization timeout') {\n workerHandle.page.close();\n workerHandle.page = await newPage();\n }\n\n throw new ExportError(\n (options.payload?.requestId\n ? `For request with ID ${options.payload?.requestId} - `\n : '') + `Error encountered during export: ${exportCounter()}ms.`\n ).setError(result);\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 ++stats.droppedExports;\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});\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 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\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 // 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 logWithStack(\n 2,\n error,\n `[chart] Unable to load the default resources.json file.`\n );\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\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\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 window = new JSDOM('').window;\n const purify = DOMPurify(window);\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\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 { 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 // Gather all requied information for the response\n const { statusCode: stCode, status, message, stack } = error;\n const statusCode = stCode || status || 500;\n\n // Set and return response\n res.status(statusCode).json({ statusCode, message, stack });\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 delay: limitConfig.delay || 0,\n trustProxy: limitConfig.trustProxy || false,\n skipKey: limitConfig.skipKey || false,\n skipToken: limitConfig.skipToken || false\n };\n\n // Set if behind a proxy\n if (rateOptions.trustProxy) {\n app.enable('trust proxy');\n }\n\n // Create a limiter\n const limiter = rateLimit({\n windowMs: rateOptions.window * 60 * 1000,\n // Limit each IP to 100 requests per windowMs\n max: rateOptions.max,\n // Disable delaying, full speed until the max limit is reached\n delayMs: rateOptions.delay,\n handler: (request, response) => {\n response.format({\n json: () => {\n response.status(429).send({ message: msg });\n },\n default: () => {\n response.status(429).send(msg);\n }\n });\n },\n skip: (request) => {\n // Allow bypassing the limiter if a valid key/token has been sent\n if (\n rateOptions.skipKey !== false &&\n rateOptions.skipToken !== false &&\n request.query.key === rateOptions.skipKey &&\n request.query.access_token === rateOptions.skipToken\n ) {\n log(4, '[rate limiting] Skipping rate limiter.');\n return true;\n }\n return false;\n }\n });\n\n // Use a limiter as a middleware\n app.use(limiter);\n\n log(\n 3,\n `[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) {\n super(message);\n this.status = this.statusCode = status;\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 if (newVersion) {\n try {\n // eslint-disable-next-line import/no-named-as-default-member\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';\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 );\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. Payload received: ${JSON.stringify(body)}.`\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 );\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 let connectionAborted = false;\n\n // In case the connection is closed, force to abort further actions\n request.socket.on('close', () => {\n connectionAborted = true;\n });\n\n log(4, `[export] Got an incoming HTTP request 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 };\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 );\n }\n\n // Start the export process\n await startExport(options, (error, info) => {\n // Remove the close event from the socket\n request.socket.removeAllListeners('close');\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 the connection was closed, do nothing\n if (connectionAborted) {\n return log(\n 3,\n `[export] The client closed the connection before the chart finished processing.`\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 );\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 from '../../pool.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 stats = pool.getStats();\n const successRatio =\n stats.exportAttempts === 0\n ? 1\n : (stats.performedExports / stats.exportAttempts) * 100;\n\n successRates.push(successRatio);\n if (successRates.length > windowSize) {\n successRates.shift();\n }\n }, recordInterval);\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 exportAttempts: stats.exportAttempts,\n sucessRatio: (stats.performedExports / stats.exportAttempts) * 100,\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 { __dirname } from '../utils.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// Enable parsing of form data (files) with Multer package\nconst storage = multer.memoryStorage();\nconst upload = multer({\n storage,\n limits: {\n fieldSize: 50 * 1024 * 1024\n }\n});\n\n// Enable body parser\napp.use(express.json({ limit: 50 * 1024 * 1024 }));\napp.use(express.urlencoded({ extended: true, limit: 50 * 1024 * 1024 }));\n\n// Use only non-file multipart form fields\napp.use(upload.none());\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) => {\n logWithStack(1, error, `[server] Client error: ${error.message}`);\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 * 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 // 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\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\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.\n */\nexport const closeServers = () => {\n log(4, `[server] Closing all servers.`);\n for (const [port, server] of activeServers) {\n server.close(() => {\n activeServers.delete(port);\n log(4, `[server] Closed server on port: ${port}.`);\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 });\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 { clearAllIntervals } from './intervals.js';\nimport { killPool } from './pool.js';\nimport { closeServers } from './server/server.js';\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 // Await freeing all resources\n await Promise.allSettled([\n // Clear all ongoing intervals\n clearAllIntervals(),\n\n // Get available server instances (HTTP/HTTPS) and close them\n closeServers(),\n\n // Close pool along with its workers and the browser instance, if exists\n killPool()\n ]);\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","highcharts","version","envLink","cdnURL","coreScripts","moduleScripts","indicatorScripts","customScripts","forceFetch","cachePath","export","infile","instr","options","outfile","constr","defaultHeight","defaultWidth","defaultScale","height","width","scale","globalOptions","themeOptions","batch","rasterizationTimeout","customLogic","allowCodeExecution","allowFileResources","customCode","callback","resources","loadConfig","legacyName","createConfig","server","enable","cliName","host","port","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","logging","level","file","dest","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","isNaN","parseFloat","envs","object","HIGHCHARTS_VERSION","test","HIGHCHARTS_CDN_URL","startsWith","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","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","__dirname","fileURLToPath","URL","url","fixType","formats","outType","pop","find","t","handleResources","allowedProps","handledResources","correctResources","isCorrectJSON","readFileSync","files","propName","item","data","parsedData","JSON","stringify","deepCopy","copy","Array","isArray","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","resolve","reject","protocol","https","http","getProtocol","get","res","on","chunk","text","ExportError","Error","constructor","super","this","setError","statusCode","cache","activeManifest","sources","hcVersion","extractVersion","indexOf","fetchAndProcessScript","script","fetchedModules","shouldThrowError","response","updateCache","highchartsOptions","proxyOptions","sourcePath","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","fetchScripts","c","m","writeFileSync","checkAndUpdateCache","manifestPath","requestUpdate","manifest","moduleMap","numberOfModules","some","moduleName","newManifest","saveConfigToManifest","getCachePath","setupHighcharts","Highcharts","animObject","duration","triggerExport","chartOptions","displayErrors","_displayErrors","merge","setOptions","wrap","setOptionsObj","Function","chart","animation","strInj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","onHighchartsRender","addEvent","Series","finalOptions","finalCallback","defaultOptions","prop","template","browser","newPage","page","setCacheEnabled","setPageContent","$eval","element","errorMessage","innerHTML","setPageEvents","clearPageResources","injectedResources","resource","dispose","evaluate","oldCharts","charts","oldChart","destroy","scriptsToRemove","document","getElementsByTagName","stylesToRemove","linksToRemove","remove","setContent","waitUntil","addScriptTag","path","setAsConfig","puppeteerExport","exportOptions","debugger","isSVG","svgTemplate","injectedJs","js","push","content","isLocal","jsResource","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","addPageResources","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","body","style","zoom","margin","viewportHeight","Math","ceil","viewportWidth","x","y","getBoundingClientRect","trunc","getClipRegion","setViewport","deviceScaleFactor","outerHTML","createSVG","encoding","clip","race","screenshot","captureBeyondViewport","fullPage","optimizeForSpeed","quality","omitBackground","_resolve","setTimeout","createImage","emulateMediaType","pdf","createPDF","stats","performedExports","exportAttempts","exportFromSvgAttempts","timeSpent","droppedExports","spentAverage","poolConfig","factory","create","id","uuid","startDate","getTime","isClosed","workCount","random","validate","workerHandle","close","initPool","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","createBrowser","parseInt","Pool","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","hardReset","goto","clearPage","eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","connected","closeBrowser","postWork","getPoolInfo","acquireCounter","payload","requestId","workStart","exportCounter","result","exportTime","getPoolInfoJSON","numFree","numUsed","available","pending","numPendingAcquires","pool$1","startExport","settings","endCallback","svg","initExportSettings","exportAsString","input","JSDOM","DOMPurify","sanitize","ADD_TAGS","doStraightInject","doExport","findChartSize","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","param","chartJson","customLogicOptions","allowCodeExecutionScoped","optionsName","stringToExport","chartJSON","intervalIds","clearAllIntervals","clearInterval","logErrorMiddleware","req","next","returnErrorMiddleware","stCode","status","json","rateLimit","app","limitConfig","msg","rateOptions","limiter","windowMs","delayMs","handler","request","format","send","default","skip","query","access_token","use","HttpError","setStatus","vSwitchRoute","post","adminToken","token","newVersion","params","updateVersion","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","stopCounter","headers","connection","remoteAddress","connectionAborted","socket","toLowerCase","substr","b64","noDownload","pattern","isPrivateRangeUrlFound","info","removeAllListeners","Buffer","from","header","attachment","filename","pkgFile","pather","serverStartTime","successRates","addHealthRoutes","setInterval","successRatio","_","period","movingAverage","reduce","a","b","bootTime","uptime","floor","highchartsVersion","averageProcessingTime","failedExports","sucessRatio","toFixed","svgExportAttempts","jsonExportAttempts","activeServers","Map","express","disable","cors","storage","multer","memoryStorage","upload","limits","fieldSize","limit","urlencoded","extended","none","attachServerErrorHandlers","startServer","serverConfig","httpServer","createServer","listen","set","cert","fsPromises","readFile","posix","httpsServer","NaN","static","healthRoute","exportRoutes","sendFile","uiRoute","errorHandler","closeServers","delete","getServers","enableRateLimiting","getExpress","getApp","middlewares","shutdownCleanUp","exitCode","allSettled","exit","index","initExport","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":"0lBAeO,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,eACA,yBACA,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,eACA,sBACA,cACA,yBACA,oBACA,uBAEFC,KAAM,WACNC,YAAa,0CAGjBC,WAAY,CACVC,QAAS,CACPJ,MAAO,SACPC,KAAM,SACNI,QAAS,qBACTH,YAAa,sCAEfI,OAAQ,CACNN,MAAO,+BACPC,KAAM,SACNI,QAAS,qBACTH,YAAa,kDAEfK,YAAa,CACXP,MAAOR,EAAaC,KACpBQ,KAAM,WACNI,QAAS,0BACTH,YAAa,yCAEfM,cAAe,CACbR,MAAOR,EAAaE,QACpBO,KAAM,WACNI,QAAS,4BACTH,YAAa,uCAEfO,iBAAkB,CAChBT,MAAOR,EAAaG,WACpBM,KAAM,WACNI,QAAS,+BACTH,YAAa,0CAEfQ,cAAe,CACbV,MAAOR,EAAaI,OACpBK,KAAM,WACNC,YAAa,uDAEfS,WAAY,CACVX,OAAO,EACPC,KAAM,UACNI,QAAS,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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/errors/HttpError.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is 0.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is null.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? path : join(__dirname, path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message. Accepts a variable amount of arguments. Arguments after\r\n * the `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 * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the rest are strings to build a message with.\r\n *\r\n * @returns {void} Ends the function execution when attempting to log\r\n * information at a higher level than what is allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object.\r\n * @param {string} customMessage - An optional custom message to be logged\r\n * along with the error.\r\n *\r\n * @returns {void} Ends the function execution when attempting to log\r\n * information at a higher level than what is allowed.\r\n */\r\nexport function 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 the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - Object containing `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (level >= 0 && level <= logging.levelsDesc.length) {\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update options for the console logging\r\n logging.toConsole = toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path for the log file.\r\n * @param {string} file - The log file name.\r\n * @param {boolean} toFile - The flag for setting the logging to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update options for the file logging\r\n logging.toFile = toFile;\r\n\r\n // Set the `dest` and `file` only if the file logging is enabled\r\n if (toFile) {\r\n logging.dest = dest;\r\n logging.file = file;\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, isObject, deepCopy, getAbsolutePath } from './utils.js';\r\nimport { defaultConfig, nestedProps, absoluteProps } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initGlobalOptions(defaultConfig);\r\n\r\n/**\r\n * Gets the reference to the global options of the server instance object\r\n * or its copy.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getReference=true] - Optional parameter to decide whether\r\n * to return the reference to the global options of the server instance object\r\n * or return a copy of it. The default value is true.\r\n *\r\n * @returns {Object} The reference to the global options of the server instance\r\n * object or its copy.\r\n */\r\nexport function getOptions(getReference = true) {\r\n return getReference ? globalOptions : deepCopy(globalOptions);\r\n}\r\n\r\n/**\r\n * Sets the global options of the export server instance, keeping the principle\r\n * of the options load priority from all available sources. It accepts optional\r\n * `customOptions` object and `cliArgs` array with arguments from the CLI. These\r\n * options will be validated and applied if provided.\r\n *\r\n * The priority order of setting values is:\r\n *\r\n * 1. Options from the `lib/schemas/config.js` file (default values).\r\n * 2. Options from a custom JSON file (loaded by the `loadConfig` option).\r\n * 3. Options from the environment variables (the `.env` file).\r\n * 4. Options from the first parameter (by default an empty object).\r\n * 5. Options from the CLI.\r\n *\r\n * @function setOptions\r\n *\r\n * @param {Object} [customOptions={}] - Optional custom options for additional\r\n * configuration. The default value is an empty object.\r\n * @param {Array.} [cliArgs=[]] - Optional command line arguments\r\n * for additional configuration. The default value is an empty array.\r\n * @param {boolean} [modifyGlobal=false] - Optional parameter to decide\r\n * whether to update and return the reference to the global options\r\n * of the server instance object or return a copy of it. The default value\r\n * is false.\r\n *\r\n * @returns {Object} The updated general options object, reflecting the merged\r\n * configuration from all available sources.\r\n */\r\nexport function setOptions(\r\n customOptions = {},\r\n cliArgs = [],\r\n modifyGlobal = false\r\n) {\r\n // Object for options loaded via the `loadConfig` option\r\n let configOptions = {};\r\n\r\n // Object for options from the CLI\r\n let cliOptions = {};\r\n\r\n // Only for the CLI usage\r\n if (cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `loadConfig`\r\n configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Get options from the CLI\r\n cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n }\r\n\r\n // Get the reference to the global options object or a copy of the object\r\n const generalOptions = getOptions(modifyGlobal);\r\n\r\n // Update values of the general options with values from each source possible\r\n _updateOptions(\r\n defaultConfig,\r\n generalOptions,\r\n configOptions,\r\n customOptions,\r\n cliOptions\r\n );\r\n\r\n // Return options\r\n return generalOptions;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function mergeOptions(originalOptions, newOptions) {\r\n // Check if the `newOptions` is a correct object\r\n if (isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key];\r\n }\r\n }\r\n\r\n // Return the result options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (Object.prototype.toString.call(oldOptions) === '[object Object]') {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is false.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is false.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is false.\r\n */\r\nexport function printVersion(noLogo = false) {\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(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg')).toString().bold\r\n .yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns global options object based on provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * @function _initGlobalOptions\r\n *\r\n * @param {Object} config - The configuration object to be used for initializing\r\n * options.\r\n *\r\n * @returns {Object} Initialized options object.\r\n */\r\nfunction _initGlobalOptions(config) {\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n options[name] = Object.prototype.hasOwnProperty.call(item, 'value')\r\n ? item.value\r\n : _initGlobalOptions(item);\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Updates options object with values from various sources, following a specific\r\n * prioritization order. The function checks for values in the following order\r\n * of precedence: the `loadConfig` configuration options, environment variables,\r\n * custom options, and CLI options.\r\n *\r\n * @function _updateOptions\r\n *\r\n * @param {Object} config - The configuration object, which includes the initial\r\n * settings and metadata for each option. This object is used to determine\r\n * the structure and default values for the options.\r\n * @param {Object} options - The options object that will be updated with values\r\n * from other sources.\r\n * @param {Object} configOpt - The configuration options object, loaded with\r\n * the `loadConfig` option, which may provide values to override defaults.\r\n * @param {Object} customOpt - The custom options object, typically containing\r\n * additional and user-defined values, which may override configuration options.\r\n * @param {Object} cliOpt - The CLI options object, which may include values\r\n * provided through command-line arguments and has the highest precedence among\r\n * options.\r\n */\r\nfunction _updateOptions(config, options, configOpt, customOpt, cliOpt) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the config entry of a specific option\r\n const entry = config[key];\r\n\r\n // Gather values for the options from every possible source, if exists\r\n const configVal = configOpt && configOpt[key];\r\n const customVal = customOpt && customOpt[key];\r\n const cliVal = cliOpt && cliOpt[key];\r\n\r\n // If the value not found, need to go deeper\r\n if (typeof entry.value === 'undefined') {\r\n _updateOptions(entry, options[key], configVal, customVal, cliVal);\r\n } else {\r\n // If a value from custom JSON options exists, it take precedence\r\n if (configVal !== undefined && configVal !== null) {\r\n options[key] = configVal;\r\n }\r\n\r\n // If a value from environment variables exists, it take precedence\r\n const envVal = envs[entry.envLink];\r\n if (entry.envLink in envs && envVal !== undefined && envVal !== null) {\r\n options[key] = envVal;\r\n }\r\n\r\n // If a value from user options exists, it take precedence\r\n if (customVal !== undefined && customVal !== null) {\r\n options[key] = customVal;\r\n }\r\n\r\n // If a value from CLI options exists, it take precedence\r\n if (cliVal !== undefined && cliVal !== null) {\r\n options[key] = cliVal;\r\n }\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Check if the `loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `loadConfig` is present and has a correct value\r\n if (configFileName) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return JSON.parse(readFileSync(getAbsolutePath(configFileName)));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[config] Unable to load the configuration from the ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options.\r\n * It may include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n setOptions,\r\n mergeOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - Object containing `highcharts` options.\r\n * @param {Object} serverProxyOptions - Object containing `server.proxy`\r\n * options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } = highchartsOptions;\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 !== highchartsOptions.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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Get the reference to the global options to update to the new version\r\n const options = getOptions();\r\n\r\n // Set to the new version\r\n options.highcharts.version = newVersion;\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is false.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - Object containing `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - Object containing `server.proxy`\r\n * options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - Object containing `highcharts` options.\r\n * @param {Object} serverProxyOptions - Object containing `server.proxy`\r\n * options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n */\r\nexport async function createChart(options) {\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\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: options.export.height,\r\n width: options.export.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${options.export.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${options.export.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(\r\n `return ${options.export.globalOptions}`\r\n )();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = options.customLogic.callback\r\n ? new Function(`return ${options.customLogic.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (options.customLogic.customCode) {\r\n new Function('options', options.customLogic.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[options.export.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is false.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The object containing `customLogic`\r\n * options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n 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(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, options) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n let isSVG = false;\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await _setAsSvg(page, exportOptions.svg);\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await _setAsOptions(page, options);\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.push(\r\n ...(await addPageResources(page, options.customLogic))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Sets the specified page's content with provided export input within\r\n * the window context using the `page.setContent` function.\r\n *\r\n * @async\r\n * @function _setAsSvg\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {Promise} A Promise that resolves after the content is set.\r\n */\r\nasync function _setAsSvg(page, svg) {\r\n await page.setContent(svgTemplate(svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n}\r\n\r\n/**\r\n * Sets the options with specified export input and sizes as configuration into\r\n * the `createChart` function within the window context using\r\n * the `page.evaluate` function.\r\n *\r\n * @async\r\n * @function _setAsOptions\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves after the configuration\r\n * is set.\r\n */\r\nasync function _setAsOptions(page, options) {\r\n await page.evaluate(createChart, options);\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { getOptions } from './config.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} [poolOptions=getOptions().pool] - Object containing `pool`\r\n * options. The default value is the global pool options of the export server\r\n * instance.\r\n * @param {Array.} [puppeteerArgs=[]] - Additional arguments\r\n * for Puppeteer launch. The default value is an empty array.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(\r\n poolOptions = getOptions().pool,\r\n puppeteerArgs = []\r\n) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\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 * @async\r\n * @function killPool\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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (getOptions().pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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._requestId\r\n ? `[benchmark] Request [${options._requestId}] - `\r\n : '[benchmark] ',\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[pool] ' +\r\n (options._requestId ? `Request [${options._requestId}] - ` : '') +\r\n `Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Perform an export on a puppeteer level\r\n const exportCounter = measureTime();\r\n const result = await puppeteerExport(workerHandle.page, options);\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 '[pool] ' +\r\n (options._requestId ? `Request [${options._requestId}] - ` : '') +\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 '[pool] ' +\r\n (options._requestId ? `Request [${options._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._requestId\r\n ? `[benchmark] Request [${options._requestId}] - `\r\n : '[benchmark] ',\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - Object containing `pool` options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { getOptions, mergeOptions, isAllowedConfig } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { killPool, postWork, getPoolStats } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the image in the provided outfile.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which may be a partial\r\n * or complete set of options. It must contain at least one of the following\r\n * properties: `infile`, `instr`, `options`, or `svg` to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(options, async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n\r\n // Kill pool and close browser after finishing single export\r\n await killPool();\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\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;...\". Results are saved\r\n * in provided outfiles.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which may be a partial\r\n * or complete set of options. It must contain the `batch` option to generate\r\n * valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `customOptions` parameter is an object that\r\n * may be partial or complete set of options. The `endCallback` is called when\r\n * the export is completed, with the `error` object as the first argument\r\n * and the `data` object as the second, which contains the Base64 representation\r\n * of the chart in the `result` property and the full set of export options\r\n * in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} customOptions - The `customOptions` object, which may\r\n * be a partial or complete set of options. If the provided options are partial,\r\n * missing values will be merged with the default general options, retrieved\r\n * using the `getOptions` function.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing work or upon error occurance of the exporting process. The first\r\n * argument is `error` object and the `data` object is the second, that contains\r\n * the Base64 representation of the chart in the `result` property and the full\r\n * set of export options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(customOptions, endCallback) {\r\n try {\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Merge the custom options into default ones\r\n const options = mergeOptions(getOptions(false), customOptions);\r\n\r\n // Get the export options\r\n const exportOptions = options.export;\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The object containing `export` options.\r\n *\r\n * @returns {Object} An object containing the calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The object containing `customLogic`\r\n * options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is null.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The object containing `export` options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n startExport,\r\n singleExport,\r\n batchExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} [rateLimitingOptions=getOptions().server.rateLimiting] -\r\n * Object containing `rateLimiting` options. The default value is the global\r\n * rate limiting options of the export server instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(\r\n app,\r\n rateLimitingOptions = getOptions().server.rateLimiting\r\n) {\r\n try {\r\n // Check if the rate limiting is enabled\r\n if (rateLimitingOptions.enable) {\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: rateLimitingOptions.maxRequests || 30,\r\n window: rateLimitingOptions.window || 1,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || false,\r\n skipToken: rateLimitingOptions.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 } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 ExportError from './ExportError.js';\r\n\r\n/**\r\n * A custom HTTP error class that extends the `ExportError`. Used to handle\r\n * errors with HTTP status codes.\r\n */\r\nclass HttpError extends ExportError {\r\n /**\r\n * Creates an instance of the `HttpError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super(message, statusCode);\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {HttpError} The updated instance of the `HttpError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\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-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport {\r\n fixConstr,\r\n fixType,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound\r\n} from '../../utils.js';\r\n\r\nimport HttpError from '../../errors/HttpError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {HttpError} Throws an `HttpError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new HttpError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {HttpError} Throws an `HttpError` if the body is not correct.\r\n * @throws {HttpError} Throws an `HttpError` if the chart data from the body\r\n * is not correct.\r\n * @throws {HttpError} Throws an `HttpError` in case of the private range url\r\n * error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid().replace(/-/g, '');\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new HttpError(\r\n \"[validation] 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new HttpError(\r\n \"[validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new HttpError(\r\n \"[validation] 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 // Get options from the body and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `_requestId` property in the validated options\r\n _requestId: requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${fixType(body.type)}`,\r\n type: fixType(body.type, body.outfile),\r\n constr: fixConstr(body.constr),\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } 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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions._requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Got an incoming HTTP request with ID ${requestId}.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new HttpError(\r\n '[export] Unexpected return of the export result from the chart generation. Please check your request data.',\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(readFileSync(join(__dirname, 'package.json')));\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { updateHighchartsVersion, getHighchartsVersion } 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 `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n // Get the token directly from envs\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 '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new HttpError(\r\n '[version] Invalid or missing token: Set the token in the hc-auth header.',\r\n 401\r\n );\r\n }\r\n\r\n // Compare versions\r\n const newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new HttpError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new HttpError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFile } from 'fs/promises';\r\nimport { join } 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 { getOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts HTTP or/and HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains all server related properties (see\r\n * the `server` section in the `lib/schemas/config.js` file for a reference).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} [serverOptions=getOptions().server] - Object containing\r\n * `server` options. The default value is the global server options\r\n * of the export server instance.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when the server should not be enabled or when no valid Express app\r\n * is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions = getOptions().server) {\r\n try {\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 readFile(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = await readFile(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n healthRoutes(app);\r\n exportRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - Object containing `rateLimiting`\r\n * options.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n rateLimitingMiddleware(app, rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Cleans up function to trigger before ending process for the graceful\r\n * shutdown.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} exitCode - An exit code for the `process.exit()` function.\r\n */\r\nexport async function shutdownCleanUp(exitCode) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport {\r\n getOptions,\r\n setOptions,\r\n mergeOptions,\r\n mapToNewOptions\r\n} from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\nimport server, { startServer } from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage. This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} customOptions - The `customOptions` object, which may\r\n * be a partial or complete set of options. If the provided options are partial,\r\n * missing values will be merged with the default general options, retrieved\r\n * using the `getOptions` function.\r\n */\r\nexport async function initExport(customOptions) {\r\n // Get the global options object copy and extend it with the incoming options\r\n const options = mergeOptions(getOptions(false), customOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] 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, `[process] 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, `[process] 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, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n server,\r\n startServer,\r\n\r\n // Options\r\n getOptions,\r\n setOptions,\r\n mergeOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Cache\r\n checkAndUpdateCache,\r\n\r\n // Pool\r\n initPool,\r\n killPool,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n\r\n // Utils\r\n shutdownCleanUp\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","join","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","existsSync","mkdirSync","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initGlobalOptions","getOptions","getReference","setOptions","customOptions","cliArgs","modifyGlobal","configOptions","cliOptions","_loadConfigFile","_pairArgumentValue","generalOptions","_updateOptions","mergeOptions","originalOptions","newOptions","entries","mapToNewOptions","oldOptions","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","configOpt","customOpt","cliOpt","configVal","customVal","cliVal","envVal","stringifyFunctions","stringify","replaceAll","Error","configIndex","findIndex","arg","configFileName","i","option","async","fetch","requestOptions","Promise","resolve","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","setupHighcharts","Highcharts","animObject","duration","createChart","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","customLogicOptions","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","exportOptions","isSVG","_setAsSvg","_setAsOptions","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","_requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","msg","rateOptions","limiter","rateLimit","windowMs","delayMs","handler","format","send","default","skip","query","access_token","HttpError","setStatus","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","requestId","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","limit","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","readFile","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","_attachProcessExitListeners","code"],"mappings":"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B,GAGP,CCrOO,MAAMiB,cAAgB,CAC3BC,UAAW,CACT7B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFqD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbnG,KAAM,OACNoG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP5D,MAAO,SACPqD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbnG,KAAM,SAGVuG,OAAQ,CACN7D,MAAO,8BACPqD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbnG,KAAM,SAGVwG,WAAY,CACV9D,OAAO,EACPqD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbnG,KAAM,WAGVyG,UAAW,CACT/D,MAAO,SACPqD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbnG,KAAM,SAGV0G,YAAa,CACXhE,MAAO,CAAC,aAAc,kBAAmB,iBACzCqD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbnG,KAAM,cACN2G,aAAc,0DAGlBC,cAAe,CACblE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,kBACA,SACA,cACA,mBACA,eACA,kBACA,cACA,eAEA,cACA,WACA,eACA,WACA,SACA,OACA,WACA,YACA,SACA,qBACA,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"} \ No newline at end of file diff --git a/msg/licenseagree.msg b/msg/licenseagree.msg index f1d81d6e..9a960a98 100644 --- a/msg/licenseagree.msg +++ b/msg/licenseagree.msg @@ -3,8 +3,8 @@ Highcharts Export Server https://github.com/highcharts/node-export-server -In order to use this application, Highcharts needs to be downloaded and -embedded. A license is required to use Highcharts if you're a +In order to use this application, Highcharts needs to be downloaded and +embedded. A license is required to use Highcharts if you're a for-profit, commercial, outfit. The license can be viewed here: https://highcharts.com/license diff --git a/public/js/main.js b/public/js/main.js index 4c613d81..bb3fd4af 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -13,6 +13,7 @@ See LICENSE file in root for details. *******************************************************************************/ /* eslint-disable no-undef */ + const highexp = {}; (function () { @@ -58,7 +59,7 @@ const highexp = {}; function toStructure(b64) { return { - infile: optionsCM.getValue(), + options: optionsCM.getValue(), width: width.value.length ? width.value : false, scale: scale.value.length ? scale.value : false, constr: constr.value, From 06623aea3234e278349ac34c2d45bc2a49a4719c Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:07:44 +0100 Subject: [PATCH 054/102] Scenarios and samples corrections and modifications. --- samples/http/requestInfile.json | 2 +- samples/module/optionsPhantom.js | 62 ++++++++----------- samples/module/optionsPuppeteer.js | 43 ++++--------- samples/module/promises.js | 5 +- samples/module/svg.js | 37 +++-------- samples/{cli => resources}/customOptions.json | 0 tests/cli/cliTestRunner.js | 2 +- tests/cli/cliTestRunnerSingle.js | 2 +- tests/cli/scenarios/loadConfig.json | 2 +- tests/http/httpTestRunner.js | 2 +- tests/http/httpTestRunnerSingle.js | 2 +- .../{infileJson.json => infile.json} | 0 tests/http/scenarios/infileStringified.json | 3 - tests/http/scenarios/instr.json | 3 + tests/node/nodeTestRunner.js | 2 +- tests/node/nodeTestRunnerSingle.js | 10 +-- tests/other/privateRangeUrl.js | 2 +- tests/other/sideBySide.js | 4 +- tests/other/stressTest.js | 4 +- 19 files changed, 70 insertions(+), 117 deletions(-) rename samples/{cli => resources}/customOptions.json (100%) rename tests/http/scenarios/{infileJson.json => infile.json} (100%) delete mode 100644 tests/http/scenarios/infileStringified.json create mode 100644 tests/http/scenarios/instr.json diff --git a/samples/http/requestInfile.json b/samples/http/requestInfile.json index 7459eb73..f141f20b 100644 --- a/samples/http/requestInfile.json +++ b/samples/http/requestInfile.json @@ -1,5 +1,5 @@ { - "infile": { + "options": { "chart": { "type": "column" }, diff --git a/samples/module/optionsPhantom.js b/samples/module/optionsPhantom.js index fa9618b4..5dbc1dad 100644 --- a/samples/module/optionsPhantom.js +++ b/samples/module/optionsPhantom.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -12,19 +12,10 @@ See LICENSE file in root for details. *******************************************************************************/ -import { writeFileSync } from 'fs'; - import exporter, { initExport } from '../../lib/index.js'; -// Export settings with the old options structure (PhantomJS) -// Will be mapped appropriately to the new structure with the `mapToNewConfig` -const exportSettings = { - type: 'png', - constr: 'chart', - outfile: './samples/module/optionsPhantom.jpeg', - logLevel: 4, - scale: 1, - workers: 1, +// Old options structure (PhantomJS) +const oldOptions = { options: { chart: { type: 'column' @@ -58,37 +49,38 @@ const exportSettings = { data: [5, 3, 4, 2] } ] - } + }, + type: 'png', + constr: 'chart', + outfile: './samples/module/optionsPhantom.png', + scale: 1, + width: 1000, + globalOptions: './samples/resources/optionsGlobal.json', + allowFileResources: true, + callback: './samples/resources/callback.js', + resources: './samples/resources/resources.json', + fromFile: './samples/resources/customOptions.json', + workers: 1, + workLimit: 5, + logLevel: 4, + logFile: 'phantom.log', + logDest: './samples/module/log', + logToFile: false }; -const start = async () => { +(async () => { try { - // Map to fit the new options structure - const mappedOptions = exporter.mapToNewConfig(exportSettings); + // Map to fit the new options structure (Puppeteer) + const newOptions = exporter.mapToNewOptions(oldOptions); // Set the new options - const options = exporter.setOptions(mappedOptions); + const options = exporter.setOptions(newOptions); // Init a pool for one export await initExport(options); // Perform an export - await exporter.startExport(options, async (error, data) => { - // Exit process and display error - if (error) { - throw error; - } - const { outfile, type } = data.options.export; - - // Save the base64 from a buffer to a correct image file - writeFileSync( - outfile, - type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result - ); - - // Kill the pool - await exporter.killPool(); - }); + await exporter.singleExport(options); } catch (error) { // Log the error with stack exporter.logWithStack(1, error); @@ -96,6 +88,4 @@ const start = async () => { // Gracefully shut down the process await exporter.shutdownCleanUp(1); } -}; - -start(); +})(); diff --git a/samples/module/optionsPuppeteer.js b/samples/module/optionsPuppeteer.js index b9a5b674..60cb9cdb 100644 --- a/samples/module/optionsPuppeteer.js +++ b/samples/module/optionsPuppeteer.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -12,16 +12,10 @@ See LICENSE file in root for details. *******************************************************************************/ -import { writeFileSync } from 'fs'; - import exporter, { initExport } from '../../lib/index.js'; -// Export settings with new options structure (Puppeteer) -const exportSettings = { - pool: { - minWorkers: 1, - maxWorkers: 1 - }, +// New options structure (Puppeteer) +const newOptions = { export: { type: 'jpeg', constr: 'chart', @@ -132,34 +126,25 @@ const exportSettings = { js: "Highcharts.charts[0].update({xAxis: {title: {text: 'Resources axis title'}}});", css: '.highcharts-yaxis .highcharts-axis-line { stroke-width: 2px; } .highcharts-color-0 { fill: #f7a35c; stroke: #f7a35c; }' } + }, + pool: { + maxWorkers: 1 + }, + logging: { + toFile: false } }; -const start = async () => { +(async () => { try { // Set the new options - const options = exporter.setOptions(exportSettings); + const options = exporter.setOptions(newOptions); // Init a pool for one export await initExport(options); // Perform an export - await exporter.startExport(options, async (error, data) => { - // Exit process and display error - if (error) { - throw error; - } - const { outfile, type } = data.options.export; - - // Save the base64 from a buffer to a correct image file - writeFileSync( - outfile, - type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result - ); - - // Kill the pool - await exporter.killPool(); - }); + await exporter.singleExport(options); } catch (error) { // Log the error with stack exporter.logWithStack(1, error); @@ -167,6 +152,4 @@ const start = async () => { // Gracefully shut down the process await exporter.shutdownCleanUp(1); } -}; - -start(); +})(); diff --git a/samples/module/promises.js b/samples/module/promises.js index 1790c1b2..12001897 100644 --- a/samples/module/promises.js +++ b/samples/module/promises.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -87,7 +87,8 @@ exportCharts( maxWorkers: 2 }, logging: { - level: 4 + level: 4, + toFile: false } } ) diff --git a/samples/module/svg.js b/samples/module/svg.js index 6028efca..08d2e33f 100644 --- a/samples/module/svg.js +++ b/samples/module/svg.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -12,12 +12,10 @@ See LICENSE file in root for details. *******************************************************************************/ -import { writeFileSync } from 'fs'; - import exporter, { initExport } from '../../lib/index.js'; -// Export settings with new options structure (Puppeteer) -const exportSettings = { +// SVG options +const svgOptions = { export: { type: 'png', outfile: './samples/module/svg.png', @@ -25,36 +23,23 @@ const exportSettings = { svg: 'Highcharts.com' }, pool: { - minWorkers: 1, maxWorkers: 1 + }, + logging: { + toFile: false } }; -const start = async () => { +(async () => { try { // Set the new options - const options = exporter.setOptions(exportSettings); + const options = exporter.setOptions(svgOptions); // Init a pool for one export await initExport(options); // Perform an export - await exporter.startExport(options, async (error, data) => { - // Exit process and display error - if (error) { - throw error; - } - const { outfile, type } = data.options.export; - - // Save the base64 from a buffer to a correct image file - writeFileSync( - outfile, - type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result - ); - - // Kill the pool - await exporter.killPool(); - }); + await exporter.singleExport(options); } catch (error) { // Log the error with stack exporter.logWithStack(1, error); @@ -62,6 +47,4 @@ const start = async () => { // Gracefully shut down the process await exporter.shutdownCleanUp(1); } -}; - -start(); +})(); diff --git a/samples/cli/customOptions.json b/samples/resources/customOptions.json similarity index 100% rename from samples/cli/customOptions.json rename to samples/resources/customOptions.json diff --git a/tests/cli/cliTestRunner.js b/tests/cli/cliTestRunner.js index 6a27a589..7e5a2c45 100644 --- a/tests/cli/cliTestRunner.js +++ b/tests/cli/cliTestRunner.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. diff --git a/tests/cli/cliTestRunnerSingle.js b/tests/cli/cliTestRunnerSingle.js index 4a06d863..7c1b92d1 100644 --- a/tests/cli/cliTestRunnerSingle.js +++ b/tests/cli/cliTestRunnerSingle.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. diff --git a/tests/cli/scenarios/loadConfig.json b/tests/cli/scenarios/loadConfig.json index bfd48789..13928970 100644 --- a/tests/cli/scenarios/loadConfig.json +++ b/tests/cli/scenarios/loadConfig.json @@ -1,3 +1,3 @@ { - "loadConfig": "./samples/cli/customOptions.json" + "loadConfig": "./samples/resources/customOptions.json" } diff --git a/tests/http/httpTestRunner.js b/tests/http/httpTestRunner.js index ff11389a..37508214 100644 --- a/tests/http/httpTestRunner.js +++ b/tests/http/httpTestRunner.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. diff --git a/tests/http/httpTestRunnerSingle.js b/tests/http/httpTestRunnerSingle.js index 8e6fc4e9..2453df4f 100644 --- a/tests/http/httpTestRunnerSingle.js +++ b/tests/http/httpTestRunnerSingle.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. diff --git a/tests/http/scenarios/infileJson.json b/tests/http/scenarios/infile.json similarity index 100% rename from tests/http/scenarios/infileJson.json rename to tests/http/scenarios/infile.json diff --git a/tests/http/scenarios/infileStringified.json b/tests/http/scenarios/infileStringified.json deleted file mode 100644 index bc1be290..00000000 --- a/tests/http/scenarios/infileStringified.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "infile": "{\"title\":{\"text\":\"Chart created from the stringified 'infile'\"},\"xAxis\":{\"categories\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\"]},\"series\":[{\"type\":\"column\",\"data\":[5,6,7,8]},{\"type\":\"line\",\"data\":[1,2,3,4]}]}" -} diff --git a/tests/http/scenarios/instr.json b/tests/http/scenarios/instr.json new file mode 100644 index 00000000..8110796a --- /dev/null +++ b/tests/http/scenarios/instr.json @@ -0,0 +1,3 @@ +{ + "instr": "{\"title\":{\"text\":\"Chart created from the stringified 'instr'\"},\"xAxis\":{\"categories\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\"]},\"series\":[{\"type\":\"column\",\"data\":[5,6,7,8]},{\"type\":\"line\",\"data\":[1,2,3,4]}]}" +} diff --git a/tests/node/nodeTestRunner.js b/tests/node/nodeTestRunner.js index 36797a5b..9d3d9eac 100644 --- a/tests/node/nodeTestRunner.js +++ b/tests/node/nodeTestRunner.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. diff --git a/tests/node/nodeTestRunnerSingle.js b/tests/node/nodeTestRunnerSingle.js index bffdccf3..89ebafcd 100644 --- a/tests/node/nodeTestRunnerSingle.js +++ b/tests/node/nodeTestRunnerSingle.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -18,11 +18,7 @@ import { basename, join } from 'path'; import 'colors'; import exporter, { initExport } from '../../lib/index.js'; -import { - __dirname, - getNewDateTime, - mergeConfigOptions -} from '../../lib/utils.js'; +import { __dirname, getNewDateTime } from '../../lib/utils.js'; console.log( 'Highcharts Export Server Node Test Runner'.yellow.bold.underline, @@ -61,7 +57,7 @@ console.log( // Set options const options = exporter.setOptions( - mergeConfigOptions(fileOptions, { + exporter.mergeOptions(fileOptions, { pool: { minWorkers: 1, maxWorkers: 1 diff --git a/tests/other/privateRangeUrl.js b/tests/other/privateRangeUrl.js index eff3e1d5..9566e76b 100644 --- a/tests/other/privateRangeUrl.js +++ b/tests/other/privateRangeUrl.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. diff --git a/tests/other/sideBySide.js b/tests/other/sideBySide.js index 43c9a9a2..84a172c0 100644 --- a/tests/other/sideBySide.js +++ b/tests/other/sideBySide.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -53,7 +53,7 @@ try { // Payload body const payload = JSON.stringify({ - infile: { + options: { title: { text: index ? 'Phantom Export Server' diff --git a/tests/other/stressTest.js b/tests/other/stressTest.js index 2f90ea89..7f548bdf 100644 --- a/tests/other/stressTest.js +++ b/tests/other/stressTest.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -26,7 +26,7 @@ console.log( // The request options const requestBody = { type: 'svg', - infile: { + options: { title: { text: 'Chart' }, From 855e5be41645638f905096021d57e822686b68e0 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:08:03 +0100 Subject: [PATCH 055/102] Unit tests corrections and modifications. --- tests/unit/cache.test.js | 4 +- tests/unit/config.test.js | 157 ++++++++++++++++++++++++++++++++++++ tests/unit/envs.test.js | 4 +- tests/unit/index.test.js | 2 +- tests/unit/sanitize.test.js | 2 +- tests/unit/utils.test.js | 42 +--------- 6 files changed, 166 insertions(+), 45 deletions(-) create mode 100644 tests/unit/config.test.js diff --git a/tests/unit/cache.test.js b/tests/unit/cache.test.js index af01d01e..105274e6 100644 --- a/tests/unit/cache.test.js +++ b/tests/unit/cache.test.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -12,6 +12,8 @@ See LICENSE file in root for details. *******************************************************************************/ +import { describe, expect, it } from '@jest/globals'; + import { extractVersion, extractModuleName } from '../../lib/cache'; describe('extractVersion', () => { diff --git a/tests/unit/config.test.js b/tests/unit/config.test.js new file mode 100644 index 00000000..b39a2379 --- /dev/null +++ b/tests/unit/config.test.js @@ -0,0 +1,157 @@ +/******************************************************************************* + +Highcharts Export Server + +Copyright (c) 2016-2025, Highsoft + +Licenced under the MIT licence. + +Additionally a valid Highcharts license is required for use. + +See LICENSE file in root for details. + +*******************************************************************************/ + +import { describe, expect, it } from '@jest/globals'; + +import { isAllowedConfig, mapToNewOptions } from '../../lib/config'; + +describe('isAllowedJSON', () => { + it('parses valid JSON strings', () => { + const json = '{"key":"value"}'; + + expect(isAllowedConfig(json)).toEqual({ key: 'value' }); + }); + + it('returns null for invalid JSON strings', () => { + const json = '{"key":value}'; + + expect(isAllowedConfig(json)).toBe(null); + }); + + it('parses JavaScript objects', () => { + const obj = { key: 'value' }; + + expect(isAllowedConfig(obj)).toEqual({ key: 'value' }); + }); + + it('parses JavaScript objects with functions when the `allowFunctions` is true', () => { + const obj = { key1: 'value', key2: function () {} }; + + expect(isAllowedConfig(obj, false, true)).toEqual({ + key1: 'value', + key2: expect.any(Function) + }); + }); + + it('returns a stringified version of a valid JSON/object when the `toString` is true', () => { + const obj = { key: 'value' }; + const json = '{"key":"value"}'; + + expect(isAllowedConfig(obj, true)).toBe(json); + expect(isAllowedConfig(json, true)).toBe(json); + }); + + it('returns a stringified version of a valid JSON/object with functions when the `toString` and `allowFunctions` are true', () => { + const obj = { key1: 'value', key2: function () {} }; + + expect(isAllowedConfig(obj, true, true)).toBe( + '{"key1":"value","key2":function () {}}' + ); + }); + + it('handles non-JSON strings', () => { + const str = 'Just a string'; + + expect(isAllowedConfig(str)).toBe(null); + }); + + it('handles non-object types (e.g., numbers, booleans)', () => { + expect(isAllowedConfig(123)).toBe(null); + expect(isAllowedConfig(true)).toBe(null); + }); + + it('correctly parses and stringifies an array when `toString` is true', () => { + const arr = [1, 2, 3]; + + expect(isAllowedConfig(arr, true)).toBe(null); + }); +}); + +describe('mapToNewOptions', () => { + it('should map the old (PhantomJS) options structure to the new (Puppetter) correctly', () => { + const oldOptions = { + options: null, + outfile: null, + type: 'png', + constr: 'chart', + width: null, + scale: null, + globalOptions: null, + allowFileResources: false, + callback: null, + resources: null, + fromFile: null, + enableServer: false, + host: '0.0.0.0', + port: 7801, + rateLimit: 10, + skipKey: null, + skipToken: null, + sslOnly: false, + sslPort: 443, + sslPath: null, + workers: 8, + workLimit: 40, + logLevel: 4, + logFile: 'highcharts-export-server.log', + logDest: 'log', + listenToProcessExits: true + }; + + expect(mapToNewOptions(oldOptions)).toEqual({ + export: { + options: null, + outfile: null, + type: 'png', + constr: 'chart', + width: null, + scale: null, + globalOptions: null + }, + customLogic: { + allowFileResources: false, + callback: null, + resources: null, + loadConfig: null + }, + server: { + enable: false, + host: '0.0.0.0', + port: 7801, + rateLimiting: { + maxRequests: 10, + skipKey: null, + skipToken: null + }, + ssl: { + force: false, + port: 443, + certPath: null + } + }, + pool: { + maxWorkers: 8, + workLimit: 40 + }, + logging: { + level: 4, + file: 'highcharts-export-server.log', + dest: 'log' + }, + other: { + listenToProcessExits: true + } + }); + }); +}); diff --git a/tests/unit/envs.test.js b/tests/unit/envs.test.js index bf13f9a8..0cd71622 100644 --- a/tests/unit/envs.test.js +++ b/tests/unit/envs.test.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -12,6 +12,8 @@ See LICENSE file in root for details. *******************************************************************************/ +import { describe, expect } from '@jest/globals'; + import { Config } from '../../lib/envs.js'; describe('Environment variables should be correctly parsed', () => { diff --git a/tests/unit/index.test.js b/tests/unit/index.test.js index b8a28276..b92c2dd3 100644 --- a/tests/unit/index.test.js +++ b/tests/unit/index.test.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. diff --git a/tests/unit/sanitize.test.js b/tests/unit/sanitize.test.js index 48dfb1de..5cbf791a 100644 --- a/tests/unit/sanitize.test.js +++ b/tests/unit/sanitize.test.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. diff --git a/tests/unit/utils.test.js b/tests/unit/utils.test.js index db693072..c8ecb954 100644 --- a/tests/unit/utils.test.js +++ b/tests/unit/utils.test.js @@ -2,7 +2,7 @@ Highcharts Export Server -Copyright (c) 2016-2024, Highsoft +Copyright (c) 2016-2025, Highsoft Licenced under the MIT licence. @@ -19,7 +19,6 @@ import { fixType, roundNumber, toBoolean, - isCorrectJSON, isObject, isObjectEmpty, isPrivateRangeUrlFound @@ -60,45 +59,6 @@ describe('toBoolean', () => { }); }); -describe('isCorrectJSON', () => { - it('parses valid JSON strings', () => { - const json = '{"key":"value"}'; - expect(isCorrectJSON(json)).toEqual({ key: 'value' }); - }); - - it('returns null for invalid JSON strings', () => { - const json = '{"key":value}'; - expect(isCorrectJSON(json)).toBe(null); - }); - - it('parses JavaScript objects', () => { - const obj = { key: 'value' }; - expect(isCorrectJSON(obj)).toEqual({ key: 'value' }); - }); - - it('returns a stringified version of a valid JSON/object when toString is true', () => { - const obj = { key: 'value' }; - const json = '{"key":"value"}'; - expect(isCorrectJSON(obj, true)).toBe(json); - expect(isCorrectJSON(json, true)).toBe(json); - }); - - it('handles non-JSON strings', () => { - const str = 'Just a string'; - expect(isCorrectJSON(str)).toBe(null); - }); - - it('handles non-object types (e.g., numbers, booleans)', () => { - expect(isCorrectJSON(123)).toBe(null); - expect(isCorrectJSON(true)).toBe(null); - }); - - it('correctly parses and stringifies an array when toString is true', () => { - const arr = [1, 2, 3]; - expect(isCorrectJSON(arr, true)).toBe(null); - }); -}); - describe('isObject', () => { it('returns true for plain objects', () => { expect(isObject({})).toBe(true); From 5c59e2646e1148071c2838d1765fde09fe676737 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:08:18 +0100 Subject: [PATCH 056/102] Documentation corrections, updates and extends. --- README.md | 206 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 07686bbe..f904827f 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ There are four main ways of loading configurations: ## Default JSON Config -The JSON below represents the default configuration stored in the `lib/schemas/config.js` file. If no `.env` file is found (more details on the file and environment variables below), these options will be used. +The JSON below represents the default configuration stored in the `lib/schemas/config.js` file. If no `.env` file is found (more details on the file and environment variables below), these options will be used. The configuration is not recommended to be modified directly, as it can typically be managed through other sources. The format, along with its default values, is as follows (using the recommended ordering of core and module scripts below). @@ -193,20 +193,17 @@ _Available default JSON config:_ "instr": null, "options": null, "svg": null, + "batch": null, "outfile": null, "type": "png", "constr": "chart", "b64": false, "noDownload": false, - "defaultHeight": 400, - "defaultWidth": 600, - "defaultScale": 1, "height": null, "width": null, "scale": null, "globalOptions": null, "themeOptions": null, - "batch": null, "rasterizationTimeout": 1500 }, "customLogic": { @@ -222,6 +219,7 @@ _Available default JSON config:_ "enable": false, "host": "0.0.0.0", "port": 7801, + "uploadLimit": 3, "benchmarking": false, "proxy": { "host": null, @@ -288,7 +286,7 @@ _Available default JSON config:_ ## Custom JSON Config -To load an additional JSON configuration file, use the `--loadConfig ` option. This JSON file can either be manually created or generated through a prompt triggered by the `--createConfig ` option. The value of the `` must be set with the _.json_ extension. +To load an additional JSON configuration file, use the `--loadConfig ` option. This JSON file can either be manually created or generated through a prompt triggered by the `--createConfig ` option. The `` value does not need a _.json_ extension, but the file's content must be valid JSON when using the `--loadConfig` option. ## Environment Variables @@ -314,10 +312,11 @@ _Available environment variables:_ ### Export Config -- `EXPORT_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 ``). -- `EXPORT_INSTR`: An input in a form of a stringified JSON or SVG file. Overrides the `infile` option (defaults to ``). +- `EXPORT_INFILE`: The input file should include a name and a type (**.json** or **.svg**) and must contain a correctly formatted JSON or SVG (defaults to ``). +- `EXPORT_INSTR`: An input in a form of a stringified JSON. - `EXPORT_OPTIONS`: An alias for the `instr` option (defaults to ``). - `EXPORT_SVG`: A string containing SVG representation to render as a chart (defaults to ``). +- `EXPORT_BATCH`: Initiates a batch job with a string containing input/output pairs: **"in=out;in=out;.."** (defaults to ``). - `EXPORT_OUTFILE`: The output filename, accompanied by a type (**jpeg**, **png**, **pdf**, or **svg**). Ignores the `type` option (defaults to ``). - `EXPORT_TYPE`: The format of the file to export to. Can be **jpeg**, **png**, **pdf**, or **svg** (defaults to `png`). - `EXPORT_CONSTR`: The constructor to use. Can be **chart**, **stockChart**, **mapChart**, or **ganttChart** (defaults to `chart`). @@ -326,12 +325,11 @@ _Available environment variables:_ - `EXPORT_HEIGHT`: The height of the exported chart. Overrides the option in the chart settings (defaults to ``). - `EXPORT_WIDTH`: The width of the exported chart. Overrides the option in the chart settings (defaults to ``). - `EXPORT_SCALE`: The scale of the exported chart. Ranges between **0.1** and **5.0** (defaults to ``). -- `EXPORT_DEFAULT_HEIGHT`: The default height for exported charts if not set explicitly (defaults to `400`). -- `EXPORT_DEFAULT_WIDTH`: The default width for exported charts if not set explicitly (defaults to `600`). -- `EXPORT_DEFAULT_SCALE`: The default scale for exported charts if not set explicitly. Ranges between **0.1** and **5.0** (defaults to `1`). +- `EXPORT_DEFAULT_HEIGHT`: The default fallback height for exported charts if not set explicitly (defaults to `400`). +- `EXPORT_DEFAULT_WIDTH`: The default fallback width for exported charts if not set explicitly (defaults to `600`). +- `EXPORT_DEFAULT_SCALE`: The default fallback scale for exported charts if not set explicitly. Ranges between **0.1** and **5.0** (defaults to `1`). - `EXPORT_GLOBAL_OPTIONS`: Either a stringified JSON or a filename containing global options to be passed into the `Highcharts.setOptions` (defaults to ``). - `EXPORT_THEME_OPTIONS`: Either a stringified JSON or a filename containing theme options to be passed into the `Highcharts.setOptions` (defaults to ``). -- `EXPORT_BATCH`: Initiates a batch job with a string containing input/output pairs: **"in=out;in=out;.."** (defaults to ``). - `EXPORT_RASTERIZATION_TIMEOUT`: The specified duration, in milliseconds, to wait for rendering a webpage (defaults to `1500`). ### Custom Logic Config @@ -339,7 +337,7 @@ _Available environment variables:_ - `CUSTOM_LOGIC_ALLOW_CODE_EXECUTION`: Controls whether the execution of arbitrary code is allowed during the exporting process (defaults to `false`). - `CUSTOM_LOGIC_ALLOW_FILE_RESOURCES`: Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server (defaults to `false`). - `CUSTOM_LOGIC_CUSTOM_CODE`: Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the _.js_ extension (defaults to ``). -- `CUSTOM_LOGIC_CALLBACK`: JavaScript code to run during construction. It can be a function or a filename with the _.js_ extension (defaults to ``). +- `CUSTOM_LOGIC_CALLBACK`: JavaScript code to run during construction. It can be a stringified function or a filename with the _.js_ extension that contains a correct Highcharts callback (defaults to ``). - `CUSTOM_LOGIC_RESOURCES`: Additional resources in the form of a stringified JSON. It may contain `files` (array of JS filenames), `js` (stringified JS), and `css` (stringified CSS) sections (defaults to ``). - `CUSTOM_LOGIC_LOAD_CONFIG`: A file containing a pre-defined configuration to use (defaults to ``). - `CUSTOM_LOGIC_CREATE_CONFIG`: Enables setting options through a prompt and saving them in a provided config file (defaults to ``). @@ -349,6 +347,7 @@ _Available environment variables:_ - `SERVER_ENABLE`: If set to **true**, the server starts on 0.0.0.0 (defaults to `false`). - `SERVER_HOST`: The hostname of the server. Additionally, it starts a server listening on the provided hostname (defaults to `0.0.0.0`). - `SERVER_PORT`: The port to be used for the server when enabled (defaults to `7801`). +- `SERVER_UPLOAD_LIMIT`: The maximum request body size in MB (defaults to `3`). - `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 Proxy Config @@ -444,10 +443,11 @@ _Available CLI arguments:_ ### Export Config -- `--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 `null`). -- `--instr`: An input in a form of a stringified JSON or SVG file. Overrides the `infile` option (defaults to `null`). +- `--infile`: The input file should include a name and a type (**.json** or **.svg**) and must contain a correctly formatted JSON or SVG (defaults to `null`). +- `--instr`: An input in a form of a stringified JSON. - `--options`: An alias for the `instr` option (defaults to `null`). - `--svg`: A string containing SVG representation to render as a chart (defaults to `null`). +- `--batch`: Initiates a batch job with a string containing input/output pairs: **"in=out;in=out;.."** (defaults to `null`). - `--outfile`: The output filename, accompanied by a type (**jpeg**, **png**, **pdf**, or **svg**). Ignores the `type` option (defaults to `null`). - `--type`: The format of the file to export to. Can be **jpeg**, **png**, **pdf**, or **svg** (defaults to `png`). - `--constr`: The constructor to use. Can be **chart**, **stockChart**, **mapChart**, or **ganttChart** (defaults to `chart`). @@ -456,12 +456,11 @@ _Available CLI arguments:_ - `--height`: The height of the exported chart. Overrides the option in the chart settings (defaults to `null`). - `--width`: The width of the exported chart. Overrides the option in the chart settings (defaults to `null`). - `--scale`: The scale of the exported chart. Ranges between **0.1** and **5.0** (defaults to `null`). -- `--defaultHeight`: The default height for exported charts if not set explicitly (defaults to `400`). -- `--defaultWidth`: The default width for exported charts if not set explicitly (defaults to `600`). -- `--defaultScale`: The default scale for exported charts if not set explicitly. Ranges between **0.1** and **5.0** (defaults to `1`). +- `--defaultHeight`: The default fallback height for exported charts if not set explicitly (defaults to `400`). +- `--defaultWidth`: The default fallback width for exported charts if not set explicitly (defaults to `600`). +- `--defaultScale`: The default fallback scale for exported charts if not set explicitly. Ranges between **0.1** and **5.0** (defaults to `1`). - `--globalOptions`: Either a stringified JSON or a filename containing global options to be passed into the `Highcharts.setOptions` (defaults to `null`). - `--themeOptions`: Either a stringified JSON or a filename containing theme options to be passed into the `Highcharts.setOptions` (defaults to `null`). -- `--batch`: Initiates a batch job with a string containing input/output pairs: **"in=out;in=out;.."** (defaults to `null`). - `--rasterizationTimeout`: The specified duration, in milliseconds, to wait for rendering a webpage (defaults to `1500`). ### Custom Logic Config @@ -469,7 +468,7 @@ _Available CLI arguments:_ - `--allowCodeExecution`: Controls whether the execution of arbitrary code is allowed during the exporting process (defaults to `false`). - `--allowFileResources`: Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server (defaults to `false`). - `--customCode`: Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the _.js_ extension (defaults to `null`). -- `--callback`: JavaScript code to run during construction. It can be a function or a filename with the _.js_ extension (defaults to `null`). +- `--callback`: JavaScript code to run during construction. It can be a stringified function or a filename with the _.js_ extension that contains a correct Highcharts callback (defaults to `null`). - `--resources`: Additional resources in the form of a stringified JSON. It may contain `files` (array of JS filenames), `js` (stringified JS), and `css` (stringified CSS) sections (defaults to `null`). - `--loadConfig`: A file containing a pre-defined configuration to use (defaults to `null`). - `--createConfig`: Enables setting options through a prompt and saving them in a provided config file (defaults to `null`). @@ -479,6 +478,7 @@ _Available CLI arguments:_ - `--enableServer`: If set to **true**, the server starts on 0.0.0.0 (defaults to `false`). - `--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`). +- `--uploadLimit`: The maximum request body size in MB (defaults to `3`). - `--serverBenchmarking`: 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 Proxy Config @@ -559,7 +559,7 @@ Apart from using as a CLI tool, which allows you to run one command at a time, i To test if the server is running correctly, you can send a simple POST request, e.g. by using Curl: ``` -curl -H "Content-Type: application/json" -X POST -d '{"infile":{"title": {"text": "Chart"}, "xAxis": {"categories": ["Jan", "Feb", "Mar"]}, "series": [{"data": [29.9, 71.5, 106.4]}]}}' 127.0.0.1:7801 -o chart.png +curl -H "Content-Type: application/json" -X POST -d '{"options":{"title": {"text": "Chart"}, "xAxis": {"categories": ["Jan", "Feb", "Mar"]}, "series": [{"data": [29.9, 71.5, 106.4]}]}}' 127.0.0.1:7801 -o chart.png ``` The above should result in a chart being generated and saved in a file named `chart.png`. @@ -577,10 +577,10 @@ The server accepts the following arguments in a POST request body. _Available request arguments:_ -- `infile`: Chart options in the form of JSON or stringified JSON. -- `options`: An alias for the `infile` option. -- `data`: Another alias for the `infile` option. +- `instr`: Chart options in the form of JSON or stringified JSON. +- `options`: An alias for the `instr` option. - `svg`: A string containing SVG representation to render as a chart. +- `outfile`: The output filename, accompanied by a type (**jpeg**, **png**, **pdf**, or **svg**). Ignores the `type` option. - `type`: The format of an exported chart (can be **png**, **jpeg**, **pdf** or **svg**). Mimetypes can also be used. - `constr`: The constructor to use (can be **chart**, **stockChart**, **mapChart** or **ganttChart**). - `height`: The height of the exported chart. @@ -674,7 +674,7 @@ const customOptions = { // Perform an export await exporter.startExport(options, async (error, data) => { - // The export result is now in the `data` (it will be base64 encoded) + // The export result is now in the `data` (it will be Base64 encoded) console.log(data.result); // Kill the pool when we are done with it @@ -683,7 +683,22 @@ const customOptions = { })(); ``` -In order for everything to work as it is supposed to, the `setOptions` function must be called before running the `initExport` and any export-related function (`startExport`, `singleExport`, or `batchExport`) to correctly initialize all option values. +In order for everything to work as it is supposed to, the `setOptions` function should be called before running the `initExport` and any export-related function (`startExport`, `singleExport`, or `batchExport`) to correctly initialize all option values. + +## Options Handling + +When starting a server or performing single or batch exports via the CLI, server's global options are initialized from the `defaultConfig` object located in `./lib/schemas/config.js`. These options are then extended with values from all other sources mentioned in the [Configuration](#configuration) section. + +For `Node.js Module` usage, the process differs slightly. Some API functions must be called manually. By default, global options are initialized solely from the `defaultConfig` object at the start, similar to the server and CLI scenarios. However, to include options from other sources (as outlined in the [Configuration](#configuration) section), you need to explicitly call the `setOptions()` function. If `setOptions()` is not used, the system will rely on the default values from the defaultConfig object. + +The `setOptions()` function allows you to either extend or copy global options. + +- `Extend`: Adds new options to the global configuration while keeping the original options intact. +- `Copy`: Merges new options into a separate set, leaving global options unaffected. + +This flexibility is useful for deciding whether global options should remain unmodified during subsequent exports or be updated dynamically. In either case, new options from other sources are merged into the configuration, and the final options are returned for use in API functions. + +Functions such as `initExport()`, `singleExport()`, `batchExport()`, and `startExport()` accept partial options. Any missing values in these options will be automatically filled in using the default values from the defaultConfig object. ## CommonJS Support @@ -697,7 +712,9 @@ This package supports both CommonJS and ES modules. - `async function startServer(serverOptions = getOptions().server)`: Starts HTTP or/and HTTPS server based on the provided configuration. The `serverOptions` object contains all server related properties (see the `server` section in the `lib/schemas/config.js` file for a reference). - - `@param {Object} [serverOptions=getOptions().server]` - Object containing server options. The default value is the global server options of the export server instance. + - `@param {Object} [serverOptions=getOptions().server]` - Object containing `server` options. The default value is the global server options of the export server instance. + + - `@returns {Promise}` A Promise that resolves to ending the function execution when the server should not be enabled or when no valid Express app is found. - `@throws {ExportError}` Throws an `ExportError` if the server cannot be configured and started. @@ -707,119 +724,154 @@ This package supports both CommonJS and ES modules. - `@returns {Array.}` Servers associated with Express app instance. -- `function enableRateLimiting(limitConfig)`: Enable rate limiting for the server. - - - `@param {Object} limitConfig` - Configuration object for rate limiting. - - - `@returns {Object}` Middleware for enabling rate limiting. - - `function getExpress()`: Get the Express instance. - - `@returns {Object}` The Express instance. + - `@returns {Express}` The Express instance. - `function getApp()`: Get the Express app instance. - - `@returns {Object}` The Express app instance. + - `@returns {Express}` The Express app instance. + +- `function enableRateLimiting(rateLimitingOptions)`: Enable rate limiting for the server. + + - `@param {Object} rateLimitingOptions` - Object containing `rateLimiting` options. - `function use(path, ...middlewares)`: Apply middleware(s) to a specific path. - `@param {string} path` - The path to which the middleware(s) should be applied. - - `@param {...Function} middlewares` - The middleware functions to be applied. + - `@param {...Function} middlewares` - The middleware function(s) to be applied. - `function get(path, ...middlewares)`: Set up a route with GET method and apply middleware(s). - - `@param {string} path` - The route path. - - `@param {...Function} middlewares` - The middleware functions to be applied. + - `@param {string} path` - The path to which the middleware(s) should be applied. + - `@param {...Function} middlewares` - The middleware function(s) to be applied. - `function post(path, ...middlewares)`: Set up a route with POST method and apply middleware(s). - - `@param {string} path` - The route path. - - `@param {...Function} middlewares` - The middleware functions to be applied. + - `@param {string} path` - The path to which the middleware(s) should be applied. + - `@param {...Function} middlewares` - The middleware function(s) to be applied. - `async function startServer(serverOptions = getOptions().server)`: Starts HTTP or/and HTTPS server based on the provided configuration. The `serverOptions` object contains all server related properties (see the `server` section in the `lib/schemas/config.js` file for a reference). - - `@param {Object} [serverOptions=getOptions().server]` - Object containing server options. The default value is the global server options of the export server instance. + - `@param {Object} [serverOptions=getOptions().server]` - Object containing `server` options. The default value is the global server options of the export server instance. + + - `@returns {Promise}` A Promise that resolves to ending the function execution when the server should not be enabled or when no valid Express app is found. - `@throws {ExportError}` Throws an `ExportError` if the server cannot be configured and started. -- `function getOptions(getGlobal = false)`: Gets the global options of the export server instance. +- `function getOptions(getReference = true)`: Gets the reference to the global options of the server instance object or its copy. + + - `@param {boolean} [getReference=true]` - Optional parameter to decide whether to return the reference to the global options of the server instance object or return a copy of it. The default value is true. - - `@param {boolean} [getGlobal = false]` - Optional parameter to decide whether to return the reference to the global options of the server instance object or return a copy of it. + - `@returns {Object}` The reference to the global options of the server instance object or its copy. - - `@returns {Object}` The global options object of the server instance. +- `function setOptions(customOptions = {}, cliArgs = [], modifyGlobal = false)`: Sets the global options of the export server instance, keeping the principle of the options load priority from all available sources. It accepts optional `customOptions` object and `cliArgs` array with arguments from the CLI. These options will be validated and applied if provided. -- `function setOptions(customOptions = {}, cliArgs = [], modifyGlobal = false)`: Sets the general options of the export server instance, keeping the principle of the options load priority from all available sources. It accepts optional `customOptions` object and `cliArgs` array with arguments from the CLI. These options will be validated and applied if provided. + The priority order of setting values is: - - `@param {Object} [customOptions={}]` - Optional custom options for additional configuration. - - `@param {Array.} [cliArgs=[]]` - Optional command line arguments for additional configuration. - - `@param {boolean} [modifyGlobal = false]` - Optional parameter to decide whether to update and return the reference to the global options of the server instance object or return a copy of it. + 1. Options from the `lib/schemas/config.js` file (default values). + 2. Options from a custom JSON file (loaded by the `loadConfig` option). + 3. Options from the environment variables (the `.env` file). + 4. Options from the first parameter (by default an empty object). + 5. Options from the CLI. + + - `@param {Object} [customOptions={}]` - Optional custom options for additional configuration. The default value is an empty object. + - `@param {Array.} [cliArgs=[]]` - Optional command line arguments for additional configuration. The default value is an empty array. + - `@param {boolean} [modifyGlobal=false]` - Optional parameter to decide whether to update and return the reference to the global options of the server instance object or return a copy of it. The default value is false. - `@returns {Object}` The updated general options object, reflecting the merged configuration from all available sources. -- `async function initExport(options = getOptions())`: Initializes the export process. Tasks such as configuring logging, checking the cache and sources, and initializing the pool of resources happen during this stage. This function must be called before attempting to export charts or set up a server. The `options` parameter is an object that contains all possible options. If the object is not provided, the default general options will be retrieved using the `getOptions` function. +- `function mergeOptions(originalOptions, newOptions)`: Merges two sets of configuration options, considering absolute properties. + + - `@param {Object} originalOptions` - Original configuration options. + - `@param {Object} newOptions` - New configuration options to be merged. - - `@param {Object} [options=getOptions()]` - The `options` object containing configuration for a custom export. The default value is the global options of the export server instance. + - `@returns {Object}` Merged configuration options. -- `async function startExport(options = getOptions(), endCallback)`: Starts an export process. The `options` object contains final options gathered from all possible sources (config, custom json, env, cli). The `endCallback` is called when the export is completed, with the `error` object as the first argument and the `data` object as the second, which contains the base64 respresentation of a chart. +- `function mapToNewOptions(oldOptions)`: Maps old-structured configuration options (PhantomJS) to a new format (Puppeteer). This function converts flat, old-structured options into a new, nested configuration format based on a predefined mapping (`nestedProps`). The new format is used for Puppeteer, while the old format was used for PhantomJS. - - `@param {Object} [options=getOptions()]` - The `options` object containing configuration for a custom export. The default value is the global options of the export server instance. - - `@param {Function} endCallback` - The callback function to be invoked upon finalizing work or upon error occurance of the exporting process. + - `@param {Object} oldOptions` - The old, flat configuration options to be converted. + + - `@returns {Object}` A new object containing options structured according to the mapping defined in `nestedProps` or an empty object if the provided `oldOptions` is not a correct object. + +- `async function initExport(customOptions)`: Initializes the export process. Tasks such as configuring logging, checking the cache and sources, and initializing the resource pool occur during this stage. This function must be called before attempting to export charts or set up a server. - - `@returns {void}` This function does not return a value directly. Instead, it communicates results via the `endCallback`. + - `@param {Object} customOptions` - The `customOptions` object, which may be a partial or complete set of options. If the provided options are partial, missing values will be merged with the default general options, retrieved using the `getOptions` function. -- `async function singleExport(options = getOptions())`: Starts a single export process based on the specified options. +- `async function singleExport(options)`: Starts a single export process based on the specified options and saves the image in the provided outfile. - - `@param {Object} [options=getOptions()]` - The `options` object containing configuration for a custom export. The default value is the global options of the export server instance. + - `@param {Object} options` - The `options` object, which may be a partial or complete set of options. It must contain at least one of the following properties: `infile`, `instr`, `options`, or `svg` to generate a valid image. - `@returns {Promise}` A Promise that resolves once the single export process is completed. - `@throws {ExportError}` Throws an `ExportError` if an error occurs during the single export process. -- `async function batchExport(options = getOptions())`: Starts a batch export process for multiple charts based on the information in the `batch` option. The `batch` is a string in the following format: "infile1.json=outfile1.png;infile2.json=outfile2.png;...". +- `async function batchExport(options)`: Starts a batch export process for multiple charts based on the information in the `batch` option. The `batch` is a string in the following format: "infile1.json=outfile1.png;infile2.json=outfile2.png;...". Results are saved in provided outfiles. - - `@param {Object} [options=getOptions()]` - The `options` object containing configuration for a custom export. The default value is the global options of the export server instance. + - `@param {Object} options` - The `options` object, which may be a partial or complete set of options. It must contain the `batch` option to generate valid images. - - `@returns {Promise}` A Promise that resolves once the batch export process is completed. + - `@returns {Promise}` A Promise that resolves once the batch export processes are completed. - `@throws {ExportError}` Throws an `ExportError` if an error occurs during any of the batch export process. -- `async function initPool(poolOptions = getOptions().pool, puppeteerArgs)`: Initializes the export pool with the provided configuration, creating a browser instance and setting up worker resources. +- `async function startExport(customOptions, endCallback)`: Starts an export process. The `customOptions` parameter is an object that may be partial or complete set of options. The `endCallback` is called when the export is completed, with the `error` object as the first argument and the `data` object as the second, which contains the Base64 representation of the chart in the `result` property and the full set of export options in the `options` property. + + - `@param {Object} customOptions` - The `customOptions` object, which may be a partial or complete set of options. If the provided options are partial, missing values will be merged with the default general options, retrieved using the `getOptions` function. + - `@param {Function} endCallback` - The callback function to be invoked upon finalizing work or upon error occurance of the exporting process. The first argument is `error` object and the `data` object is the second, that contains the Base64 representation of the chart in the `result` property and the full set of export options in the `options` property. + + - `@returns {Promise}` This function does not return a value directly. Instead, it communicates results via the `endCallback`. - - `@param {Object} poolOptions` - Object containing pool options. - - `@param {Array.} puppeteerArgs` - Array of custom puppeteer arguments for the puppeteer.launch function. + - `@throws {ExportError}` Throws an `ExportError` if there is a problem with processing input of any type. The error is passed into the `endCallback` function and processed there. + +- `async function checkAndUpdateCache(highchartsOptions, serverProxyOptions)`: Checks the cache for Highcharts dependencies, updates the cache if needed, and loads the sources. + + - `@param {Object} highchartsOptions` - Object containing `highcharts` options. + - `@param {Object} serverProxyOptions` - Object containing `server.proxy` options. + +- `async function initPool(poolOptions = getOptions().pool, puppeteerArgs = [])`: Initializes the export pool with the provided configuration, creating a browser instance and setting up worker resources. + + - `@param {Object} [poolOptions=getOptions().pool]` - Object containing `pool` options. The default value is the global pool options of the export server instance. + - `@param {Array.} [puppeteerArgs=[]]` - Additional arguments for Puppeteer launch. The default value is an empty array. + + - `@returns {Promise}` A Promise that resolves to ending the function execution when an already initialized pool of resources is found. + + - `@throws {ExportError}` Throws an `ExportError` if could not create the pool of workers. - `async function killPool()`: Kills all workers in the pool, destroys the pool, and closes the browser instance. - - `@returns {Promise}` A promise that resolves after the workers are killed, the pool is destroyed, and the browser is closed. + - `@returns {Promise}` A Promise that resolves after the workers are killed, the pool is destroyed, and the browser is closed. - `function log(...args)`: Logs a message. Accepts a variable amount of arguments. Arguments after the `level` will be passed directly to `console.log`, and/or will be joined and appended to the log file. - `@param {...unknown} args` - An array of arguments where the first is the log level and the rest are strings to build a message with. + - `@returns {void}` Ends the function execution when attempting to log information at a higher level than what is allowed. + - `function logWithStack(newLevel, error, customMessage)`: Logs an error message with its stack trace. Optionally, a custom message can be provided. - `@param {number} newLevel` - The log level. - `@param {Error} error` - The error object. - `@param {string} customMessage` - An optional custom message to be logged along with the error. -- `function setLogLevel(newLevel)`: Sets the log level to the specified value. Log levels are (0 = no logging, 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark). + - `@returns {void}` Ends the function execution when attempting to log information at a higher level than what is allowed. - - `@param {number} newLevel` - The new log level to be set. +- `function setLogLevel(level)`: Sets the log level to the specified value. Log levels are (0 = no logging, 1 = error, 2 = warning, 3 = notice, 4 = verbose, or 5 = benchmark). -- `function enableFileLogging(dest, file)`: Enables file logging with the specified destination and log file. + - `@param {number} level` - The log level to be set. - - `@param {string} logDest` - The destination path for log files. - - `@param {string} logFile` - The log file name. +- `function enableConsoleLogging(toConsole)`: Enables console logging. -- `async function shutdownCleanUp(exitCode)`: Clean up function to trigger before ending process for the graceful shutdown. + - `@param {boolean} toConsole` - The flag for setting the logging to the console. - - `@param {number} exitCode` - An exit code for the `process.exit()` function. +- `function enableFileLogging(dest, file, toFile)`: Enables file logging with the specified destination and log file. -- `function mapToNewConfig(oldOptions)`: Maps old-structured configuration options (PhantomJS) to a new format (Puppeteer). This function converts flat, old-structured options into a new, nested configuration format based on a predefined mapping (`nestedArgs`). The new format is used for Puppeteer, while the old format was used for PhantomJS. + - `@param {string} dest` - The destination path for the log file. + - `@param {string} file` - The log file name. + - `@param {boolean} toFile` - The flag for setting the logging to a file. - - `@param {Object} oldOptions` - The old, flat configuration options to be converted. +- `async function shutdownCleanUp(exitCode)`: Cleans up function to trigger before ending process for the graceful shutdown. - - `@returns {Object}` A new object containing options structured according to the mapping defined in `nestedArgs`. + - `@param {number} exitCode` - An exit code for the `process.exit()` function. # Examples @@ -849,7 +901,7 @@ Additionally, some options are now named differently due to the new structure an - `rateLimit` -> `maxRequests` - `workers` -> `maxWorkers` -If you depend on any of the above options, the optimal approach is to directly change the old names to the new ones in the options. However, you don't have to do it manually, as there is a utility function called `mapToNewConfig` that can easily transfer the old-structured options to the new format. For an example, refer to the `./samples/module/optionsPhantom.js` file. +If you depend on any of the above options, the optimal approach is to directly change the old names to the new ones in the options. However, you don't have to do it manually, as there is a utility function called `mapToNewOptions` that can easily transfer the old-structured options to the new format. For an example, refer to the `./samples/module/optionsPhantom.js` file. ## Note About Chart Size @@ -869,12 +921,14 @@ The latter is preferred, as it allows you to set separate sizing when exporting Like previously mentioned, there are multiple ways to set and prioritize options, and the `height`, `width` and `scale` are no exceptions here. The priority goes like this: -1. Options from the `export` section of the provided options (CLI, JSON, etc.). +1. The `height`, `width`, and `scale` options from the `export` section of the provided options (CLI, JSON, envs). 2. The `sourceHeight`, `sourceWidth` and `scale` from the `chart.exporting` section of chart's Highcharts options. 3. The `height` and `width` from the `chart` section of chart's Highcharts options. 4. The `sourceHeight`, `sourceWidth` and `scale` from the `chart.exporting` section of chart's Highcharts global options, if provided. 5. The `height` and `width` from the `chart` section of chart's Highcharts global options, if provided. -6. If no options are found to this point, the default values will be used (`height = 400`, `width = 600` and `scale = 1`). +6. The `sourceHeight`, `sourceWidth` and `scale` from the `chart.exporting` section of chart's Highcharts theme options, if provided. +7. The `height` and `width` from the `chart` section of chart's Highcharts theme options, if provided. +8. If no options are found to this point, the default values will be used (`height = 400`, `width = 600` and `scale = 1`). ## Note About Event Listeners From 14231663bc4003486b9e6a581ee016cdacc62719 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Sun, 5 Jan 2025 22:09:07 +0100 Subject: [PATCH 057/102] Updated package.json and package-lock.json. --- package-lock.json | 1238 +++++++++++++++++++++++---------------------- package.json | 25 +- 2 files changed, 655 insertions(+), 608 deletions(-) diff --git a/package-lock.json b/package-lock.json index 17c471cc..c264790c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,19 +11,18 @@ "dependencies": { "colors": "1.4.0", "cors": "^2.8.5", - "dompurify": "^3.2.3", + "dompurify": "3.1.6", "dotenv": "^16.4.7", "express": "^4.21.2", - "express-rate-limit": "^7.4.1", + "express-rate-limit": "^7.5.0", "https-proxy-agent": "^7.0.6", "jsdom": "^25.0.1", - "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^23.10.3", + "puppeteer": "^23.11.1", "tarn": "^3.0.2", - "uuid": "^11.0.3", - "zod": "^3.24.0" + "uuid": "^11.0.4", + "zod": "^3.24.1" }, "bin": { "highcharts-export-server": "bin/cli.js" @@ -37,10 +36,10 @@ "eslint-plugin-prettier": "^5.2.1", "husky": "^9.1.7", "jest": "^29.7.0", - "lint-staged": "^15.2.11", - "nodemon": "^3.1.7", + "lint-staged": "^15.3.0", + "nodemon": "^3.1.9", "prettier": "^3.4.2", - "rollup": "^4.28.1" + "rollup": "^4.29.2" }, "engines": { "node": ">=18.12.0" @@ -1072,9 +1071,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "license": "MIT", "dependencies": { @@ -1244,9 +1243,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", - "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.2.tgz", + "integrity": "sha512-s/8RiF4bdmGnc/J0N7lHAr5ZFJj+NdJqJ/Hj29K+c4lEdoVlukzvWXB9XpWZCdakVT0YAw8iyIqUP2iFRz5/jA==", "cpu": [ "arm" ], @@ -1258,9 +1257,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz", - "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.2.tgz", + "integrity": "sha512-mKRlVj1KsKWyEOwR6nwpmzakq6SgZXW4NUHNWlYSiyncJpuXk7wdLzuKdWsRoR1WLbWsZBKvsUCdCTIAqRn9cA==", "cpu": [ "arm64" ], @@ -1272,9 +1271,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz", - "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.2.tgz", + "integrity": "sha512-vJX+vennGwygmutk7N333lvQ/yKVAHnGoBS2xMRQgXWW8tvn46YWuTDOpKroSPR9BEW0Gqdga2DHqz8Pwk6X5w==", "cpu": [ "arm64" ], @@ -1286,9 +1285,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz", - "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.2.tgz", + "integrity": "sha512-e2rW9ng5O6+Mt3ht8fH0ljfjgSCC6ffmOipiLUgAnlK86CHIaiCdHCzHzmTkMj6vEkqAiRJ7ss6Ibn56B+RE5w==", "cpu": [ "x64" ], @@ -1300,9 +1299,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz", - "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.2.tgz", + "integrity": "sha512-/xdNwZe+KesG6XJCK043EjEDZTacCtL4yurMZRLESIgHQdvtNyul3iz2Ab03ZJG0pQKbFTu681i+4ETMF9uE/Q==", "cpu": [ "arm64" ], @@ -1314,9 +1313,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz", - "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.2.tgz", + "integrity": "sha512-eXKvpThGzREuAbc6qxnArHh8l8W4AyTcL8IfEnmx+bcnmaSGgjyAHbzZvHZI2csJ+e0MYddl7DX0X7g3sAuXDQ==", "cpu": [ "x64" ], @@ -1328,9 +1327,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz", - "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.2.tgz", + "integrity": "sha512-h4VgxxmzmtXLLYNDaUcQevCmPYX6zSj4SwKuzY7SR5YlnCBYsmvfYORXgiU8axhkFCDtQF3RW5LIXT8B14Qykg==", "cpu": [ "arm" ], @@ -1342,9 +1341,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz", - "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.2.tgz", + "integrity": "sha512-EObwZ45eMmWZQ1w4N7qy4+G1lKHm6mcOwDa+P2+61qxWu1PtQJ/lz2CNJ7W3CkfgN0FQ7cBUy2tk6D5yR4KeXw==", "cpu": [ "arm" ], @@ -1356,9 +1355,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz", - "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.2.tgz", + "integrity": "sha512-Z7zXVHEXg1elbbYiP/29pPwlJtLeXzjrj4241/kCcECds8Zg9fDfURWbZHRIKrEriAPS8wnVtdl4ZJBvZr325w==", "cpu": [ "arm64" ], @@ -1370,9 +1369,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz", - "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.2.tgz", + "integrity": "sha512-TF4kxkPq+SudS/r4zGPf0G08Bl7+NZcFrUSR3484WwsHgGgJyPQRLCNrQ/R5J6VzxfEeQR9XRpc8m2t7lD6SEQ==", "cpu": [ "arm64" ], @@ -1384,9 +1383,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz", - "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.2.tgz", + "integrity": "sha512-kO9Fv5zZuyj2zB2af4KA29QF6t7YSxKrY7sxZXfw8koDQj9bx5Tk5RjH+kWKFKok0wLGTi4bG117h31N+TIBEg==", "cpu": [ "loong64" ], @@ -1398,9 +1397,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz", - "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.2.tgz", + "integrity": "sha512-gIh776X7UCBaetVJGdjXPFurGsdWwHHinwRnC5JlLADU8Yk0EdS/Y+dMO264OjJFo7MXQ5PX4xVFbxrwK8zLqA==", "cpu": [ "ppc64" ], @@ -1412,9 +1411,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz", - "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.2.tgz", + "integrity": "sha512-YgikssQ5UNq1GoFKZydMEkhKbjlUq7G3h8j6yWXLBF24KyoA5BcMtaOUAXq5sydPmOPEqB6kCyJpyifSpCfQ0w==", "cpu": [ "riscv64" ], @@ -1426,9 +1425,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz", - "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.2.tgz", + "integrity": "sha512-9ouIR2vFWCyL0Z50dfnon5nOrpDdkTG9lNDs7MRaienQKlTyHcDxplmk3IbhFlutpifBSBr2H4rVILwmMLcaMA==", "cpu": [ "s390x" ], @@ -1440,9 +1439,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz", - "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.2.tgz", + "integrity": "sha512-ckBBNRN/F+NoSUDENDIJ2U9UWmIODgwDB/vEXCPOMcsco1niTkxTXa6D2Y/pvCnpzaidvY2qVxGzLilNs9BSzw==", "cpu": [ "x64" ], @@ -1454,9 +1453,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz", - "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.2.tgz", + "integrity": "sha512-jycl1wL4AgM2aBFJFlpll/kGvAjhK8GSbEmFT5v3KC3rP/b5xZ1KQmv0vQQ8Bzb2ieFQ0kZFPRMbre/l3Bu9JA==", "cpu": [ "x64" ], @@ -1468,9 +1467,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz", - "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.2.tgz", + "integrity": "sha512-S2V0LlcOiYkNGlRAWZwwUdNgdZBfvsDHW0wYosYFV3c7aKgEVcbonetZXsHv7jRTTX+oY5nDYT4W6B1oUpMNOg==", "cpu": [ "arm64" ], @@ -1482,9 +1481,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz", - "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.2.tgz", + "integrity": "sha512-pW8kioj9H5f/UujdoX2atFlXNQ9aCfAxFRaa+mhczwcsusm6gGrSo4z0SLvqLF5LwFqFTjiLCCzGkNK/LE0utQ==", "cpu": [ "ia32" ], @@ -1496,9 +1495,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz", - "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.2.tgz", + "integrity": "sha512-p6fTArexECPf6KnOHvJXRpAEq0ON1CBtzG/EY4zw08kCHk/kivBc5vUEtnCFNCHOpJZ2ne77fxwRLIKD4wuW2Q==", "cpu": [ "x64" ], @@ -1646,9 +1645,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", - "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", + "version": "22.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", + "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1662,13 +1661,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "license": "MIT", - "optional": true - }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -1845,14 +1837,14 @@ "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -1910,16 +1902,16 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -1929,16 +1921,16 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -1948,20 +1940,19 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -2134,9 +2125,9 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", - "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.1.tgz", + "integrity": "sha512-Bw2PgKSrZ3uCuSV9WQ998c/GTJTd+9bWj97n7aDQMP8dP/exAZQlJeswPty0ISy+HZD+9Ex+C7CCnc9Q5QJFmQ==", "license": "Apache-2.0", "optional": true }, @@ -2170,13 +2161,13 @@ } }, "node_modules/bare-stream": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.4.2.tgz", - "integrity": "sha512-XZ4ln/KV4KT+PXdIWTKjsLY+quqCaEtqqtgGJVPw9AoM73By03ij64YjepK0aQvHSWDb6AfAZwqKaFu68qkrdA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.1.tgz", + "integrity": "sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g==", "license": "Apache-2.0", "optional": true, "dependencies": { - "streamx": "^2.20.0" + "streamx": "^2.21.0" } }, "node_modules/base64-js": { @@ -2285,9 +2276,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", + "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", "dev": true, "funding": [ { @@ -2305,9 +2296,9 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -2360,12 +2351,6 @@ "node": "*" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2396,6 +2381,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", @@ -2423,6 +2409,22 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2443,9 +2445,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001687", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", - "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", + "version": "1.0.30001690", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", + "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", "dev": true, "funding": [ { @@ -2529,13 +2531,12 @@ } }, "node_modules/chromium-bidi": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.8.0.tgz", - "integrity": "sha512-uJydbGdTw0DEUjhoogGveneJVWX/9YuqkWePzMmkBYwtdAqo5d3J/ovNKFr+/2hWXYmYCr6it8mSSTIj6SS6Ug==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz", + "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==", "license": "Apache-2.0", "dependencies": { "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0", "zod": "3.23.8" }, "peerDependencies": { @@ -2923,15 +2924,15 @@ } }, "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2941,31 +2942,31 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -3035,6 +3036,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -3148,13 +3150,10 @@ } }, "node_modules/dompurify": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz", - "integrity": "sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA==", - "license": "(MPL-2.0 OR Apache-2.0)", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" - } + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", + "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==", + "license": "(MPL-2.0 OR Apache-2.0)" }, "node_modules/dotenv": { "version": "16.4.7", @@ -3169,12 +3168,12 @@ } }, "node_modules/dunder-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", - "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", + "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" }, @@ -3182,15 +3181,6 @@ "node": ">= 0.4" } }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -3198,9 +3188,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.72", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.72.tgz", - "integrity": "sha512-ZpSAUOZ2Izby7qnZluSrAlGgGQzucmFbN0n64dYzocYxnxV5ufurpj3VgEe4cUp7ir9LmeLxNYo8bVnlM8bQHw==", + "version": "1.5.76", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz", + "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==", "dev": true, "license": "ISC" }, @@ -3286,58 +3276,63 @@ } }, "node_modules/es-abstract": { - "version": "1.23.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", - "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dev": true, "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.3", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { "node": ">= 0.4" @@ -3368,7 +3363,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3378,15 +3372,16 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -3895,9 +3890,9 @@ } }, "node_modules/express-rate-limit": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.4.1.tgz", - "integrity": "sha512-KS3efpnpIDVIXopMc65EMbWbUht7qvTCdtCR2dD/IZmi9MIkopYESwyRqLgv8Pfu589+KqDqOdzJWW7AHoACeg==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", + "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", "license": "MIT", "engines": { "node": ">= 16" @@ -3906,7 +3901,7 @@ "url": "https://github.com/sponsors/express-rate-limit" }, "peerDependencies": { - "express": "4 || 5 || ^5.0.0-beta.1" + "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "node_modules/express/node_modules/debug": { @@ -3994,9 +3989,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", "dev": true, "license": "ISC", "dependencies": { @@ -4194,16 +4189,18 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -4255,19 +4252,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz", - "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "dunder-proto": "^1.0.0", + "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", + "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", - "hasown": "^2.0.2" + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4286,6 +4285,19 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -4300,15 +4312,15 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4426,11 +4438,14 @@ "license": "MIT" }, "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4449,6 +4464,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -4714,15 +4730,15 @@ "license": "ISC" }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4751,14 +4767,15 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4774,13 +4791,16 @@ "license": "MIT" }, "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", + "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4819,13 +4839,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.0.tgz", - "integrity": "sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", + "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" }, "engines": { @@ -4849,9 +4869,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { @@ -4865,12 +4885,14 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -4881,13 +4903,14 @@ } }, "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4907,13 +4930,13 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.0.tgz", - "integrity": "sha512-qfMdqbAQEwBw78ZyReKnlA8ezmPdb9BemzIIip/JkjaZUhitfXDkkr+3QTboW0JrSXT1QWyYShpvnNHGZ4c4yA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -4946,13 +4969,16 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4987,19 +5013,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -5011,13 +5024,13 @@ } }, "node_modules/is-number-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.0.tgz", - "integrity": "sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -5044,14 +5057,14 @@ "license": "MIT" }, "node_modules/is-regex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.0.tgz", - "integrity": "sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "gopd": "^1.1.0", + "call-bound": "^1.0.2", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" }, @@ -5076,13 +5089,13 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -5105,13 +5118,13 @@ } }, "node_modules/is-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.0.tgz", - "integrity": "sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -5122,15 +5135,15 @@ } }, "node_modules/is-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.0.tgz", - "integrity": "sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "has-symbols": "^1.0.3", - "safe-regex-test": "^1.0.3" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5140,13 +5153,13 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -5169,27 +5182,30 @@ } }, "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", + "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -5956,9 +5972,9 @@ } }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -6008,61 +6024,6 @@ "node": ">=6" } }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6126,13 +6087,13 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "15.2.11", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.11.tgz", - "integrity": "sha512-Ev6ivCTYRTGs9ychvpVw35m/bcNDuBN+mnTeObCL5h+boS5WzBEC6LHI4I9F/++sZm1m+J2LEiy0gxL/R9TBqQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.3.0.tgz", + "integrity": "sha512-vHFahytLoF2enJklgtOtCtIjZrKD/LoxlaUusd5nh7dWv/dkKQJY74ndFSzxCdv7g0ueGg1ORgTSt4Y9LPZn9A==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "~5.3.0", + "chalk": "~5.4.1", "commander": "~12.1.0", "debug": "~4.4.0", "execa": "~8.0.1", @@ -6154,9 +6115,9 @@ } }, "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "license": "MIT", "engines": { @@ -6344,42 +6305,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6387,12 +6312,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, "node_modules/log-update": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", @@ -6553,6 +6472,15 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -6761,9 +6689,9 @@ "license": "MIT" }, "node_modules/nodemon": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz", - "integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", "dev": true, "license": "MIT", "dependencies": { @@ -6886,15 +6814,17 @@ } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -6939,13 +6869,14 @@ } }, "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -7011,6 +6942,24 @@ "node": ">= 0.8.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -7475,17 +7424,17 @@ } }, "node_modules/puppeteer": { - "version": "23.10.3", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.10.3.tgz", - "integrity": "sha512-ODG+L9vCSPkQ1j+yDtNDdkSsWt2NXNrQO5C8MlwkYgE2hYnXdqVRbBpsHnoP7+EULJJKbWyR2Q4BdfohjQor3A==", + "version": "23.11.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.11.1.tgz", + "integrity": "sha512-53uIX3KR5en8l7Vd8n5DUv90Ae9QDQsyIthaUFVzwV6yU750RjqRznEtNMBT20VthqAdemnJN+hxVdmMHKt7Zw==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.6.1", - "chromium-bidi": "0.8.0", + "chromium-bidi": "0.11.0", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1367902", - "puppeteer-core": "23.10.3", + "puppeteer-core": "23.11.1", "typed-query-selector": "^2.12.0" }, "bin": { @@ -7496,13 +7445,13 @@ } }, "node_modules/puppeteer-core": { - "version": "23.10.3", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.10.3.tgz", - "integrity": "sha512-7JG8klL2qHLyH8t2pOmM9zgykhaulUf7cxnmmqupjdwGfNMiGaYehQka20iUB9R/fwVyG8mFMZcsmw1FHrgKVw==", + "version": "23.11.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.11.1.tgz", + "integrity": "sha512-3HZ2/7hdDKZvZQ7dhhITOUg4/wOrDRjyK2ZBllRB0ZCOi9u0cwq1ACHDjBB+nX+7+kltHjQvBRdeY7+W0T+7Gg==", "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.6.1", - "chromium-bidi": "0.8.0", + "chromium-bidi": "0.11.0", "debug": "^4.4.0", "devtools-protocol": "0.0.1367902", "typed-query-selector": "^2.12.0", @@ -7647,20 +7596,20 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz", - "integrity": "sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "dunder-proto": "^1.0.0", - "es-abstract": "^1.23.5", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.2.0", - "which-builtin-type": "^1.2.0" + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -7670,15 +7619,17 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "set-function-name": "^2.0.2" }, "engines": { @@ -7698,19 +7649,22 @@ } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7839,9 +7793,9 @@ } }, "node_modules/rollup": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz", - "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==", + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.2.tgz", + "integrity": "sha512-tJXpsEkzsEzyAKIaB3qv3IuvTVcTN7qBw1jL4SPPXM3vzDrJgiLGFY6+HodgFaUHAJ2RYJ94zV5MKRJCoQzQeA==", "dev": true, "license": "MIT", "dependencies": { @@ -7855,25 +7809,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.28.1", - "@rollup/rollup-android-arm64": "4.28.1", - "@rollup/rollup-darwin-arm64": "4.28.1", - "@rollup/rollup-darwin-x64": "4.28.1", - "@rollup/rollup-freebsd-arm64": "4.28.1", - "@rollup/rollup-freebsd-x64": "4.28.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.28.1", - "@rollup/rollup-linux-arm-musleabihf": "4.28.1", - "@rollup/rollup-linux-arm64-gnu": "4.28.1", - "@rollup/rollup-linux-arm64-musl": "4.28.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.28.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1", - "@rollup/rollup-linux-riscv64-gnu": "4.28.1", - "@rollup/rollup-linux-s390x-gnu": "4.28.1", - "@rollup/rollup-linux-x64-gnu": "4.28.1", - "@rollup/rollup-linux-x64-musl": "4.28.1", - "@rollup/rollup-win32-arm64-msvc": "4.28.1", - "@rollup/rollup-win32-ia32-msvc": "4.28.1", - "@rollup/rollup-win32-x64-msvc": "4.28.1", + "@rollup/rollup-android-arm-eabi": "4.29.2", + "@rollup/rollup-android-arm64": "4.29.2", + "@rollup/rollup-darwin-arm64": "4.29.2", + "@rollup/rollup-darwin-x64": "4.29.2", + "@rollup/rollup-freebsd-arm64": "4.29.2", + "@rollup/rollup-freebsd-x64": "4.29.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.29.2", + "@rollup/rollup-linux-arm-musleabihf": "4.29.2", + "@rollup/rollup-linux-arm64-gnu": "4.29.2", + "@rollup/rollup-linux-arm64-musl": "4.29.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.29.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.29.2", + "@rollup/rollup-linux-riscv64-gnu": "4.29.2", + "@rollup/rollup-linux-s390x-gnu": "4.29.2", + "@rollup/rollup-linux-x64-gnu": "4.29.2", + "@rollup/rollup-linux-x64-musl": "4.29.2", + "@rollup/rollup-win32-arm64-msvc": "4.29.2", + "@rollup/rollup-win32-ia32-msvc": "4.29.2", + "@rollup/rollup-win32-x64-msvc": "4.29.2", "fsevents": "~2.3.2" } }, @@ -7908,15 +7862,16 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -7953,16 +7908,40 @@ ], "license": "MIT" }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -8076,6 +8055,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -8105,6 +8085,21 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -8135,15 +8130,69 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -8344,9 +8393,9 @@ } }, "node_modules/streamx": { - "version": "2.21.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.0.tgz", - "integrity": "sha512-Qz6MsDZXJ6ur9u+b+4xCG18TluU7PGlRfXVAAjNiGsFrBUt/ioyLkxbFaKJygoPs+/kW4VyBj0bSj89Qu0IGyg==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", + "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", "license": "MIT", "dependencies": { "fast-fifo": "^1.3.2", @@ -8444,16 +8493,19 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -8463,16 +8515,20 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -8676,9 +8732,9 @@ } }, "node_modules/text-decoder": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.2.tgz", - "integrity": "sha512-/MDslo7ZyWTA2vnk1j7XoDVfXsGk3tp+zFEJHJGm0UjIlQifonVFwlVbQDFh8KJzTBnT8ie115TYqir6bclddA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "license": "Apache-2.0", "dependencies": { "b4a": "^1.6.4" @@ -8698,21 +8754,21 @@ "license": "MIT" }, "node_modules/tldts": { - "version": "6.1.66", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.66.tgz", - "integrity": "sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ==", + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.70.tgz", + "integrity": "sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.66" + "tldts-core": "^6.1.70" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.66", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.66.tgz", - "integrity": "sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g==", + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz", + "integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==", "license": "MIT" }, "node_modules/tmpl": { @@ -8870,32 +8926,32 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -8905,19 +8961,19 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.3.tgz", - "integrity": "sha512-GsvTyUHTriq6o/bHcTd0vM7OQ9JEdlvluu9YISaA7+KzDzPaIzEeDFNkTfhdE3MYcNhNi0vq/LlegYgIs5yPAw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "reflect.getprototypeof": "^1.0.6" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -8960,16 +9016,19 @@ "license": "MIT" }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9049,12 +9108,6 @@ "punycode": "^2.1.0" } }, - "node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "license": "MIT" - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9071,9 +9124,9 @@ } }, "node_modules/uuid": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", - "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.4.tgz", + "integrity": "sha512-IzL6VtTTYcAhA/oghbFJ1Dkmqev+FpQWnCBaKq/gUluLxliWvO8DPFWfIviRmYbtaavtSQe4WBL++rFjdcGWEg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -9201,17 +9254,17 @@ } }, "node_modules/which-boxed-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.0.tgz", - "integrity": "sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, "license": "MIT", "dependencies": { "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.0", - "is-number-object": "^1.1.0", - "is-string": "^1.1.0", - "is-symbol": "^1.1.0" + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -9221,25 +9274,25 @@ } }, "node_modules/which-builtin-type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.0.tgz", - "integrity": "sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", + "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -9275,16 +9328,17 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.16.tgz", - "integrity": "sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", + "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "for-each": "^0.3.3", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -9538,9 +9592,9 @@ } }, "node_modules/zod": { - "version": "3.24.0", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.0.tgz", - "integrity": "sha512-Hz+wiY8yD0VLA2k/+nsg2Abez674dDGTai33SwNvMPuf9uIrBC9eFgIMQxBBbHFxVXi8W+5nX9DcAh9YNSQm/w==", + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 08746625..3721eab1 100644 --- a/package.json +++ b/package.json @@ -37,33 +37,26 @@ "complete": "npm run lint && npm run format && npm run build", "lint": "eslint ./ --ext .js,.ts --fix", "format": "prettier ./ --config .prettierrc.cjs --write", - "build": "rollup -c", + "build": "rollup --config --silent", "prepack": "npm run build", - "cli-tests": "node ./tests/cli/cliTestRunner.js", - "cli-tests-single": "node ./tests/cli/cliTestRunnerSingle.js", - "http-tests": "node ./tests/http/httpTestRunner.js", - "http-tests-single": "node ./tests/http/httpTestRunnerSingle.js", - "node-tests": "node ./tests/node/nodeTestRunner.js", - "node-tests-single": "node ./tests/node/nodeTestRunnerSingle.js", "unit:test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", "prepare": "husky || true" }, "dependencies": { "colors": "1.4.0", "cors": "^2.8.5", - "dompurify": "^3.2.3", + "dompurify": "3.1.6", "dotenv": "^16.4.7", "express": "^4.21.2", - "express-rate-limit": "^7.4.1", + "express-rate-limit": "^7.5.0", "https-proxy-agent": "^7.0.6", "jsdom": "^25.0.1", - "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^23.10.3", + "puppeteer": "^23.11.1", "tarn": "^3.0.2", - "uuid": "^11.0.3", - "zod": "^3.24.0" + "uuid": "^11.0.4", + "zod": "^3.24.1" }, "devDependencies": { "@jest/globals": "^29.7.0", @@ -74,9 +67,9 @@ "eslint-plugin-prettier": "^5.2.1", "husky": "^9.1.7", "jest": "^29.7.0", - "lint-staged": "^15.2.11", - "nodemon": "^3.1.7", + "lint-staged": "^15.3.0", + "nodemon": "^3.1.9", "prettier": "^3.4.2", - "rollup": "^4.28.1" + "rollup": "^4.29.2" } } From 0e1cd189eaecfce5e347cdd81ad0318c6a07a08d Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:12:03 +0100 Subject: [PATCH 058/102] Corrected parameters usage in the API functions for the CLI export scenario. --- bin/cli.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 88c649be..bd2f243c 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -26,12 +26,13 @@ import { printLicense, printUsage, printVersion, - setOptions + setGlobalOptions } from '../lib/config.js'; import { initExport } from '../lib/index.js'; import { log, logWithStack } from '../lib/logger.js'; import { manualConfig } from '../lib/prompt.js'; import { shutdownCleanUp } from '../lib/resourceRelease.js'; + import { startServer } from '../lib/server/server.js'; import ExportError from '../lib/errors/ExportError.js'; @@ -81,7 +82,7 @@ async function start() { } // Set the options, keeping the priority order of setting values - const options = setOptions({}, args, true); + const options = setGlobalOptions({}, args); // If all options are correctly parsed if (options) { @@ -90,35 +91,44 @@ async function start() { // In this case we want to prepare config manually if (options.customLogic.createConfig) { - manualConfig(options.customLogic.createConfig); + manualConfig( + options.customLogic.createConfig, + options.customLogic.allowCodeExecution + ); return; } // Start server if (options.server.enable) { // Init the export mechanism for the server configuration - await initExport(options); + await initExport(); // Run the server - await startServer(options.server); + await startServer(); } else { // Perform batch exports if (options.export.batch) { // Init the export mechanism for batch exports - await initExport(options); + await initExport(); // Start batch exports - await batchExport(options); + await batchExport({ + export: options.export, + customLogic: options.customLogic + }); } else { // No need for multiple workers in case of a single CLI export options.pool.minWorkers = 1; options.pool.maxWorkers = 1; // Init the export mechanism for a single export - await initExport(options); + await initExport(); // Start a single export - await singleExport(options); + await singleExport({ + export: options.export, + customLogic: options.customLogic + }); } } } else { From a56f4a037a5b4b9427415ec97dc88f5609e3898c Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:12:29 +0100 Subject: [PATCH 059/102] Modified exported API functions. --- lib/index.js | 93 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/lib/index.js b/lib/index.js index 738cf54d..5af42ea5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -30,39 +30,42 @@ import { } from './chart.js'; import { getOptions, - setOptions, - mergeOptions, + updateOptions, + setGlobalOptions, mapToNewOptions } from './config.js'; import { log, logWithStack, initLogging, - setLogLevel, enableConsoleLogging, - enableFileLogging + enableFileLogging, + setLogLevel } from './logger.js'; import { initPool, killPool } from './pool.js'; import { shutdownCleanUp } from './resourceRelease.js'; -import server, { startServer } from './server/server.js'; + +import server from './server/server.js'; /** * Initializes the export process. Tasks such as configuring logging, checking * the cache and sources, and initializing the resource pool occur during this - * stage. This function must be called before attempting to export charts or set + * stage. + * + * This function must be called before attempting to export charts or set * up a server. * * @async * @function initExport * - * @param {Object} customOptions - The `customOptions` object, which may - * be a partial or complete set of options. If the provided options are partial, - * missing values will be merged with the default general options, retrieved - * using the `getOptions` function. + * @param {Object} [initOptions={}] - The `initOptions` object, which may + * be a partial or complete set of options. If the options are partial, missing + * values will default to the current global configuration. The default value + * is an empty object. */ -export async function initExport(customOptions) { - // Get the global options object copy and extend it with the incoming options - const options = mergeOptions(getOptions(false), customOptions); +export async function initExport(initOptions = {}) { + // Init and update the instance options object + const options = updateOptions(initOptions, true); // Set the `allowCodeExecution` per export module scope setAllowCodeExecution(options.customLogic.allowCodeExecution); @@ -100,19 +103,19 @@ function _attachProcessExitListeners() { // Handler for the 'SIGINT' process.on('SIGINT', async (name, code) => { log(4, `[process] The ${name} event with code: ${code}.`); - await shutdownCleanUp(0); + await shutdownCleanUp(); }); // Handler for the 'SIGTERM' process.on('SIGTERM', async (name, code) => { log(4, `[process] The ${name} event with code: ${code}.`); - await shutdownCleanUp(0); + await shutdownCleanUp(); }); // Handler for the 'SIGHUP' process.on('SIGHUP', async (name, code) => { log(4, `[process] The ${name} event with code: ${code}.`); - await shutdownCleanUp(0); + await shutdownCleanUp(); }); // Handler for the 'uncaughtException' @@ -124,13 +127,11 @@ function _attachProcessExitListeners() { export default { // Server - server, - startServer, + ...server, // Options getOptions, - setOptions, - mergeOptions, + setGlobalOptions, mapToNewOptions, // Exporting @@ -139,20 +140,50 @@ export default { batchExport, startExport, - // Cache - checkAndUpdateCache, - - // Pool - initPool, + // Release killPool, + shutdownCleanUp, // Logs log, logWithStack, - setLogLevel, - enableConsoleLogging, - enableFileLogging, - - // Utils - shutdownCleanUp + setLogLevel: function (level) { + // Update the instance options object + const options = updateOptions({ + logging: { + level + } + }); + + // Call the function + setLogLevel(options.logging.level); + }, + enableConsoleLogging: function (toConsole) { + // Update the instance options object + const options = updateOptions({ + logging: { + toConsole + } + }); + + // Call the function + enableConsoleLogging(options.logging.toConsole); + }, + enableFileLogging: function (dest, file, toFile) { + // Update the instance options object + const options = updateOptions({ + logging: { + dest, + file, + toFile + } + }); + + // Call the function + enableFileLogging( + options.logging.dest, + options.logging.file, + options.logging.toFile + ); + } }; From 7cef88cf16e6d4e1958a44b7e4119704f6255e49 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:12:49 +0100 Subject: [PATCH 060/102] Try catched the checkAndUpdateCache function. --- lib/cache.js | 189 +++++++++++++++++++++++++++------------------------ 1 file changed, 101 insertions(+), 88 deletions(-) diff --git a/lib/cache.js b/lib/cache.js index c40eab91..4622469f 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -48,106 +48,117 @@ const cache = { * @async * @function checkAndUpdateCache * - * @param {Object} highchartsOptions - Object containing `highcharts` options. - * @param {Object} serverProxyOptions - Object containing `server.proxy` - * options. + * @param {Object} highchartsOptions - The configuration object containing + * `highcharts` options. + * @param {Object} serverProxyOptions- The configuration object containing + * `server.proxy` options. */ export async function checkAndUpdateCache( highchartsOptions, serverProxyOptions ) { - let fetchedModules; - - // Get the cache path - const cachePath = getCachePath(); - - // Prepare paths to manifest and sources from the cache folder - const manifestPath = join(cachePath, 'manifest.json'); - const sourcePath = join(cachePath, 'sources.js'); - - // Create the cache destination if it doesn't exist already - !existsSync(cachePath) && mkdirSync(cachePath, { recursive: true }); - - // Fetch all the scripts either if the `manifest.json` does not exist - // or if the `forceFetch` option is enabled - if (!existsSync(manifestPath) || highchartsOptions.forceFetch) { - log(3, '[cache] Fetching and caching Highcharts dependencies.'); - fetchedModules = await _updateCache( - highchartsOptions, - serverProxyOptions, - sourcePath - ); - } else { - let requestUpdate = false; + try { + let fetchedModules; - // Read the manifest JSON - const manifest = JSON.parse(readFileSync(manifestPath)); + // Get the cache path + const cachePath = getCachePath(); - // Check if the modules is an array, if so, we rewrite it to a map to make - // it easier to resolve modules. - if (manifest.modules && Array.isArray(manifest.modules)) { - const moduleMap = {}; - manifest.modules.forEach((m) => (moduleMap[m] = 1)); - manifest.modules = moduleMap; - } + // Prepare paths to manifest and sources from the cache folder + const manifestPath = join(cachePath, 'manifest.json'); + const sourcePath = join(cachePath, 'sources.js'); - // Get the actual number of scripts to be fetched - const { coreScripts, moduleScripts, indicatorScripts } = highchartsOptions; - const numberOfModules = - coreScripts.length + moduleScripts.length + indicatorScripts.length; - - // Compare the loaded highcharts config with the contents in cache. - // If there are changes, fetch requested modules and products, - // and bake them into a giant blob. Save the blob. - if (manifest.version !== highchartsOptions.version) { - log( - 2, - '[cache] A Highcharts version mismatch in the cache, need to re-fetch.' - ); - requestUpdate = true; - } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) { - log( - 2, - '[cache] The cache and the requested modules do not match, need to re-fetch.' - ); - requestUpdate = true; - } else { - // Check each module, if anything is missing refetch everything - requestUpdate = (moduleScripts || []).some((moduleName) => { - if (!manifest.modules[moduleName]) { - log( - 2, - `[cache] The ${moduleName} is missing in the cache, need to re-fetch.` - ); - return true; - } - }); - } + // Create the cache destination if it doesn't exist already + !existsSync(cachePath) && mkdirSync(cachePath, { recursive: true }); - // Update cache if needed - if (requestUpdate) { + // Fetch all the scripts either if the `manifest.json` does not exist + // or if the `forceFetch` option is enabled + if (!existsSync(manifestPath) || highchartsOptions.forceFetch) { + log(3, '[cache] Fetching and caching Highcharts dependencies.'); fetchedModules = await _updateCache( highchartsOptions, serverProxyOptions, sourcePath ); } else { - log(3, '[cache] Dependency cache is up to date, proceeding.'); + let requestUpdate = false; - // Load the sources - cache.sources = readFileSync(sourcePath, 'utf8'); + // Read the manifest JSON + const manifest = JSON.parse(readFileSync(manifestPath), 'utf8'); - // Get current modules map - fetchedModules = manifest.modules; + // Check if the modules is an array, if so, we rewrite it to a map to make + // it easier to resolve modules. + if (manifest.modules && Array.isArray(manifest.modules)) { + const moduleMap = {}; + manifest.modules.forEach((m) => (moduleMap[m] = 1)); + manifest.modules = moduleMap; + } + + // Get the actual number of scripts to be fetched + const { coreScripts, moduleScripts, indicatorScripts } = + highchartsOptions; + const numberOfModules = + coreScripts.length + moduleScripts.length + indicatorScripts.length; + + // Compare the loaded highcharts config with the contents in cache. + // If there are changes, fetch requested modules and products, + // and bake them into a giant blob. Save the blob. + if (manifest.version !== highchartsOptions.version) { + log( + 2, + '[cache] A Highcharts version mismatch in the cache, need to re-fetch.' + ); + requestUpdate = true; + } else if ( + Object.keys(manifest.modules || {}).length !== numberOfModules + ) { + log( + 2, + '[cache] The cache and the requested modules do not match, need to re-fetch.' + ); + requestUpdate = true; + } else { + // Check each module, if anything is missing refetch everything + requestUpdate = (moduleScripts || []).some((moduleName) => { + if (!manifest.modules[moduleName]) { + log( + 2, + `[cache] The ${moduleName} is missing in the cache, need to re-fetch.` + ); + return true; + } + }); + } + + // Update cache if needed + if (requestUpdate) { + fetchedModules = await _updateCache( + highchartsOptions, + serverProxyOptions, + sourcePath + ); + } else { + log(3, '[cache] Dependency cache is up to date, proceeding.'); - // Extract and save version of currently used Highcharts - cache.hcVersion = extractVersion(cache.sources); + // Load the sources + cache.sources = readFileSync(sourcePath, 'utf8'); + + // Get current modules map + fetchedModules = manifest.modules; + + // Extract and save version of currently used Highcharts + cache.hcVersion = extractVersion(cache.sources); + } } - } - // Finally, save the new manifest, which is basically our current config - // in a slightly different format - await _saveConfigToManifest(highchartsOptions, fetchedModules); + // Finally, save the new manifest, which is basically our current config + // in a slightly different format + await _saveConfigToManifest(highchartsOptions, fetchedModules); + } catch (error) { + throw new ExportError( + '[cache] Could not configure cache and create or update the config manifest.', + 500 + ).setError(error); + } } /** @@ -235,7 +246,7 @@ export function getCache() { * @returns {string} The absolute path to the cache directory for Highcharts. */ export function getCachePath() { - return getAbsolutePath(getOptions().highcharts.cachePath); // #562 + return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562 } /** @@ -251,7 +262,7 @@ export function getCachePath() { * modules have been fetched. * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error * should be thrown. This should be used only for the core scripts. The default - * value is false. + * value is `false`. * * @returns {Promise} A Promise that resolves to the text representation * of the fetched script. @@ -304,7 +315,8 @@ async function _fetchAndProcessScript( * @async * @function _saveConfigToManifest * - * @param {Object} highchartsOptions - Object containing `highcharts` options. + * @param {Object} highchartsOptions - The configuration object containing + * `highcharts` options. * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts * modules have been fetched. The default value is an empty object. * @@ -345,8 +357,8 @@ async function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) { * @param {Array.} moduleScripts - Highcharts modules to fetch. * @param {Array.} customScripts - Custom script paths to fetch (full * URLs). - * @param {Object} serverProxyOptions - Object containing `server.proxy` - * options. + * @param {Object} serverProxyOptions - The configuration object containing + * `server.proxy` options. * @param {Object} fetchedModules - An object which tracks which Highcharts * modules have been fetched. * @@ -413,9 +425,10 @@ async function _fetchScripts( * @async * @function _updateCache * - * @param {Object} highchartsOptions - Object containing `highcharts` options. - * @param {Object} serverProxyOptions - Object containing `server.proxy` - * options. + * @param {Object} highchartsOptions - The configuration object containing + * `highcharts` options. + * @param {Object} serverProxyOptions - The configuration object containing + * `server.proxy` options. * @param {string} sourcePath - The path to the source file in the cache. * * @returns {Promise} A Promise that resolves to an object representing From 8270d01c74fd0e6ebc51777a88fe9172903a38c0 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:13:36 +0100 Subject: [PATCH 061/102] Optimized main functions for exporting process. --- lib/chart.js | 185 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 108 insertions(+), 77 deletions(-) diff --git a/lib/chart.js b/lib/chart.js index 74197540..69d7d6d3 100644 --- a/lib/chart.js +++ b/lib/chart.js @@ -21,15 +21,18 @@ See LICENSE file in root for details. import { readFileSync, writeFileSync } from 'fs'; -import { getOptions, mergeOptions, isAllowedConfig } from './config.js'; +import { getOptions, isAllowedConfig, mergeOptions } from './config.js'; import { log, logWithStack } from './logger.js'; -import { killPool, postWork, getPoolStats } from './pool.js'; +import { getPoolStats, killPool, postWork } from './pool.js'; import { sanitize } from './sanitize.js'; import { + deepCopy, + fixConstr, fixOutfile, fixType, getAbsolutePath, getBase64, + isObject, roundNumber, wrapAround } from './utils.js'; @@ -41,14 +44,16 @@ let allowCodeExecution = false; /** * Starts a single export process based on the specified options and saves - * the image in the provided outfile. + * the resulting image to the provided output file. * * @async * @function singleExport * - * @param {Object} options - The `options` object, which may be a partial - * or complete set of options. It must contain at least one of the following - * properties: `infile`, `instr`, `options`, or `svg` to generate a valid image. + * @param {Object} options - The `options` object, which should include settings + * from the `export` and `customLogic` sections. It can be a partial or complete + * set of options from these sections. The object must contain at least one + * of the following `export` properties: `infile`, `instr`, `options`, or `svg` + * to generate a valid image. * * @returns {Promise} A Promise that resolves once the single export * process is completed. @@ -60,40 +65,43 @@ export async function singleExport(options) { // Check if the export makes sense if (options && options.export) { // Perform an export - await startExport(options, async (error, data) => { - // Exit process when error exists - if (error) { - throw error; - } - - // Get the `b64`, `outfile`, and `type` for a chart - const { b64, outfile, type } = data.options.export; + await startExport( + { export: options.export, customLogic: options.customLogic }, + async (error, data) => { + // Exit process when error exists + if (error) { + throw error; + } - // Save the result - try { - if (b64) { - // As a Base64 string to a txt file - writeFileSync( - `${outfile.split('.').shift() || 'chart'}.txt`, - getBase64(data.result, type) - ); - } else { - // As a correct image format - writeFileSync( - outfile || `chart.${type}`, - type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result - ); + // Get the `b64`, `outfile`, and `type` for a chart + const { b64, outfile, type } = data.options.export; + + // Save the result + try { + if (b64) { + // As a Base64 string to a txt file + writeFileSync( + `${outfile.split('.').shift() || 'chart'}.txt`, + getBase64(data.result, type) + ); + } else { + // As a correct image format + writeFileSync( + outfile || `chart.${type}`, + type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result + ); + } + } catch (error) { + throw new ExportError( + '[chart] Error while saving a chart.', + 500 + ).setError(error); } - } catch (error) { - throw new ExportError( - '[chart] Error while saving a chart.', - 500 - ).setError(error); - } - // Kill pool and close browser after finishing single export - await killPool(); - }); + // Kill pool and close browser after finishing single export + await killPool(); + } + ); } else { throw new ExportError( '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.', @@ -103,17 +111,18 @@ export async function singleExport(options) { } /** - * Starts a batch export process for multiple charts based on the information - * in the `batch` option. The `batch` is a string in the following format: - * "infile1.json=outfile1.png;infile2.json=outfile2.png;...". Results are saved - * in provided outfiles. + * Starts a batch export process for multiple charts based on information + * provided in the `batch` option. The `batch` is a string in the following + * format: "infile1.json=outfile1.png;infile2.json=outfile2.png;...". Results + * are saved to the specified output files. * * @async * @function batchExport * - * @param {Object} options - The `options` object, which may be a partial - * or complete set of options. It must contain the `batch` option to generate - * valid images. + * @param {Object} options - The `options` object, which should include settings + * from the `export` and `customLogic` sections. It can be a partial or complete + * set of options from these sections. It must contain the `batch` option from + * the `export` section to generate valid images. * * @returns {Promise} A Promise that resolves once the batch export * processes are completed. @@ -134,12 +143,12 @@ export async function batchExport(options) { batchFunctions.push( startExport( { - ...options, export: { ...options.export, infile: pair[0], outfile: pair[1] - } + }, + customLogic: options.customLogic }, (error, data) => { // Exit process when error exists @@ -207,25 +216,31 @@ export async function batchExport(options) { } /** - * Starts an export process. The `customOptions` parameter is an object that - * may be partial or complete set of options. The `endCallback` is called when - * the export is completed, with the `error` object as the first argument - * and the `data` object as the second, which contains the Base64 representation - * of the chart in the `result` property and the full set of export options - * in the `options` property. + * Starts an export process. The `exportingOptions` parameter is an object that + * should include settings from the `export` and `customLogic` sections. It can + * be a partial or complete set of options from these sections. If partial + * options are provided, missing values will be merged with the current global + * options. + * + * The `endCallback` function is invoked upon the completion of the export, + * either successfully or with an error. The `error` object is provided + * as the first argument, and the `data` object is the second, containing + * the Base64 representation of the chart in the `result` property + * and the complete set of options in the `options` property. * * @async * @function startExport * - * @param {Object} customOptions - The `customOptions` object, which may - * be a partial or complete set of options. If the provided options are partial, - * missing values will be merged with the default general options, retrieved - * using the `getOptions` function. + * @param {Object} exportingOptions - The `exportingOptions` object, which + * should include settings from the `export` and `customLogic` sections. It can + * be a partial or complete set of options from these sections. If the provided + * options are partial, missing values will be merged with the current global + * options. * @param {Function} endCallback - The callback function to be invoked upon - * finalizing work or upon error occurance of the exporting process. The first - * argument is `error` object and the `data` object is the second, that contains - * the Base64 representation of the chart in the `result` property and the full - * set of export options in the `options` property. + * finalizing the export process or upon encountering an error. The first + * argument is the `error` object, and the second argument is the `data` object, + * which includes the Base64 representation of the chart in the `result` + * property and the full set of options in the `options` property. * * @returns {Promise} This function does not return a value directly. * Instead, it communicates results via the `endCallback`. @@ -234,17 +249,28 @@ export async function batchExport(options) { * processing input of any type. The error is passed into the `endCallback` * function and processed there. */ -export async function startExport(customOptions, endCallback) { +export async function startExport(exportingOptions, endCallback) { try { - // Starting exporting process message - log(4, '[chart] Starting the exporting process.'); + // Check if provided options is an object + if (!isObject(exportingOptions)) { + throw new ExportError( + '[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.', + 400 + ); + } - // Merge the custom options into default ones - const options = mergeOptions(getOptions(false), customOptions); + // Merge additional options to the copy of the instance options + const options = mergeOptions(deepCopy(getOptions()), { + export: exportingOptions.export, + customLogic: exportingOptions.customLogic + }); - // Get the export options + // Get the `export` options const exportOptions = options.export; + // Starting exporting process message + log(4, '[chart] Starting the exporting process.'); + // Export using options from the file as an input if (exportOptions.infile !== null) { log(4, '[chart] Attempting to export from a file input.'); @@ -346,7 +372,7 @@ export function getAllowCodeExecution() { * * @function setAllowCodeExecution * - * @param {boolean} value - The value to be assigned to the global + * @param {boolean} value - The boolean value to be assigned to the global * `allowCodeExecution` option. */ export function setAllowCodeExecution(value) { @@ -360,7 +386,7 @@ export function setAllowCodeExecution(value) { * @function _exportFromSvg * * @param {string} inputToExport - The SVG based input to be exported. - * @param {Object} options - The `options` object containing complete set + * @param {Object} options - The configuration object containing complete set * of options. * * @returns {Promise} A Promise that resolves to a result of the export @@ -398,7 +424,7 @@ async function _exportFromSvg(inputToExport, options) { * @function _exportFromOptions * * @param {string} inputToExport - The options based input to be exported. - * @param {Object} options - The `options` object containing complete set + * @param {Object} options - The configuration object containing complete set * of options. * * @returns {Promise} A Promise that resolves to a result of the export @@ -446,7 +472,7 @@ async function _exportFromOptions(inputToExport, options) { * @async * @function _prepareExport * - * @param {Object} options - The `options` object containing complete set + * @param {Object} options - The configuration object containing complete set * of options. * * @returns {Promise} A Promise that resolves to a result of the export @@ -461,6 +487,9 @@ async function _prepareExport(options) { // Prepare the `outfile` option exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile); + // Prepare the `constr` option + exportOptions.constr = fixConstr(exportOptions.constr); + // Notify about the custom logic usage status log( 3, @@ -500,9 +529,10 @@ async function _prepareExport(options) { * * @function _findChartSize * - * @param {Object} exportOptions - The object containing `export` options. + * @param {Object} exportOptions - The configuration object containing `export` + * options. * - * @returns {Object} An object containing the calculated `height`, `width` + * @returns {Object} The object containing calculated `height`, `width` * and `scale` values for the chart export. */ function _findChartSize(exportOptions) { @@ -583,8 +613,8 @@ function _findChartSize(exportOptions) { * * @function _handleCustomLogic * - * @param {Object} customLogicOptions - The object containing `customLogic` - * options. + * @param {Object} customLogicOptions - The configuration object containing + * `customLogic` options. * @param {boolean} allowCodeExecution - A flag indicating whether code * execution is allowed. * @@ -688,7 +718,7 @@ function _handleCustomLogic(customLogicOptions, allowCodeExecution) { * * @param {(Object|string|null)} [resources=null] - The resources to be handled. * Can be either a JSON object, stringified JSON, a path to a JSON file, - * or null. The default value is null. + * or null. The default value is `null`. * @param {boolean} allowFileResources - A flag indicating whether loading * resources from files is allowed. * @param {boolean} allowCodeExecution - A flag indicating whether code @@ -766,7 +796,8 @@ function _handleResources( * * @function _handleGlobalAndTheme * - * @param {Object} exportOptions - The object containing `export` options. + * @param {Object} exportOptions - The configuration object containing `export` + * options. * @param {boolean} allowFileResources - A flag indicating whether loading * resources from files is allowed. * @param {boolean} allowCodeExecution - A flag indicating whether code @@ -827,9 +858,9 @@ function _handleGlobalAndTheme( } export default { - startExport, singleExport, batchExport, + startExport, getAllowCodeExecution, setAllowCodeExecution }; From 55963e4b831235328dc4e22d63f089a6666f3a4f Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:13:51 +0100 Subject: [PATCH 062/102] Optimized the puppeteerExport function, in the export module. --- lib/export.js | 58 +++++++++++---------------------------------------- 1 file changed, 12 insertions(+), 46 deletions(-) diff --git a/lib/export.js b/lib/export.js index af7b927d..cb43417e 100644 --- a/lib/export.js +++ b/lib/export.js @@ -35,8 +35,10 @@ import ExportError from './errors/ExportError.js'; * @function puppeteerExport * * @param {Object} page - Puppeteer page object. - * @param {Object} options - The `options` object containing complete set - * of options. + * @param {Object} exportOptions - The configuration object containing `export` + * options. + * @param {Object} customLogicOptions - The configuration object containing + * `customLogic` options. * * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves * to the exported data or rejecting with an `ExportError`. @@ -44,15 +46,14 @@ import ExportError from './errors/ExportError.js'; * @throws {ExportError} Throws an `ExportError` if export to an unsupported * output format occurs. */ -export async function puppeteerExport(page, options) { +export async function puppeteerExport(page, exportOptions, customLogicOptions) { // Injected resources array (additional JS and CSS) const injectedResources = []; try { - // Get the `export` options - const exportOptions = options.export; - let isSVG = false; + + // Decide on the export method if (exportOptions.svg) { log(4, '[export] Treating as SVG input.'); @@ -65,19 +66,21 @@ export async function puppeteerExport(page, options) { isSVG = true; // SVG export - await _setAsSvg(page, exportOptions.svg); + await page.setContent(svgTemplate(exportOptions.svg), { + waitUntil: 'domcontentloaded' + }); } else { log(4, '[export] Treating as JSON config.'); // Options export - await _setAsOptions(page, options); + await page.evaluate(createChart, exportOptions, customLogicOptions); } // Keeps track of all resources added on the page with addXXXTag. etc // It's VITAL that all added resources ends up here so we can clear things // out when doing a new export in the same page! injectedResources.push( - ...(await addPageResources(page, options.customLogic)) + ...(await addPageResources(page, customLogicOptions)) ); // Get the real chart size and set the zoom accordingly @@ -183,43 +186,6 @@ export async function puppeteerExport(page, options) { } } -/** - * Sets the specified page's content with provided export input within - * the window context using the `page.setContent` function. - * - * @async - * @function _setAsSvg - * - * @param {Object} page - Puppeteer page object. - * @param {string} svg - The SVG input content to be exported. - * - * @returns {Promise} A Promise that resolves after the content is set. - */ -async function _setAsSvg(page, svg) { - await page.setContent(svgTemplate(svg), { - waitUntil: 'domcontentloaded' - }); -} - -/** - * Sets the options with specified export input and sizes as configuration into - * the `createChart` function within the window context using - * the `page.evaluate` function. - * - * @async - * @function _setAsOptions - * - * @param {Object} page - Puppeteer page object. - * @param {Object} options - The `options` object containing complete set - * of options. - * - * @returns {Promise} A Promise that resolves after the configuration - * is set. - */ -async function _setAsOptions(page, options) { - await page.evaluate(createChart, options); -} - /** * Retrieves the clipping region coordinates of the specified page element * with the 'chart-container' id. From 05c793a0ef07b3d6111baef8497170268b66d203 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:14:06 +0100 Subject: [PATCH 063/102] Optimized the createChart function, in the highcharts module. --- lib/highcharts.js | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/highcharts.js b/lib/highcharts.js index e783937a..c3d62a2a 100644 --- a/lib/highcharts.js +++ b/lib/highcharts.js @@ -39,12 +39,15 @@ export function setupHighcharts() { * @async * @function createChart * - * @param {Object} options - The `options` object containing complete set - * of options. + * @param {Object} exportOptions - The configuration object containing `export` + * options. + * @param {Object} customLogicOptions - The configuration object containing + * `customLogic` options. + * */ -export async function createChart(options) { +export async function createChart(exportOptions, customLogicOptions) { // Get required functions - const { getOptions, merge, setOptions, wrap } = Highcharts; + const { getOptions, setOptions, merge, wrap } = Highcharts; // Create a separate object for a potential `setOptions` usages in order // to prevent from polluting other exports that can happen on the same page @@ -53,7 +56,7 @@ export async function createChart(options) { // NOTE: Is this used for anything useful? window.isRenderComplete = false; wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, cb) { - // Override userOptions with image friendly options + // Override the `userOptions` with image friendly options userOptions = merge(userOptions, { exporting: { enabled: false @@ -65,7 +68,7 @@ export async function createChart(options) { } } }, - /* Expects tooltip in userOptions when forExport is true. + /* Expects tooltip in the `userOptions` when `forExport` is true. https://github.com/highcharts/highcharts/blob/3ad430a353b8056b9e764aa4e5cd6828aa479db2/js/parts/Chart.js#L241 */ tooltip: {} @@ -95,8 +98,8 @@ export async function createChart(options) { // By default animation is disabled animation: false, // Get the right size values - height: options.export.height, - width: options.export.width + height: exportOptions.height, + width: exportOptions.width }, exporting: { // No need for the exporting button @@ -105,15 +108,13 @@ export async function createChart(options) { }; // Get the input to export from the `instr` option - const userOptions = new Function(`return ${options.export.instr}`)(); + const userOptions = new Function(`return ${exportOptions.instr}`)(); // Get the `themeOptions` option - const themeOptions = new Function(`return ${options.export.themeOptions}`)(); + const themeOptions = new Function(`return ${exportOptions.themeOptions}`)(); // Get the `globalOptions` option - const globalOptions = new Function( - `return ${options.export.globalOptions}` - )(); + const globalOptions = new Function(`return ${exportOptions.globalOptions}`)(); // Merge the following options objects to create final options const finalOptions = merge( @@ -125,13 +126,13 @@ export async function createChart(options) { ); // Prepare the `callback` option - const finalCallback = options.customLogic.callback - ? new Function(`return ${options.customLogic.callback}`)() + const finalCallback = customLogicOptions.callback + ? new Function(`return ${customLogicOptions.callback}`)() : null; // Trigger the `customCode` option - if (options.customLogic.customCode) { - new Function('options', options.customLogic.customCode)(userOptions); + if (customLogicOptions.customCode) { + new Function('options', customLogicOptions.customCode)(userOptions); } // Set the global options if exist @@ -140,7 +141,7 @@ export async function createChart(options) { } // Call the chart creation - Highcharts[options.export.constr]('container', finalOptions, finalCallback); + Highcharts[exportOptions.constr]('container', finalOptions, finalCallback); // Get the current global options const defaultOptions = getOptions(); From e54188dd33d4a7a9bbc6d78afea67fb0e2f547de Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:14:24 +0100 Subject: [PATCH 064/102] Optimized the pool module. --- lib/pool.js | 64 ++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/lib/pool.js b/lib/pool.js index 6a64c184..a67766b2 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -24,7 +24,6 @@ import { Pool } from 'tarn'; import { v4 as uuid } from 'uuid'; import { createBrowser, closeBrowser, newPage, clearPage } from './browser.js'; -import { getOptions } from './config.js'; import { puppeteerExport } from './export.js'; import { log, logWithStack } from './logger.js'; import { getNewDateTime, measureTime } from './utils.js'; @@ -54,11 +53,10 @@ const poolStats = { * @async * @function initPool * - * @param {Object} [poolOptions=getOptions().pool] - Object containing `pool` - * options. The default value is the global pool options of the export server - * instance. - * @param {Array.} [puppeteerArgs=[]] - Additional arguments - * for Puppeteer launch. The default value is an empty array. + * @param {Object} poolOptions - The configuration object containing `pool` + * options. + * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer + * launch. * * @returns {Promise} A Promise that resolves to ending the function * execution when an already initialized pool of resources is found. @@ -66,10 +64,7 @@ const poolStats = { * @throws {ExportError} Throws an `ExportError` if could not create the pool * of workers. */ -export async function initPool( - poolOptions = getOptions().pool, - puppeteerArgs = [] -) { +export async function initPool(poolOptions, puppeteerArgs) { // Create a browser instance with the puppeteer arguments await createBrowser(puppeteerArgs); @@ -154,14 +149,14 @@ export async function initPool( } /** - * Kills all workers in the pool, destroys the pool, and closes the browser + * Terminates all workers in the pool, destroys the pool, and closes the browser * instance. * * @async * @function killPool * - * @returns {Promise} A Promise that resolves after the workers are - * killed, the pool is destroyed, and the browser is closed. + * @returns {Promise} A Promise that resolves once all workers are + * terminated, the pool is destroyed, and the browser is successfully closed. */ export async function killPool() { log(3, '[pool] Killing pool with all workers and closing browser.'); @@ -193,7 +188,7 @@ export async function killPool() { * @async * @function postWork * - * @param {Object} options - The `options` object containing complete set + * @param {Object} options - The configuration object containing complete set * of options. * * @returns {Promise} A Promise that resolves to the export result @@ -212,7 +207,7 @@ export async function postWork(options) { ++poolStats.exportsAttempted; // Display the pool information if needed - if (getOptions().pool.benchmarking) { + if (options.pool.benchmarking) { getPoolInfo(); } @@ -238,17 +233,15 @@ export async function postWork(options) { if (options.server.benchmarking) { log( 5, - options._requestId - ? `[benchmark] Request [${options._requestId}] - ` - : '[benchmark] ', + `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`, `Acquiring a worker handle took ${acquireCounter()}ms.` ); } } catch (error) { throw new ExportError( - '[pool] ' + - (options._requestId ? `Request [${options._requestId}] - ` : '') + - `Error encountered when acquiring an available entry: ${acquireCounter()}ms.`, + `[pool] ${ + options.requestId ? `Request [${options.requestId}] - ` : '' + }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`, 400 ).setError(error); } @@ -271,9 +264,15 @@ export async function postWork(options) { `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.` ); - // Perform an export on a puppeteer level + // Start measuring export time const exportCounter = measureTime(); - const result = await puppeteerExport(workerHandle.page, options); + + // Perform an export on a puppeteer level + const result = await puppeteerExport( + workerHandle.page, + options.export, + options.customLogic + ); // Check if it's an error if (result instanceof Error) { @@ -300,15 +299,15 @@ export async function postWork(options) { result.message === 'Rasterization timeout' ) { throw new ExportError( - '[pool] ' + - (options._requestId ? `Request [${options._requestId}] - ` : '') + - 'Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.' + `[pool] ${ + options.requestId ? `Request [${options.requestId}] - ` : '' + }Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.` ).setError(result); } else { throw new ExportError( - '[pool] ' + - (options._requestId ? `Request [${options._requestId}] - ` : '') + - `Error encountered during export: ${exportCounter()}ms.` + `[pool] ${ + options.requestId ? `Request [${options.requestId}] - ` : '' + }Error encountered during export: ${exportCounter()}ms.` ).setError(result); } } @@ -317,9 +316,7 @@ export async function postWork(options) { if (options.server.benchmarking) { log( 5, - options._requestId - ? `[benchmark] Request [${options._requestId}] - ` - : '[benchmark] ', + `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`, `Exporting a chart sucessfully took ${exportCounter()}ms.` ); } @@ -460,7 +457,8 @@ export function getPoolInfo() { * * @function _factory * - * @param {Object} poolOptions - Object containing `pool` options. + * @param {Object} poolOptions - The configuration object containing `pool` + * options. */ function _factory(poolOptions) { return { From 2acf13fa41c535b6db52ec36fb490593a0a377b9 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:14:33 +0100 Subject: [PATCH 065/102] Enhanced logger functions. --- lib/logger.js | 72 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/lib/logger.js b/lib/logger.js index eda50167..0f7e9ef6 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -61,17 +61,17 @@ const logging = { }; /** - * Logs a message. Accepts a variable amount of arguments. Arguments after - * the `level` will be passed directly to `console.log`, and/or will be joined - * and appended to the log file. + * Logs a message with a specified log level. Accepts a variable number + * of arguments. The arguments after the `level` are passed to `console.log` + * and/or used to construct and append messages to a log file. * * @function log * * @param {...unknown} args - An array of arguments where the first is the log - * level and the rest are strings to build a message with. + * level and the remaining are strings used to build the log message. * - * @returns {void} Ends the function execution when attempting to log - * information at a higher level than what is allowed. + * @returns {void} Exits the function execution if attempting to log at a level + * higher than allowed. */ export function log(...args) { const [newLevel, ...texts] = args; @@ -105,22 +105,22 @@ export function log(...args) { } /** - * Logs an error message with its stack trace. Optionally, a custom message + * Logs an error message along with its stack trace. Optionally, a custom message * can be provided. * * @function logWithStack * * @param {number} newLevel - The log level. - * @param {Error} error - The error object. - * @param {string} customMessage - An optional custom message to be logged - * along with the error. + * @param {Error} error - The error object containing the stack trace. + * @param {string} customMessage - An optional custom message to be included + * in the log alongside the error. * - * @returns {void} Ends the function execution when attempting to log - * information at a higher level than what is allowed. + * @returns {void} Exits the function execution if attempting to log at a level + * higher than allowed. */ export function logWithStack(newLevel, error, customMessage) { // Get the main message - const mainMessage = customMessage || error.message; + const mainMessage = customMessage || (error && error.message) || ''; // Current logging options const { level, levelsDesc } = logging; @@ -134,7 +134,7 @@ export function logWithStack(newLevel, error, customMessage) { const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`; // Add the whole stack message - const stackMessage = error.stack; + const stackMessage = error && error.stack; // Combine custom message or error message with error stack message, if exists const texts = [mainMessage]; @@ -164,12 +164,17 @@ export function logWithStack(newLevel, error, customMessage) { * * @function initLogging * - * @param {Object} loggingOptions - Object containing `logging` options. + * @param {Object} loggingOptions - The configuration object containing + * `logging` options. */ export function initLogging(loggingOptions) { // Get options from the `loggingOptions` object const { level, dest, file, toConsole, toFile } = loggingOptions; + // Reset flags to the default values + logging.pathCreated = false; + logging.pathToLog = ''; + // Set the logging level setLogLevel(level); @@ -181,15 +186,20 @@ export function initLogging(loggingOptions) { } /** - * Sets the log level to the specified value. Log levels are (0 = no logging, - * 1 = error, 2 = warning, 3 = notice, 4 = verbose, or 5 = benchmark). + * Sets the log level to the specified value. Log levels are (`0` = no logging, + * `1` = error, `2` = warning, `3` = notice, `4` = verbose, or `5` = benchmark). * * @function setLogLevel * * @param {number} level - The log level to be set. */ export function setLogLevel(level) { - if (level >= 0 && level <= logging.levelsDesc.length) { + if ( + Number.isInteger(level) && + level >= 0 && + level <= logging.levelsDesc.length + ) { + // Update the module logging's `level` option logging.level = level; } } @@ -202,27 +212,29 @@ export function setLogLevel(level) { * @param {boolean} toConsole - The flag for setting the logging to the console. */ export function enableConsoleLogging(toConsole) { - // Update options for the console logging - logging.toConsole = toConsole; + // Update the module logging's `toConsole` option + logging.toConsole = !!toConsole; } /** - * Enables file logging with the specified destination and log file. + * Enables file logging with the specified destination and log file name. * * @function enableFileLogging * - * @param {string} dest - The destination path for the log file. - * @param {string} file - The log file name. - * @param {boolean} toFile - The flag for setting the logging to a file. + * @param {string} dest - The destination path where the log file should + * be saved. + * @param {string} file - The name of the log file. + * @param {boolean} toFile - A flag indicating whether logging should + * be directed to a file. */ export function enableFileLogging(dest, file, toFile) { - // Update options for the file logging - logging.toFile = toFile; + // Update the module logging's `toFile` option + logging.toFile = !!toFile; - // Set the `dest` and `file` only if the file logging is enabled - if (toFile) { - logging.dest = dest; - logging.file = file; + // Set the `dest` and `file` options only if the file logging is enabled + if (logging.toFile) { + logging.dest = dest || ''; + logging.file = file || ''; } } From 546ae5fd8d80bf21ced6e441dd8de055ffcfc5ec Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:14:45 +0100 Subject: [PATCH 066/102] Corrected saving and loading the config created by the prompt functionality. --- lib/prompt.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/prompt.js b/lib/prompt.js index 512e15f2..d4ac3f6b 100644 --- a/lib/prompt.js +++ b/lib/prompt.js @@ -23,8 +23,10 @@ import { writeFile } from 'fs/promises'; import prompts from 'prompts'; +import { isAllowedConfig } from './config.js'; import { log, logWithStack } from './logger.js'; import { getAbsolutePath } from './utils.js'; + import { defaultConfig } from './schemas/config.js'; /** @@ -37,11 +39,13 @@ import { defaultConfig } from './schemas/config.js'; * * @param {string} configFileName - The name of the configuration file to save * to. + * @param {boolean} allowCodeExecution - A flag indicating whether code + * execution is allowed. * * @returns {Promise} A Promise that resolves to true once the manual * configuration process is completed and the updated configuration is saved. */ -export async function manualConfig(configFileName) { +export async function manualConfig(configFileName, allowCodeExecution) { // Initialize an empty object to hold the config data let configFile = {}; @@ -49,8 +53,10 @@ export async function manualConfig(configFileName) { if (existsSync(getAbsolutePath(configFileName))) { try { // If the file exists, read and parse its contents - configFile = JSON.parse( - readFileSync(getAbsolutePath(configFileName), 'utf8') + configFile = isAllowedConfig( + readFileSync(getAbsolutePath(configFileName), 'utf8'), + false, + allowCodeExecution ); } catch (error) { log( @@ -160,9 +166,10 @@ export async function manualConfig(configFileName) { // If all questions have been answered, save the updated config if (++questionsCounter === allQuestions.length) { try { + // Save the prompt result await writeFile( getAbsolutePath(configFileName), - JSON.stringify(configFile, null, 2), + isAllowedConfig(configFile, true, allowCodeExecution), 'utf8' ); } catch (error) { From 4b2230c7adff92764efccd32e4029060265f4bcf Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:15:03 +0100 Subject: [PATCH 067/102] Corrected types of the config based options. --- lib/schemas/config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/schemas/config.js b/lib/schemas/config.js index 2da50bb3..1c44f1b7 100644 --- a/lib/schemas/config.js +++ b/lib/schemas/config.js @@ -256,7 +256,7 @@ export const defaultConfig = { }, instr: { value: null, - types: ['string', 'null'], + types: ['Object', 'string', 'null'], envLink: 'EXPORT_INSTR', description: 'Overrides the `infile` with JSON, stringified JSON, or SVG input', @@ -266,7 +266,7 @@ export const defaultConfig = { }, options: { value: null, - types: ['Object', 'null'], + types: ['Object', 'string', 'null'], envLink: 'EXPORT_OPTIONS', description: 'Alias for the `instr` option', promptOptions: { From f99b57dd042509bd28697f2a159acd796c150216 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:15:16 +0100 Subject: [PATCH 068/102] Improved the options processing logic of the config module. --- lib/config.js | 202 +++++++++++++++++++++++++++++++------------------- 1 file changed, 126 insertions(+), 76 deletions(-) diff --git a/lib/config.js b/lib/config.js index 033a9009..ba1a3c9e 100644 --- a/lib/config.js +++ b/lib/config.js @@ -25,31 +25,38 @@ import { join } from 'path'; import { log, logWithStack } from './logger.js'; import { envs } from './envs.js'; -import { __dirname, isObject, deepCopy, getAbsolutePath } from './utils.js'; -import { defaultConfig, nestedProps, absoluteProps } from './schemas/config.js'; +import { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js'; + +import { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js'; + +import ExportError from './errors/ExportError.js'; // Sets the global options with initial values from the default config const globalOptions = _initGlobalOptions(defaultConfig); +// An object for the instance options, created each time the `initExport` occurs +const instanceOptions = deepCopy(globalOptions); + /** - * Gets the reference to the global options of the server instance object - * or its copy. + * Retrieves a reference to the options object. Depending on the `getInstance` + * parameter, it returns either the global options or the instance-specific + * options object. * * @function getOptions * - * @param {boolean} [getReference=true] - Optional parameter to decide whether - * to return the reference to the global options of the server instance object - * or return a copy of it. The default value is true. + * @param {boolean} [getInstance=true] - Optional parameter that decides whether + * to return the instance-specific options (when `true`) or the global options + * (when `false`). The default value is `true`. * - * @returns {Object} The reference to the global options of the server instance - * object or its copy. + * @returns {Object} A reference to either the global options + * or the instance-specific options, based on the `getInstance` parameter. */ -export function getOptions(getReference = true) { - return getReference ? globalOptions : deepCopy(globalOptions); +export function getOptions(getInstance = true) { + return getInstance ? instanceOptions : globalOptions; } /** - * Sets the global options of the export server instance, keeping the principle + * Sets the global options of the export server, keeping the principle * of the options load priority from all available sources. It accepts optional * `customOptions` object and `cliArgs` array with arguments from the CLI. These * options will be validated and applied if provided. @@ -59,28 +66,21 @@ export function getOptions(getReference = true) { * 1. Options from the `lib/schemas/config.js` file (default values). * 2. Options from a custom JSON file (loaded by the `loadConfig` option). * 3. Options from the environment variables (the `.env` file). - * 4. Options from the first parameter (by default an empty object). - * 5. Options from the CLI. + * 4. Options from the command line interface (CLI). + * 5. Options from the first parameter (the `customOptions` is by default + * an empty object). * - * @function setOptions + * @function setGlobalOptions * * @param {Object} [customOptions={}] - Optional custom options for additional * configuration. The default value is an empty object. * @param {Array.} [cliArgs=[]] - Optional command line arguments * for additional configuration. The default value is an empty array. - * @param {boolean} [modifyGlobal=false] - Optional parameter to decide - * whether to update and return the reference to the global options - * of the server instance object or return a copy of it. The default value - * is false. * - * @returns {Object} The updated general options object, reflecting the merged + * @returns {Object} The updated global options object, reflecting the merged * configuration from all available sources. */ -export function setOptions( - customOptions = {}, - cliArgs = [], - modifyGlobal = false -) { +export function setGlobalOptions(customOptions = {}, cliArgs = []) { // Object for options loaded via the `loadConfig` option let configOptions = {}; @@ -88,28 +88,57 @@ export function setOptions( let cliOptions = {}; // Only for the CLI usage - if (cliArgs.length) { + if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) { // Get options from the custom JSON loaded via the `loadConfig` - configOptions = _loadConfigFile(cliArgs); + configOptions = _loadConfigFile(cliArgs, globalOptions.customLogic); // Get options from the CLI cliOptions = _pairArgumentValue(nestedProps, cliArgs); } - // Get the reference to the global options object or a copy of the object - const generalOptions = getOptions(modifyGlobal); - - // Update values of the general options with values from each source possible - _updateOptions( + // Update values of the global options with values from each source possible + _updateGlobalOptions( defaultConfig, - generalOptions, + globalOptions, configOptions, - customOptions, - cliOptions + cliOptions, + customOptions ); - // Return options - return generalOptions; + // Return updated global options + return globalOptions; +} + +/** + * Updates the instance options with additional options. It optionally allows + * to reinitialize the instance options with the values of a current global + * options object. + * + * @param {Object} updateOptions - The update options to merge into the instance + * options. + * @param {boolean} [newInstance=false] - A flag to indicate whether to init + * options for a new instance. If `true`, the existing instance options will + * be cleared and reinitialized based on the global options. + * + * @returns {Object} - The updated instance options. + */ +export function updateOptions(updateOptions, newInstance = false) { + // Check if options need to be created for a new instance + if (newInstance) { + // Get rid of the old instance options + Object.keys(instanceOptions).forEach((key) => { + delete instanceOptions[key]; + }); + + // Init the new instance options based on the global options + mergeOptions(instanceOptions, deepCopy(globalOptions)); + } + + // Merge additional options to the instance options + mergeOptions(instanceOptions, updateOptions); + + // Return the reference to the instance options object + return instanceOptions; } /** @@ -123,8 +152,8 @@ export function setOptions( * @returns {Object} Merged configuration options. */ export function mergeOptions(originalOptions, newOptions) { - // Check if the `newOptions` is a correct object - if (isObject(newOptions)) { + // Check if the `originalOptions` and `newOptions` are correct objects + if (isObject(originalOptions) && isObject(newOptions)) { for (const [key, value] of Object.entries(newOptions)) { originalOptions[key] = isObject(value) && @@ -133,11 +162,11 @@ export function mergeOptions(originalOptions, newOptions) { ? mergeOptions(originalOptions[key], value) : value !== undefined ? value - : originalOptions[key]; + : originalOptions[key] || null; } } - // Return the result options + // Return the original (modified or not) options return originalOptions; } @@ -162,7 +191,7 @@ export function mapToNewOptions(oldOptions) { const newOptions = {}; // Check if provided value is a correct object - if (Object.prototype.toString.call(oldOptions) === '[object Object]') { + if (isObject(oldOptions)) { // Iterate over each key-value pair in the old-structured options for (const [key, value] of Object.entries(oldOptions)) { // If there is a nested mapping, split it into a properties chain @@ -179,6 +208,11 @@ export function mapToNewOptions(oldOptions) { newOptions ); } + } else { + log( + 2, + '[config] No correct object with options was provided. Returning an empty array.' + ); } // Return the new, structured options object @@ -194,10 +228,10 @@ export function mapToNewOptions(oldOptions) { * @param {unknown} config - The config to be validated and parsed as a set * of options. Must be either an object or a string. * @param {boolean} [toString=false] - Whether to return a stringified version - * of the parsed config. The default value is false. + * of the parsed config. The default value is `false`. * @param {boolean} [allowFunctions=false] - Whether to allow functions * in the parsed config. If true, functions are preserved. Otherwise, when - * a function is found, null is returned. The default value is false. + * a function is found, null is returned. The default value is `false`. * * @returns {(Object|string|null)} Returns a parsed set of options object, * a stringified set of options object if the `toString` is true, and null @@ -301,12 +335,12 @@ export function printUsage() { * @function printVersion * * @param {boolean} [noLogo=false] - If true, only prints text with the version - * information, without the logo. The default value is false. + * information, without the logo. The default value is `false`. */ export function printVersion(noLogo = false) { // Get package version either from `.env` or from `package.json` const packageVersion = JSON.parse( - readFileSync(join(__dirname, 'package.json')) + readFileSync(join(__dirname, 'package.json'), 'utf8') ).version; // Print text only @@ -315,8 +349,8 @@ export function printVersion(noLogo = false) { } else { // Print the logo console.log( - readFileSync(join(__dirname, 'msg', 'startup.msg')).toString().bold - .yellow, + readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString() + .bold.yellow, `v${packageVersion}\n`.bold ); } @@ -338,9 +372,17 @@ function _initGlobalOptions(config) { // Start initializing the `options` object recursively for (const [name, item] of Object.entries(config)) { - options[name] = Object.prototype.hasOwnProperty.call(item, 'value') - ? item.value - : _initGlobalOptions(item); + if (Object.prototype.hasOwnProperty.call(item, 'value')) { + // If a value from environment variables exists, it takes precedence + const envVal = envs[item.envLink]; + if (envVal !== undefined && envVal !== null) { + options[name] = envVal; + } else { + options[name] = item.value; + } + } else { + options[name] = _initGlobalOptions(item); + } } // Return the created `options` object @@ -348,60 +390,61 @@ function _initGlobalOptions(config) { } /** - * Updates options object with values from various sources, following a specific - * prioritization order. The function checks for values in the following order + * Updates global options object with values from various sources, following + * a specific prioritization order. The function checks for values in the order * of precedence: the `loadConfig` configuration options, environment variables, * custom options, and CLI options. * - * @function _updateOptions + * @function _updateGlobalOptions * * @param {Object} config - The configuration object, which includes the initial * settings and metadata for each option. This object is used to determine * the structure and default values for the options. - * @param {Object} options - The options object that will be updated with values - * from other sources. + * @param {Object} options - The global options object that will be updated + * with values from other sources. * @param {Object} configOpt - The configuration options object, loaded with * the `loadConfig` option, which may provide values to override defaults. - * @param {Object} customOpt - The custom options object, typically containing - * additional and user-defined values, which may override configuration options. * @param {Object} cliOpt - The CLI options object, which may include values - * provided through command-line arguments and has the highest precedence among + * provided through command-line arguments and may override configuration + * options. + * @param {Object} customOpt - The custom options object, typically containing + * additional and user-defined values, which has the highest precedence among * options. */ -function _updateOptions(config, options, configOpt, customOpt, cliOpt) { +function _updateGlobalOptions(config, options, configOpt, cliOpt, customOpt) { Object.keys(config).forEach((key) => { // Get the config entry of a specific option const entry = config[key]; // Gather values for the options from every possible source, if exists const configVal = configOpt && configOpt[key]; - const customVal = customOpt && customOpt[key]; const cliVal = cliOpt && cliOpt[key]; + const customVal = customOpt && customOpt[key]; // If the value not found, need to go deeper if (typeof entry.value === 'undefined') { - _updateOptions(entry, options[key], configVal, customVal, cliVal); + _updateGlobalOptions(entry, options[key], configVal, cliVal, customVal); } else { - // If a value from custom JSON options exists, it take precedence + // If a value from custom JSON options exists, it takes precedence if (configVal !== undefined && configVal !== null) { options[key] = configVal; } - // If a value from environment variables exists, it take precedence + // If a value from environment variables exists, it takes precedence const envVal = envs[entry.envLink]; if (entry.envLink in envs && envVal !== undefined && envVal !== null) { options[key] = envVal; } - // If a value from user options exists, it take precedence - if (customVal !== undefined && customVal !== null) { - options[key] = customVal; - } - - // If a value from CLI options exists, it take precedence + // If a value from CLI options exists, it takes precedence if (cliVal !== undefined && cliVal !== null) { options[key] = cliVal; } + + // If a value from user options exists, it takes precedence + if (customVal !== undefined && customVal !== null) { + options[key] = customVal; + } } }); } @@ -473,12 +516,14 @@ export function _optionsStringify(options, allowFunctions, stringifyFunctions) { * * @param {Array.} cliArgs - Command-line arguments to search * for the `loadConfig` option and the corresponding file path. + * @param {Object} customLogicOptions - The configuration object containing + * `customLogic` options. * * @returns {Object} The additional configuration loaded from the specified * file, or an empty object if the file is not found, invalid, or an error * occurs. */ -function _loadConfigFile(cliArgs) { +function _loadConfigFile(cliArgs, customLogicOptions) { // Check if the `loadConfig` option was used const configIndex = cliArgs.findIndex( (arg) => arg.replace(/-/g, '') === 'loadConfig' @@ -488,10 +533,14 @@ function _loadConfigFile(cliArgs) { const configFileName = configIndex > -1 && cliArgs[configIndex + 1]; // Check if the `loadConfig` is present and has a correct value - if (configFileName) { + if (configFileName && customLogicOptions.allowFileResources) { try { // Load an optional custom JSON config file - return JSON.parse(readFileSync(getAbsolutePath(configFileName))); + return isAllowedConfig( + readFileSync(getAbsolutePath(configFileName), 'utf8'), + false, + customLogicOptions.allowCodeExecution + ); } catch (error) { logWithStack( 2, @@ -562,8 +611,8 @@ function _pairArgumentValue(nestedProps, cliArgs) { * * @function _cycleCategories * - * @param {Object} options - The options object containing CLI options. - * It may include nested categories and individual options. + * @param {Object} options - The options object containing CLI options. It may + * include nested categories and individual options. */ function _cycleCategories(options) { for (const [name, option] of Object.entries(options)) { @@ -601,7 +650,8 @@ function _cycleCategories(options) { export default { getOptions, - setOptions, + setGlobalOptions, + updateOptions, mergeOptions, mapToNewOptions, isAllowedConfig, From b98f9e071a3fa0abfe10b9ee266c7e747771c8fd Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:15:41 +0100 Subject: [PATCH 069/102] Enhanced updates of the options in the API functions of the server modules. --- lib/server/server.js | 49 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/lib/server/server.js b/lib/server/server.js index 19e8a04e..ba744174 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -30,7 +30,7 @@ import http from 'http'; import https from 'https'; import multer from 'multer'; -import { getOptions } from '../config.js'; +import { updateOptions } from '../config.js'; import { log, logWithStack } from '../logger.js'; import { __dirname, getAbsolutePath } from '../utils.js'; @@ -52,26 +52,35 @@ const activeServers = new Map(); const app = express(); /** - * Starts HTTP or/and HTTPS server based on the provided configuration. - * The `serverOptions` object contains all server related properties (see - * the `server` section in the `lib/schemas/config.js` file for a reference). + * Starts an HTTP and/or HTTPS server based on the provided configuration. + * The `serverOptions` object contains server-related properties (refer + * to the `server` section in the `lib/schemas/config.js` file for details). * * @async * @function startServer * - * @param {Object} [serverOptions=getOptions().server] - Object containing - * `server` options. The default value is the global server options - * of the export server instance. + * @param {Object} serverOptions - The configuration object containing `server` + * options. This object may include a partial or complete set of the `server` + * options. If the options are partial, missing values will default + * to the current global configuration. * - * @returns {Promise} A Promise that resolves to ending the function - * execution when the server should not be enabled or when no valid Express app - * is found. + * @returns {Promise} A Promise that resolves when the server is either + * not enabled or no valid Express app is found, signaling the end of the + * function's execution. * * @throws {ExportError} Throws an `ExportError` if the server cannot * be configured and started. */ -export async function startServer(serverOptions = getOptions().server) { +export async function startServer(serverOptions) { try { + // Update the instance options object + const options = updateOptions({ + server: serverOptions + }); + + // Use validated options + serverOptions = options.server; + // Stop if not enabled if (!serverOptions.enable || !app) { throw new ExportError( @@ -204,8 +213,8 @@ export async function startServer(serverOptions = getOptions().server) { validationMiddleware(app); // Set up routes - healthRoutes(app); exportRoutes(app); + healthRoutes(app); uiRoutes(app); versionChangeRoutes(app); @@ -277,11 +286,21 @@ export function getApp() { * * @function enableRateLimiting * - * @param {Object} rateLimitingOptions - Object containing `rateLimiting` - * options. + * @param {Object} rateLimitingOptions - The configuration object containing + * `rateLimiting` options. This object may include a partial or complete set + * of the `rateLimiting` options. If the options are partial, missing values + * will default to the current global configuration. */ export function enableRateLimiting(rateLimitingOptions) { - rateLimitingMiddleware(app, rateLimitingOptions); + // Update the instance options object + const options = updateOptions({ + server: { + rateLimiting: rateLimitingOptions + } + }); + + // Set the rate limiting options + rateLimitingMiddleware(app, options.server.rateLimitingOptions); } /** From fec665f30559a613308317e396574998985e1d19 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:15:57 +0100 Subject: [PATCH 070/102] Optimized the rateLimitingMiddleware function, in the rateLimiting module. --- lib/server/middlewares/rateLimiting.js | 34 ++++++++++++-------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/lib/server/middlewares/rateLimiting.js b/lib/server/middlewares/rateLimiting.js index 9ebfb517..9eedaaae 100644 --- a/lib/server/middlewares/rateLimiting.js +++ b/lib/server/middlewares/rateLimiting.js @@ -19,7 +19,6 @@ See LICENSE file in root for details. import rateLimit from 'express-rate-limit'; -import { getOptions } from '../../config.js'; import { log } from '../../logger.js'; import ExportError from '../../errors/ExportError.js'; @@ -29,31 +28,27 @@ import ExportError from '../../errors/ExportError.js'; * * @param {Express} app - The Express app instance. * - * @param {Object} [rateLimitingOptions=getOptions().server.rateLimiting] - - * Object containing `rateLimiting` options. The default value is the global - * rate limiting options of the export server instance. + * @param {Object} rateLimitingOptions - The configuration object containing + * `rateLimiting` options. * * @throws {ExportError} Throws an `ExportError` if could not configure and set * the rate limiting options. */ -export default function rateLimitingMiddleware( - app, - rateLimitingOptions = getOptions().server.rateLimiting -) { +export default function rateLimitingMiddleware(app, rateLimitingOptions) { try { // Check if the rate limiting is enabled if (rateLimitingOptions.enable) { - const msg = + const message = 'Too many requests, you have been rate limited. Please try again later.'; // Options for the rate limiter const rateOptions = { - max: rateLimitingOptions.maxRequests || 30, window: rateLimitingOptions.window || 1, + maxRequests: rateLimitingOptions.maxRequests || 30, delay: rateLimitingOptions.delay || 0, trustProxy: rateLimitingOptions.trustProxy || false, - skipKey: rateLimitingOptions.skipKey || false, - skipToken: rateLimitingOptions.skipToken || false + skipKey: rateLimitingOptions.skipKey || null, + skipToken: rateLimitingOptions.skipToken || null }; // Set if behind a proxy @@ -63,26 +58,27 @@ export default function rateLimitingMiddleware( // Create a limiter const limiter = rateLimit({ + // Time frame for which requests are checked and remembered windowMs: rateOptions.window * 60 * 1000, - // Limit each IP to 100 requests per windowMs - max: rateOptions.max, + // Limit each IP to 100 requests per `windowMs` + limit: rateOptions.maxRequests, // Disable delaying, full speed until the max limit is reached delayMs: rateOptions.delay, handler: (request, response) => { response.format({ json: () => { - response.status(429).send({ message: msg }); + response.status(429).send({ message }); }, default: () => { - response.status(429).send(msg); + response.status(429).send(message); } }); }, skip: (request) => { // Allow bypassing the limiter if a valid key/token has been sent if ( - rateOptions.skipKey !== false && - rateOptions.skipToken !== false && + rateOptions.skipKey !== null && + rateOptions.skipToken !== null && request.query.key === rateOptions.skipKey && request.query.access_token === rateOptions.skipToken ) { @@ -98,7 +94,7 @@ export default function rateLimitingMiddleware( log( 3, - `[rate limiting] Enabled rate limiting with ${rateOptions.max} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.` + `[rate limiting] Enabled rate limiting with ${rateOptions.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.` ); } } catch (error) { From a91e72f2b51ef65d4983db09ac753b1ca6a7fbd6 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:16:07 +0100 Subject: [PATCH 071/102] Optimized the validation middleware. --- lib/server/middlewares/validation.js | 47 +++++++++++++--------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/lib/server/middlewares/validation.js b/lib/server/middlewares/validation.js index 3d281d12..45b105df 100644 --- a/lib/server/middlewares/validation.js +++ b/lib/server/middlewares/validation.js @@ -28,14 +28,9 @@ import { v4 as uuid } from 'uuid'; import { getAllowCodeExecution } from '../../chart.js'; import { isAllowedConfig } from '../../config.js'; import { log } from '../../logger.js'; -import { - fixConstr, - fixType, - isObjectEmpty, - isPrivateRangeUrlFound -} from '../../utils.js'; +import { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js'; -import HttpError from '../../errors/HttpError.js'; +import ExportError from '../../errors/ExportError.js'; /** * Middleware for validating the content-type header. @@ -48,7 +43,7 @@ import HttpError from '../../errors/HttpError.js'; * * @returns {undefined} The call to the next middleware function. * - * @throws {HttpError} Throws an `HttpError` if the content-type + * @throws {ExportError} Throws an `ExportError` if the content-type * is not correct. */ function contentTypeMiddleware(request, response, next) { @@ -62,7 +57,7 @@ function contentTypeMiddleware(request, response, next) { !contentType.includes('application/x-www-form-urlencoded') && !contentType.includes('multipart/form-data') ) { - throw new HttpError( + throw new ExportError( '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.', 415 ); @@ -86,11 +81,11 @@ function contentTypeMiddleware(request, response, next) { * * @returns {undefined} The call to the next middleware function. * - * @throws {HttpError} Throws an `HttpError` if the body is not correct. - * @throws {HttpError} Throws an `HttpError` if the chart data from the body + * @throws {ExportError} Throws an `ExportError` if the body is not correct. + * @throws {ExportError} Throws an `ExportError` if the chart data from the body * is not correct. - * @throws {HttpError} Throws an `HttpError` in case of the private range url - * error. + * @throws {ExportError} Throws an `ExportError` in case of the private range + * url error. */ function requestBodyMiddleware(request, response, next) { try { @@ -98,7 +93,7 @@ function requestBodyMiddleware(request, response, next) { const body = request.body; // Create a unique ID for a request - const requestId = uuid().replace(/-/g, ''); + const requestId = uuid(); // Throw an error if there is no correct body if (!body || isObjectEmpty(body)) { @@ -109,8 +104,8 @@ function requestBodyMiddleware(request, response, next) { } was incorrect. Received payload is empty.` ); - throw new HttpError( - "[validation] The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.", + throw new ExportError( + `[validation] Request [${requestId}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`, 400 ); } @@ -137,32 +132,32 @@ function requestBodyMiddleware(request, response, next) { } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.` ); - throw new HttpError( - "[validation] 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.", + throw new ExportError( + `Request [${requestId}] - [validation] 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 ); } // Throw an error if test of xlink:href elements from payload's SVG fails if (body.svg && isPrivateRangeUrlFound(body.svg)) { - throw new HttpError( - "[validation] 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.", + throw new ExportError( + `Request [${requestId}] - [validation] 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 ); } - // Get options from the body and store parsed structure in the request + // Get the request options and store parsed structure in the request request.validatedOptions = { - // Set the created ID as a `_requestId` property in the validated options - _requestId: requestId, + // Set the created ID as a `requestId` property in the options + requestId, export: { instr, svg: body.svg, outfile: body.outfile || - `${request.params.filename || 'chart'}.${fixType(body.type)}`, - type: fixType(body.type, body.outfile), - constr: fixConstr(body.constr), + `${request.params.filename || 'chart'}.${body.type || 'png'}`, + type: body.type, + constr: body.constr, b64: body.b64, noDownload: body.noDownload, height: body.height, From e30b014c2d8c4cfa7d05b59bccd4ceb1595f5198 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:16:25 +0100 Subject: [PATCH 072/102] Got rid of the HttpError and moved the setStatus function to the ExportError. --- lib/errors/ExportError.js | 13 +++++++++ lib/errors/HttpError.js | 47 ------------------------------ lib/server/routes/versionChange.js | 17 ++++++----- 3 files changed, 23 insertions(+), 54 deletions(-) delete mode 100644 lib/errors/HttpError.js diff --git a/lib/errors/ExportError.js b/lib/errors/ExportError.js index 760554e3..a580e886 100644 --- a/lib/errors/ExportError.js +++ b/lib/errors/ExportError.js @@ -36,6 +36,19 @@ class ExportError extends Error { } } + /** + * Sets or updates the HTTP status code for the error. + * + * @param {number} statusCode - The HTTP status code to assign to the error. + * + * @returns {ExportError} The updated instance of the `ExportError` class. + */ + setStatus(statusCode) { + this.statusCode = statusCode; + + return this; + } + /** * Sets additional error details based on an existing error object. * diff --git a/lib/errors/HttpError.js b/lib/errors/HttpError.js deleted file mode 100644 index d63a922a..00000000 --- a/lib/errors/HttpError.js +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - -Highcharts Export Server - -Copyright (c) 2016-2025, Highsoft - -Licenced under the MIT licence. - -Additionally a valid Highcharts license is required for use. - -See LICENSE file in root for details. - -*******************************************************************************/ - -import ExportError from './ExportError.js'; - -/** - * A custom HTTP error class that extends the `ExportError`. Used to handle - * errors with HTTP status codes. - */ -class HttpError extends ExportError { - /** - * Creates an instance of the `HttpError`. - * - * @param {string} message - The error message to be displayed. - * @param {number} statusCode - Optional HTTP status code associated - * with the error (e.g., 400, 500). - */ - constructor(message, statusCode) { - super(message, statusCode); - } - - /** - * Sets or updates the HTTP status code for the error. - * - * @param {number} statusCode - The HTTP status code to assign to the error. - * - * @returns {HttpError} The updated instance of the `HttpError` class. - */ - setStatus(statusCode) { - this.statusCode = statusCode; - - return this; - } -} - -export default HttpError; diff --git a/lib/server/routes/versionChange.js b/lib/server/routes/versionChange.js index edbe89d5..32b615e2 100644 --- a/lib/server/routes/versionChange.js +++ b/lib/server/routes/versionChange.js @@ -17,10 +17,11 @@ See LICENSE file in root for details. * on the server, with authentication and validation. */ -import { updateHighchartsVersion, getHighchartsVersion } from '../../cache.js'; +import { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js'; import { envs } from '../../envs.js'; +import { log } from '../../logger.js'; -import HttpError from '../../errors/HttpError.js'; +import ExportError from '../../errors/ExportError.js'; /** * Adds the `version_change` routes. @@ -36,12 +37,14 @@ export default function versionChangeRoutes(app) { */ app.post('/version_change/:newVersion', async (request, response, next) => { try { + log(4, '[version] Changing Highcharts version.'); + // Get the token directly from envs const adminToken = envs.HIGHCHARTS_ADMIN_TOKEN; // Check the existence of the token if (!adminToken || !adminToken.length) { - throw new HttpError( + throw new ExportError( '[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.', 401 ); @@ -52,20 +55,20 @@ export default function versionChangeRoutes(app) { // Check if the hc-auth header contain a correct token if (!token || token !== adminToken) { - throw new HttpError( + throw new ExportError( '[version] Invalid or missing token: Set the token in the hc-auth header.', 401 ); } // Compare versions - const newVersion = request.params.newVersion; + let newVersion = request.params.newVersion; if (newVersion) { try { // Update version await updateHighchartsVersion(newVersion); } catch (error) { - throw new HttpError( + throw new ExportError( `[version] Version change: ${error.message}`, 400 ).setError(error); @@ -79,7 +82,7 @@ export default function versionChangeRoutes(app) { }); } else { // No version specified - throw new HttpError('[version] No new version supplied.', 400); + throw new ExportError('[version] No new version supplied.', 400); } } catch (error) { return next(error); From 8df1c58c14c39f527ecd9218d4e8f22c8617213d Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:17:03 +0100 Subject: [PATCH 073/102] Other, smaller corrections. --- lib/browser.js | 6 +++--- lib/resourceRelease.js | 12 ++++++++---- lib/server/routes/export.js | 10 +++++----- lib/server/routes/health.js | 4 +++- lib/server/routes/ui.js | 3 +++ lib/utils.js | 4 ++-- 6 files changed, 24 insertions(+), 15 deletions(-) diff --git a/lib/browser.js b/lib/browser.js index e2b5f892..df625324 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -221,7 +221,7 @@ export async function newPage(poolResource) { * @param {boolean} [hardReset=false] - A flag indicating the type of clearing * to be performed. If true, navigates to `about:blank` and resets content * and scripts. If false, clears the body content by setting a predefined HTML - * structure. The default value is false. + * structure. The default value is `false`. * * @returns {Promise} A Promise that resolves to true when page * is correctly cleared and false when it is not. @@ -268,8 +268,8 @@ export async function clearPage(poolResource, hardReset = false) { * * @param {Object} page - The Puppeteer page object to which resources will * be added. - * @param {Object} customLogicOptions - The object containing `customLogic` - * options. + * @param {Object} customLogicOptions - The configuration object containing + * `customLogic` options. * * @returns {Promise>} A Promise that resolves to an array * of injected resources. diff --git a/lib/resourceRelease.js b/lib/resourceRelease.js index dba0d026..150b217a 100644 --- a/lib/resourceRelease.js +++ b/lib/resourceRelease.js @@ -19,17 +19,21 @@ See LICENSE file in root for details. import { killPool } from './pool.js'; import { clearAllTimers } from './timer.js'; + import { closeServers } from './server/server.js'; /** - * Cleans up function to trigger before ending process for the graceful - * shutdown. + * Performs cleanup operations to ensure a graceful shutdown of the process. + * This includes clearing all registered timeouts/intervals, closing active + * servers, terminating resources (pages) of the pool, pool itself, and closing + * the browser. * * @function shutdownCleanUp * - * @param {number} exitCode - An exit code for the `process.exit()` function. + * @param {number} [exitCode=0] - The exit code to use with `process.exit()`. + * The default value is `0`. */ -export async function shutdownCleanUp(exitCode) { +export async function shutdownCleanUp(exitCode = 0) { // Await freeing all resources await Promise.allSettled([ // Clear all ongoing intervals diff --git a/lib/server/routes/export.js b/lib/server/routes/export.js index e5e1d708..1def9744 100644 --- a/lib/server/routes/export.js +++ b/lib/server/routes/export.js @@ -25,7 +25,7 @@ import { startExport } from '../../chart.js'; import { log } from '../../logger.js'; import { getBase64, measureTime } from '../../utils.js'; -import HttpError from '../../errors/HttpError.js'; +import ExportError from '../../errors/ExportError.js'; // Reversed MIME types const reversedMime = { @@ -66,10 +66,10 @@ async function requestExport(request, response, next) { const requestOptions = request.validatedOptions; // Get the request id - const requestId = requestOptions._requestId; + const requestId = requestOptions.requestId; // Info about an incoming request with correct data - log(4, `[export] Got an incoming HTTP request with ID ${requestId}.`); + log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`); // Start the export process await startExport(requestOptions, (error, data) => { @@ -100,8 +100,8 @@ async function requestExport(request, response, next) { } was incorrect. Received result is ${data.result}.` ); - throw new HttpError( - '[export] Unexpected return of the export result from the chart generation. Please check your request data.', + throw new ExportError( + `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`, 400 ); } diff --git a/lib/server/routes/health.js b/lib/server/routes/health.js index f1ba3d51..1e50ab42 100644 --- a/lib/server/routes/health.js +++ b/lib/server/routes/health.js @@ -30,7 +30,9 @@ import { __dirname, getNewDateTime } from '../../utils.js'; const serverStartTime = new Date(); // Get the `package.json` content -const packageFile = JSON.parse(readFileSync(join(__dirname, 'package.json'))); +const packageFile = JSON.parse( + readFileSync(join(__dirname, 'package.json'), 'utf8') +); // An array for success rate ratios const successRates = []; diff --git a/lib/server/routes/ui.js b/lib/server/routes/ui.js index 6ce88d98..7b4ef89a 100644 --- a/lib/server/routes/ui.js +++ b/lib/server/routes/ui.js @@ -20,6 +20,7 @@ See LICENSE file in root for details. import { join } from 'path'; import { getOptions } from '../../config.js'; +import { log } from '../../logger.js'; import { __dirname } from '../../utils.js'; /** @@ -35,6 +36,8 @@ export default function uiRoutes(app) { */ app.get(getOptions().ui.route || '/', (request, response, next) => { try { + log(4, '[ui] Returning UI for the export.'); + response.sendFile(join(__dirname, 'public', 'index.html'), { acceptRanges: false }); diff --git a/lib/utils.js b/lib/utils.js index 8eb66292..0624e79f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -85,7 +85,7 @@ export function deepCopy(objArr) { * * @param {Function} fn - The function to be retried. * @param {number} [attempt=0] - The current attempt number. The default value - * is 0. + * is `0`. * @param {...unknown} args - Arguments to be passed to the function. * * @returns {Promise} A Promise that resolves to the result @@ -182,7 +182,7 @@ export function fixOutfile(type, outfile) { * * @param {string} type - The original export type. * @param {string} [outfile=null] - The file path or name. The default value - * is null. + * is `null`. * * @returns {string} The corrected export type. */ From 8c7c3fc8946634028b87a66723317360f5c8ccf2 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:17:46 +0100 Subject: [PATCH 074/102] Samples and scenarios runners corrections. --- samples/module/optionsPhantom.js | 2 +- samples/module/optionsPuppeteer.js | 2 +- samples/module/promises.js | 2 +- samples/module/svg.js | 2 +- tests/node/nodeTestRunner.js | 5 +---- tests/node/nodeTestRunnerSingle.js | 25 ++++++++++--------------- 6 files changed, 15 insertions(+), 23 deletions(-) diff --git a/samples/module/optionsPhantom.js b/samples/module/optionsPhantom.js index 5dbc1dad..83e6fe1a 100644 --- a/samples/module/optionsPhantom.js +++ b/samples/module/optionsPhantom.js @@ -74,7 +74,7 @@ const oldOptions = { const newOptions = exporter.mapToNewOptions(oldOptions); // Set the new options - const options = exporter.setOptions(newOptions); + const options = exporter.setGlobalOptions(newOptions); // Init a pool for one export await initExport(options); diff --git a/samples/module/optionsPuppeteer.js b/samples/module/optionsPuppeteer.js index 60cb9cdb..9fc74799 100644 --- a/samples/module/optionsPuppeteer.js +++ b/samples/module/optionsPuppeteer.js @@ -138,7 +138,7 @@ const newOptions = { (async () => { try { // Set the new options - const options = exporter.setOptions(newOptions); + const options = exporter.setGlobalOptions(newOptions); // Init a pool for one export await initExport(options); diff --git a/samples/module/promises.js b/samples/module/promises.js index 12001897..26196a6b 100644 --- a/samples/module/promises.js +++ b/samples/module/promises.js @@ -18,7 +18,7 @@ import exporter, { initExport } from '../../lib/index.js'; const exportCharts = async (charts, exportOptions = {}) => { // Set the new options - const options = exporter.setOptions(exportOptions); + const options = exporter.setGlobalOptions(exportOptions); // Init the pool await initExport(options); diff --git a/samples/module/svg.js b/samples/module/svg.js index 08d2e33f..62f59162 100644 --- a/samples/module/svg.js +++ b/samples/module/svg.js @@ -33,7 +33,7 @@ const svgOptions = { (async () => { try { // Set the new options - const options = exporter.setOptions(svgOptions); + const options = exporter.setGlobalOptions(svgOptions); // Init a pool for one export await initExport(options); diff --git a/tests/node/nodeTestRunner.js b/tests/node/nodeTestRunner.js index 9d3d9eac..f6403b88 100644 --- a/tests/node/nodeTestRunner.js +++ b/tests/node/nodeTestRunner.js @@ -87,12 +87,9 @@ console.log( // The start date of a startExport function run const startTime = getNewDateTime(); - // Init options - const options = exporter.setOptions(fileOptions); - // Start the export process exporter - .startExport(options, (error, data) => { + .startExport(fileOptions, (error, data) => { // Throw an error if (error) { throw error; diff --git a/tests/node/nodeTestRunnerSingle.js b/tests/node/nodeTestRunnerSingle.js index 89ebafcd..248bb35d 100644 --- a/tests/node/nodeTestRunnerSingle.js +++ b/tests/node/nodeTestRunnerSingle.js @@ -55,21 +55,16 @@ console.log( ) ); - // Set options - const options = exporter.setOptions( - exporter.mergeOptions(fileOptions, { - pool: { - minWorkers: 1, - maxWorkers: 1 - }, - logging: { - level: 0 - } - }) - ); - // Initialize pool with disabled logging - await initExport(options); + await initExport({ + pool: { + minWorkers: 1, + maxWorkers: 1 + }, + logging: { + level: 0 + } + }); // Start the export console.log('[Test runner]'.blue, `Processing test ${file}.`); @@ -79,7 +74,7 @@ console.log( try { // Start the export process - await exporter.startExport(options, async (error, data) => { + await exporter.startExport(fileOptions, async (error, data) => { // Throw an error if (error) { throw error; From 14219f2f341cc6b175fbe04b1b2096267482b083 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:18:06 +0100 Subject: [PATCH 075/102] Packages updates. --- package-lock.json | 617 +++++++++++++++++++++++++++++----------------- package.json | 14 +- 2 files changed, 396 insertions(+), 235 deletions(-) diff --git a/package-lock.json b/package-lock.json index c264790c..41988281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,12 +16,12 @@ "express": "^4.21.2", "express-rate-limit": "^7.5.0", "https-proxy-agent": "^7.0.6", - "jsdom": "^25.0.1", + "jsdom": "^26.0.0", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^23.11.1", + "puppeteer": "^24.1.0", "tarn": "^3.0.2", - "uuid": "^11.0.4", + "uuid": "^11.0.5", "zod": "^3.24.1" }, "bin": { @@ -31,15 +31,15 @@ "@jest/globals": "^29.7.0", "@rollup/plugin-terser": "^0.4.4", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", + "eslint-config-prettier": "^10.0.1", "eslint-plugin-import": "^2.31.0", - "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-prettier": "^5.2.3", "husky": "^9.1.7", "jest": "^29.7.0", - "lint-staged": "^15.3.0", + "lint-staged": "^15.4.1", "nodemon": "^3.1.9", "prettier": "^3.4.2", - "rollup": "^4.29.2" + "rollup": "^4.31.0" }, "engines": { "node": ">=18.12.0" @@ -59,6 +59,25 @@ "node": ">=6.0.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.3.tgz", + "integrity": "sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==", + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.1", + "@csstools/css-color-parser": "^3.0.7", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -74,9 +93,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", "dev": true, "license": "MIT", "engines": { @@ -115,14 +134,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -132,13 +151,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -181,9 +200,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, "license": "MIT", "engines": { @@ -234,13 +253,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -504,17 +523,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", + "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.5", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/types": "^7.26.5", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -533,9 +552,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -553,6 +572,116 @@ "dev": true, "license": "MIT" }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz", + "integrity": "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.1.tgz", + "integrity": "sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.7.tgz", + "integrity": "sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.1", + "@csstools/css-calc": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -1186,9 +1315,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz", - "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.0.tgz", + "integrity": "sha512-bO61XnTuopsz9kvtfqhVbH6LTM1koxK0IlBR+yuVrM2LB7mk8+5o1w18l5zqd5cs8xlf+ntgambqRqGifMDjog==", "license": "Apache-2.0", "dependencies": { "debug": "^4.4.0", @@ -1243,9 +1372,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.2.tgz", - "integrity": "sha512-s/8RiF4bdmGnc/J0N7lHAr5ZFJj+NdJqJ/Hj29K+c4lEdoVlukzvWXB9XpWZCdakVT0YAw8iyIqUP2iFRz5/jA==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.31.0.tgz", + "integrity": "sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA==", "cpu": [ "arm" ], @@ -1257,9 +1386,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.2.tgz", - "integrity": "sha512-mKRlVj1KsKWyEOwR6nwpmzakq6SgZXW4NUHNWlYSiyncJpuXk7wdLzuKdWsRoR1WLbWsZBKvsUCdCTIAqRn9cA==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.31.0.tgz", + "integrity": "sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g==", "cpu": [ "arm64" ], @@ -1271,9 +1400,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.2.tgz", - "integrity": "sha512-vJX+vennGwygmutk7N333lvQ/yKVAHnGoBS2xMRQgXWW8tvn46YWuTDOpKroSPR9BEW0Gqdga2DHqz8Pwk6X5w==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.31.0.tgz", + "integrity": "sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g==", "cpu": [ "arm64" ], @@ -1285,9 +1414,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.2.tgz", - "integrity": "sha512-e2rW9ng5O6+Mt3ht8fH0ljfjgSCC6ffmOipiLUgAnlK86CHIaiCdHCzHzmTkMj6vEkqAiRJ7ss6Ibn56B+RE5w==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.31.0.tgz", + "integrity": "sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ==", "cpu": [ "x64" ], @@ -1299,9 +1428,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.2.tgz", - "integrity": "sha512-/xdNwZe+KesG6XJCK043EjEDZTacCtL4yurMZRLESIgHQdvtNyul3iz2Ab03ZJG0pQKbFTu681i+4ETMF9uE/Q==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.31.0.tgz", + "integrity": "sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew==", "cpu": [ "arm64" ], @@ -1313,9 +1442,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.2.tgz", - "integrity": "sha512-eXKvpThGzREuAbc6qxnArHh8l8W4AyTcL8IfEnmx+bcnmaSGgjyAHbzZvHZI2csJ+e0MYddl7DX0X7g3sAuXDQ==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.31.0.tgz", + "integrity": "sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA==", "cpu": [ "x64" ], @@ -1327,9 +1456,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.2.tgz", - "integrity": "sha512-h4VgxxmzmtXLLYNDaUcQevCmPYX6zSj4SwKuzY7SR5YlnCBYsmvfYORXgiU8axhkFCDtQF3RW5LIXT8B14Qykg==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.31.0.tgz", + "integrity": "sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==", "cpu": [ "arm" ], @@ -1341,9 +1470,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.2.tgz", - "integrity": "sha512-EObwZ45eMmWZQ1w4N7qy4+G1lKHm6mcOwDa+P2+61qxWu1PtQJ/lz2CNJ7W3CkfgN0FQ7cBUy2tk6D5yR4KeXw==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.31.0.tgz", + "integrity": "sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==", "cpu": [ "arm" ], @@ -1355,9 +1484,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.2.tgz", - "integrity": "sha512-Z7zXVHEXg1elbbYiP/29pPwlJtLeXzjrj4241/kCcECds8Zg9fDfURWbZHRIKrEriAPS8wnVtdl4ZJBvZr325w==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.31.0.tgz", + "integrity": "sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==", "cpu": [ "arm64" ], @@ -1369,9 +1498,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.2.tgz", - "integrity": "sha512-TF4kxkPq+SudS/r4zGPf0G08Bl7+NZcFrUSR3484WwsHgGgJyPQRLCNrQ/R5J6VzxfEeQR9XRpc8m2t7lD6SEQ==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.31.0.tgz", + "integrity": "sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==", "cpu": [ "arm64" ], @@ -1383,9 +1512,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.2.tgz", - "integrity": "sha512-kO9Fv5zZuyj2zB2af4KA29QF6t7YSxKrY7sxZXfw8koDQj9bx5Tk5RjH+kWKFKok0wLGTi4bG117h31N+TIBEg==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.31.0.tgz", + "integrity": "sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==", "cpu": [ "loong64" ], @@ -1397,9 +1526,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.2.tgz", - "integrity": "sha512-gIh776X7UCBaetVJGdjXPFurGsdWwHHinwRnC5JlLADU8Yk0EdS/Y+dMO264OjJFo7MXQ5PX4xVFbxrwK8zLqA==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.31.0.tgz", + "integrity": "sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==", "cpu": [ "ppc64" ], @@ -1411,9 +1540,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.2.tgz", - "integrity": "sha512-YgikssQ5UNq1GoFKZydMEkhKbjlUq7G3h8j6yWXLBF24KyoA5BcMtaOUAXq5sydPmOPEqB6kCyJpyifSpCfQ0w==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.31.0.tgz", + "integrity": "sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==", "cpu": [ "riscv64" ], @@ -1425,9 +1554,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.2.tgz", - "integrity": "sha512-9ouIR2vFWCyL0Z50dfnon5nOrpDdkTG9lNDs7MRaienQKlTyHcDxplmk3IbhFlutpifBSBr2H4rVILwmMLcaMA==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.31.0.tgz", + "integrity": "sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==", "cpu": [ "s390x" ], @@ -1439,9 +1568,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.2.tgz", - "integrity": "sha512-ckBBNRN/F+NoSUDENDIJ2U9UWmIODgwDB/vEXCPOMcsco1niTkxTXa6D2Y/pvCnpzaidvY2qVxGzLilNs9BSzw==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.31.0.tgz", + "integrity": "sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==", "cpu": [ "x64" ], @@ -1453,9 +1582,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.2.tgz", - "integrity": "sha512-jycl1wL4AgM2aBFJFlpll/kGvAjhK8GSbEmFT5v3KC3rP/b5xZ1KQmv0vQQ8Bzb2ieFQ0kZFPRMbre/l3Bu9JA==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.31.0.tgz", + "integrity": "sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==", "cpu": [ "x64" ], @@ -1467,9 +1596,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.2.tgz", - "integrity": "sha512-S2V0LlcOiYkNGlRAWZwwUdNgdZBfvsDHW0wYosYFV3c7aKgEVcbonetZXsHv7jRTTX+oY5nDYT4W6B1oUpMNOg==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.31.0.tgz", + "integrity": "sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==", "cpu": [ "arm64" ], @@ -1481,9 +1610,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.2.tgz", - "integrity": "sha512-pW8kioj9H5f/UujdoX2atFlXNQ9aCfAxFRaa+mhczwcsusm6gGrSo4z0SLvqLF5LwFqFTjiLCCzGkNK/LE0utQ==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.31.0.tgz", + "integrity": "sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ==", "cpu": [ "ia32" ], @@ -1495,9 +1624,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.2.tgz", - "integrity": "sha512-p6fTArexECPf6KnOHvJXRpAEq0ON1CBtzG/EY4zw08kCHk/kivBc5vUEtnCFNCHOpJZ2ne77fxwRLIKD4wuW2Q==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.31.0.tgz", + "integrity": "sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw==", "cpu": [ "x64" ], @@ -1645,9 +1774,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.5.tgz", - "integrity": "sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==", + "version": "22.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", + "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2125,49 +2254,67 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.1.tgz", - "integrity": "sha512-Bw2PgKSrZ3uCuSV9WQ998c/GTJTd+9bWj97n7aDQMP8dP/exAZQlJeswPty0ISy+HZD+9Ex+C7CCnc9Q5QJFmQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", + "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", "license": "Apache-2.0", "optional": true }, "node_modules/bare-fs": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", - "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.0.1.tgz", + "integrity": "sha512-ilQs4fm/l9eMfWY2dY0WCIUplSUp7U0CT1vrqMg1MUdeZl4fypu5UP0XcDBK5WBQPJAKP1b7XEodISmekH/CEg==", "license": "Apache-2.0", "optional": true, "dependencies": { "bare-events": "^2.0.0", - "bare-path": "^2.0.0", + "bare-path": "^3.0.0", "bare-stream": "^2.0.0" + }, + "engines": { + "bare": ">=1.7.0" } }, "node_modules/bare-os": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", - "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.4.0.tgz", + "integrity": "sha512-9Ous7UlnKbe3fMi7Y+qh0DwAup6A1JkYgPnjvMDNOlmnxNRQvQ/7Nst+OnUQKzk0iAT0m9BisbDVp9gCv8+ETA==", "license": "Apache-2.0", - "optional": true + "optional": true, + "engines": { + "bare": ">=1.6.0" + } }, "node_modules/bare-path": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", - "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "license": "Apache-2.0", "optional": true, "dependencies": { - "bare-os": "^2.1.0" + "bare-os": "^3.0.1" } }, "node_modules/bare-stream": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.1.tgz", - "integrity": "sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.4.tgz", + "integrity": "sha512-G6i3A74FjNq4nVrrSTUz5h3vgXzBJnjmWAVlBWaZETkgu+LgKd7AiyOml3EDJY1AHlIbBHKDXE+TUT53Ff8OaA==", "license": "Apache-2.0", "optional": true, "dependencies": { "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } } }, "node_modules/base64-js": { @@ -2276,9 +2423,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -2445,9 +2592,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "version": "1.0.30001695", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", + "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", "dev": true, "funding": [ { @@ -2531,27 +2678,18 @@ } }, "node_modules/chromium-bidi": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz", - "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.12.0.tgz", + "integrity": "sha512-xzXveJmX826GGq1MeE5okD8XxaDT8172CXByhFJ687eY65rbjOIebdbUuQh+jXKaNyGKI14Veb3KjLLmSueaxA==", "license": "Apache-2.0", "dependencies": { "mitt": "3.0.1", - "zod": "3.23.8" + "zod": "3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, - "node_modules/chromium-bidi/node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -2890,12 +3028,13 @@ } }, "node_modules/cssstyle": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", - "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.2.1.tgz", + "integrity": "sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==", "license": "MIT", "dependencies": { - "rrweb-cssom": "^0.7.1" + "@asamuzakjp/css-color": "^2.8.2", + "rrweb-cssom": "^0.8.0" }, "engines": { "node": ">=18" @@ -3121,9 +3260,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1367902", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", - "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", + "version": "0.0.1380148", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1380148.tgz", + "integrity": "sha512-1CJABgqLxbYxVI+uJY/UDUHJtJ0KZTSjNYJYKqd9FRoXT33WDakDHNxRapMEgzeJ/C3rcs01+avshMnPmKQbvA==", "license": "BSD-3-Clause" }, "node_modules/diff-sequences": { @@ -3188,9 +3327,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.76", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz", - "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==", + "version": "1.5.84", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.84.tgz", + "integrity": "sha512-I+DQ8xgafao9Ha6y0qjHHvpZ9OfyA1qKlkHkjywxzniORU2awxyz7f/iVJcULmrF2yrM3nHQf+iDjJtbbexd/g==", "dev": true, "license": "ISC" }, @@ -3360,9 +3499,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3522,13 +3661,13 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz", + "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==", "dev": true, "license": "MIT", "bin": { - "eslint-config-prettier": "bin/cli.js" + "eslint-config-prettier": "build/bin/cli.js" }, "peerDependencies": { "eslint": ">=7.0.0" @@ -3642,9 +3781,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", - "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", + "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", "dev": true, "license": "MIT", "dependencies": { @@ -5932,22 +6071,22 @@ "license": "MIT" }, "node_modules/jsdom": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", - "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", + "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", "license": "MIT", "dependencies": { - "cssstyle": "^4.1.0", + "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.4.3", - "form-data": "^4.0.0", + "form-data": "^4.0.1", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", + "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.12", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.0.0", @@ -5955,7 +6094,7 @@ "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0", + "whatwg-url": "^14.1.0", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, @@ -5963,7 +6102,7 @@ "node": ">=18" }, "peerDependencies": { - "canvas": "^2.11.2" + "canvas": "^3.0.0" }, "peerDependenciesMeta": { "canvas": { @@ -6087,9 +6226,9 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.3.0.tgz", - "integrity": "sha512-vHFahytLoF2enJklgtOtCtIjZrKD/LoxlaUusd5nh7dWv/dkKQJY74ndFSzxCdv7g0ueGg1ORgTSt4Y9LPZn9A==", + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.4.1.tgz", + "integrity": "sha512-P8yJuVRyLrm5KxCtFx+gjI5Bil+wO7wnTl7C3bXhvtTaAFGirzeB24++D0wGoUwxrUKecNiehemgCob9YL39NA==", "dev": true, "license": "MIT", "dependencies": { @@ -7424,17 +7563,17 @@ } }, "node_modules/puppeteer": { - "version": "23.11.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.11.1.tgz", - "integrity": "sha512-53uIX3KR5en8l7Vd8n5DUv90Ae9QDQsyIthaUFVzwV6yU750RjqRznEtNMBT20VthqAdemnJN+hxVdmMHKt7Zw==", + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.1.0.tgz", + "integrity": "sha512-F+3yKILaosLToT7amR7LIkTKkKMR0EGQPjFBch+MtgS8vRPS+4cPnLJuXDVTfCj2NqfrCnShtOr7yD+9dEgHRQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.6.1", - "chromium-bidi": "0.11.0", + "@puppeteer/browsers": "2.7.0", + "chromium-bidi": "0.12.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1367902", - "puppeteer-core": "23.11.1", + "devtools-protocol": "0.0.1380148", + "puppeteer-core": "24.1.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -7445,15 +7584,15 @@ } }, "node_modules/puppeteer-core": { - "version": "23.11.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.11.1.tgz", - "integrity": "sha512-3HZ2/7hdDKZvZQ7dhhITOUg4/wOrDRjyK2ZBllRB0ZCOi9u0cwq1ACHDjBB+nX+7+kltHjQvBRdeY7+W0T+7Gg==", + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.1.0.tgz", + "integrity": "sha512-ReefWoQgqdyl67uWEBy/TMZ4mAB7hP0JB5HIxSE8B1ot/4ningX1gmzHCOSNfMbTiS/VJHCvaZAe3oJTXph7yw==", "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.6.1", + "@puppeteer/browsers": "2.7.0", "chromium-bidi": "0.11.0", "debug": "^4.4.0", - "devtools-protocol": "0.0.1367902", + "devtools-protocol": "0.0.1380148", "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, @@ -7461,6 +7600,28 @@ "node": ">=18" } }, + "node_modules/puppeteer-core/node_modules/chromium-bidi": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz", + "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==", + "license": "Apache-2.0", + "dependencies": { + "mitt": "3.0.1", + "zod": "3.23.8" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/puppeteer-core/node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -7793,9 +7954,9 @@ } }, "node_modules/rollup": { - "version": "4.29.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.2.tgz", - "integrity": "sha512-tJXpsEkzsEzyAKIaB3qv3IuvTVcTN7qBw1jL4SPPXM3vzDrJgiLGFY6+HodgFaUHAJ2RYJ94zV5MKRJCoQzQeA==", + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.31.0.tgz", + "integrity": "sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==", "dev": true, "license": "MIT", "dependencies": { @@ -7809,32 +7970,32 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.29.2", - "@rollup/rollup-android-arm64": "4.29.2", - "@rollup/rollup-darwin-arm64": "4.29.2", - "@rollup/rollup-darwin-x64": "4.29.2", - "@rollup/rollup-freebsd-arm64": "4.29.2", - "@rollup/rollup-freebsd-x64": "4.29.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.29.2", - "@rollup/rollup-linux-arm-musleabihf": "4.29.2", - "@rollup/rollup-linux-arm64-gnu": "4.29.2", - "@rollup/rollup-linux-arm64-musl": "4.29.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.29.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.29.2", - "@rollup/rollup-linux-riscv64-gnu": "4.29.2", - "@rollup/rollup-linux-s390x-gnu": "4.29.2", - "@rollup/rollup-linux-x64-gnu": "4.29.2", - "@rollup/rollup-linux-x64-musl": "4.29.2", - "@rollup/rollup-win32-arm64-msvc": "4.29.2", - "@rollup/rollup-win32-ia32-msvc": "4.29.2", - "@rollup/rollup-win32-x64-msvc": "4.29.2", + "@rollup/rollup-android-arm-eabi": "4.31.0", + "@rollup/rollup-android-arm64": "4.31.0", + "@rollup/rollup-darwin-arm64": "4.31.0", + "@rollup/rollup-darwin-x64": "4.31.0", + "@rollup/rollup-freebsd-arm64": "4.31.0", + "@rollup/rollup-freebsd-x64": "4.31.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.31.0", + "@rollup/rollup-linux-arm-musleabihf": "4.31.0", + "@rollup/rollup-linux-arm64-gnu": "4.31.0", + "@rollup/rollup-linux-arm64-musl": "4.31.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.31.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.31.0", + "@rollup/rollup-linux-riscv64-gnu": "4.31.0", + "@rollup/rollup-linux-s390x-gnu": "4.31.0", + "@rollup/rollup-linux-x64-gnu": "4.31.0", + "@rollup/rollup-linux-x64-musl": "4.31.0", + "@rollup/rollup-win32-arm64-msvc": "4.31.0", + "@rollup/rollup-win32-ia32-msvc": "4.31.0", + "@rollup/rollup-win32-x64-msvc": "4.31.0", "fsevents": "~2.3.2" } }, "node_modules/rrweb-cssom": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", - "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", "license": "MIT" }, "node_modules/run-parallel": { @@ -8646,17 +8807,17 @@ } }, "node_modules/tar-fs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", - "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.8.tgz", + "integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==", "license": "MIT", "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" } }, "node_modules/tar-stream": { @@ -8754,21 +8915,21 @@ "license": "MIT" }, "node_modules/tldts": { - "version": "6.1.70", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.70.tgz", - "integrity": "sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==", + "version": "6.1.73", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.73.tgz", + "integrity": "sha512-/h4bVmuEMm57c2uCiAf1Q9mlQk7cA22m+1Bu0K92vUUtTVT9D4mOFWD9r4WQuTULcG9eeZtNKhLl0Il1LdKGog==", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.70" + "tldts-core": "^6.1.73" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.70", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz", - "integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==", + "version": "6.1.73", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.73.tgz", + "integrity": "sha512-k1g5eX87vxu3g//6XMn62y4qjayu4cYby/PF7Ksnh4F4uUK1Z1ze/mJ4a+y5OjdJ+cXRp+YTInZhH+FGdUWy1w==", "license": "MIT" }, "node_modules/tmpl": { @@ -8811,9 +8972,9 @@ } }, "node_modules/tough-cookie": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", - "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz", + "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==", "license": "BSD-3-Clause", "dependencies": { "tldts": "^6.1.32" @@ -9068,9 +9229,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -9089,7 +9250,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -9124,9 +9285,9 @@ } }, "node_modules/uuid": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.4.tgz", - "integrity": "sha512-IzL6VtTTYcAhA/oghbFJ1Dkmqev+FpQWnCBaKq/gUluLxliWvO8DPFWfIviRmYbtaavtSQe4WBL++rFjdcGWEg==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" diff --git a/package.json b/package.json index 3721eab1..518930a1 100644 --- a/package.json +++ b/package.json @@ -50,26 +50,26 @@ "express": "^4.21.2", "express-rate-limit": "^7.5.0", "https-proxy-agent": "^7.0.6", - "jsdom": "^25.0.1", + "jsdom": "^26.0.0", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^23.11.1", + "puppeteer": "^24.1.0", "tarn": "^3.0.2", - "uuid": "^11.0.4", + "uuid": "^11.0.5", "zod": "^3.24.1" }, "devDependencies": { "@jest/globals": "^29.7.0", "@rollup/plugin-terser": "^0.4.4", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", + "eslint-config-prettier": "^10.0.1", "eslint-plugin-import": "^2.31.0", - "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-prettier": "^5.2.3", "husky": "^9.1.7", "jest": "^29.7.0", - "lint-staged": "^15.3.0", + "lint-staged": "^15.4.1", "nodemon": "^3.1.9", "prettier": "^3.4.2", - "rollup": "^4.29.2" + "rollup": "^4.31.0" } } From c51338328da2b4e6d9995ecce0d0a1a94134e998 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:18:18 +0100 Subject: [PATCH 076/102] Updated API functions descriptions. --- README.md | 116 +++++++++++++++++++++--------------------------------- 1 file changed, 44 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index f904827f..0b8e6e24 100644 --- a/README.md +++ b/README.md @@ -667,7 +667,7 @@ const customOptions = { // Logic must be triggered in an asynchronous function (async () => { // Set options with user configuration - const options = exporter.setOptions(customOptions); + const options = exporter.setGlobalOptions(customOptions); // Must initialize exporting before being able to export charts await exporter.initExport(options); @@ -683,15 +683,15 @@ const customOptions = { })(); ``` -In order for everything to work as it is supposed to, the `setOptions` function should be called before running the `initExport` and any export-related function (`startExport`, `singleExport`, or `batchExport`) to correctly initialize all option values. +In order for everything to work as it is supposed to, the `setGlobalOptions` function should be called before running the `initExport` and any export-related function (`startExport`, `singleExport`, or `batchExport`) to correctly initialize all option values. ## Options Handling When starting a server or performing single or batch exports via the CLI, server's global options are initialized from the `defaultConfig` object located in `./lib/schemas/config.js`. These options are then extended with values from all other sources mentioned in the [Configuration](#configuration) section. -For `Node.js Module` usage, the process differs slightly. Some API functions must be called manually. By default, global options are initialized solely from the `defaultConfig` object at the start, similar to the server and CLI scenarios. However, to include options from other sources (as outlined in the [Configuration](#configuration) section), you need to explicitly call the `setOptions()` function. If `setOptions()` is not used, the system will rely on the default values from the defaultConfig object. +For `Node.js Module` usage, the process differs slightly. Some API functions must be called manually. By default, global options are initialized solely from the `defaultConfig` object at the start, similar to the server and CLI scenarios. However, to include options from other sources (as outlined in the [Configuration](#configuration) section), you need to explicitly call the `setGlobalOptions()` function. If `setGlobalOptions()` is not used, the system will rely on the default values from the defaultConfig object. -The `setOptions()` function allows you to either extend or copy global options. +The `setGlobalOptions()` function allows you to either extend or copy global options. - `Extend`: Adds new options to the global configuration while keeping the original options intact. - `Copy`: Merges new options into a separate set, leaving global options unaffected. @@ -708,13 +708,11 @@ This package supports both CommonJS and ES modules. **highcharts-export-server module** -- `server`: The server instance which offers the following functions: +- `async function startServer(serverOptions)`: Starts an HTTP and/or HTTPS server based on the provided configuration. The `serverOptions` object contains server-related properties (refer to the `server` section in the `lib/schemas/config.js` file for details). -- `async function startServer(serverOptions = getOptions().server)`: Starts HTTP or/and HTTPS server based on the provided configuration. The `serverOptions` object contains all server related properties (see the `server` section in the `lib/schemas/config.js` file for a reference). + - `@param {Object} serverOptions` - The configuration object containing `server` options. This object may include a partial or complete set of the `server` options. If the options are partial, missing values will default to the current global configuration. - - `@param {Object} [serverOptions=getOptions().server]` - Object containing `server` options. The default value is the global server options of the export server instance. - - - `@returns {Promise}` A Promise that resolves to ending the function execution when the server should not be enabled or when no valid Express app is found. + - `@returns {Promise}` A Promise that resolves when the server is either not enabled or no valid Express app is found, signaling the end of the function's execution. - `@throws {ExportError}` Throws an `ExportError` if the server cannot be configured and started. @@ -734,7 +732,7 @@ This package supports both CommonJS and ES modules. - `function enableRateLimiting(rateLimitingOptions)`: Enable rate limiting for the server. - - `@param {Object} rateLimitingOptions` - Object containing `rateLimiting` options. + - `@param {Object} rateLimitingOptions` - The configuration object containing `rateLimiting` options. This object may include a partial or complete set of the `rateLimiting` options. If the options are partial, missing values will default to the current global configuration. - `function use(path, ...middlewares)`: Apply middleware(s) to a specific path. @@ -751,42 +749,26 @@ This package supports both CommonJS and ES modules. - `@param {string} path` - The path to which the middleware(s) should be applied. - `@param {...Function} middlewares` - The middleware function(s) to be applied. -- `async function startServer(serverOptions = getOptions().server)`: Starts HTTP or/and HTTPS server based on the provided configuration. The `serverOptions` object contains all server related properties (see the `server` section in the `lib/schemas/config.js` file for a reference). - - - `@param {Object} [serverOptions=getOptions().server]` - Object containing `server` options. The default value is the global server options of the export server instance. - - - `@returns {Promise}` A Promise that resolves to ending the function execution when the server should not be enabled or when no valid Express app is found. - - - `@throws {ExportError}` Throws an `ExportError` if the server cannot be configured and started. +- `function getOptions(getInstance = true)`: Retrieves a reference to the options object. Depending on the `getInstance` parameter, it returns either the global options or the instance-specific options object. -- `function getOptions(getReference = true)`: Gets the reference to the global options of the server instance object or its copy. + - `@param {boolean} [getInstance=true]` - Optional parameter that decides whether to return the instance-specific options (when `true`) or the global options (when `false`). The default value is `true`. - - `@param {boolean} [getReference=true]` - Optional parameter to decide whether to return the reference to the global options of the server instance object or return a copy of it. The default value is true. + - `@returns {Object}` A reference to either the global options or the instance-specific options, based on the `getInstance` parameter. - - `@returns {Object}` The reference to the global options of the server instance object or its copy. - -- `function setOptions(customOptions = {}, cliArgs = [], modifyGlobal = false)`: Sets the global options of the export server instance, keeping the principle of the options load priority from all available sources. It accepts optional `customOptions` object and `cliArgs` array with arguments from the CLI. These options will be validated and applied if provided. +- `function setGlobalOptions(customOptions = {}, cliArgs = [])`: Sets the global options of the export server, keeping the principle of the options load priority from all available sources. It accepts optional `customOptions` object and `cliArgs` array with arguments from the CLI. These options will be validated and applied if provided. The priority order of setting values is: 1. Options from the `lib/schemas/config.js` file (default values). 2. Options from a custom JSON file (loaded by the `loadConfig` option). 3. Options from the environment variables (the `.env` file). - 4. Options from the first parameter (by default an empty object). - 5. Options from the CLI. + 4. Options from the command line interface (CLI). + 5. Options from the first parameter (the `customOptions` is by default an empty object). - `@param {Object} [customOptions={}]` - Optional custom options for additional configuration. The default value is an empty object. - `@param {Array.} [cliArgs=[]]` - Optional command line arguments for additional configuration. The default value is an empty array. - - `@param {boolean} [modifyGlobal=false]` - Optional parameter to decide whether to update and return the reference to the global options of the server instance object or return a copy of it. The default value is false. - - - `@returns {Object}` The updated general options object, reflecting the merged configuration from all available sources. - -- `function mergeOptions(originalOptions, newOptions)`: Merges two sets of configuration options, considering absolute properties. - - - `@param {Object} originalOptions` - Original configuration options. - - `@param {Object} newOptions` - New configuration options to be merged. - - `@returns {Object}` Merged configuration options. + - `@returns {Object}` The updated global options object, reflecting the merged configuration from all available sources. - `function mapToNewOptions(oldOptions)`: Maps old-structured configuration options (PhantomJS) to a new format (Puppeteer). This function converts flat, old-structured options into a new, nested configuration format based on a predefined mapping (`nestedProps`). The new format is used for Puppeteer, while the old format was used for PhantomJS. @@ -794,68 +776,62 @@ This package supports both CommonJS and ES modules. - `@returns {Object}` A new object containing options structured according to the mapping defined in `nestedProps` or an empty object if the provided `oldOptions` is not a correct object. -- `async function initExport(customOptions)`: Initializes the export process. Tasks such as configuring logging, checking the cache and sources, and initializing the resource pool occur during this stage. This function must be called before attempting to export charts or set up a server. +- `async function initExport(initOptions = {})`: Initializes the export process. Tasks such as configuring logging, checking the cache and sources, and initializing the resource pool occur during this stage. - - `@param {Object} customOptions` - The `customOptions` object, which may be a partial or complete set of options. If the provided options are partial, missing values will be merged with the default general options, retrieved using the `getOptions` function. + This function must be called before attempting to export charts or set up a server. -- `async function singleExport(options)`: Starts a single export process based on the specified options and saves the image in the provided outfile. + - `@param {Object} [initOptions={}]` - The `initOptions` object, which may be a partial or complete set of options. If the options are partial, missing values will default to the current global configuration. The default value is an empty object. - - `@param {Object} options` - The `options` object, which may be a partial or complete set of options. It must contain at least one of the following properties: `infile`, `instr`, `options`, or `svg` to generate a valid image. +- `async function singleExport(options)`: Starts a single export process based on the specified options and saves the resulting image to the provided output file. + + - `@param {Object} options` - The `options` object, which should include settings from the `export` and `customLogic` sections. It can be a partial or complete set of options from these sections. The object must contain at least one of the following `export` properties: `infile`, `instr`, `options`, or `svg` to generate a valid image. - `@returns {Promise}` A Promise that resolves once the single export process is completed. - `@throws {ExportError}` Throws an `ExportError` if an error occurs during the single export process. -- `async function batchExport(options)`: Starts a batch export process for multiple charts based on the information in the `batch` option. The `batch` is a string in the following format: "infile1.json=outfile1.png;infile2.json=outfile2.png;...". Results are saved in provided outfiles. +- `async function batchExport(options)`: Starts a batch export process for multiple charts based on information provided in the `batch` option. The `batch` is a string in the following format: "infile1.json=outfile1.png;infile2.json=outfile2.png;...". Results are saved to the specified output files. - - `@param {Object} options` - The `options` object, which may be a partial or complete set of options. It must contain the `batch` option to generate valid images. + - `@param {Object} options` - The `options` object, which should include settings from the `export` and `customLogic` sections. It can be a partial or complete set of options from these sections. It must contain the `batch` option from the `export` section to generate valid images. - `@returns {Promise}` A Promise that resolves once the batch export processes are completed. - `@throws {ExportError}` Throws an `ExportError` if an error occurs during any of the batch export process. -- `async function startExport(customOptions, endCallback)`: Starts an export process. The `customOptions` parameter is an object that may be partial or complete set of options. The `endCallback` is called when the export is completed, with the `error` object as the first argument and the `data` object as the second, which contains the Base64 representation of the chart in the `result` property and the full set of export options in the `options` property. +- `async function startExport(exportingOptions, endCallback)`: Starts an export process. The `exportingOptions` parameter is an object that should include settings from the `export` and `customLogic` sections. It can be a partial or complete set of options from these sections. If partial options are provided, missing values will be merged with the current global options. + + The `endCallback` function is invoked upon the completion of the export, either successfully or with an error. The `error` object is provided as the first argument, and the `data` object is the second, containing the Base64 representation of the chart in the `result` property and the complete set of options in the `options` property. - - `@param {Object} customOptions` - The `customOptions` object, which may be a partial or complete set of options. If the provided options are partial, missing values will be merged with the default general options, retrieved using the `getOptions` function. - - `@param {Function} endCallback` - The callback function to be invoked upon finalizing work or upon error occurance of the exporting process. The first argument is `error` object and the `data` object is the second, that contains the Base64 representation of the chart in the `result` property and the full set of export options in the `options` property. + - `@param {Object} exportingOptions` - The `exportingOptions` object, which should include settings from the `export` and `customLogic` sections. It can be a partial or complete set of options from these sections. If the provided options are partial, missing values will be merged with the current global options. + - `@param {Function} endCallback` - The callback function to be invoked upon finalizing the export process or upon encountering an error. The first argument is the `error` object, and the second argument is the `data` object, which includes the Base64 representation of the chart in the `result` property and the full set of options in the `options` property. - `@returns {Promise}` This function does not return a value directly. Instead, it communicates results via the `endCallback`. - `@throws {ExportError}` Throws an `ExportError` if there is a problem with processing input of any type. The error is passed into the `endCallback` function and processed there. -- `async function checkAndUpdateCache(highchartsOptions, serverProxyOptions)`: Checks the cache for Highcharts dependencies, updates the cache if needed, and loads the sources. - - - `@param {Object} highchartsOptions` - Object containing `highcharts` options. - - `@param {Object} serverProxyOptions` - Object containing `server.proxy` options. - -- `async function initPool(poolOptions = getOptions().pool, puppeteerArgs = [])`: Initializes the export pool with the provided configuration, creating a browser instance and setting up worker resources. +- `async function killPool()`: Terminates all workers in the pool, destroys the pool, and closes the browser instance. - - `@param {Object} [poolOptions=getOptions().pool]` - Object containing `pool` options. The default value is the global pool options of the export server instance. - - `@param {Array.} [puppeteerArgs=[]]` - Additional arguments for Puppeteer launch. The default value is an empty array. + - `@returns {Promise}` A Promise that resolves once all workers are terminated, the pool is destroyed, and the browser is successfully closed. - - `@returns {Promise}` A Promise that resolves to ending the function execution when an already initialized pool of resources is found. +- `async function shutdownCleanUp(exitCode = 0)`: Performs cleanup operations to ensure a graceful shutdown of the process. This includes clearing all registered timeouts/intervals, closing active servers, terminating resources (pages) of the pool, pool itself, and closing the browser. - - `@throws {ExportError}` Throws an `ExportError` if could not create the pool of workers. + - `@param {number} [exitCode=0]` - The exit code to use with `process.exit()`. The default value is `0`. -- `async function killPool()`: Kills all workers in the pool, destroys the pool, and closes the browser instance. +- `function log(...args)`: Logs a message with a specified log level. Accepts a variable number of arguments. The arguments after the `level` are passed to `console.log` and/or used to construct and append messages to a log file. - - `@returns {Promise}` A Promise that resolves after the workers are killed, the pool is destroyed, and the browser is closed. + - `@param {...unknown} args` - An array of arguments where the first is the log level and the remaining are strings used to build the log message. -- `function log(...args)`: Logs a message. Accepts a variable amount of arguments. Arguments after the `level` will be passed directly to `console.log`, and/or will be joined and appended to the log file. + - `@returns {void}` Exits the function execution if attempting to log at a level higher than allowed. - - `@param {...unknown} args` - An array of arguments where the first is the log level and the rest are strings to build a message with. - - - `@returns {void}` Ends the function execution when attempting to log information at a higher level than what is allowed. - -- `function logWithStack(newLevel, error, customMessage)`: Logs an error message with its stack trace. Optionally, a custom message can be provided. +- `function logWithStack(newLevel, error, customMessage)`: Logs an error message along with its stack trace. Optionally, a custom message can be provided. - `@param {number} newLevel` - The log level. - - `@param {Error} error` - The error object. - - `@param {string} customMessage` - An optional custom message to be logged along with the error. + - `@param {Error} error` - The error object containing the stack trace. + - `@param {string} customMessage` - An optional custom message to be included in the log alongside the error. - - `@returns {void}` Ends the function execution when attempting to log information at a higher level than what is allowed. + - `@returns {void}` Exits the function execution if attempting to log at a level higher than allowed. -- `function setLogLevel(level)`: Sets the log level to the specified value. Log levels are (0 = no logging, 1 = error, 2 = warning, 3 = notice, 4 = verbose, or 5 = benchmark). +- `function setLogLevel(level)`: Sets the log level to the specified value. Log levels are (`0` = no logging, `1` = error, `2` = warning, `3` = notice, `4` = verbose, or `5` = benchmark). - `@param {number} level` - The log level to be set. @@ -863,15 +839,11 @@ This package supports both CommonJS and ES modules. - `@param {boolean} toConsole` - The flag for setting the logging to the console. -- `function enableFileLogging(dest, file, toFile)`: Enables file logging with the specified destination and log file. - - - `@param {string} dest` - The destination path for the log file. - - `@param {string} file` - The log file name. - - `@param {boolean} toFile` - The flag for setting the logging to a file. - -- `async function shutdownCleanUp(exitCode)`: Cleans up function to trigger before ending process for the graceful shutdown. +- `function enableFileLogging(dest, file, toFile)`: Enables file logging with the specified destination and log file name. - - `@param {number} exitCode` - An exit code for the `process.exit()` function. + - `@param {string} dest` - The destination path where the log file should be saved. + - `@param {string} file` - The name of the log file. + - `@param {boolean} toFile` - A flag indicating whether logging should be directed to a file. # Examples From b663fe7dcb3ebee4b01db8f0dab271962df36cd4 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 21 Jan 2025 01:25:19 +0100 Subject: [PATCH 077/102] Built scripts. --- dist/index.cjs | 4 ++-- dist/index.esm.js | 2 +- dist/index.esm.js.map | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/index.cjs b/dist/index.cjs index 40d941ee..6dc0dbd7 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),promises=require("fs/promises"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?e:path.join(__dirname$1,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t.message,{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=e}function enableFileLogging(e,t,o){logging.toFile=o,o&&(logging.dest=e,logging.file=t)}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initGlobalOptions(defaultConfig);function getOptions(e=!0){return e?globalOptions:deepCopy(globalOptions)}function setOptions(e={},t=[],o=!1){let r={},n={};t.length&&(r=_loadConfigFile(t),n=_pairArgumentValue(nestedProps,t));const i=getOptions(o);return _updateOptions(defaultConfig,i,r,e,n),i}function mergeOptions(e,t){if(isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?mergeOptions(e[o],r):void 0!==r?r:e[o];return e}function mapToNewOptions(e){const t={};if("[object Object]"===Object.prototype.toString.call(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initGlobalOptions(e){const t={};for(const[o,r]of Object.entries(e))t[o]=Object.prototype.hasOwnProperty.call(r,"value")?r.value:_initGlobalOptions(r);return t}function _updateOptions(e,t,o,r,n){Object.keys(e).forEach((i=>{const s=e[i],a=o&&o[i],l=r&&r[i],c=n&&n[i];if(void 0===s.value)_updateOptions(s,t[i],a,l,c);else{null!=a&&(t[i]=a);const e=envs[s.envLink];s.envLink in envs&&null!=e&&(t[i]=e),null!=l&&(t[i]=l),null!=c&&(t[i]=c)}}))}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _loadConfigFile(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,""))),o=t>-1&&e[t+1];if(o)try{return JSON.parse(fs.readFileSync(getAbsolutePath(o)))}catch(e){logWithStack(2,e,`[config] Unable to load the configuration from the ${o} file.`)}return{}}function _pairArgumentValue(e,t){const o={};for(let r=0;r{if(i.length-1===s){const i=t[++r];i||log(2,`[config] Missing value for the CLI '--${n}' argument. Using the default value.`),e[o]=i||null}else void 0===e[o]&&(e[o]={});return e[o]}),o)}return o}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n));if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=getOptions();t.highcharts.version=e,await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e){const{getOptions:t,merge:o,setOptions:r,wrap:n}=Highcharts;Highcharts.setOptionsObj=o(!1,{},t()),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=o(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,o){e.apply(this,[t,o])}));const i={chart:{animation:!1,height:e.export.height,width:e.export.width},exporting:{enabled:!1}},s=new Function(`return ${e.export.instr}`)(),a=new Function(`return ${e.export.themeOptions}`)(),l=new Function(`return ${e.export.globalOptions}`)(),c=o(!1,a,s,i),p=e.customLogic.callback?new Function(`return ${e.customLogic.callback}`)():null;e.customLogic.customCode&&new Function("options",e.customLogic.customCode)(s),l&&r(l),Highcharts[e.export.constr]("container",c,p);const u=t();for(const e in u)"function"!=typeof u[e]&&delete u[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:path.join(__dirname$1,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t){const o=[];try{const r=t.export;let n=!1;if(r.svg){if(log(4,"[export] Treating as SVG input."),"svg"===r.type)return r.svg;n=!0,await _setAsSvg(e,r.svg)}else log(4,"[export] Treating as JSON config."),await _setAsOptions(e,t);o.push(...await addPageResources(e,t.customLogic));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(r.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||r.height)),c=Math.abs(Math.ceil(i.chartWidth||r.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(r.scale)}),r.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,r.type,{width:c,height:l,x:s,y:a},r.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,r.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${r.type}.`,400)}return await clearPageResources(e,o),p}catch(t){return await clearPageResources(e,o),t}}async function _setAsSvg(e,t){await e.setContent(svgTemplate(t),{waitUntil:"domcontentloaded"})}async function _setAsOptions(e,t){await e.evaluate(createChart,t)}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e=getOptions().pool,t=[]){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,getOptions().pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,e._requestId?`[benchmark] Request [${e._requestId}] - `:"[benchmark] ",`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+"Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.").setError(i):new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+`Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,e._requestId?`[benchmark] Request [${e._requestId}] - `:"[benchmark] ",`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport(e,(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({...e,export:{...e.export,infile:o[0],outfile:o[1]}},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{log(4,"[chart] Starting the exporting process.");const o=mergeOptions(getOptions(!1),e),r=o.export;if(null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t=getOptions().server.rateLimiting){try{if(t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,max:r.max,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>!1!==r.skipKey&&!1!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.max} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}class HttpError extends ExportError{constructor(e,t){super(e,t)}setStatus(e){return this.statusCode=e,this}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new HttpError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4().replace(/-/g,"");if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new HttpError("[validation] The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.",400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new HttpError("[validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new HttpError("[validation] 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);return e.validatedOptions={_requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${fixType(t.type)}`,type:fixType(t.type,t.outfile),constr:fixConstr(t.constr),b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n._requestId;log(4,`[export] Got an incoming HTTP request with ID ${i}.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new HttpError("[export] Unexpected return of the export result from the chart generation. Please check your request data.",400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"))),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new HttpError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new HttpError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new HttpError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new HttpError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e=getOptions().server){try{if(!e.enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const t=1024*e.uploadLimit*1024,o=multer.memoryStorage(),r=multer({storage:o,limits:{fieldSize:t}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:t})),app.use(express.urlencoded({extended:!0,limit:t})),app.use(r.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),healthRoutes(app),exportRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){rateLimitingMiddleware(app,e)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=mergeOptions(getOptions(!1),e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={server:server,startServer:startServer,getOptions:getOptions,setOptions:setOptions,mergeOptions:mergeOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,checkAndUpdateCache:checkAndUpdateCache,initPool:initPool,killPool:killPool,log:log,logWithStack:logWithStack,setLogLevel:setLogLevel,enableConsoleLogging:enableConsoleLogging,enableFileLogging:enableFileLogging,shutdownCleanUp:shutdownCleanUp};exports.default=index,exports.initExport=initExport; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL2Vycm9ycy9IdHRwRXJyb3IuanMiLCIuLi9saWIvc2VydmVyL21pZGRsZXdhcmVzL3ZhbGlkYXRpb24uanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy9leHBvcnQuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy9oZWFsdGguanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy91aS5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL3ZlcnNpb25DaGFuZ2UuanMiLCIuLi9saWIvc2VydmVyL3NlcnZlci5qcyIsIi4uL2xpYi9yZXNvdXJjZVJlbGVhc2UuanMiLCIuLi9saWIvaW5kZXguanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdXRpbGl0eSBtb2R1bGUgcHJvdmlkZXNcclxuICogYSBjb21wcmVoZW5zaXZlIHNldCBvZiBoZWxwZXIgZnVuY3Rpb25zIGFuZCBjb25zdGFudHMgZGVzaWduZWQgdG8gc3RyZWFtbGluZVxyXG4gKiBhbmQgZW5oYW5jZSB2YXJpb3VzIG9wZXJhdGlvbnMgcmVxdWlyZWQgZm9yIEhpZ2hjaGFydHMgZXhwb3J0IHRhc2tzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgaXNBYnNvbHV0ZSwgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBkaXJlY3RvcnkgcGF0aFxyXG5leHBvcnQgY29uc3QgX19kaXJuYW1lID0gZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuLi8uJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFuZCBzdGFuZGFyZGl6ZXMgdGV4dCBieSByZXBsYWNpbmcgbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZVxyXG4gKiBjaGFyYWN0ZXJzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIHRyaW1taW5nIGFueSBsZWFkaW5nIG9yIHRyYWlsaW5nXHJcbiAqIHdoaXRlc3BhY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhclRleHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyB0aGUgJy9cXHNcXHMrL2cnIFJlZ0V4cC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtyZXBsYWNlcj0nICddIC0gVGhlIHN0cmluZyB1c2VkIHRvIHJlcGxhY2UgbXVsdGlwbGVcclxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgJyAnIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyVGV4dCh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpIHtcclxuICByZXR1cm4gdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBkZWVwQ29weVxyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8QXJyYXkpfSBvYmpBcnIgLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fEFycmF5KX0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDb3B5KG9iakFycikge1xyXG4gIC8vIElmIHRoZSBgb2JqQXJyYCBpcyBudWxsIG9yIG5vdCBvZiB0aGUgYG9iamVjdGAgdHlwZSwgcmV0dXJuIGl0XHJcbiAgaWYgKG9iakFyciA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqQXJyICE9PSAnb2JqZWN0Jykge1xyXG4gICAgcmV0dXJuIG9iakFycjtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZWl0aGVyIGEgbmV3IGFycmF5IG9yIGEgbmV3IG9iamVjdFxyXG4gIGNvbnN0IG9iakFyckNvcHkgPSBBcnJheS5pc0FycmF5KG9iakFycikgPyBbXSA6IHt9O1xyXG5cclxuICAvLyBSZWN1cnNpdmVseSBjb3B5IGVhY2ggcHJvcGVydHlcclxuICBmb3IgKGNvbnN0IGtleSBpbiBvYmpBcnIpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqQXJyLCBrZXkpKSB7XHJcbiAgICAgIG9iakFyckNvcHlba2V5XSA9IGRlZXBDb3B5KG9iakFycltrZXldKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY29waWVkIG9iamVjdFxyXG4gIHJldHVybiBvYmpBcnJDb3B5O1xyXG59XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZXhwQmFja29mZlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBmdW5jdGlvbiB0byBiZSByZXRyaWVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgMC5cclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0XHJcbiAqIG9mIHRoZSBmdW5jdGlvbiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwQmFja29mZihmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlcGVhdCwgdGhyb3cgYW4gZXJyb3JcclxuICAgIGlmICgrK2F0dGVtcHQgPj0gTUFYX0JBQ0tPRkZfQVRURU1QVFMpIHtcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxyXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc3BvbnNlKSA9PiBzZXRUaW1lb3V0KHJlc3BvbnNlLCBkZWxheUluTXMpKTtcclxuXHJcbiAgICAvLy8gVE8gRE86IENvcnJlY3RcclxuICAgIC8vIC8vIEluZm9ybWF0aW9uIGFib3V0IHRoZSByZXNvdXJjZSB0aW1lb3V0XHJcbiAgICAvLyBsb2coXHJcbiAgICAvLyAgIDMsXHJcbiAgICAvLyAgIGBbdXRpbHNdIFdhaXRlZCAke2RlbGF5SW5Nc31tcyB1bnRpbCBuZXh0IGNhbGwgZm9yIHRoZSByZXNvdXJjZSBvZiBJRDogJHthcmdzWzBdfS5gXHJcbiAgICAvLyApO1xyXG5cclxuICAgIC8vIFRyeSBhZ2FpblxyXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkanVzdHMgdGhlIGNvbnN0cnVjdG9yIG5hbWUgYnkgdHJhbnNmb3JtaW5nIGFuZCBub3JtYWxpemluZyBpdCBiYXNlZFxyXG4gKiBvbiBjb21tb24gY2hhcnQgdHlwZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhDb25zdHJcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnN0ciAtIFRoZSBvcmlnaW5hbCBjb25zdHJ1Y3RvciBuYW1lIHRvIGJlIGZpeGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGNvbnN0cnVjdG9yIG5hbWUsIG9yICdjaGFydCcgaWYgdGhlIGlucHV0XHJcbiAqIGlzIG5vdCByZWNvZ25pemVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeENvbnN0cihjb25zdHIpIHtcclxuICB0cnkge1xyXG4gICAgLy8gRml4IHRoZSBjb25zdHJ1Y3RvciBieSBsb3dlcmluZyBjYXNpbmdcclxuICAgIGNvbnN0IGZpeGVkQ29uc3RyID0gYCR7Y29uc3RyLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnY2hhcnQnLCAnJyl9Q2hhcnRgO1xyXG5cclxuICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgcmVzdWx0IGlzIGp1c3QgJ0NoYXJ0J1xyXG4gICAgaWYgKGZpeGVkQ29uc3RyID09PSAnQ2hhcnQnKSB7XHJcbiAgICAgIGZpeGVkQ29uc3RyLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IsIG90aGVyd2lzZSBkZWZhdWx0IHRvICdjaGFydCdcclxuICAgIHJldHVybiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddLmluY2x1ZGVzKFxyXG4gICAgICBmaXhlZENvbnN0clxyXG4gICAgKVxyXG4gICAgICA/IGZpeGVkQ29uc3RyXHJcbiAgICAgIDogJ2NoYXJ0JztcclxuICB9IGNhdGNoIHtcclxuICAgIC8vIERlZmF1bHQgdG8gJ2NoYXJ0JyBpbiBjYXNlIG9mIGFueSBlcnJvclxyXG4gICAgcmV0dXJuICdjaGFydCc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIG91dGZpbGUgYmFzZWQgb24gcHJvdmlkZWQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeE91dGZpbGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBvdXRmaWxlIC0gVGhlIGZpbGUgcGF0aCBvciBuYW1lLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIG91dGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4T3V0ZmlsZSh0eXBlLCBvdXRmaWxlKSB7XHJcbiAgLy8gR2V0IHRoZSBmaWxlIG5hbWUgZnJvbSB0aGUgYG91dGZpbGVgIG9wdGlvblxyXG4gIGNvbnN0IGZpbGVOYW1lID0gZ2V0QWJzb2x1dGVQYXRoKG91dGZpbGUgfHwgJ2NoYXJ0JylcclxuICAgIC5zcGxpdCgnLicpXHJcbiAgICAuc2hpZnQoKTtcclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCBvdXRmaWxlXHJcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke3R5cGV9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhUeXBlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW291dGZpbGU9bnVsbF0gLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIG51bGwuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgZXhwb3J0IHR5cGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4VHlwZSh0eXBlLCBvdXRmaWxlID0gbnVsbCkge1xyXG4gIC8vIE1JTUUgdHlwZXNcclxuICBjb25zdCBtaW1lVHlwZXMgPSB7XHJcbiAgICAnaW1hZ2UvcG5nJzogJ3BuZycsXHJcbiAgICAnaW1hZ2UvanBlZyc6ICdqcGVnJyxcclxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcclxuICAgICdpbWFnZS9zdmcreG1sJzogJ3N2ZydcclxuICB9O1xyXG5cclxuICAvLyBHZXQgZm9ybWF0c1xyXG4gIGNvbnN0IGZvcm1hdHMgPSBPYmplY3QudmFsdWVzKG1pbWVUeXBlcyk7XHJcblxyXG4gIC8vIENoZWNrIGlmIHR5cGUgYW5kIG91dGZpbGUncyBleHRlbnNpb25zIGFyZSB0aGUgc2FtZVxyXG4gIGlmIChvdXRmaWxlKSB7XHJcbiAgICBjb25zdCBvdXRUeXBlID0gb3V0ZmlsZS5zcGxpdCgnLicpLnBvcCgpO1xyXG5cclxuICAgIC8vIFN1cHBvcnQgdGhlIEpQRyB0eXBlXHJcbiAgICBpZiAob3V0VHlwZSA9PT0gJ2pwZycpIHtcclxuICAgICAgdHlwZSA9ICdqcGVnJztcclxuICAgIH0gZWxzZSBpZiAoZm9ybWF0cy5pbmNsdWRlcyhvdXRUeXBlKSAmJiB0eXBlICE9PSBvdXRUeXBlKSB7XHJcbiAgICAgIHR5cGUgPSBvdXRUeXBlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXHJcbiAgcmV0dXJuIG1pbWVUeXBlc1t0eXBlXSB8fCBmb3JtYXRzLmZpbmQoKHQpID0+IHQgPT09IHR5cGUpIHx8ICdwbmcnO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBwYXRoIGlzIHJlbGF0aXZlIG9yIGFic29sdXRlIGFuZCByZXR1cm5zIHRoZSBjb3JyZWN0ZWQsXHJcbiAqIGFic29sdXRlIHBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0Fic29sdXRlUGF0aFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIGJlIGNoZWNrZWQgb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBhYnNvbHV0ZSBwYXRoLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEFic29sdXRlUGF0aChwYXRoKSB7XHJcbiAgcmV0dXJuIGlzQWJzb2x1dGUocGF0aCkgPyBwYXRoIDogam9pbihfX2Rpcm5hbWUsIHBhdGgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ29udmVydHMgaW5wdXQgZGF0YSB0byBhIEJhc2U2NCBzdHJpbmcgYmFzZWQgb24gdGhlIGV4cG9ydCB0eXBlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QmFzZTY0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCAtIFRoZSBpbnB1dCB0byBiZSB0cmFuc2Zvcm1lZCB0byBCYXNlNjQgZm9ybWF0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSBvcmlnaW5hbCBleHBvcnQgdHlwZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIEJhc2U2NCBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGlucHV0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEJhc2U2NChpbnB1dCwgdHlwZSkge1xyXG4gIC8vIEZvciBwZGYgYW5kIHN2ZyB0eXBlcyB0aGUgaW5wdXQgbXVzdCBiZSB0cmFuc2Zvcm1lZCB0byBCYXNlNjQgZnJvbSBhIGJ1ZmZlclxyXG4gIGlmICh0eXBlID09PSAncGRmJyB8fCB0eXBlID09ICdzdmcnKSB7XHJcbiAgICByZXR1cm4gQnVmZmVyLmZyb20oaW5wdXQsICd1dGY4JykudG9TdHJpbmcoJ2Jhc2U2NCcpO1xyXG4gIH1cclxuXHJcbiAgLy8gRm9yIHBuZyBhbmQganBlZyBpbnB1dCBpcyBhbHJlYWR5IGEgQmFzZTY0IHN0cmluZ1xyXG4gIHJldHVybiBpbnB1dDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgc3RyaW5naWZpZWQgZGF0ZSB3aXRob3V0IHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldE5ld0RhdGVcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXROZXdEYXRlKCkge1xyXG4gIC8vIEdldCByaWQgb2YgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uXHJcbiAgcmV0dXJuIG5ldyBEYXRlKCkudG9TdHJpbmcoKS5zcGxpdCgnKCcpWzBdLnRyaW0oKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgdGhlIHN0b3JlZCB0aW1lIHZhbHVlIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldE5ld0RhdGVUaW1lXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3RGF0ZVRpbWUoKSB7XHJcbiAgcmV0dXJuIG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBpdGVtIGlzIGFuIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzT2JqZWN0XHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gaXRlbSAtIFRoZSBpdGVtIHRvIGJlIGNoZWNrZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSBpdGVtIGlzIGFuIG9iamVjdCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzT2JqZWN0KGl0ZW0pIHtcclxuICByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZy5jYWxsKGl0ZW0pID09PSAnW29iamVjdCBPYmplY3RdJztcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gb2JqZWN0IGlzIGVtcHR5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNPYmplY3RFbXB0eVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaXRlbSAtIFRoZSBvYmplY3QgdG8gYmUgY2hlY2tlZC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIG9iamVjdCBpcyBlbXB0eSwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHkoaXRlbSkge1xyXG4gIHJldHVybiAoXHJcbiAgICB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcgJiZcclxuICAgICFBcnJheS5pc0FycmF5KGl0ZW0pICYmXHJcbiAgICBpdGVtICE9PSBudWxsICYmXHJcbiAgICBPYmplY3Qua2V5cyhpdGVtKS5sZW5ndGggPT09IDBcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQgaW4gdGhlIGdpdmVuIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGl0ZW0gLSBUaGUgc3RyaW5nIHRvIGJlIGNoZWNrZWQgZm9yIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKGl0ZW0pIHtcclxuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pP2xvY2FsaG9zdFxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTBcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTcyXFwuKDFbNi05XXwyWzAtOV18M1swLTFdKVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE5MlxcLjE2OFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvXHJcbiAgXTtcclxuXHJcbiAgcmV0dXJuIHJlZ2V4UGF0dGVybnMuc29tZSgocGF0dGVybikgPT4gcGF0dGVybi50ZXN0KGl0ZW0pKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFV0aWxpdHkgdG8gbWVhc3VyZSBlbGFwc2VkIHRpbWUgdXNpbmcgdGhlIE5vZGUuanMgYHByb2Nlc3MuaHJ0aW1lKClgIG1ldGhvZC5cclxuICpcclxuICogQGZ1bmN0aW9uIG1lYXN1cmVUaW1lXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gQSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGVsYXBzZWQgdGltZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWVhc3VyZVRpbWUoKSB7XHJcbiAgY29uc3Qgc3RhcnQgPSBwcm9jZXNzLmhydGltZS5iaWdpbnQoKTtcclxuICByZXR1cm4gKCkgPT4gTnVtYmVyKHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpIC0gc3RhcnQpIC8gMTAwMDAwMDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJvdW5kcyBhIG51bWJlciB0byB0aGUgc3BlY2lmaWVkIHByZWNpc2lvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHJvdW5kTnVtYmVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB2YWx1ZSAtIFRoZSBudW1iZXIgdG8gYmUgcm91bmRlZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHByZWNpc2lvbiAtIFRoZSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgdG8gcm91bmQgdG8uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtudW1iZXJ9IFRoZSByb3VuZGVkIG51bWJlci5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiByb3VuZE51bWJlcih2YWx1ZSwgcHJlY2lzaW9uID0gMSkge1xyXG4gIGNvbnN0IG11bHRpcGxpZXIgPSBNYXRoLnBvdygxMCwgcHJlY2lzaW9uIHx8IDApO1xyXG4gIHJldHVybiBNYXRoLnJvdW5kKCt2YWx1ZSAqIG11bHRpcGxpZXIpIC8gbXVsdGlwbGllcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGEgdmFsdWUgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdG9Cb29sZWFuXHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gaXRlbSAtIFRoZSB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVGhlIGJvb2xlYW4gcmVwcmVzZW50YXRpb24gb2YgdGhlIGlucHV0IHZhbHVlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHRvQm9vbGVhbihpdGVtKSB7XHJcbiAgcmV0dXJuIFsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJywgJzAnLCAnJ10uaW5jbHVkZXMoaXRlbSlcclxuICAgID8gZmFsc2VcclxuICAgIDogISFpdGVtO1xyXG59XHJcblxyXG4vKipcclxuICogV3JhcHMgY3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBpdCBzYWZlbHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB3cmFwQXJvdW5kXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21Db2RlIC0gVGhlIGN1c3RvbSBjb2RlIHRvIGJlIHdyYXBwZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gRmxhZyB0byBhbGxvdyBsb2FkaW5nIGNvZGUgZnJvbSBhIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2lzQ2FsbGJhY2s9ZmFsc2VdIC0gRmxhZyB0aGF0IGluZGljYXRlcyB0aGUgcmV0dXJuZWQgY29kZVxyXG4gKiBtdXN0IGJlIGluIGEgY2FsbGJhY2sgZm9ybWF0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KHN0cmluZ3xudWxsKX0gVGhlIHdyYXBwZWQgY3VzdG9tIGNvZGUgb3IgbnVsbCBpZiB3cmFwcGluZyBmYWlscy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB3cmFwQXJvdW5kKGN1c3RvbUNvZGUsIGFsbG93RmlsZVJlc291cmNlcywgaXNDYWxsYmFjayA9IGZhbHNlKSB7XHJcbiAgaWYgKGN1c3RvbUNvZGUgJiYgdHlwZW9mIGN1c3RvbUNvZGUgPT09ICdzdHJpbmcnKSB7XHJcbiAgICBjdXN0b21Db2RlID0gY3VzdG9tQ29kZS50cmltKCk7XHJcblxyXG4gICAgaWYgKGN1c3RvbUNvZGUuZW5kc1dpdGgoJy5qcycpKSB7XHJcbiAgICAgIC8vIExvYWQgYSBmaWxlIGlmIHRoZSBmaWxlIHJlc291cmNlcyBhcmUgYWxsb3dlZFxyXG4gICAgICByZXR1cm4gYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICAgPyB3cmFwQXJvdW5kKFxyXG4gICAgICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGN1c3RvbUNvZGUpLCAndXRmOCcpLFxyXG4gICAgICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgICAgIGlzQ2FsbGJhY2tcclxuICAgICAgICAgIClcclxuICAgICAgICA6IG51bGw7XHJcbiAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAhaXNDYWxsYmFjayAmJlxyXG4gICAgICAoY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbigpJykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgpJykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpPT4nKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCkgPT4nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBUcmVhdCBhIGZ1bmN0aW9uIGFzIGEgc2VsZi1pbnZva2luZyBleHByZXNzaW9uXHJcbiAgICAgIHJldHVybiBgKCR7Y3VzdG9tQ29kZX0pKClgO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE9yIHJldHVybiBhcyBhIHN0cmluZ2lmaWVkIGNvZGVcclxuICAgIHJldHVybiBjdXN0b21Db2RlLnJlcGxhY2UoLzskLywgJycpO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIF9fZGlybmFtZSxcclxuICBjbGVhclRleHQsXHJcbiAgZGVlcENvcHksXHJcbiAgZXhwQmFja29mZixcclxuICBmaXhDb25zdHIsXHJcbiAgZml4T3V0ZmlsZSxcclxuICBmaXhUeXBlLFxyXG4gIGdldEFic29sdXRlUGF0aCxcclxuICBnZXRCYXNlNjQsXHJcbiAgZ2V0TmV3RGF0ZSxcclxuICBnZXROZXdEYXRlVGltZSxcclxuICBpc09iamVjdCxcclxuICBpc09iamVjdEVtcHR5LFxyXG4gIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQsXHJcbiAgbWVhc3VyZVRpbWUsXHJcbiAgcm91bmROdW1iZXIsXHJcbiAgdG9Cb29sZWFuLFxyXG4gIHdyYXBBcm91bmRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEEgbW9kdWxlIGZvciBtYW5hZ2luZyBsb2dnaW5nIGZ1bmN0aW9uYWxpdHkgd2l0aCBjdXN0b21pemFibGVcclxuICogbG9nIGxldmVscywgY29uc29sZSBhbmQgZmlsZSBsb2dnaW5nIG9wdGlvbnMsIGFuZCBlcnJvciBoYW5kbGluZyBzdXBwb3J0LlxyXG4gKiBUaGUgbW9kdWxlIGFsc28gZW5zdXJlcyB0aGF0IGZpbGUtYmFzZWQgbG9ncyBhcmUgc3RvcmVkIGluIGEgc3RydWN0dXJlZFxyXG4gKiBkaXJlY3RvcnksIGNyZWF0aW5nIHRoZSBuZWNlc3NhcnkgcGF0aHMgYXV0b21hdGljYWxseSBpZiB0aGV5IGRvIG5vdCBleGlzdC5cclxuICovXHJcblxyXG5pbXBvcnQgeyBhcHBlbmRGaWxlLCBleGlzdHNTeW5jLCBta2RpclN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldEFic29sdXRlUGF0aCwgZ2V0TmV3RGF0ZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuLy8gVGhlIGF2YWlsYWJsZSBjb2xvcnNcclxuY29uc3QgY29sb3JzID0gWydyZWQnLCAneWVsbG93JywgJ2JsdWUnLCAnZ3JheScsICdncmVlbiddO1xyXG5cclxuLy8gVGhlIGRlZmF1bHQgbG9nZ2luZyBjb25maWdcclxuY29uc3QgbG9nZ2luZyA9IHtcclxuICAvLyBGbGFncyBmb3IgbG9nZ2luZyBzdGF0dXNcclxuICB0b0NvbnNvbGU6IHRydWUsXHJcbiAgdG9GaWxlOiBmYWxzZSxcclxuICBwYXRoQ3JlYXRlZDogZmFsc2UsXHJcbiAgLy8gRnVsbCBwYXRoIHRvIHRoZSBsb2cgZmlsZVxyXG4gIHBhdGhUb0xvZzogJycsXHJcbiAgLy8gTG9nIGxldmVsc1xyXG4gIGxldmVsc0Rlc2M6IFtcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdlcnJvcicsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMF1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnd2FybmluZycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMV1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnbm90aWNlJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1syXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd2ZXJib3NlJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1szXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdiZW5jaG1hcmsnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzRdXHJcbiAgICB9XHJcbiAgXVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgYSBtZXNzYWdlLiBBY2NlcHRzIGEgdmFyaWFibGUgYW1vdW50IG9mIGFyZ3VtZW50cy4gQXJndW1lbnRzIGFmdGVyXHJcbiAqIHRoZSBgbGV2ZWxgIHdpbGwgYmUgcGFzc2VkIGRpcmVjdGx5IHRvIGBjb25zb2xlLmxvZ2AsIGFuZC9vciB3aWxsIGJlIGpvaW5lZFxyXG4gKiBhbmQgYXBwZW5kZWQgdG8gdGhlIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Li4udW5rbm93bn0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZ1xyXG4gKiBsZXZlbCBhbmQgdGhlIHJlc3QgYXJlIHN0cmluZ3MgdG8gYnVpbGQgYSBtZXNzYWdlIHdpdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt2b2lkfSBFbmRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gd2hlbiBhdHRlbXB0aW5nIHRvIGxvZ1xyXG4gKiBpbmZvcm1hdGlvbiBhdCBhIGhpZ2hlciBsZXZlbCB0aGFuIHdoYXQgaXMgYWxsb3dlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBsb2coLi4uYXJncykge1xyXG4gIGNvbnN0IFtuZXdMZXZlbCwgLi4udGV4dHNdID0gYXJncztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsc0Rlc2MsIGxldmVsIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2Ugb3IgaXMgaXQgYSBiZW5jaG1hcmsgbG9nXHJcbiAgaWYgKFxyXG4gICAgbmV3TGV2ZWwgIT09IDUgJiZcclxuICAgIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpXHJcbiAgKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KHRleHRzKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGFuIGVycm9yIG1lc3NhZ2Ugd2l0aCBpdHMgc3RhY2sgdHJhY2UuIE9wdGlvbmFsbHksIGEgY3VzdG9tIG1lc3NhZ2VcclxuICogY2FuIGJlIHByb3ZpZGVkLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nV2l0aFN0YWNrXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBuZXdMZXZlbCAtIFRoZSBsb2cgbGV2ZWwuXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbU1lc3NhZ2UgLSBBbiBvcHRpb25hbCBjdXN0b20gbWVzc2FnZSB0byBiZSBsb2dnZWRcclxuICogYWxvbmcgd2l0aCB0aGUgZXJyb3IuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt2b2lkfSBFbmRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gd2hlbiBhdHRlbXB0aW5nIHRvIGxvZ1xyXG4gKiBpbmZvcm1hdGlvbiBhdCBhIGhpZ2hlciBsZXZlbCB0aGFuIHdoYXQgaXMgYWxsb3dlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBsb2dXaXRoU3RhY2sobmV3TGV2ZWwsIGVycm9yLCBjdXN0b21NZXNzYWdlKSB7XHJcbiAgLy8gR2V0IHRoZSBtYWluIG1lc3NhZ2VcclxuICBjb25zdCBtYWluTWVzc2FnZSA9IGN1c3RvbU1lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQWRkIHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXHJcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID0gZXJyb3Iuc3RhY2s7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2UsIGlmIGV4aXN0c1xyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlXTtcclxuICBpZiAoc3RhY2tNZXNzYWdlKSB7XHJcbiAgICB0ZXh0cy5wdXNoKCdcXG4nLCBzdGFja01lc3NhZ2UpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcclxuICAgICAgICB0ZXh0cy5zaGlmdCgpW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAuLi50ZXh0c1xyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpbml0TG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgbG9nZ2luZ2Agb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpbml0TG9nZ2luZyhsb2dnaW5nT3B0aW9ucykge1xyXG4gIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGBsb2dnaW5nT3B0aW9uc2Agb2JqZWN0XHJcbiAgY29uc3QgeyBsZXZlbCwgZGVzdCwgZmlsZSwgdG9Db25zb2xlLCB0b0ZpbGUgfSA9IGxvZ2dpbmdPcHRpb25zO1xyXG5cclxuICAvLyBTZXQgdGhlIGxvZ2dpbmcgbGV2ZWxcclxuICBzZXRMb2dMZXZlbChsZXZlbCk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29uc29sZSBsb2dnaW5nXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmcodG9Db25zb2xlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBmaWxlIGxvZ2dpbmdcclxuICBlbmFibGVGaWxlTG9nZ2luZyhkZXN0LCBmaWxlLCB0b0ZpbGUpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgbG9nIGxldmVsIHRvIHRoZSBzcGVjaWZpZWQgdmFsdWUuIExvZyBsZXZlbHMgYXJlICgwID0gbm8gbG9nZ2luZyxcclxuICogMSA9IGVycm9yLCAyID0gd2FybmluZywgMyA9IG5vdGljZSwgNCA9IHZlcmJvc2UsIG9yIDUgPSBiZW5jaG1hcmspLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0TG9nTGV2ZWxcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxvZyBsZXZlbCB0byBiZSBzZXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0TG9nTGV2ZWwobGV2ZWwpIHtcclxuICBpZiAobGV2ZWwgPj0gMCAmJiBsZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICBsb2dnaW5nLmxldmVsID0gbGV2ZWw7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRW5hYmxlcyBjb25zb2xlIGxvZ2dpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVDb25zb2xlTG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvQ29uc29sZSAtIFRoZSBmbGFnIGZvciBzZXR0aW5nIHRoZSBsb2dnaW5nIHRvIHRoZSBjb25zb2xlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZUNvbnNvbGVMb2dnaW5nKHRvQ29uc29sZSkge1xyXG4gIC8vIFVwZGF0ZSBvcHRpb25zIGZvciB0aGUgY29uc29sZSBsb2dnaW5nXHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSB0b0NvbnNvbGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlRmlsZUxvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGRlc3QgLSBUaGUgZGVzdGluYXRpb24gcGF0aCBmb3IgdGhlIGxvZyBmaWxlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZmlsZSAtIFRoZSBsb2cgZmlsZSBuYW1lLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvRmlsZSAtIFRoZSBmbGFnIGZvciBzZXR0aW5nIHRoZSBsb2dnaW5nIHRvIGEgZmlsZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVGaWxlTG9nZ2luZyhkZXN0LCBmaWxlLCB0b0ZpbGUpIHtcclxuICAvLyBVcGRhdGUgb3B0aW9ucyBmb3IgdGhlIGZpbGUgbG9nZ2luZ1xyXG4gIGxvZ2dpbmcudG9GaWxlID0gdG9GaWxlO1xyXG5cclxuICAvLyBTZXQgdGhlIGBkZXN0YCBhbmQgYGZpbGVgIG9ubHkgaWYgdGhlIGZpbGUgbG9nZ2luZyBpcyBlbmFibGVkXHJcbiAgaWYgKHRvRmlsZSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ID0gZGVzdDtcclxuICAgIGxvZ2dpbmcuZmlsZSA9IGZpbGU7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyB0aGUgcHJvdmlkZWQgdGV4dHMgdG8gYSBmaWxlLCBpZiBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZC4gSXQgY3JlYXRlc1xyXG4gKiB0aGUgbmVjZXNzYXJ5IGRpcmVjdG9yeSBzdHJ1Y3R1cmUgaWYgbm90IGFscmVhZHkgY3JlYXRlZCBhbmQgYXBwZW5kc1xyXG4gKiB0aGUgY29udGVudCwgaW5jbHVkaW5nIGFuIG9wdGlvbmFsIHByZWZpeCwgdG8gdGhlIHNwZWNpZmllZCBsb2cgZmlsZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2dUb0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBBbiBvcHRpb25hbCBwcmVmaXggdG8gYmUgYWRkZWQgdG8gZWFjaCBsb2cgZW50cnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpIHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpICYmXHJcbiAgICAgIG1rZGlyU3luYyhnZXRBYnNvbHV0ZVBhdGgobG9nZ2luZy5kZXN0KSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBmdWxsIHBhdGhcclxuICAgIGxvZ2dpbmcucGF0aFRvTG9nID0gZ2V0QWJzb2x1dGVQYXRoKGpvaW4obG9nZ2luZy5kZXN0LCBsb2dnaW5nLmZpbGUpKTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yICYmIGxvZ2dpbmcudG9GaWxlICYmIGxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29uZmlndXJhdGlvbiBtYW5hZ2VtZW50IG1vZHVsZSBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci5cclxuICogUHJvdmlkZXMgZGVmYXVsdCBjb25maWd1cmF0aW9ucyB0aGF0IHN1cHBvcnQgZW52aXJvbm1lbnQgdmFyaWFibGVzLCBDTElcclxuICogYXJndW1lbnRzLCBhbmQgaW50ZXJhY3RpdmUgcHJvbXB0cyBmb3IgY3VzdG9taXphdGlvbiBvZiBvcHRpb25zIGFuZCBmZWF0dXJlcy5cclxuICogQWRkaXRpb25hbGx5LCBpdCBtYXBzIGxlZ2FjeSBvcHRpb25zIHRvIG1vZGVybiBzdHJ1Y3R1cmVzLCBnZW5lcmF0ZXMgbmVzdGVkXHJcbiAqIGFyZ3VtZW50IG1hcHBpbmdzLCBhbmQgZGlzcGxheXMgQ0xJIHVzYWdlIGluZm9ybWF0aW9uLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBhbGwgYXZhaWxhYmxlIG9wdGlvbnMsIG9yZ2FuaXplZFxyXG4gKiBieSBzZWN0aW9ucy5cclxuICpcclxuICogVGhpcyBvYmplY3QgaW5jbHVkZXM6XHJcbiAqIC0gRGVmYXVsdCB2YWx1ZXMgZm9yIGVhY2ggb3B0aW9uXHJcbiAqIC0gRGF0YSB0eXBlcyBmb3IgdmFsaWRhdGlvblxyXG4gKiAtIE5hbWVzIG9mIGNvcnJlc3BvbmRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIC0gRGVzY3JpcHRpb25zIG9mIGVhY2ggcHJvcGVydHlcclxuICogLSBJbmZvcm1hdGlvbiB1c2VkIGZvciBwcm9tcHRzIGluIGludGVyYWN0aXZlIGNvbmZpZ3VyYXRpb25cclxuICogLSBbT3B0aW9uYWxdIENvcnJlc3BvbmRpbmcgQ0xJIGFyZ3VtZW50IG5hbWVzIGZvciBDTEkgdXNhZ2VcclxuICogLSBbT3B0aW9uYWxdIExlZ2FjeSBuYW1lcyBmcm9tIHRoZSBwcmV2aW91cyBQaGFudG9tSlMtYmFzZWQgc2VydmVyXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IHtcclxuICAgIGFyZ3M6IHtcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnLS1hbGxvdy1ydW5uaW5nLWluc2VjdXJlLWNvbnRlbnQnLFxyXG4gICAgICAgICctLWFzaC1uby1udWRnZXMnLFxyXG4gICAgICAgICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICAgICAgICctLWJsb2NrLW5ldy13ZWItY29udGVudHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYWNjZWxlcmF0ZWQtMmQtY2FudmFzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtbmV0d29ya2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLXRpbWVyLXRocm90dGxpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZGluZy1vY2NsdWRlZC13aW5kb3dzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJyZWFrcGFkJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNoZWNrZXItaW1hZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jbGllbnQtc2lkZS1waGlzaGluZy1kZXRlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LWV4dGVuc2lvbnMtd2l0aC1iYWNrZ3JvdW5kLXBhZ2VzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICAgICAgICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAgICAgICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZmVhdHVyZXM9Q2FsY3VsYXRlTmF0aXZlV2luT2NjbHVzaW9uLEludGVyZXN0RmVlZENvbnRlbnRTdWdnZXN0aW9ucyxXZWJPVFAnLFxyXG4gICAgICAgICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWlwYy1mbG9vZGluZy1wcm90ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWxvZ2dpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtbm90aWZpY2F0aW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1vZmZlci1zdG9yZS11bm1hc2tlZC13YWxsZXQtY2FyZHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtcG9wdXAtYmxvY2tpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJpbnQtcHJldmlldycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcm9tcHQtb24tcmVwb3N0JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXJlbmRlcmVyLWJhY2tncm91bmRpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2VhcmNoLWVuZ2luZS1jaG9pY2Utc2NyZWVuJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlc3Npb24tY3Jhc2hlZC1idWJibGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2l0ZS1pc29sYXRpb24tdHJpYWxzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICAgICAgICctLWRpc2FibGUtc3luYycsXHJcbiAgICAgICAgJy0tZW5hYmxlLXVuc2FmZS13ZWJncHUnLFxyXG4gICAgICAgICctLWhpZGUtY3Jhc2gtcmVzdG9yZS1idWJibGUnLFxyXG4gICAgICAgICctLWhpZGUtc2Nyb2xsYmFycycsXHJcbiAgICAgICAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgICAgICAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgICAgICAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAgICAgICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICAgICAgICctLW5vLXBpbmdzJyxcclxuICAgICAgICAnLS1uby1zYW5kYm94JyxcclxuICAgICAgICAnLS1uby1zdGFydHVwLXdpbmRvdycsXHJcbiAgICAgICAgJy0tbm8tenlnb3RlJyxcclxuICAgICAgICAnLS1wYXNzd29yZC1zdG9yZT1iYXNpYycsXHJcbiAgICAgICAgJy0tcHJvY2Vzcy1wZXItdGFiJyxcclxuICAgICAgICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuICAgICAgXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ1BVUFBFVEVFUl9BUkdTJyxcclxuICAgICAgY2xpTmFtZTogJ3B1cHBldGVlckFyZ3MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FycmF5IG9mIFB1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfVkVSU0lPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2RuVXJsOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NETl9VUkwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0NETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGbGFnIHRvIHJlZmV0Y2ggc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdG9yeSBwYXRoIGZvciBjYWNoZWQgSGlnaGNoYXJ0cyBzY3JpcHRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DT1JFX1NDUklQVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtb2R1bGVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ3N0b2NrJyxcclxuICAgICAgICAnbWFwJyxcclxuICAgICAgICAnZ2FudHQnLFxyXG4gICAgICAgICdleHBvcnRpbmcnLFxyXG4gICAgICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAgICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgICAgIC8vICdhbm5vdGF0aW9ucy1hZHZhbmNlZCcsXHJcbiAgICAgICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAgICAgJ2Jvb3N0JyxcclxuICAgICAgICAnZGF0YScsXHJcbiAgICAgICAgJ2RhdGEtdG9vbHMnLFxyXG4gICAgICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICAgICAnc3RhdGljLXNjYWxlJyxcclxuICAgICAgICAnYnJva2VuLWF4aXMnLFxyXG4gICAgICAgICdoZWF0bWFwJyxcclxuICAgICAgICAndGlsZW1hcCcsXHJcbiAgICAgICAgJ3RpbGVkd2VibWFwJyxcclxuICAgICAgICAndGltZWxpbmUnLFxyXG4gICAgICAgICd0cmVlbWFwJyxcclxuICAgICAgICAndHJlZWdyYXBoJyxcclxuICAgICAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgICAgICdkcmlsbGRvd24nLFxyXG4gICAgICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICAgICAnYnVsbGV0JyxcclxuICAgICAgICAnZnVubmVsJyxcclxuICAgICAgICAnZnVubmVsM2QnLFxyXG4gICAgICAgICdnZW9oZWF0bWFwJyxcclxuICAgICAgICAncHlyYW1pZDNkJyxcclxuICAgICAgICAnbmV0d29ya2dyYXBoJyxcclxuICAgICAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAgICAgJ3BhcmV0bycsXHJcbiAgICAgICAgJ3BhdHRlcm4tZmlsbCcsXHJcbiAgICAgICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAgICAgJ3ByaWNlLWluZGljYXRvcicsXHJcbiAgICAgICAgJ3NhbmtleScsXHJcbiAgICAgICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICAgICAnZGVwZW5kZW5jeS13aGVlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1sYWJlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1vbi1wb2ludCcsXHJcbiAgICAgICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICAgICAnc29uaWZpY2F0aW9uJyxcclxuICAgICAgICAvLyAnc3RvY2stdG9vbHMnLFxyXG4gICAgICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAgICAgJ3N1bmJ1cnN0JyxcclxuICAgICAgICAndmFyaWFibGUtcGllJyxcclxuICAgICAgICAndmFyaXdpZGUnLFxyXG4gICAgICAgICd2ZWN0b3InLFxyXG4gICAgICAgICd2ZW5uJyxcclxuICAgICAgICAnd2luZGJhcmInLFxyXG4gICAgICAgICd3b3JkY2xvdWQnLFxyXG4gICAgICAgICd4cmFuZ2UnLFxyXG4gICAgICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgICAgICdkcmFnLXBhbmVzJyxcclxuICAgICAgICAnZGVidWdnZXInLFxyXG4gICAgICAgICdkdW1iYmVsbCcsXHJcbiAgICAgICAgJ2xvbGxpcG9wJyxcclxuICAgICAgICAnY3lsaW5kZXInLFxyXG4gICAgICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgICAgICdkb3RwbG90JyxcclxuICAgICAgICAnbWFya2VyLWNsdXN0ZXJzJyxcclxuICAgICAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgICAgICdoZWlraW5hc2hpJyxcclxuICAgICAgICAnZmxvd21hcCcsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAnbmF2aWdhdG9yJyxcclxuICAgICAgICAndGV4dHBhdGgnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIG1vZHVsZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbmRpY2F0b3JTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2luZGljYXRvcnMtYWxsJ10sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGluZGljYXRvciBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjMwLjEvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuNDUvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBZGRpdGlvbmFsIGN1c3RvbSBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgICAgc2VwYXJhdG9yOiAnOydcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5GSUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0IGZpbGVuYW1lIHdpdGggdHlwZSwgZm9ybWF0dGVkIGNvcnJlY3RseSBhcyBKU09OIG9yIFNWRycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0lOU1RSJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ092ZXJyaWRlcyB0aGUgYGluZmlsZWAgd2l0aCBKU09OLCBzdHJpbmdpZmllZCBKU09OLCBvciBTVkcgaW5wdXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09QVElPTlMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FsaWFzIGZvciB0aGUgYGluc3RyYCBvcHRpb24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzdmc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU1ZHJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTVkcgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCB0byByZW5kZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CQVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdCYXRjaCBqb2Igc3RyaW5nIHdpdGggaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09VVEZJTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3V0cHV0IGZpbGVuYW1lIHdpdGggdHlwZS4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcgYW5kIGlnbm9yZXMgYHR5cGVgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIGV4cG9ydCBmb3JtYXQuIENhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBwbmcnLFxyXG4gICAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0NPTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDaGFydCBjb25zdHJ1Y3Rvci4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgICBoaW50OiAnRGVmYXVsdDogY2hhcnQnLFxyXG4gICAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGI2NDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CNjQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gdGhlIGNoYXJ0IHNob3VsZCBiZSByZWNlaXZlZCBpbiBCYXNlNjQgZm9ybWF0IGluc3RlYWQgb2YgYmluYXJ5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBub0Rvd25sb2FkOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX05PX0RPV05MT0FEJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZXRoZXIgb3Igbm90IHRvIGluY2x1ZGUgb3IgZXhjbHVkZSBhdHRhY2htZW50IGhlYWRlcnMgaW4gdGhlIHJlc3BvbnNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSEVJR0hUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1NDQUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0RlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgbWluOiAwLjEsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9HTE9CQUxfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggZ2xvYmFsIG9wdGlvbnMgZm9yIEhpZ2hjaGFydHMuc2V0T3B0aW9ucycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVEhFTUVfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHdlYnBhZ2UgcmVuZGVyaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgZHVyaW5nIGV4cG9ydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FsbG93cyBvciBkaXNhbGxvd3MgaW5qZWN0aW9uIG9mIGZpbGVzeXN0ZW0gcmVzb3VyY2VzIChkaXNhYmxlZCBpbiBzZXJ2ZXIgbW9kZSknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DQUxMQkFDSycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIENhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgLmpzIGZpbGVuYW1lJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZXMgYXMgSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgZmlsZW5hbWUsIGNvbnRhaW5pbmcgZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSB3aXRoIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdQcm9tcHQtYmFzZWQgb3B0aW9uIHNldHRpbmcsIHNhdmVkIHRvIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVTZXJ2ZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0cyB0aGUgc2VydmVyIHdoZW4gdHJ1ZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaG9zdDoge1xyXG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIb3N0bmFtZSBvZiB0aGUgc2VydmVyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUG9ydCBudW1iZXIgZm9yIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHVwbG9hZExpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiAzLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9VUExPQURfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVxdWVzdCBib2R5IHNpemUgaW4gTUInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAnc2VydmVyQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Rpc3BsYXlzIG9yIG5vdCBhY3Rpb24gZHVyYXRpb25zIGluIG1pbGxpc2Vjb25kcyBkdXJpbmcgc2VydmVyIHJlcXVlc3RzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwcm94eToge1xyXG4gICAgICBob3N0OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eUhvc3QnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnSG9zdCBvZiB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB3aW5kb3c6IHtcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RpbWUgd2luZG93IGluIG1pbnV0ZXMgZm9yIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnRGVsYXkgZHVyYXRpb24gYmV0d2VlbiBzdWNjZXNzaXZlIHJlcXVlc3RzIGJlZm9yZSByZWFjaGluZyB0aGUgbGltaXQnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcEtleToge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0tleSB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwVG9rZW5gJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBUb2tlbjoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVG9rZW4gdG8gYnlwYXNzIHRoZSByYXRlIGxpbWl0ZXIsIHVzZWQgd2l0aCBgc2tpcEtleWAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdGb3JjZXMgdGhlIHNlcnZlciB0byB1c2UgSFRUUFMgb25seSB3aGVuIHRydWUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IGZvciB0aGUgU1NMIHNlcnZlcicsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xDZXJ0UGF0aCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWluaW11bSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICB3b3JrTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDQwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTnVtYmVyIG9mIHRhc2tzIGEgd29ya2VyIGNhbiBoYW5kbGUgYmVmb3JlIHJlc3RhcnRpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpZGxlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMzAwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGlkbGUgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAyMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGJlZm9yZSByZXRyeWluZyByZXNvdXJjZSBjcmVhdGlvbiBvbiBmYWlsdXJlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgdG8gY2hlY2sgYW5kIGRlc3Ryb3kgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1Nob3dzIHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdMb2dnaW5nIHZlcmJvc2l0eSBsZXZlbCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICByb3VuZDogMCxcclxuICAgICAgICBtaW46IDAsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnTG9nIGZpbGUgbmFtZS4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCB0byBiZSBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZXN0OiB7XHJcbiAgICAgIHZhbHVlOiAnbG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFJlcXVpcmVzIGBsb2dUb0ZpbGVgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBjb25zb2xlIGxvZ2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvRmlsZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICB1aToge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVVpJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGVuZHBvaW50IHJvdXRlIGZvciB0aGUgVUknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBOb2RlLmpzIGVudmlyb25tZW50IHR5cGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0Rpc3BsYXkgb3Igc2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoYXJkUmVzZXRQYWdlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfSEFSRF9SRVNFVF9QQUdFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaGV0aGVyIG9yIG5vdCB0byByZXNldCB0aGUgcGFnZSBjb250ZW50IGVudGlyZWx5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBicm93c2VyU2hlbGxNb2RlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHNldCB0aGUgYnJvd3NlciB0byBydW4gaW4gc2hlbGwgbW9kZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBkZWJ1Zzoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZURlYnVnJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSB1bmRlcmx5aW5nIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlYWRsZXNzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBoZWFkbGVzcyBtb2RlIGR1cmluZyBkZWJ1Z2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRldnRvb2xzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgRGV2VG9vbHMgaW4gaGVhZGZ1bCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGxpc3RlbmluZyB0byBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGR1bXBpbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RVTVBJTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdSZWRpcmVjdHMgb3Igbm90IGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gcHJvY2Vzcy5zdGRvdXQgYW5kIHByb2Nlc3Muc3RkZXJyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzbG93TW86IHtcclxuICAgICAgdmFsdWU6IDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVsYXlzIFB1cHBldGVlciBvcGVyYXRpb25zIGJ5IHRoZSBzcGVjaWZpZWQgbWlsbGlzZWNvbmRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IHVzZWQgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLy8gUHJvcGVydGllcyBuZXN0aW5nIGxldmVsIG9mIGFsbCBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRQcm9wcyA9IF9jcmVhdGVOZXN0ZWRQcm9wcyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIFByb3BlcnRpZXMgbmFtZXMgdGhhdCBzaG91bGQgbm90IGJlIHJlY3Vyc2l2ZWx5IG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IF9jcmVhdGVBYnNvbHV0ZVByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdlbmVyYXRlcyBhIG1hcHBpbmcgb2YgbmVzdGVkIGFyZ3VtZW50IGNoYWlucyBmcm9tIGEgbmVzdGVkXHJcbiAqIGNvbmZpZyBvYmplY3QuIFRoaXMgZnVuY3Rpb24gdHJhdmVyc2VzIGEgbmVzdGVkIG9iamVjdCBhbmQgY3JlYXRlcyBhIG1hcHBpbmdcclxuICogd2hlcmUgZWFjaCBrZXkgaXMgYW4gYXJndW1lbnQgbmFtZSAoZWl0aGVyIGZyb20gYGNsaU5hbWVgLCBgbGVnYWN5TmFtZWAsXHJcbiAqIG9yIHRoZSBvcmlnaW5hbCBrZXkpIGFuZCBlYWNoIHZhbHVlIGlzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY2hhaW5cclxuICogb2YgbmVzdGVkIHByb3BlcnRpZXMgbGVhZGluZyB0byB0aGF0IGFyZ3VtZW50LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbbmVzdGVkUHJvcHM9e31dIC0gVGhlIGFjY3VtdWxhdG9yIG9iamVjdCBmb3Igc3RvcmluZ1xyXG4gKiB0aGUgcmVzdWx0aW5nIGFyZ3VtZW50cyBjaGFpbnMuICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcHJvcENoYWluPScnXSAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzLFxyXG4gKiB1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbi4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gb2JqZWN0IG1hcHBpbmcgYXJndW1lbnQgbmFtZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZ1xyXG4gKiBuZXN0ZWQgcHJvcGVydHkgY2hhaW5zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzKGNvbmZpZywgbmVzdGVkUHJvcHMgPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgX2NyZWF0ZU5lc3RlZFByb3BzKGVudHJ5LCBuZXN0ZWRQcm9wcywgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBuZXN0ZWRQcm9wc1tlbnRyeS5jbGlOYW1lIHx8IGtleV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgaWYgKGVudHJ5LmxlZ2FjeU5hbWUgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2tleX1gLnN1YnN0cmluZygxKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIG9iamVjdCB3aXRoIG5lc3RlZCBhcmd1bWVudCBjaGFpbnNcclxuICByZXR1cm4gbmVzdGVkUHJvcHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSBnYXRoZXJzIHRoZSBuYW1lcyBvZiBwcm9wZXJ0aWVzIGZyb20gYSBjb25maWd1cmF0aW9uIG9iamVjdCB0aGF0XHJcbiAqIGNhbiBiZSB0cmVhdGVkIGFzIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZXNlIHByb3BlcnRpZXMgaGF2ZSB2YWx1ZXMgdGhhdFxyXG4gKiBhcmUgb2JqZWN0cyBhbmQgZG8gbm90IGNvbnRhaW4gZnVydGhlciBuZXN0ZWQgZGVwdGggd2hlbiBtZXJnaW5nIGFuIG9iamVjdFxyXG4gKiBjb250YWluaW5nIHRoZXNlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbYWJzb2x1dGVQcm9wcz1bXV0gLSBBbiBhcnJheSB0byBjb2xsZWN0IHRoZSBuYW1lc1xyXG4gKiBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxzdHJpbmc+fSBBbiBhcnJheSBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZVxyXG4gKiBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZUFic29sdXRlUHJvcHMoY29uZmlnLCBhYnNvbHV0ZVByb3BzID0gW10pIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudHlwZXMgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzXHJcbiAgICAgIF9jcmVhdGVBYnNvbHV0ZVByb3BzKGVudHJ5LCBhYnNvbHV0ZVByb3BzKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIHRoZSBvcHRpb24gY2FuIGJlIGFuIG9iamVjdCwgc2F2ZSBpdHMgdHlwZSBpbiB0aGUgYXJyYXlcclxuICAgICAgaWYgKGVudHJ5LnR5cGVzLmluY2x1ZGVzKCdPYmplY3QnKSkge1xyXG4gICAgICAgIGFic29sdXRlUHJvcHMucHVzaChrZXkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgYXJyYXkgd2l0aCB0aGUgbmFtZXMgb2YgYWJzb2x1dGUgcHJvcGVydGllc1xyXG4gIHJldHVybiBhYnNvbHV0ZVByb3BzO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRQcm9wcyxcclxuICBhYnNvbHV0ZVByb3BzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZFxyXG4gKiB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBgZW52c2AuIFdlIHNob3VsZCBub3QgdXNlIHRoZSBgcHJvY2Vzcy5lbnZgXHJcbiAqIGRpcmVjdGx5IGluIHRoZSBhcHBsaWNhdGlvbiBhcyB0aGVzZSB3b3VsZCBub3QgYmUgcGFyc2VkIHByb3Blcmx5LlxyXG4gKlxyXG4gKiBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSBwYXJzZWQgYW5kIHZhbGlkYXRlZCBvbmx5IG9uY2Ugd2hlblxyXG4gKiB0aGUgYXBwbGljYXRpb24gc3RhcnRzLiBXZSBzaG91bGQgd3JpdGUgYSBjdXN0b20gdmFsaWRhdG9yIG9yIGEgdHJhbnNmb3JtZXJcclxuICogZm9yIGVhY2ggb2YgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gcHVwcGV0ZWVyXHJcbiAgUFVQUEVURUVSX0FSR1M6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIGhpZ2hjaGFydHNcclxuICBISUdIQ0hBUlRTX1ZFUlNJT046IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PiAvXihsYXRlc3R8XFxkKyhcXC5cXGQrKXswLDJ9KSQvLnRlc3QodmFsdWUpIHx8IHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vJykgfHxcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcclxuICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgSElHSENIQVJUU19DRE5fVVJMLiBJdCBzaG91bGQgc3RhcnQgd2l0aCBodHRwOi8vIG9yIGh0dHBzOi8vLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTOiB2LmFycmF5KGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jb3JlU2NyaXB0cy52YWx1ZSksXHJcbiAgSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5tb2R1bGVTY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9JTkZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0lOU1RSOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9TVkc6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0JBVENIOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PVVRGSUxFOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9UWVBFOiB2LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLFxyXG4gIEVYUE9SVF9DT05TVFI6IHYuZW51bShbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddKSxcclxuICBFWFBPUlRfQjY0OiB2LmJvb2xlYW4oKSxcclxuICBFWFBPUlRfTk9fRE9XTkxPQUQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0dMT0JBTF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9USEVNRV9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19DVVNUT01fQ09ERTogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ0FMTEJBQ0s6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX1JFU09VUkNFUzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfTE9BRF9DT05GSUc6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUc6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlclxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1VQTE9BRF9MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgTE9HR0lOR19UT19GSUxFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gdWlcclxuICBVSV9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PREVfRU5WOiB2LmVudW0oWydkZXZlbG9wbWVudCcsICdwcm9kdWN0aW9uJywgJ3Rlc3QnXSksXHJcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0hBUkRfUkVTRVRfUEFHRTogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgTWFuYWdlcyBjb25maWd1cmF0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGJ5IGxvYWRpbmdcclxuICogYW5kIG1lcmdpbmcgb3B0aW9ucyBmcm9tIG11bHRpcGxlIHNvdXJjZXMsIHN1Y2ggYXMgZGVmYXVsdCBzZXR0aW5ncyxcclxuICogZW52aXJvbm1lbnQgdmFyaWFibGVzLCB1c2VyLXByb3ZpZGVkIG9wdGlvbnMsIGFuZCBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKiBFbnN1cmVzIHRoZSBnbG9iYWwgb3B0aW9ucyBhcmUgdXAtdG8tZGF0ZSB3aXRoIHRoZSBoaWdoZXN0IHByaW9yaXR5IHZhbHVlcy5cclxuICogUHJvdmlkZXMgZnVuY3Rpb25zIGZvciBhY2Nlc3NpbmcgYW5kIHVwZGF0aW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgaXNPYmplY3QsIGRlZXBDb3B5LCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuaW1wb3J0IHsgZGVmYXVsdENvbmZpZywgbmVzdGVkUHJvcHMsIGFic29sdXRlUHJvcHMgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0R2xvYmFsT3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zIG9mIHRoZSBzZXJ2ZXIgaW5zdGFuY2Ugb2JqZWN0XHJcbiAqIG9yIGl0cyBjb3B5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRSZWZlcmVuY2U9dHJ1ZV0gLSBPcHRpb25hbCBwYXJhbWV0ZXIgdG8gZGVjaWRlIHdoZXRoZXJcclxuICogdG8gcmV0dXJuIHRoZSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zIG9mIHRoZSBzZXJ2ZXIgaW5zdGFuY2Ugb2JqZWN0XHJcbiAqIG9yIHJldHVybiBhIGNvcHkgb2YgaXQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIHRydWUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zIG9mIHRoZSBzZXJ2ZXIgaW5zdGFuY2VcclxuICogb2JqZWN0IG9yIGl0cyBjb3B5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoZ2V0UmVmZXJlbmNlID0gdHJ1ZSkge1xyXG4gIHJldHVybiBnZXRSZWZlcmVuY2UgPyBnbG9iYWxPcHRpb25zIDogZGVlcENvcHkoZ2xvYmFsT3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBnbG9iYWwgb3B0aW9ucyBvZiB0aGUgZXhwb3J0IHNlcnZlciBpbnN0YW5jZSwga2VlcGluZyB0aGUgcHJpbmNpcGxlXHJcbiAqIG9mIHRoZSBvcHRpb25zIGxvYWQgcHJpb3JpdHkgZnJvbSBhbGwgYXZhaWxhYmxlIHNvdXJjZXMuIEl0IGFjY2VwdHMgb3B0aW9uYWxcclxuICogYGN1c3RvbU9wdGlvbnNgIG9iamVjdCBhbmQgYGNsaUFyZ3NgIGFycmF5IHdpdGggYXJndW1lbnRzIGZyb20gdGhlIENMSS4gVGhlc2VcclxuICogb3B0aW9ucyB3aWxsIGJlIHZhbGlkYXRlZCBhbmQgYXBwbGllZCBpZiBwcm92aWRlZC5cclxuICpcclxuICogVGhlIHByaW9yaXR5IG9yZGVyIG9mIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBPcHRpb25zIGZyb20gdGhlIGBsaWIvc2NoZW1hcy9jb25maWcuanNgIGZpbGUgKGRlZmF1bHQgdmFsdWVzKS5cclxuICogMi4gT3B0aW9ucyBmcm9tIGEgY3VzdG9tIEpTT04gZmlsZSAobG9hZGVkIGJ5IHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgKHRoZSBgLmVudmAgZmlsZSkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgZmlyc3QgcGFyYW1ldGVyIChieSBkZWZhdWx0IGFuIGVtcHR5IG9iamVjdCkuXHJcbiAqIDUuIE9wdGlvbnMgZnJvbSB0aGUgQ0xJLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2N1c3RvbU9wdGlvbnM9e31dIC0gT3B0aW9uYWwgY3VzdG9tIG9wdGlvbnMgZm9yIGFkZGl0aW9uYWxcclxuICogY29uZmlndXJhdGlvbi4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbY2xpQXJncz1bXV0gLSBPcHRpb25hbCBjb21tYW5kIGxpbmUgYXJndW1lbnRzXHJcbiAqIGZvciBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24uIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFttb2RpZnlHbG9iYWw9ZmFsc2VdIC0gT3B0aW9uYWwgcGFyYW1ldGVyIHRvIGRlY2lkZVxyXG4gKiB3aGV0aGVyIHRvIHVwZGF0ZSBhbmQgcmV0dXJuIHRoZSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9mIHRoZSBzZXJ2ZXIgaW5zdGFuY2Ugb2JqZWN0IG9yIHJldHVybiBhIGNvcHkgb2YgaXQuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGZhbHNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnZW5lcmFsIG9wdGlvbnMgb2JqZWN0LCByZWZsZWN0aW5nIHRoZSBtZXJnZWRcclxuICogY29uZmlndXJhdGlvbiBmcm9tIGFsbCBhdmFpbGFibGUgc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRPcHRpb25zKFxyXG4gIGN1c3RvbU9wdGlvbnMgPSB7fSxcclxuICBjbGlBcmdzID0gW10sXHJcbiAgbW9kaWZ5R2xvYmFsID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gT2JqZWN0IGZvciBvcHRpb25zIGxvYWRlZCB2aWEgdGhlIGBsb2FkQ29uZmlnYCBvcHRpb25cclxuICBsZXQgY29uZmlnT3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBPYmplY3QgZm9yIG9wdGlvbnMgZnJvbSB0aGUgQ0xJXHJcbiAgbGV0IGNsaU9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzLmxlbmd0aCkge1xyXG4gICAgLy8gR2V0IG9wdGlvbnMgZnJvbSB0aGUgY3VzdG9tIEpTT04gbG9hZGVkIHZpYSB0aGUgYGxvYWRDb25maWdgXHJcbiAgICBjb25maWdPcHRpb25zID0gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIENMSVxyXG4gICAgY2xpT3B0aW9ucyA9IF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncyk7XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgdGhlIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IG9yIGEgY29weSBvZiB0aGUgb2JqZWN0XHJcbiAgY29uc3QgZ2VuZXJhbE9wdGlvbnMgPSBnZXRPcHRpb25zKG1vZGlmeUdsb2JhbCk7XHJcblxyXG4gIC8vIFVwZGF0ZSB2YWx1ZXMgb2YgdGhlIGdlbmVyYWwgb3B0aW9ucyB3aXRoIHZhbHVlcyBmcm9tIGVhY2ggc291cmNlIHBvc3NpYmxlXHJcbiAgX3VwZGF0ZU9wdGlvbnMoXHJcbiAgICBkZWZhdWx0Q29uZmlnLFxyXG4gICAgZ2VuZXJhbE9wdGlvbnMsXHJcbiAgICBjb25maWdPcHRpb25zLFxyXG4gICAgY3VzdG9tT3B0aW9ucyxcclxuICAgIGNsaU9wdGlvbnNcclxuICApO1xyXG5cclxuICAvLyBSZXR1cm4gb3B0aW9uc1xyXG4gIHJldHVybiBnZW5lcmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGNvbnNpZGVyaW5nIGFic29sdXRlIHByb3BlcnRpZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtZXJnZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9yaWdpbmFsT3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTWVyZ2VkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBtZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zLCBuZXdPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGBuZXdPcHRpb25zYCBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG5ld09wdGlvbnMpKSB7XHJcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhuZXdPcHRpb25zKSkge1xyXG4gICAgICBvcmlnaW5hbE9wdGlvbnNba2V5XSA9XHJcbiAgICAgICAgaXNPYmplY3QodmFsdWUpICYmXHJcbiAgICAgICAgIWFic29sdXRlUHJvcHMuaW5jbHVkZXMoa2V5KSAmJlxyXG4gICAgICAgIG9yaWdpbmFsT3B0aW9uc1trZXldICE9PSB1bmRlZmluZWRcclxuICAgICAgICAgID8gbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9uc1trZXldLCB2YWx1ZSlcclxuICAgICAgICAgIDogdmFsdWUgIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICAgIDogb3JpZ2luYWxPcHRpb25zW2tleV07XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIHJlc3VsdCBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvbGRPcHRpb25zKSA9PT0gJ1tvYmplY3QgT2JqZWN0XScpIHtcclxuICAgIC8vIEl0ZXJhdGUgb3ZlciBlYWNoIGtleS12YWx1ZSBwYWlyIGluIHRoZSBvbGQtc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhvbGRPcHRpb25zKSkge1xyXG4gICAgICAvLyBJZiB0aGVyZSBpcyBhIG5lc3RlZCBtYXBwaW5nLCBzcGxpdCBpdCBpbnRvIGEgcHJvcGVydGllcyBjaGFpblxyXG4gICAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRQcm9wc1trZXldXHJcbiAgICAgICAgPyBuZXN0ZWRQcm9wc1trZXldLnNwbGl0KCcuJylcclxuICAgICAgICA6IFtdO1xyXG5cclxuICAgICAgLy8gSWYgaXQgaXMgdGhlIGxhc3QgcHJvcGVydHkgaW4gdGhlIGNoYWluLCBhc3NpZ24gdGhlIHZhbHVlLCBvdGhlcndpc2UsXHJcbiAgICAgIC8vIGNyZWF0ZSBvciByZXVzZSB0aGUgbmVzdGVkIG9iamVjdFxyXG4gICAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKFxyXG4gICAgICAgIChvYmosIHByb3AsIGluZGV4KSA9PlxyXG4gICAgICAgICAgKG9ialtwcm9wXSA9XHJcbiAgICAgICAgICAgIHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCA/IHZhbHVlIDogb2JqW3Byb3BdIHx8IHt9KSxcclxuICAgICAgICBuZXdPcHRpb25zXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIG5ldywgc3RydWN0dXJlZCBvcHRpb25zIG9iamVjdFxyXG4gIHJldHVybiBuZXdPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogVmFsaWRhdGVzLCBwYXJzZXMsIGFuZCBjaGVja3MgaWYgdGhlIHByb3ZpZGVkIGNvbmZpZyBpcyBhbGxvd2VkIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnXHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gY29uZmlnIC0gVGhlIGNvbmZpZyB0byBiZSB2YWxpZGF0ZWQgYW5kIHBhcnNlZCBhcyBhIHNldFxyXG4gKiBvZiBvcHRpb25zLiBNdXN0IGJlIGVpdGhlciBhbiBvYmplY3Qgb3IgYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3RvU3RyaW5nPWZhbHNlXSAtIFdoZXRoZXIgdG8gcmV0dXJuIGEgc3RyaW5naWZpZWQgdmVyc2lvblxyXG4gKiBvZiB0aGUgcGFyc2VkIGNvbmZpZy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgZmFsc2UuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2FsbG93RnVuY3Rpb25zPWZhbHNlXSAtIFdoZXRoZXIgdG8gYWxsb3cgZnVuY3Rpb25zXHJcbiAqIGluIHRoZSBwYXJzZWQgY29uZmlnLiBJZiB0cnVlLCBmdW5jdGlvbnMgYXJlIHByZXNlcnZlZC4gT3RoZXJ3aXNlLCB3aGVuXHJcbiAqIGEgZnVuY3Rpb24gaXMgZm91bmQsIG51bGwgaXMgcmV0dXJuZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGZhbHNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgZmFsc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRWZXJzaW9uKG5vTG9nbyA9IGZhbHNlKSB7XHJcbiAgLy8gR2V0IHBhY2thZ2UgdmVyc2lvbiBlaXRoZXIgZnJvbSBgLmVudmAgb3IgZnJvbSBgcGFja2FnZS5qc29uYFxyXG4gIGNvbnN0IHBhY2thZ2VWZXJzaW9uID0gSlNPTi5wYXJzZShcclxuICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ3BhY2thZ2UuanNvbicpKVxyXG4gICkudmVyc2lvbjtcclxuXHJcbiAgLy8gUHJpbnQgdGV4dCBvbmx5XHJcbiAgaWYgKG5vTG9nbykge1xyXG4gICAgY29uc29sZS5sb2coYEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciB2JHtwYWNrYWdlVmVyc2lvbn1gKTtcclxuICB9IGVsc2Uge1xyXG4gICAgLy8gUHJpbnQgdGhlIGxvZ29cclxuICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICdtc2cnLCAnc3RhcnR1cC5tc2cnKSkudG9TdHJpbmcoKS5ib2xkXHJcbiAgICAgICAgLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyBnbG9iYWwgb3B0aW9ucyBvYmplY3QgYmFzZWQgb24gcHJvdmlkZWRcclxuICogY29uZmlndXJhdGlvbiwgc2V0dGluZyB2YWx1ZXMgZnJvbSBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9pbml0R2xvYmFsT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvIGJlIHVzZWQgZm9yIGluaXRpYWxpemluZ1xyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBJbml0aWFsaXplZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmZ1bmN0aW9uIF9pbml0R2xvYmFsT3B0aW9ucyhjb25maWcpIHtcclxuICBjb25zdCBvcHRpb25zID0ge307XHJcblxyXG4gIC8vIFN0YXJ0IGluaXRpYWxpemluZyB0aGUgYG9wdGlvbnNgIG9iamVjdCByZWN1cnNpdmVseVxyXG4gIGZvciAoY29uc3QgW25hbWUsIGl0ZW1dIG9mIE9iamVjdC5lbnRyaWVzKGNvbmZpZykpIHtcclxuICAgIG9wdGlvbnNbbmFtZV0gPSBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoaXRlbSwgJ3ZhbHVlJylcclxuICAgICAgPyBpdGVtLnZhbHVlXHJcbiAgICAgIDogX2luaXRHbG9iYWxPcHRpb25zKGl0ZW0pO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBjcmVhdGVkIGBvcHRpb25zYCBvYmplY3RcclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgb3B0aW9ucyBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSB2YXJpb3VzIHNvdXJjZXMsIGZvbGxvd2luZyBhIHNwZWNpZmljXHJcbiAqIHByaW9yaXRpemF0aW9uIG9yZGVyLiBUaGUgZnVuY3Rpb24gY2hlY2tzIGZvciB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlclxyXG4gKiBvZiBwcmVjZWRlbmNlOiB0aGUgYGxvYWRDb25maWdgIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgZW52aXJvbm1lbnQgdmFyaWFibGVzLFxyXG4gKiBjdXN0b20gb3B0aW9ucywgYW5kIENMSSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCwgd2hpY2ggaW5jbHVkZXMgdGhlIGluaXRpYWxcclxuICogc2V0dGluZ3MgYW5kIG1ldGFkYXRhIGZvciBlYWNoIG9wdGlvbi4gVGhpcyBvYmplY3QgaXMgdXNlZCB0byBkZXRlcm1pbmVcclxuICogdGhlIHN0cnVjdHVyZSBhbmQgZGVmYXVsdCB2YWx1ZXMgZm9yIHRoZSBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0aGF0IHdpbGwgYmUgdXBkYXRlZCB3aXRoIHZhbHVlc1xyXG4gKiBmcm9tIG90aGVyIHNvdXJjZXMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWdPcHQgLSBUaGUgY29uZmlndXJhdGlvbiBvcHRpb25zIG9iamVjdCwgbG9hZGVkIHdpdGhcclxuICogdGhlIGBsb2FkQ29uZmlnYCBvcHRpb24sIHdoaWNoIG1heSBwcm92aWRlIHZhbHVlcyB0byBvdmVycmlkZSBkZWZhdWx0cy5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbU9wdCAtIFRoZSBjdXN0b20gb3B0aW9ucyBvYmplY3QsIHR5cGljYWxseSBjb250YWluaW5nXHJcbiAqIGFkZGl0aW9uYWwgYW5kIHVzZXItZGVmaW5lZCB2YWx1ZXMsIHdoaWNoIG1heSBvdmVycmlkZSBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjbGlPcHQgLSBUaGUgQ0xJIG9wdGlvbnMgb2JqZWN0LCB3aGljaCBtYXkgaW5jbHVkZSB2YWx1ZXNcclxuICogcHJvdmlkZWQgdGhyb3VnaCBjb21tYW5kLWxpbmUgYXJndW1lbnRzIGFuZCBoYXMgdGhlIGhpZ2hlc3QgcHJlY2VkZW5jZSBhbW9uZ1xyXG4gKiBvcHRpb25zLlxyXG4gKi9cclxuZnVuY3Rpb24gX3VwZGF0ZU9wdGlvbnMoY29uZmlnLCBvcHRpb25zLCBjb25maWdPcHQsIGN1c3RvbU9wdCwgY2xpT3B0KSB7XHJcbiAgT2JqZWN0LmtleXMoY29uZmlnKS5mb3JFYWNoKChrZXkpID0+IHtcclxuICAgIC8vIEdldCB0aGUgY29uZmlnIGVudHJ5IG9mIGEgc3BlY2lmaWMgb3B0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIEdhdGhlciB2YWx1ZXMgZm9yIHRoZSBvcHRpb25zIGZyb20gZXZlcnkgcG9zc2libGUgc291cmNlLCBpZiBleGlzdHNcclxuICAgIGNvbnN0IGNvbmZpZ1ZhbCA9IGNvbmZpZ09wdCAmJiBjb25maWdPcHRba2V5XTtcclxuICAgIGNvbnN0IGN1c3RvbVZhbCA9IGN1c3RvbU9wdCAmJiBjdXN0b21PcHRba2V5XTtcclxuICAgIGNvbnN0IGNsaVZhbCA9IGNsaU9wdCAmJiBjbGlPcHRba2V5XTtcclxuXHJcbiAgICAvLyBJZiB0aGUgdmFsdWUgbm90IGZvdW5kLCBuZWVkIHRvIGdvIGRlZXBlclxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgX3VwZGF0ZU9wdGlvbnMoZW50cnksIG9wdGlvbnNba2V5XSwgY29uZmlnVmFsLCBjdXN0b21WYWwsIGNsaVZhbCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gY3VzdG9tIEpTT04gb3B0aW9ucyBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY29uZmlnVmFsICE9PSB1bmRlZmluZWQgJiYgY29uZmlnVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY29uZmlnVmFsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzIGV4aXN0cywgaXQgdGFrZSBwcmVjZWRlbmNlXHJcbiAgICAgIGNvbnN0IGVudlZhbCA9IGVudnNbZW50cnkuZW52TGlua107XHJcbiAgICAgIGlmIChlbnRyeS5lbnZMaW5rIGluIGVudnMgJiYgZW52VmFsICE9PSB1bmRlZmluZWQgJiYgZW52VmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gZW52VmFsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gdXNlciBvcHRpb25zIGV4aXN0cywgaXQgdGFrZSBwcmVjZWRlbmNlXHJcbiAgICAgIGlmIChjdXN0b21WYWwgIT09IHVuZGVmaW5lZCAmJiBjdXN0b21WYWwgIT09IG51bGwpIHtcclxuICAgICAgICBvcHRpb25zW2tleV0gPSBjdXN0b21WYWw7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGEgdmFsdWUgZnJvbSBDTEkgb3B0aW9ucyBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY2xpVmFsICE9PSB1bmRlZmluZWQgJiYgY2xpVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY2xpVmFsO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmdcclxuICogd2l0aCB0aGUgb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy4gSW4gb3JkZXIgZm9yIGEgZnVuY3Rpb25cclxuICogdG8gYmUgcHJlc2VydmVkLCBpdCBuZWVkcyB0byBmb2xsb3cgdGhlIGZvcm1hdCBgZnVuY3Rpb24gKC4uLikgey4uLn1gLlxyXG4gKiBTdWNoIGEgZnVuY3Rpb24gY2FuIGFsc28gYmUgc3RyaW5naWZpZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWRcclxuICogaW4gdGhlIG91dHB1dC4gT3RoZXJ3aXNlIGFuIGVycm9yIGlzIHRocm93bi5cclxuICogQHBhcmFtIHtib29sZWFufSBzdHJpbmdpZnlGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBzYXZlZFxyXG4gKiBhcyBzdHJpbmdzLiBUaGUgYGFsbG93RnVuY3Rpb25zYCBtdXN0IGJlIHNldCB0byB0cnVlIGFzIHdlbGwgZm9yIHRoaXMgdG8gdGFrZVxyXG4gKiBhbiBlZmZlY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgd2hlbiBmdW5jdGlvbnMgYXJlIG5vdCBhbGxvd2VkIGJ1dCBhcmVcclxuICogZm91bmQgaW4gcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX29wdGlvbnNTdHJpbmdpZnkob3B0aW9ucywgYWxsb3dGdW5jdGlvbnMsIHN0cmluZ2lmeUZ1bmN0aW9ucykge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAoXywgdmFsdWUpID0+IHtcclxuICAgIC8vIFRyaW0gc3RyaW5nIHZhbHVlc1xyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgdmFsdWUgaXMgYSBmdW5jdGlvbiBvciBzdHJpbmdpZmllZCBmdW5jdGlvblxyXG4gICAgaWYgKFxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgfHxcclxuICAgICAgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpICYmXHJcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBJZiBhbGxvd0Z1bmN0aW9ucyBpcyBzZXQgdG8gdHJ1ZSwgcHJlc2VydmUgZnVuY3Rpb25zXHJcbiAgICAgIGlmIChhbGxvd0Z1bmN0aW9ucykge1xyXG4gICAgICAgIC8vIEJhc2VkIG9uIHRoZSBgc3RyaW5naWZ5RnVuY3Rpb25zYCBvcHRpb25zLCBzZXQgZnVuY3Rpb24gdmFsdWVzXHJcbiAgICAgICAgcmV0dXJuIHN0cmluZ2lmeUZ1bmN0aW9uc1xyXG4gICAgICAgICAgPyAvLyBBcyBzdHJpbmdpZmllZCBmdW5jdGlvbnNcclxuICAgICAgICAgICAgYFwiRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOXCJgXHJcbiAgICAgICAgICA6IC8vIEFzIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOYDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaHJvdyBhbiBlcnJvciBvdGhlcndpc2VcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgc2ltcGx5IHJldHVybiB0aGUgdmFsdWVcclxuICAgIHJldHVybiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIHN0cmluZ2lmeUZ1bmN0aW9ucyA/IC9cXFxcXCJFWFBfRlVOfEVYUF9GVU5cXFxcXCIvZyA6IC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHByb3ZpZGVkIHZpYVxyXG4gKiB0aGUgYGxvYWRDb25maWdgIG9wdGlvbiBpbiB0aGUgY29tbWFuZC1saW5lIGFyZ3VtZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2FkQ29uZmlnRmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjbGlBcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyB0byBzZWFyY2hcclxuICogZm9yIHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uIHdhcyB1c2VkXHJcbiAgY29uc3QgY29uZmlnSW5kZXggPSBjbGlBcmdzLmZpbmRJbmRleChcclxuICAgIChhcmcpID0+IGFyZy5yZXBsYWNlKC8tL2csICcnKSA9PT0gJ2xvYWRDb25maWcnXHJcbiAgKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uIHZhbHVlXHJcbiAgY29uc3QgY29uZmlnRmlsZU5hbWUgPSBjb25maWdJbmRleCA+IC0xICYmIGNsaUFyZ3NbY29uZmlnSW5kZXggKyAxXTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGBsb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBMb2FkIGFuIG9wdGlvbmFsIGN1c3RvbSBKU09OIGNvbmZpZyBmaWxlXHJcbiAgICAgIHJldHVybiBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpKSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgMixcclxuICAgICAgICBlcnJvcixcclxuICAgICAgICBgW2NvbmZpZ10gVW5hYmxlIHRvIGxvYWQgdGhlIGNvbmZpZ3VyYXRpb24gZnJvbSB0aGUgJHtjb25maWdGaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBObyBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gcmV0dXJuXHJcbiAgcmV0dXJuIHt9O1xyXG59XHJcblxyXG4vKipcclxuICogUGFyc2VzIGNvbW1hbmQtbGluZSBhcmd1bWVudHMgYW5kIHBhaXJzIGVhY2ggYXJndW1lbnQgd2l0aCBpdHMgY29ycmVzcG9uZGluZ1xyXG4gKiBvcHRpb24gaW4gdGhlIGNvbmZpZ3VyYXRpb24uIFRoZSB2YWx1ZXMgYXJlIHN0cnVjdHVyZWQgaW50byBhIG5lc3RlZCBvcHRpb25zXHJcbiAqIG9iamVjdCwgYmFzZWQgb24gcHJlZGVmaW5lZCBtYXBwaW5ncy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBuZXN0ZWRQcm9wcyAtIEFuIGFycmF5IG9mIG5lc3RpbmcgbGV2ZWwgZm9yIGFsbFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjbGlBcmdzIC0gQW4gYXJyYXkgb2YgY29tbWFuZC1saW5lIGFyZ3VtZW50c1xyXG4gKiBjb250YWluaW5nIG9wdGlvbnMgYW5kIHRoZWlyIGFzc29jaWF0ZWQgdmFsdWVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0IHdoZXJlIGVhY2ggb3B0aW9uIGZyb21cclxuICogdGhlIGNvbW1hbmQtbGluZSBpcyBwYWlyZWQgd2l0aCBpdHMgdmFsdWUsIHN0cnVjdHVyZWQgaW50byBuZXN0ZWQgb2JqZWN0c1xyXG4gKiBhcyBkZWZpbmVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX3BhaXJBcmd1bWVudFZhbHVlKG5lc3RlZFByb3BzLCBjbGlBcmdzKSB7XHJcbiAgLy8gQW4gZW1wdHkgb2JqZWN0IHRvIGNvbGxlY3QgYW5kIHN0cnVjdHVyaXplIGRhdGEgZnJvbSB0aGUgYXJnc1xyXG4gIGNvbnN0IGNsaU9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gQ3ljbGUgdGhyb3VnaCBhbGwgQ0xJIGFyZ3MgYW5kIGZpbHRlciB0aGVtXHJcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBjbGlBcmdzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICBjb25zdCBvcHRpb24gPSBjbGlBcmdzW2ldLnJlcGxhY2UoLy0vZywgJycpO1xyXG5cclxuICAgIC8vIEZpbmQgdGhlIHJpZ2h0IHBsYWNlIGZvciBwcm9wZXJ0eSdzIHZhbHVlXHJcbiAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRQcm9wc1tvcHRpb25dXHJcbiAgICAgID8gbmVzdGVkUHJvcHNbb3B0aW9uXS5zcGxpdCgnLicpXHJcbiAgICAgIDogW107XHJcblxyXG4gICAgLy8gQ3JlYXRlIG9wdGlvbnMgb2JqZWN0IHdpdGggdmFsdWVzIGZyb20gQ0xJIGZvciBsYXRlciBwYXJzaW5nIGFuZCBtZXJnaW5nXHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XHJcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcclxuICAgICAgICBjb25zdCB2YWx1ZSA9IGNsaUFyZ3NbKytpXTtcclxuICAgICAgICBpZiAoIXZhbHVlKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGBbY29uZmlnXSBNaXNzaW5nIHZhbHVlIGZvciB0aGUgQ0xJICctLSR7b3B0aW9ufScgYXJndW1lbnQuIFVzaW5nIHRoZSBkZWZhdWx0IHZhbHVlLmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIG9ialtwcm9wXSA9IHZhbHVlIHx8IG51bGw7XHJcbiAgICAgIH0gZWxzZSBpZiAob2JqW3Byb3BdID09PSB1bmRlZmluZWQpIHtcclxuICAgICAgICBvYmpbcHJvcF0gPSB7fTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xyXG4gICAgfSwgY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcGFyc2VkIENMSSBvcHRpb25zXHJcbiAgcmV0dXJuIGNsaU9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSB0cmF2ZXJzZXMgdGhlIG9wdGlvbnMgb2JqZWN0IHRvIHByaW50IHRoZSB1c2FnZSBpbmZvcm1hdGlvblxyXG4gKiBmb3IgZWFjaCBvcHRpb24gY2F0ZWdvcnkgYW5kIGluZGl2aWR1YWwgb3B0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2N5Y2xlQ2F0ZWdvcmllc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIENMSSBvcHRpb25zLlxyXG4gKiBJdCBtYXkgaW5jbHVkZSBuZXN0ZWQgY2F0ZWdvcmllcyBhbmQgaW5kaXZpZHVhbCBvcHRpb25zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb25zKSB7XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgb3B0aW9uXSBvZiBPYmplY3QuZW50cmllcyhvcHRpb25zKSkge1xyXG4gICAgLy8gSWYgdGhlIGN1cnJlbnQgZW50cnkgaXMgYSBjYXRlZ29yeSBhbmQgbm90IGEgbGVhZiBvcHRpb24sIHJlY3Vyc2UgaW50byBpdFxyXG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob3B0aW9uLCAndmFsdWUnKSkge1xyXG4gICAgICBfY3ljbGVDYXRlZ29yaWVzKG9wdGlvbik7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBQcmVwYXJlIGRlc2NyaXB0aW9uXHJcbiAgICAgIGNvbnN0IGRlc2NOYW1lID0gYCAtLSR7b3B0aW9uLmNsaU5hbWUgfHwgbmFtZX1gO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB2YWx1ZVxyXG4gICAgICBsZXQgb3B0aW9uVmFsdWUgPSBvcHRpb24udmFsdWU7XHJcblxyXG4gICAgICAvLyBQcmVwYXJlIHZhbHVlIGZvciBvcHRpb24gdGhhdCBpcyBub3QgbnVsbCBhbmQgaXMgYXJyYXkgb2Ygc3RyaW5nc1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmdbXScpKSB7XHJcbiAgICAgICAgb3B0aW9uVmFsdWUgPVxyXG4gICAgICAgICAgJ1snICsgb3B0aW9uVmFsdWUubWFwKChpdGVtKSA9PiBgJyR7aXRlbX0nYCkuam9pbignLCAnKSArICddJztcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGEgc3RyaW5nXHJcbiAgICAgIGlmIChvcHRpb25WYWx1ZSAhPT0gbnVsbCAmJiBvcHRpb24udHlwZXMuaW5jbHVkZXMoJ3N0cmluZycpKSB7XHJcbiAgICAgICAgb3B0aW9uVmFsdWUgPSBgJyR7b3B0aW9uVmFsdWV9J2A7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERpc3BsYXkgY29ycmVjdGx5IGFsaWduZWQgbWVzc2FnZXNcclxuICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgZGVzY05hbWUuZ3JlZW4sXHJcbiAgICAgICAgYCR7KCc8JyArIG9wdGlvbi50eXBlcy5qb2luKCd8JykgKyAnPicpLnllbGxvd31gLFxyXG4gICAgICAgIGAke1N0cmluZyhvcHRpb25WYWx1ZSkuYm9sZH1gLmJsdWUsXHJcbiAgICAgICAgYC0gJHtvcHRpb24uZGVzY3JpcHRpb259LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHNldE9wdGlvbnMsXHJcbiAgbWVyZ2VPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuICBpc0FsbG93ZWRDb25maWcsXHJcbiAgcHJpbnRMaWNlbnNlLFxyXG4gIHByaW50VXNhZ2UsXHJcbiAgcHJpbnRWZXJzaW9uXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBIVFRQIHV0aWxpdHkgbW9kdWxlIGZvciBmZXRjaGluZyBhbmQgcG9zdGluZyBkYXRhLiBTdXBwb3J0cyBib3RoXHJcbiAqIEhUVFAgYW5kIEhUVFBTIHByb3RvY29scywgcHJvdmlkaW5nIG1ldGhvZHMgdG8gbWFrZSBHRVQgYW5kIFBPU1QgcmVxdWVzdHNcclxuICogd2l0aCBjdXN0b21pemFibGUgb3B0aW9ucy4gSW5jbHVkZXMgcHJvdG9jb2wgZGV0ZXJtaW5hdGlvbiBiYXNlZCBvbiBVUkxcclxuICogYW5kIGF1Z21lbnRzIHJlc3BvbnNlIG9iamVjdHMgd2l0aCBhICd0ZXh0JyBwcm9wZXJ0eSBmb3IgZWFzaWVyIGRhdGEgYWNjZXNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgZGF0YSBmcm9tIHRoZSBzcGVjaWZpZWQgVVJMIHVzaW5nIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGZldGNoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGZldGNoIGRhdGEgZnJvbS5cclxuICogQHBhcmFtIHtPYmplY3R9IFtyZXF1ZXN0T3B0aW9ucz17fV0gLSBPcHRpb25zIGZvciB0aGUgSFRUUC9IVFRQUyByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBIVFRQL0hUVFBTIHJlc3BvbnNlXHJcbiAqIG9iamVjdCB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmZXRjaCh1cmwsIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgX2dldFByb3RvY29sTW9kdWxlKHVybClcclxuICAgICAgLmdldCh1cmwsIHJlcXVlc3RPcHRpb25zLCAocmVzcG9uc2UpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIXJlc3BvbnNlRGF0YSkge1xyXG4gICAgICAgICAgICByZWplY3QoJ05vdGhpbmcgd2FzIGZldGNoZWQgZnJvbSB0aGUgVVJMLicpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZW5kcyBhIFBPU1QgcmVxdWVzdCB0byB0aGUgc3BlY2lmaWVkIFVSTCB3aXRoIHRoZSBwcm92aWRlZCBKU09OIGJvZHkgdXNpbmdcclxuICogZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBzZW5kIHRoZSBQT1NUIHJlcXVlc3QgdG8uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbYm9keT17fV0gLSBUaGUgSlNPTiBib2R5IHRvIGluY2x1ZGUgaW4gdGhlIFBPU1QgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3QodXJsLCBib2R5ID0ge30sIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KGJvZHkpO1xyXG5cclxuICAgIC8vIFNldCBkZWZhdWx0IGhlYWRlcnMgYW5kIG1lcmdlIHdpdGggcmVxdWVzdE9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSBPYmplY3QuYXNzaWduKFxyXG4gICAgICB7XHJcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXHJcbiAgICAgICAgaGVhZGVyczoge1xyXG4gICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcclxuICAgICAgICAgICdDb250ZW50LUxlbmd0aCc6IGRhdGEubGVuZ3RoXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICByZXF1ZXN0T3B0aW9uc1xyXG4gICAgKTtcclxuXHJcbiAgICBjb25zdCByZXF1ZXN0ID0gX2dldFByb3RvY29sTW9kdWxlKHVybClcclxuICAgICAgLnJlcXVlc3QodXJsLCBvcHRpb25zLCAocmVzcG9uc2UpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAvLyBXcml0ZSB0aGUgcmVxdWVzdCBib2R5IGFuZCBlbmQgdGhlIHJlcXVlc3RcclxuICAgIHJlcXVlc3Qud3JpdGUoZGF0YSk7XHJcbiAgICByZXF1ZXN0LmVuZCgpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgYmFzZWQgb24gdGhlIHByb3ZpZGVkIFVSTC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9nZXRQcm90b2NvbE1vZHVsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBkZXRlcm1pbmUgdGhlIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgKGh0dHAgb3IgaHR0cHMpLlxyXG4gKi9cclxuZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlKHVybCkge1xyXG4gIHJldHVybiB1cmwuc3RhcnRzV2l0aCgnaHR0cHMnKSA/IGh0dHBzIDogaHR0cDtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGZldGNoLFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQSBjdXN0b20gZXJyb3IgY2xhc3MgZm9yIGhhbmRsaW5nIGV4cG9ydC1yZWxhdGVkIGVycm9ycy4gRXh0ZW5kcyB0aGUgbmF0aXZlXHJcbiAqIGBFcnJvcmAgY2xhc3MgdG8gaW5jbHVkZSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgbGlrZSBzdGF0dXMgY29kZSBhbmQgc3RhY2tcclxuICogdHJhY2UgZGV0YWlscy5cclxuICovXHJcbmNsYXNzIEV4cG9ydEVycm9yIGV4dGVuZHMgRXJyb3Ige1xyXG4gIC8qKlxyXG4gICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIFRoZSBlcnJvciBtZXNzYWdlIHRvIGJlIGRpc3BsYXllZC5cclxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzQ29kZSAtIE9wdGlvbmFsIEhUVFAgc3RhdHVzIGNvZGUgYXNzb2NpYXRlZFxyXG4gICAqIHdpdGggdGhlIGVycm9yIChlLmcuLCA0MDAsIDUwMCkuXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSwgc3RhdHVzQ29kZSkge1xyXG4gICAgc3VwZXIoKTtcclxuXHJcbiAgICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gICAgdGhpcy5zdGFja01lc3NhZ2UgPSBtZXNzYWdlO1xyXG5cclxuICAgIGlmIChzdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IHN0YXR1c0NvZGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXRzIGFkZGl0aW9uYWwgZXJyb3IgZGV0YWlscyBiYXNlZCBvbiBhbiBleGlzdGluZyBlcnJvciBvYmplY3QuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIEFuIGVycm9yIG9iamVjdCBjb250YWluaW5nIGRldGFpbHMgdG8gcG9wdWxhdGVcclxuICAgKiB0aGUgYEV4cG9ydEVycm9yYCBpbnN0YW5jZS5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtFeHBvcnRFcnJvcn0gVGhlIHVwZGF0ZWQgaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAgY2xhc3MuXHJcbiAgICovXHJcbiAgc2V0RXJyb3IoZXJyb3IpIHtcclxuICAgIHRoaXMuZXJyb3IgPSBlcnJvcjtcclxuXHJcbiAgICBpZiAoZXJyb3IubmFtZSkge1xyXG4gICAgICB0aGlzLm5hbWUgPSBlcnJvci5uYW1lO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChlcnJvci5zdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XHJcbiAgICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcclxuICAgICAgdGhpcy5zdGFjayA9IGVycm9yLnN0YWNrO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgRXhwb3J0RXJyb3I7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgY2FjaGUgbWFuYWdlciBpcyByZXNwb25zaWJsZSBmb3IgaGFuZGxpbmcgYW5kIG1hbmFnaW5nXHJcbiAqIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYWxvbmcgd2l0aCBpdHMgZGVwZW5kZW5jaWVzLiBJdCBlbnN1cmVzIHRoYXQgdGhlc2VcclxuICogcmVzb3VyY2VzIGFyZSBzdG9yZWQgYW5kIHJldHJpZXZlZCBlZmZpY2llbnRseSB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZVxyXG4gKiBhbmQgcmVkdWNlIHJlZHVuZGFudCBuZXR3b3JrIHJlcXVlc3RzLiBUaGUgY2FjaGUgaXMgc3RvcmVkIGluIHRoZSBgLmNhY2hlYFxyXG4gKiBkaXJlY3RvcnkgYnkgZGVmYXVsdCwgd2hpY2ggc2VydmVzIGFzIGEgZGVkaWNhdGVkIGZvbGRlciBmb3Iga2VlcGluZyBjYWNoZWRcclxuICogZmlsZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IEh0dHBzUHJveHlBZ2VudCB9IGZyb20gJ2h0dHBzLXByb3h5LWFnZW50JztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGZldGNoIH0gZnJvbSAnLi9mZXRjaC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gT2JqZWN0IGNvbnRhaW5pbmcgYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgc2VydmVyLnByb3h5YFxyXG4gKiBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGUoXHJcbiAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgc2VydmVyUHJveHlPcHRpb25zXHJcbikge1xyXG4gIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgLy8gR2V0IHRoZSBjYWNoZSBwYXRoXHJcbiAgY29uc3QgY2FjaGVQYXRoID0gZ2V0Q2FjaGVQYXRoKCk7XHJcblxyXG4gIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgY29uc3Qgc291cmNlUGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnc291cmNlcy5qcycpO1xyXG5cclxuICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICFleGlzdHNTeW5jKGNhY2hlUGF0aCkgJiYgbWtkaXJTeW5jKGNhY2hlUGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XHJcblxyXG4gIC8vIEZldGNoIGFsbCB0aGUgc2NyaXB0cyBlaXRoZXIgaWYgdGhlIGBtYW5pZmVzdC5qc29uYCBkb2VzIG5vdCBleGlzdFxyXG4gIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICBpZiAoIWV4aXN0c1N5bmMobWFuaWZlc3RQYXRoKSB8fCBoaWdoY2hhcnRzT3B0aW9ucy5mb3JjZUZldGNoKSB7XHJcbiAgICBsb2coMywgJ1tjYWNoZV0gRmV0Y2hpbmcgYW5kIGNhY2hpbmcgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMuJyk7XHJcbiAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICAgICAgc291cmNlUGF0aFxyXG4gICAgKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBSZWFkIHRoZSBtYW5pZmVzdCBKU09OXHJcbiAgICBjb25zdCBtYW5pZmVzdCA9IEpTT04ucGFyc2UocmVhZEZpbGVTeW5jKG1hbmlmZXN0UGF0aCkpO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZSBtb2R1bGVzIGlzIGFuIGFycmF5LCBpZiBzbywgd2UgcmV3cml0ZSBpdCB0byBhIG1hcCB0byBtYWtlXHJcbiAgICAvLyBpdCBlYXNpZXIgdG8gcmVzb2x2ZSBtb2R1bGVzLlxyXG4gICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICBjb25zdCBtb2R1bGVNYXAgPSB7fTtcclxuICAgICAgbWFuaWZlc3QubW9kdWxlcy5mb3JFYWNoKChtKSA9PiAobW9kdWxlTWFwW21dID0gMSkpO1xyXG4gICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgYWN0dWFsIG51bWJlciBvZiBzY3JpcHRzIHRvIGJlIGZldGNoZWRcclxuICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9IGhpZ2hjaGFydHNPcHRpb25zO1xyXG4gICAgY29uc3QgbnVtYmVyT2ZNb2R1bGVzID1cclxuICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgaGlnaGNoYXJ0cyBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAvLyBJZiB0aGVyZSBhcmUgY2hhbmdlcywgZmV0Y2ggcmVxdWVzdGVkIG1vZHVsZXMgYW5kIHByb2R1Y3RzLFxyXG4gICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgICdbY2FjaGVdIEEgSGlnaGNoYXJ0cyB2ZXJzaW9uIG1pc21hdGNoIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC4nXHJcbiAgICAgICk7XHJcbiAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgfSBlbHNlIGlmIChPYmplY3Qua2V5cyhtYW5pZmVzdC5tb2R1bGVzIHx8IHt9KS5sZW5ndGggIT09IG51bWJlck9mTW9kdWxlcykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICApO1xyXG4gICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENoZWNrIGVhY2ggbW9kdWxlLCBpZiBhbnl0aGluZyBpcyBtaXNzaW5nIHJlZmV0Y2ggZXZlcnl0aGluZ1xyXG4gICAgICByZXF1ZXN0VXBkYXRlID0gKG1vZHVsZVNjcmlwdHMgfHwgW10pLnNvbWUoKG1vZHVsZU5hbWUpID0+IHtcclxuICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgaWYgKHJlcXVlc3RVcGRhdGUpIHtcclxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCBfdXBkYXRlQ2FjaGUoXHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgIHNvdXJjZVBhdGhcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxvZygzLCAnW2NhY2hlXSBEZXBlbmRlbmN5IGNhY2hlIGlzIHVwIHRvIGRhdGUsIHByb2NlZWRpbmcuJyk7XHJcblxyXG4gICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgIGNhY2hlLnNvdXJjZXMgPSByZWFkRmlsZVN5bmMoc291cmNlUGF0aCwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgIC8vIEdldCBjdXJyZW50IG1vZHVsZXMgbWFwXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICAgIGNhY2hlLmhjVmVyc2lvbiA9IGV4dHJhY3RWZXJzaW9uKGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRmluYWxseSwgc2F2ZSB0aGUgbmV3IG1hbmlmZXN0LCB3aGljaCBpcyBiYXNpY2FsbHkgb3VyIGN1cnJlbnQgY29uZmlnXHJcbiAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgYXdhaXQgX3NhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHNPcHRpb25zLCBmZXRjaGVkTW9kdWxlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIEdldCB0aGUgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyB0byB1cGRhdGUgdG8gdGhlIG5ldyB2ZXJzaW9uXHJcbiAgY29uc3Qgb3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRvIHRoZSBuZXcgdmVyc2lvblxyXG4gIG9wdGlvbnMuaGlnaGNoYXJ0cy52ZXJzaW9uID0gbmV3VmVyc2lvbjtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgpOyAvLyAjNTYyXHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGEgc2luZ2xlIHNjcmlwdCBhbmQgdXBkYXRlcyB0aGUgYGZldGNoZWRNb2R1bGVzYCBhY2NvcmRpbmdseS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHQgLSBBIHBhdGggdG8gc2NyaXB0IHRvIGdldC5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gQWRkaXRpb25hbCBvcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnRcclxuICogdG8gdXNlIGZvciBhIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtzaG91bGRUaHJvd0Vycm9yPWZhbHNlXSAtIEEgZmxhZyB0byBpbmRpY2F0ZSBpZiB0aGUgZXJyb3JcclxuICogc2hvdWxkIGJlIHRocm93bi4gVGhpcyBzaG91bGQgYmUgdXNlZCBvbmx5IGZvciB0aGUgY29yZSBzY3JpcHRzLiBUaGUgZGVmYXVsdFxyXG4gKiB2YWx1ZSBpcyBmYWxzZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIHRleHQgcmVwcmVzZW50YXRpb25cclxuICogb2YgdGhlIGZldGNoZWQgc2NyaXB0LlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYSBwcm9ibGVtXHJcbiAqIHdpdGggZmV0Y2hpbmcgdGhlIHNjcmlwdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoXHJcbiAgc2NyaXB0LFxyXG4gIHJlcXVlc3RPcHRpb25zLFxyXG4gIGZldGNoZWRNb2R1bGVzLFxyXG4gIHNob3VsZFRocm93RXJyb3IgPSBmYWxzZVxyXG4pIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSAuanMgZnJvbSB0aGUgY3VzdG9tIHN0cmluZ3NcclxuICBpZiAoc2NyaXB0LmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgc2NyaXB0ID0gc2NyaXB0LnN1YnN0cmluZygwLCBzY3JpcHQubGVuZ3RoIC0gMyk7XHJcbiAgfVxyXG4gIGxvZyg0LCBgW2NhY2hlXSBGZXRjaGluZyBzY3JpcHQgLSAke3NjcmlwdH0uanNgKTtcclxuXHJcbiAgLy8gRmV0Y2ggdGhlIHNjcmlwdFxyXG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goYCR7c2NyaXB0fS5qc2AsIHJlcXVlc3RPcHRpb25zKTtcclxuXHJcbiAgLy8gSWYgT0ssIHJldHVybiBpdHMgdGV4dCByZXByZXNlbnRhdGlvblxyXG4gIGlmIChyZXNwb25zZS5zdGF0dXNDb2RlID09PSAyMDAgJiYgdHlwZW9mIHJlc3BvbnNlLnRleHQgPT0gJ3N0cmluZycpIHtcclxuICAgIGlmIChmZXRjaGVkTW9kdWxlcykge1xyXG4gICAgICBjb25zdCBtb2R1bGVOYW1lID0gZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0KTtcclxuICAgICAgZmV0Y2hlZE1vZHVsZXNbbW9kdWxlTmFtZV0gPSAxO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHJlc3BvbnNlLnRleHQ7XHJcbiAgfVxyXG5cclxuICAvLyBCYXNlZCBvbiB0aGUgYHNob3VsZFRocm93RXJyb3JgIGZsYWcsIGRlY2lkZSBob3cgdG8gc2VydmUgZXJyb3IgbWVzc2FnZVxyXG4gIGlmIChzaG91bGRUaHJvd0Vycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgIGBbY2FjaGVdIENvdWxkIG5vdCBmZXRjaCB0aGUgJHtzY3JpcHR9LmpzLiBUaGUgc2NyaXB0IG1pZ2h0IG5vdCBleGlzdCBpbiB0aGUgcmVxdWVzdGVkIHZlcnNpb24gKHN0YXR1cyBjb2RlOiAke3Jlc3BvbnNlLnN0YXR1c0NvZGV9KS5gLFxyXG4gICAgICA0MDRcclxuICAgICkuc2V0RXJyb3IocmVzcG9uc2UpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgIGBbY2FjaGVdIENvdWxkIG5vdCBmZXRjaCB0aGUgJHtzY3JpcHR9LmpzLiBUaGUgc2NyaXB0IG1pZ2h0IG5vdCBleGlzdCBpbiB0aGUgcmVxdWVzdGVkIHZlcnNpb24uYFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTYXZlcyB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgZmV0Y2hlZCBtb2R1bGVzIHRvIHRoZSBjYWNoZSBtYW5pZmVzdFxyXG4gKiBmaWxlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaGlnaGNoYXJ0c09wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgaGlnaGNoYXJ0c2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IFtmZXRjaGVkTW9kdWxlcz17fV0gLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHNcclxuICogbW9kdWxlcyBoYXZlIGJlZW4gZmV0Y2hlZC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIHdoaWxlXHJcbiAqIHdyaXRpbmcgdGhlIGNhY2hlIG1hbmlmZXN0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3NhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHNPcHRpb25zLCBmZXRjaGVkTW9kdWxlcyA9IHt9KSB7XHJcbiAgY29uc3QgbmV3TWFuaWZlc3QgPSB7XHJcbiAgICB2ZXJzaW9uOiBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uLFxyXG4gICAgbW9kdWxlczogZmV0Y2hlZE1vZHVsZXNcclxuICB9O1xyXG5cclxuICAvLyBVcGRhdGUgY2FjaGUgb2JqZWN0IHdpdGggdGhlIGN1cnJlbnQgbW9kdWxlc1xyXG4gIGNhY2hlLmFjdGl2ZU1hbmlmZXN0ID0gbmV3TWFuaWZlc3Q7XHJcblxyXG4gIGxvZygzLCAnW2NhY2hlXSBXcml0aW5nIGEgbmV3IG1hbmlmZXN0LicpO1xyXG4gIHRyeSB7XHJcbiAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICBqb2luKGdldENhY2hlUGF0aCgpLCAnbWFuaWZlc3QuanNvbicpLFxyXG4gICAgICBKU09OLnN0cmluZ2lmeShuZXdNYW5pZmVzdCksXHJcbiAgICAgICd1dGY4J1xyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBFcnJvciB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgSGlnaGNoYXJ0cyBgc2NyaXB0c2AgYW5kIGBjdXN0b21TY3JpcHRzYCBmcm9tIHRoZSBnaXZlbiBDRE5zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHNcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY29yZVNjcmlwdHMgLSBIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaC5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gbW9kdWxlU2NyaXB0cyAtIEhpZ2hjaGFydHMgbW9kdWxlcyB0byBmZXRjaC5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY3VzdG9tU2NyaXB0cyAtIEN1c3RvbSBzY3JpcHQgcGF0aHMgdG8gZmV0Y2ggKGZ1bGxcclxuICogVVJMcykuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgc2VydmVyLnByb3h5YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHNcclxuICogbW9kdWxlcyBoYXZlIGJlZW4gZmV0Y2hlZC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGZldGNoZWQgc2NyaXB0c1xyXG4gKiBjb250ZW50IGpvaW5lZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiBzZXR0aW5nIGFuIEhUVFAgQWdlbnQgZm9yIHByb3h5LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2ZldGNoU2NyaXB0cyhcclxuICBjb3JlU2NyaXB0cyxcclxuICBtb2R1bGVTY3JpcHRzLFxyXG4gIGN1c3RvbVNjcmlwdHMsXHJcbiAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gIGZldGNoZWRNb2R1bGVzXHJcbikge1xyXG4gIC8vIENvbmZpZ3VyZSBwcm94eSBpZiBleGlzdHNcclxuICBsZXQgcHJveHlBZ2VudDtcclxuICBjb25zdCBwcm94eUhvc3QgPSBzZXJ2ZXJQcm94eU9wdGlvbnMuaG9zdDtcclxuICBjb25zdCBwcm94eVBvcnQgPSBzZXJ2ZXJQcm94eU9wdGlvbnMucG9ydDtcclxuXHJcbiAgLy8gVHJ5IHRvIGNyZWF0ZSBhIFByb3h5IEFnZW50XHJcbiAgaWYgKHByb3h5SG9zdCAmJiBwcm94eVBvcnQpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIHByb3h5QWdlbnQgPSBuZXcgSHR0cHNQcm94eUFnZW50KHtcclxuICAgICAgICBob3N0OiBwcm94eUhvc3QsXHJcbiAgICAgICAgcG9ydDogcHJveHlQb3J0XHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2FjaGVdIENvdWxkIG5vdCBjcmVhdGUgYSBQcm94eSBBZ2VudC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIElmIGV4aXN0cywgYWRkIHByb3h5IGFnZW50IHRvIHJlcXVlc3Qgb3B0aW9uc1xyXG4gIGNvbnN0IHJlcXVlc3RPcHRpb25zID0gcHJveHlBZ2VudFxyXG4gICAgPyB7XHJcbiAgICAgICAgYWdlbnQ6IHByb3h5QWdlbnQsXHJcbiAgICAgICAgdGltZW91dDogc2VydmVyUHJveHlPcHRpb25zLnRpbWVvdXRcclxuICAgICAgfVxyXG4gICAgOiB7fTtcclxuXHJcbiAgY29uc3QgYWxsRmV0Y2hQcm9taXNlcyA9IFtcclxuICAgIC4uLmNvcmVTY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMsIHRydWUpXHJcbiAgICApLFxyXG4gICAgLi4ubW9kdWxlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzKVxyXG4gICAgKSxcclxuICAgIC4uLmN1c3RvbVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zKVxyXG4gICAgKVxyXG4gIF07XHJcblxyXG4gIGNvbnN0IGZldGNoZWRTY3JpcHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoYWxsRmV0Y2hQcm9taXNlcyk7XHJcbiAgcmV0dXJuIGZldGNoZWRTY3JpcHRzLmpvaW4oJztcXG4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGxvY2FsIGNhY2hlIHdpdGggSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCB0aGVpciB2ZXJzaW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfdXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gT2JqZWN0IGNvbnRhaW5pbmcgYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgc2VydmVyLnByb3h5YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc291cmNlUGF0aCAtIFRoZSBwYXRoIHRvIHRoZSBzb3VyY2UgZmlsZSBpbiB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCByZXByZXNlbnRpbmdcclxuICogdGhlIGZldGNoZWQgbW9kdWxlcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGFuIGlzc3VlIHVwZGF0aW5nXHJcbiAqIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3VwZGF0ZUNhY2hlKGhpZ2hjaGFydHNPcHRpb25zLCBzZXJ2ZXJQcm94eU9wdGlvbnMsIHNvdXJjZVBhdGgpIHtcclxuICAvLyBHZXQgSGlnaGNoYXJ0cyB2ZXJzaW9uIGZvciBzY3JpcHRzXHJcbiAgY29uc3QgaGNWZXJzaW9uID1cclxuICAgIGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24gPT09ICdsYXRlc3QnXHJcbiAgICAgID8gbnVsbFxyXG4gICAgICA6IGAke2hpZ2hjaGFydHNPcHRpb25zLnZlcnNpb259YDtcclxuXHJcbiAgLy8gR2V0IHRoZSBDRE4gdXJsIGZvciBzY3JpcHRzXHJcbiAgY29uc3QgY2RuVXJsID0gaGlnaGNoYXJ0c09wdGlvbnMuY2RuVXJsIHx8IGNhY2hlLmNkblVybDtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGNvbnN0IGZldGNoZWRNb2R1bGVzID0ge307XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW2NhY2hlXSBVcGRhdGluZyBjYWNoZSB2ZXJzaW9uIHRvIEhpZ2hjaGFydHM6ICR7aGNWZXJzaW9uIHx8ICdsYXRlc3QnfS5gXHJcbiAgICApO1xyXG5cclxuICAgIGNhY2hlLnNvdXJjZXMgPSBhd2FpdCBfZmV0Y2hTY3JpcHRzKFxyXG4gICAgICBbXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuY29yZVNjcmlwdHMubWFwKChjKSA9PlxyXG4gICAgICAgICAgaGNWZXJzaW9uID8gYCR7Y2RuVXJsfS8ke2hjVmVyc2lvbn0vJHtjfWAgOiBgJHtjZG5Vcmx9LyR7Y31gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBbXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMubW9kdWxlU2NyaXB0cy5tYXAoKG0pID0+XHJcbiAgICAgICAgICBtID09PSAnbWFwJ1xyXG4gICAgICAgICAgICA/IGhjVmVyc2lvblxyXG4gICAgICAgICAgICAgID8gYCR7Y2RuVXJsfS9tYXBzLyR7aGNWZXJzaW9ufS9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgICAgOiBgJHtjZG5Vcmx9L21hcHMvbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICA6IGhjVmVyc2lvblxyXG4gICAgICAgICAgICAgID8gYCR7Y2RuVXJsfS8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgKSxcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5pbmRpY2F0b3JTY3JpcHRzLm1hcCgoaSkgPT5cclxuICAgICAgICAgIGhjVmVyc2lvblxyXG4gICAgICAgICAgICA/IGAke2NkblVybH0vc3RvY2svJHtoY1ZlcnNpb259L2luZGljYXRvcnMvJHtpfWBcclxuICAgICAgICAgICAgOiBgJHtjZG5Vcmx9L3N0b2NrL2luZGljYXRvcnMvJHtpfWBcclxuICAgICAgICApXHJcbiAgICAgIF0sXHJcbiAgICAgIGhpZ2hjaGFydHNPcHRpb25zLmN1c3RvbVNjcmlwdHMsXHJcbiAgICAgIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICAgICAgZmV0Y2hlZE1vZHVsZXNcclxuICAgICk7XHJcblxyXG4gICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgIGNhY2hlLmhjVmVyc2lvbiA9IGV4dHJhY3RWZXJzaW9uKGNhY2hlLnNvdXJjZXMpO1xyXG5cclxuICAgIC8vIFNhdmUgdGhlIGZldGNoZWQgbW9kdWxlcyBpbnRvIGNhY2hlcycgc291cmNlIEpTT05cclxuICAgIHdyaXRlRmlsZVN5bmMoc291cmNlUGF0aCwgY2FjaGUuc291cmNlcyk7XHJcbiAgICByZXR1cm4gZmV0Y2hlZE1vZHVsZXM7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gVW5hYmxlIHRvIHVwZGF0ZSB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGNoZWNrQW5kVXBkYXRlQ2FjaGUsXHJcbiAgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24sXHJcbiAgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24sXHJcbiAgZXh0cmFjdFZlcnNpb24sXHJcbiAgZXh0cmFjdE1vZHVsZU5hbWUsXHJcbiAgZ2V0Q2FjaGUsXHJcbiAgZ2V0Q2FjaGVQYXRoXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtZXRob2RzIGZvciBpbml0aWFsaXppbmcgSGlnaGNoYXJ0cyB3aXRoIGN1c3RvbWl6ZWRcclxuICogYW5pbWF0aW9uIHNldHRpbmdzIGFuZCB0cmlnZ2VyaW5nIHRoZSBjcmVhdGlvbiBvZiBIaWdoY2hhcnRzIGNoYXJ0cyB3aXRoXHJcbiAqIGV4cG9ydC1zcGVjaWZpYyBjb25maWd1cmF0aW9ucyBpbiB0aGUgcGFnZSBjb250ZXh0LiBTdXBwb3J0cyBkeW5hbWljIG9wdGlvblxyXG4gKiBtZXJnaW5nLCBjdXN0b20gbG9naWMgaW5qZWN0aW9uLCBhbmQgY29udHJvbCBvdmVyIHJlbmRlcmluZyBiZWhhdmlvcnMuIFVzZWRcclxuICogYnkgdGhlIFB1cHBldGVlciBwYWdlLlxyXG4gKi9cclxuXHJcbi8qIGVzbGludC1kaXNhYmxlIG5vLXVuZGVmICovXHJcblxyXG4vKipcclxuICogU2V0dGluZyB0aGUgYEhpZ2hjaGFydHMuYW5pbU9iamVjdGAgZnVuY3Rpb24uIENhbGxlZCB3aGVuIGluaXRpbmcgdGhlIHBhZ2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXR1cEhpZ2hjaGFydHNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXR1cEhpZ2hjaGFydHMoKSB7XHJcbiAgSGlnaGNoYXJ0cy5hbmltT2JqZWN0ID0gZnVuY3Rpb24gKCkge1xyXG4gICAgcmV0dXJuIHsgZHVyYXRpb246IDAgfTtcclxuICB9O1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyB0aGUgYWN0dWFsIEhpZ2hjaGFydHMgY2hhcnQgb24gYSBwYWdlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUNoYXJ0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVDaGFydChvcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgbWVyZ2UsIHNldE9wdGlvbnMsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHVzZXJPcHRpb25zIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB1c2VyT3B0aW9ucyB3aGVuIGZvckV4cG9ydCBpcyB0cnVlLlxyXG4gICAgICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL2hpZ2hjaGFydHMvYmxvYi8zYWQ0MzBhMzUzYjgwNTZiOWU3NjRhYTRlNWNkNjgyOGFhNDc5ZGIyL2pzL3BhcnRzL0NoYXJ0LmpzI0wyNDFcclxuICAgICAgICAqL1xyXG4gICAgICB0b29sdGlwOiB7fVxyXG4gICAgfSk7XHJcblxyXG4gICAgKHVzZXJPcHRpb25zLnNlcmllcyB8fCBbXSkuZm9yRWFjaChmdW5jdGlvbiAoc2VyaWVzKSB7XHJcbiAgICAgIHNlcmllcy5hbmltYXRpb24gPSBmYWxzZTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEFkZCBmbGFnIHRvIGtub3cgaWYgY2hhcnQgcmVuZGVyIGhhcyBiZWVuIGNhbGxlZC5cclxuICAgIGlmICghd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlcikge1xyXG4gICAgICB3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyID0gSGlnaGNoYXJ0cy5hZGRFdmVudCh0aGlzLCAncmVuZGVyJywgKCkgPT4ge1xyXG4gICAgICAgIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gdHJ1ZTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbdXNlck9wdGlvbnMsIGNiXSk7XHJcbiAgfSk7XHJcblxyXG4gIHdyYXAoSGlnaGNoYXJ0cy5TZXJpZXMucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCBjaGFydCwgb3B0aW9ucykge1xyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbY2hhcnQsIG9wdGlvbnNdKTtcclxuICB9KTtcclxuXHJcbiAgLy8gU29tZSBtYW5kYXRvcnkgYWRkaXRpb25hbCBgY2hhcnRgIGFuZCBgZXhwb3J0aW5nYCBvcHRpb25zXHJcbiAgY29uc3QgYWRkaXRpb25hbE9wdGlvbnMgPSB7XHJcbiAgICBjaGFydDoge1xyXG4gICAgICAvLyBCeSBkZWZhdWx0IGFuaW1hdGlvbiBpcyBkaXNhYmxlZFxyXG4gICAgICBhbmltYXRpb246IGZhbHNlLFxyXG4gICAgICAvLyBHZXQgdGhlIHJpZ2h0IHNpemUgdmFsdWVzXHJcbiAgICAgIGhlaWdodDogb3B0aW9ucy5leHBvcnQuaGVpZ2h0LFxyXG4gICAgICB3aWR0aDogb3B0aW9ucy5leHBvcnQud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtvcHRpb25zLmV4cG9ydC5pbnN0cn1gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvblxyXG4gIGNvbnN0IHRoZW1lT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7b3B0aW9ucy5leHBvcnQudGhlbWVPcHRpb25zfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGdsb2JhbE9wdGlvbnNgIG9wdGlvblxyXG4gIGNvbnN0IGdsb2JhbE9wdGlvbnMgPSBuZXcgRnVuY3Rpb24oXHJcbiAgICBgcmV0dXJuICR7b3B0aW9ucy5leHBvcnQuZ2xvYmFsT3B0aW9uc31gXHJcbiAgKSgpO1xyXG5cclxuICAvLyBNZXJnZSB0aGUgZm9sbG93aW5nIG9wdGlvbnMgb2JqZWN0cyB0byBjcmVhdGUgZmluYWwgb3B0aW9uc1xyXG4gIGNvbnN0IGZpbmFsT3B0aW9ucyA9IG1lcmdlKFxyXG4gICAgZmFsc2UsXHJcbiAgICB0aGVtZU9wdGlvbnMsXHJcbiAgICB1c2VyT3B0aW9ucyxcclxuICAgIC8vIFBsYWNlZCBpdCBoZXJlIGluc3RlYWQgaW4gdGhlIGluaXQgYmVjYXVzZSBvZiB0aGUgc2l6ZSBpc3N1ZXNcclxuICAgIGFkZGl0aW9uYWxPcHRpb25zXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNhbGxiYWNrYCBvcHRpb25cclxuICBjb25zdCBmaW5hbENhbGxiYWNrID0gb3B0aW9ucy5jdXN0b21Mb2dpYy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke29wdGlvbnMuY3VzdG9tTG9naWMuY2FsbGJhY2t9YCkoKVxyXG4gICAgOiBudWxsO1xyXG5cclxuICAvLyBUcmlnZ2VyIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgaWYgKG9wdGlvbnMuY3VzdG9tTG9naWMuY3VzdG9tQ29kZSkge1xyXG4gICAgbmV3IEZ1bmN0aW9uKCdvcHRpb25zJywgb3B0aW9ucy5jdXN0b21Mb2dpYy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbb3B0aW9ucy5leHBvcnQuY29uc3RyXSgnY29udGFpbmVyJywgZmluYWxPcHRpb25zLCBmaW5hbENhbGxiYWNrKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBjdXJyZW50IGdsb2JhbCBvcHRpb25zXHJcbiAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIENsZWFyIGl0IGp1c3QgaW4gY2FzZSAoZS5nLiB0aGUgYHNldE9wdGlvbnNgIHdhcyB1c2VkIGluIHRoZSBgY3VzdG9tQ29kZWApXHJcbiAgZm9yIChjb25zdCBwcm9wIGluIGRlZmF1bHRPcHRpb25zKSB7XHJcbiAgICBpZiAodHlwZW9mIGRlZmF1bHRPcHRpb25zW3Byb3BdICE9PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgIGRlbGV0ZSBkZWZhdWx0T3B0aW9uc1twcm9wXTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgZGVmYXVsdCBvcHRpb25zIGJhY2tcclxuICBzZXRPcHRpb25zKEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaik7XHJcblxyXG4gIC8vIEVtcHR5IHRoZSBjdXN0b20gZ2xvYmFsIG9wdGlvbnMgb2JqZWN0XHJcbiAgSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqID0ge307XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzZXR1cEhpZ2hjaGFydHMsXHJcbiAgY3JlYXRlQ2hhcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgbWFuYWdpbmcgUHVwcGV0ZWVyIGJyb3dzZXJcclxuICogaW5zdGFuY2UsIGNyZWF0aW5nIGFuZCBjbGVhcmluZyBwYWdlcywgaW5qZWN0aW5nIGN1c3RvbSByZXNvdXJjZXMsXHJcbiAqIGFuZCBzZXR0aW5nIHVwIEhpZ2hjaGFydHMgZm9yIHNlcnZlci1zaWRlIHJlbmRlcmluZy4gVGhlIG1vZHVsZSBlbnN1cmVzXHJcbiAqIHRoYXQgcmVzb3VyY2VzIGFyZSBjb3JyZWN0bHkgbWFuYWdlZCBhbmQgY2FuIGhhbmRsZSBmYWlsdXJlcyBkdXJpbmdcclxuICogb3BlcmF0aW9ucyBsaWtlIGxhdW5jaGluZyB0aGUgYnJvd3NlciBvciBjcmVhdGluZyBuZXcgcGFnZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgcHVwcGV0ZWVyIGZyb20gJ3B1cHBldGVlcic7XHJcblxyXG5pbXBvcnQgeyBnZXRDYWNoZVBhdGggfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgc2V0dXBIaWdoY2hhcnRzIH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgcGFnZXNcclxuY29uc3QgdGVtcGxhdGUgPSByZWFkRmlsZVN5bmMoXHJcbiAgam9pbihfX2Rpcm5hbWUsICd0ZW1wbGF0ZXMnLCAndGVtcGxhdGUuaHRtbCcpLFxyXG4gICd1dGY4J1xyXG4pO1xyXG5cclxuLy8gVG8gc2F2ZSB0aGUgYnJvd3NlclxyXG5sZXQgYnJvd3NlciA9IG51bGw7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBleGlzdGluZyBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJyb3dzZXJcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEJyb3dzZXIoKSB7XHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBObyB2YWxpZCBicm93c2VyIGhhcyBiZWVuIGNyZWF0ZWQuJywgNTAwKTtcclxuICB9XHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVCcm93c2VyXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyXHJcbiAqIGxhdW5jaC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyXHJcbiAqIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBtYXggcmV0cmllcyB0byBvcGVuXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhcmUgcmVhY2hlZCwgb3IgaWYgbm8gYnJvd3NlciBpbnN0YW5jZSBpcyBmb3VuZCBhZnRlclxyXG4gKiByZXRyaWVzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncykge1xyXG4gIC8vIEdldCBgZGVidWdgIGFuZCBgb3RoZXJgIG9wdGlvbnNcclxuICBjb25zdCB7IGRlYnVnLCBvdGhlciB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZW5hYmxlOiBlbmFibGVkRGVidWcsIC4uLmRlYnVnT3B0aW9ucyB9ID0gZGVidWc7XHJcblxyXG4gIC8vIExhdW5jaCBvcHRpb25zIGZvciB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGNvbnN0IGxhdW5jaE9wdGlvbnMgPSB7XHJcbiAgICBoZWFkbGVzczogb3RoZXIuYnJvd3NlclNoZWxsTW9kZSA/ICdzaGVsbCcgOiB0cnVlLFxyXG4gICAgdXNlckRhdGFEaXI6ICd0bXAnLFxyXG4gICAgYXJnczogcHVwcGV0ZWVyQXJncyB8fCBbXSxcclxuICAgIGhhbmRsZVNJR0lOVDogZmFsc2UsXHJcbiAgICBoYW5kbGVTSUdURVJNOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR0hVUDogZmFsc2UsXHJcbiAgICB3YWl0Rm9ySW5pdGlhbFBhZ2U6IGZhbHNlLFxyXG4gICAgZGVmYXVsdFZpZXdwb3J0OiBudWxsLFxyXG4gICAgLi4uKGVuYWJsZWREZWJ1ZyAmJiBkZWJ1Z09wdGlvbnMpXHJcbiAgfTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlclxyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgLy8gQSBjb3VudGVyIGZvciB0aGUgYnJvd3NlcidzIGxhdW5jaCByZXRyaWVzXHJcbiAgICBsZXQgdHJ5Q291bnQgPSAwO1xyXG5cclxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbYnJvd3Nlcl0gQXR0ZW1wdGluZyB0byBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIExhdW5jaCB0aGUgYnJvd3NlclxyXG4gICAgICAgIGJyb3dzZXIgPSBhd2FpdCBwdXBwZXRlZXIubGF1bmNoKGxhdW5jaE9wdGlvbnMpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuXHJcbiAgICAgICAgICAvLyBXYWl0IGZvciBhIDQgc2Vjb25kcyBiZWZvcmUgdHJ5aW5nIGFnYWluXHJcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIDQwMDApKTtcclxuICAgICAgICAgIGF3YWl0IG9wZW4oKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIGF3YWl0IG9wZW4oKTtcclxuXHJcbiAgICAgIC8vIFNoZWxsIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChsYXVuY2hPcHRpb25zLmhlYWRsZXNzID09PSAnc2hlbGwnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBzaGVsbCBtb2RlLmApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEZWJ1ZyBtb2RlIGluZm9ybVxyXG4gICAgICBpZiAoZW5hYmxlZERlYnVnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1ticm93c2VyXSBNYXhpbXVtIHJldHJpZXMgdG8gb3BlbiBhIGJyb3dzZXIgaW5zdGFuY2UgcmVhY2hlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJywgNTAwKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGJyb3dzZXIgaW5zdGFuY2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyB0aGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgaWYgaXQgaXMgY29ubmVjdGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsb3NlQnJvd3NlclxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsb3NlQnJvd3NlcigpIHtcclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciB3aGVuIGNvbm5lY3RlZFxyXG4gIGlmIChicm93c2VyICYmIGJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgfVxyXG4gIGJyb3dzZXIgPSBudWxsO1xyXG4gIGxvZyg0LCAnW2Jyb3dzZXJdIENsb3NlZCB0aGUgYnJvd3Nlci4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBuZXcgUHVwcGV0ZWVyIHBhZ2Ugd2l0aGluIGFuIGV4aXN0aW5nIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqIFRoZSBmdW5jdGlvbiBjcmVhdGVzIGEgbmV3IHBhZ2UsIGRpc2FibGVzIGNhY2hpbmcsIHNldHMgY29udGVudCB1c2luZ1xyXG4gKiB0aGUgYF9zZXRQYWdlQ29udGVudCgpYCwgYW5kIHJldHVybnMgdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gbmV3UGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBgaWRgLFxyXG4gKiBgd29ya0NvdW50YCwgYW5kIGBwYWdlYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG5vIHZhbGlkIGJyb3dzZXJcclxuICogaGFzIGJlZW4gY29ubmVjdGVkIG9yIGlmIGEgcGFnZSBpcyBpbnZhbGlkIG9yIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBuZXdQYWdlKHBvb2xSZXNvdXJjZSkge1xyXG4gIC8vIEVycm9yIGluIGNhc2Ugb2Ygbm8gY29ubmVjdGVkIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIgfHwgIWJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFticm93c2VyXSBCcm93c2VyIGlzIG5vdCB5ZXQgY29ubmVjdGVkLmAsIDUwMCk7XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBwYWdlXHJcbiAgcG9vbFJlc291cmNlLnBhZ2UgPSBhd2FpdCBicm93c2VyLm5ld1BhZ2UoKTtcclxuXHJcbiAgLy8gRGlzYWJsZSBjYWNoZVxyXG4gIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IF9zZXRQYWdlQ29udGVudChwb29sUmVzb3VyY2UucGFnZSk7XHJcblxyXG4gIC8vIFNldCBwYWdlIGV2ZW50c1xyXG4gIF9zZXRQYWdlRXZlbnRzKHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIHBhZ2UgaXMgY29ycmVjdGx5IGNyZWF0ZWRcclxuICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlIHx8IHBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIFRoZSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLicsIDQwMCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLiBMb2dzXHJcbiAqIHRocm93biBlcnJvciBpZiBjbGVhcmluZyBvZiBhIHBhZ2UncyBjb250ZW50IGZhaWxzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBwYWdlIGFuZCBpZC5cclxuICogQHBhcmFtIHtib29sZWFufSBbaGFyZFJlc2V0PWZhbHNlXSAtIEEgZmxhZyBpbmRpY2F0aW5nIHRoZSB0eXBlIG9mIGNsZWFyaW5nXHJcbiAqIHRvIGJlIHBlcmZvcm1lZC4gSWYgdHJ1ZSwgbmF2aWdhdGVzIHRvIGBhYm91dDpibGFua2AgYW5kIHJlc2V0cyBjb250ZW50XHJcbiAqIGFuZCBzY3JpcHRzLiBJZiBmYWxzZSwgY2xlYXJzIHRoZSBib2R5IGNvbnRlbnQgYnkgc2V0dGluZyBhIHByZWRlZmluZWQgSFRNTFxyXG4gKiBzdHJ1Y3R1cmUuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGZhbHNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSB3aGVuIHBhZ2VcclxuICogaXMgY29ycmVjdGx5IGNsZWFyZWQgYW5kIGZhbHNlIHdoZW4gaXQgaXMgbm90LlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZShwb29sUmVzb3VyY2UsIGhhcmRSZXNldCA9IGZhbHNlKSB7XHJcbiAgdHJ5IHtcclxuICAgIGlmIChwb29sUmVzb3VyY2UucGFnZSAmJiAhcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICBpZiAoaGFyZFJlc2V0KSB7XHJcbiAgICAgICAgLy8gTmF2aWdhdGUgdG8gYGFib3V0OmJsYW5rYFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmdvdG8oJ2Fib3V0OmJsYW5rJywge1xyXG4gICAgICAgICAgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCdcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gU2V0IHRoZSBjb250ZW50IGFuZCBhbmQgc2NyaXB0cyBhZ2FpblxyXG4gICAgICAgIGF3YWl0IF9zZXRQYWdlQ29udGVudChwb29sUmVzb3VyY2UucGFnZSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2xlYXIgYm9keSBjb250ZW50XHJcbiAgICAgICAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5pbm5lckhUTUwgPVxyXG4gICAgICAgICAgICAnPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPjxkaXYgaWQ9XCJjb250YWluZXJcIj48L2Rpdj48L2Rpdj4nO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDIsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBDb250ZW50IG9mIHRoZSBwYWdlIGNvdWxkIG5vdCBiZSBjbGVhcmVkLmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgIHBvb2xSZXNvdXJjZS53b3JrQ291bnQgPSBnZXRPcHRpb25zKCkucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gIH1cclxuICByZXR1cm4gZmFsc2U7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIGN1c3RvbSBKUyBhbmQgQ1NTIHJlc291cmNlcyB0byBhIFB1cHBldGVlciBQYWdlIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBhZGRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBvYmplY3QgY29udGFpbmluZyBgY3VzdG9tTG9naWNgXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9ICFmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRydWUgOiBmYWxzZTtcclxuXHJcbiAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICBpbmplY3RlZEpzLnB1c2goXHJcbiAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgID8ge1xyXG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChmaWxlKSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZvciAoY29uc3QganNSZXNvdXJjZSBvZiBpbmplY3RlZEpzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhqc1Jlc291cmNlKSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIFRoZSBKUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpbmplY3RlZEpzLmxlbmd0aCA9IDA7XHJcblxyXG4gICAgLy8gTG9hZCBDU1NcclxuICAgIGNvbnN0IGluamVjdGVkQ3NzID0gW107XHJcbiAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgcGF0aDogam9pbihfX2Rpcm5hbWUsIGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoY3NzUmVzb3VyY2UpKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICAgYFticm93c2VyXSBUaGUgQ1NTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaW5qZWN0ZWRDc3MubGVuZ3RoID0gMDtcclxuICAgIH1cclxuICB9XHJcbiAgcmV0dXJuIGluamVjdGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIG91dCBhbGwgc3RhdGUgc2V0IG9uIHRoZSBwYWdlIHdpdGggYGFkZFNjcmlwdFRhZ2AgYW5kIGBhZGRTdHlsZVRhZ2AuXHJcbiAqIFJlbW92ZXMgaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS5cclxuICogQWRkaXRpb25hbGx5LCBpdCBkZXN0cm95cyBwcmV2aW91c2x5IGV4aXN0aW5nIGNoYXJ0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtBcnJheS48T2JqZWN0Pn0gaW5qZWN0ZWRSZXNvdXJjZXMgLSBBcnJheSBvZiBpbmplY3RlZCByZXNvdXJjZXNcclxuICogdG8gYmUgY2xlYXJlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICB0cnkge1xyXG4gICAgZm9yIChjb25zdCByZXNvdXJjZSBvZiBpbmplY3RlZFJlc291cmNlcykge1xyXG4gICAgICBhd2FpdCByZXNvdXJjZS5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzIGFmdGVyIGV4cG9ydCBpcyBkb25lIGFuZCByZXNldCBhbGwgQ1NTIGFuZCBzY3JpcHQgdGFnc1xyXG4gICAgYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCB3aGVuIGRvaW5nIFNWRyBleHBvcnRzXHJcbiAgICAgIGlmICh0eXBlb2YgSGlnaGNoYXJ0cyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcclxuXHJcbiAgICAgICAgLy8gQ2hlY2sgaW4gYW55IGFscmVhZHkgZXhpc3RpbmcgY2hhcnRzXHJcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XHJcbiAgICAgICAgICAvLyBEZXN0cm95IG9sZCBjaGFydHNcclxuICAgICAgICAgIGZvciAoY29uc3Qgb2xkQ2hhcnQgb2Ygb2xkQ2hhcnRzKSB7XHJcbiAgICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcclxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICAgIEhpZ2hjaGFydHMuY2hhcnRzLnNoaWZ0KCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLnNjcmlwdHNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLCAuLi5zdHlsZXNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc3R5bGUnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5saW5rc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdsaW5rJyk7XHJcblxyXG4gICAgICAvLyBSZW1vdmUgdGFnc1xyXG4gICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgW1xyXG4gICAgICAgIC4uLnNjcmlwdHNUb1JlbW92ZSxcclxuICAgICAgICAuLi5zdHlsZXNUb1JlbW92ZSxcclxuICAgICAgICAuLi5saW5rc1RvUmVtb3ZlXHJcbiAgICAgIF0pIHtcclxuICAgICAgICBlbGVtZW50LnJlbW92ZSgpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciBwYWdlJ3MgcmVzb3VyY2VzLmApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvbnRlbnQgZm9yIGEgUHVwcGV0ZWVyIFBhZ2UgdXNpbmcgYSBwcmVkZWZpbmVkIHRlbXBsYXRlXHJcbiAqIGFuZCBhZGRpdGlvbmFsIHNjcmlwdHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VDb250ZW50XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgY29udGVudFxyXG4gKiBpcyBiZWluZyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnQocGFnZSkge1xyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBwYWdlIGNvbnRlbnRcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gIC8vIEFkZCBhbGwgcmVnaXN0ZXJlZCBIaWdjaGFydHMgc2NyaXB0cywgcXVpdGUgZGVtYW5kaW5nXHJcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBqb2luKGdldENhY2hlUGF0aCgpLCAnc291cmNlcy5qcycpIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGluaXRpYWwgYGFuaW1PYmplY3RgIGZvciBIaWdoY2hhcnRzXHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IGV2ZW50cyAobGlrZSBgcGFnZWVycm9yYCBhbmQgYGNvbnNvbGVgKSBmb3IgYSBQdXBwZXRlZXIgUGFnZSBpbiBvcmRlclxyXG4gKiB0byBjYXRjaCBhbmQgZGlzcGxheSBlcnJvcnMgYW5kIGNvbnNvbGUgbG9ncyBmcm9tIHRoZSB3aW5kb3cgY29udGV4dC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgbGlzdGVuZXJzXHJcbiAqIGFyZSBiZWluZyBzZXQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc2V0UGFnZUV2ZW50cyhwYWdlKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgcGFnZWVycm9yYCBsaXN0ZW5lclxyXG4gIHBhZ2Uub24oJ3BhZ2VlcnJvcicsIGFzeW5jICgpID0+IHtcclxuICAgIC8vIEl0IHdvdWxkIHNlZW0gbGlrZSB0aGlzIG1heSBmaXJlIGF0IHRoZSBzYW1lIHRpbWUgb3Igc2hvcnRseSBiZWZvcmVcclxuICAgIC8vIGEgcGFnZSBpcyBjbG9zZWQuXHJcbiAgICBpZiAocGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgY29uc29sZWAgbGlzdGVuZXIsIGlmIG5lZWRlZFxyXG4gIGlmIChkZWJ1Zy5lbmFibGUgJiYgZGVidWcubGlzdGVuVG9Db25zb2xlKSB7XHJcbiAgICBwYWdlLm9uKCdjb25zb2xlJywgKG1lc3NhZ2UpID0+IHtcclxuICAgICAgY29uc29sZS5sb2coYFtkZWJ1Z10gJHttZXNzYWdlLnRleHQoKX1gKTtcclxuICAgIH0pO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldEJyb3dzZXIsXHJcbiAgY3JlYXRlQnJvd3NlcixcclxuICBjbG9zZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSxcclxuICBjbGVhclBhZ2UsXHJcbiAgYWRkUGFnZVJlc291cmNlcyxcclxuICBjbGVhclBhZ2VSZXNvdXJjZXNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogVGhlIENTUyB0byBiZSB1c2VkIG9uIHRoZSBleHBvcnRlZCBwYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQ1NTIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoKSA9PiBgXHJcblxyXG5odG1sLCBib2R5IHtcclxuICBtYXJnaW46IDA7XHJcbiAgcGFkZGluZzogMDtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG59XHJcblxyXG4jdGFibGUtZGl2LCAjc2xpZGVycywgI2RhdGF0YWJsZSwgI2NvbnRyb2xzLCAubGQtcm93IHtcclxuICBkaXNwbGF5OiBub25lO1xyXG4gIGhlaWdodDogMDtcclxufVxyXG5cclxuI2NoYXJ0LWNvbnRhaW5lciB7XHJcbiAgYm94LXNpemluZzogYm9yZGVyLWJveDtcclxuICBtYXJnaW46IDA7XHJcbiAgb3ZlcmZsb3c6IGF1dG87XHJcbiAgZm9udC1zaXplOiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyID4gZmlndXJlLCBkaXYge1xyXG4gIG1hcmdpbi10b3A6IDAgIWltcG9ydGFudDtcclxuICBtYXJnaW4tYm90dG9tOiAwICFpbXBvcnRhbnQ7XHJcbn1cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNzc1RlbXBsYXRlIGZyb20gJy4vY3NzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBUaGUgU1ZHIHRlbXBsYXRlIHRvIHVzZSB3aGVuIGxvYWRpbmcgU1ZHIGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIGlucHV0IGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBTVkcgdGVtcGxhdGUuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoc3ZnKSA9PiBgXHJcbjwhRE9DVFlQRSBodG1sPlxyXG48aHRtbCBsYW5nPSdlbi1VUyc+XHJcbiAgPGhlYWQ+XHJcbiAgICA8bWV0YSBodHRwLWVxdWl2PVwiQ29udGVudC1UeXBlXCIgY29udGVudD1cInRleHQvaHRtbDsgY2hhcnNldD11dGYtOFwiPlxyXG4gICAgPHRpdGxlPkhpZ2hjaGFydHMgRXhwb3J0PC90aXRsZT5cclxuICA8L2hlYWQ+XHJcbiAgPHN0eWxlPlxyXG4gICAgJHtjc3NUZW1wbGF0ZSgpfVxyXG4gIDwvc3R5bGU+XHJcbiAgPGJvZHk+XHJcbiAgICA8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+XHJcbiAgICAgICR7c3ZnfVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgaGFuZGxlcyBjaGFydCBleHBvcnQgZnVuY3Rpb25hbGl0eSB1c2luZyBQdXBwZXRlZXIuXHJcbiAqIEl0IHN1cHBvcnRzIGV4cG9ydGluZyBjaGFydHMgYXMgU1ZHLCBQTkcsIEpQRUcsIGFuZCBQREYgZm9ybWF0cy4gVGhlIG1vZHVsZVxyXG4gKiBtYW5hZ2VzIHBhZ2UgcmVzb3VyY2VzLCBzZXRzIHVwIHRoZSBleHBvcnQgZW52aXJvbm1lbnQsIGFuZCBwcm9jZXNzZXMgY2hhcnRcclxuICogY29uZmlndXJhdGlvbnMgb3IgU1ZHIGlucHV0cyBmb3IgcmVuZGVyaW5nLiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2VcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFkZFBhZ2VSZXNvdXJjZXMsIGNsZWFyUGFnZVJlc291cmNlcyB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IGNyZWF0ZUNoYXJ0IH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IHN2Z1RlbXBsYXRlIGZyb20gJy4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8KHN0cmluZ3xCdWZmZXJ8RXhwb3J0RXJyb3IpPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXNcclxuICogdG8gdGhlIGV4cG9ydGVkIGRhdGEgb3IgcmVqZWN0aW5nIHdpdGggYW4gYEV4cG9ydEVycm9yYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGV4cG9ydCB0byBhbiB1bnN1cHBvcnRlZFxyXG4gKiBvdXRwdXQgZm9ybWF0IG9jY3Vycy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnQocGFnZSwgb3B0aW9ucykge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheSAoYWRkaXRpb25hbCBKUyBhbmQgQ1NTKVxyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIGBleHBvcnRgIG9wdGlvbnNcclxuICAgIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICBsZXQgaXNTVkcgPSBmYWxzZTtcclxuICAgIGlmIChleHBvcnRPcHRpb25zLnN2Zykge1xyXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBgdHlwZWAgaXMgYWxzbyBTVkcsIHJldHVybiB0aGUgaW5wdXRcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgICAgICByZXR1cm4gZXhwb3J0T3B0aW9ucy5zdmc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIE1hcmsgYXMgU1ZHIGV4cG9ydCBmb3IgdGhlIGxhdGVyIHNpemUgY29ycmVjdGlvbnNcclxuICAgICAgaXNTVkcgPSB0cnVlO1xyXG5cclxuICAgICAgLy8gU1ZHIGV4cG9ydFxyXG4gICAgICBhd2FpdCBfc2V0QXNTdmcocGFnZSwgZXhwb3J0T3B0aW9ucy5zdmcpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBKU09OIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0XHJcbiAgICAgIGF3YWl0IF9zZXRBc09wdGlvbnMocGFnZSwgb3B0aW9ucyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXHJcbiAgICAvLyBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAuLi4oYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBvcHRpb25zLmN1c3RvbUxvZ2ljKSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZWFsIGNoYXJ0IHNpemUgYW5kIHNldCB0aGUgem9vbSBhY2NvcmRpbmdseVxyXG4gICAgY29uc3Qgc2l6ZSA9IGlzU1ZHXHJcbiAgICAgID8gYXdhaXQgcGFnZS5ldmFsdWF0ZSgoc2NhbGUpID0+IHtcclxuICAgICAgICAgIGNvbnN0IHN2Z0VsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFxyXG4gICAgICAgICAgICAnI2NoYXJ0LWNvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZSdcclxuICAgICAgICAgICk7XHJcblxyXG4gICAgICAgICAgLy8gR2V0IHRoZSB2YWx1ZXMgY29ycmVjdGx5IHNjYWxlZFxyXG4gICAgICAgICAgY29uc3QgY2hhcnRIZWlnaHQgPSBzdmdFbGVtZW50LmhlaWdodC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcbiAgICAgICAgICBjb25zdCBjaGFydFdpZHRoID0gc3ZnRWxlbWVudC53aWR0aC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gSW4gY2FzZSBvZiBTVkcgdGhlIHpvb20gbXVzdCBiZSBzZXQgZGlyZWN0bHkgZm9yIGJvZHkgYXMgc2NhbGVcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUubWFyZ2luID0gJzBweCc7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSwgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKSlcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG5cclxuICAgICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXNcclxuICAgICAgICAgIC8vIG9mIGV4cG9ydHMuIFJlc2V0IHRoZSB6b29tIGZvciBvdGhlciBleHBvcnRzIHRoYW4gdG8gU1ZHc1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY2xpcCByZWdpb24gZm9yIHRoZSBwYWdlXHJcbiAgICBjb25zdCB7IHgsIHkgfSA9IGF3YWl0IF9nZXRDbGlwUmVnaW9uKHBhZ2UpO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgaGVpZ2h0YCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0SGVpZ2h0ID0gTWF0aC5hYnMoXHJcbiAgICAgIE1hdGguY2VpbChzaXplLmNoYXJ0SGVpZ2h0IHx8IGV4cG9ydE9wdGlvbnMuaGVpZ2h0KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgZmluYWwgYHdpZHRoYCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0V2lkdGggPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRXaWR0aCB8fCBleHBvcnRPcHRpb25zLndpZHRoKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGZpbmFsIHZpZXdwb3J0IG5vdyB0aGF0IHdlIGhhdmUgdGhlIHJlYWwgaGVpZ2h0XHJcbiAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcclxuICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgIGRldmljZVNjYWxlRmFjdG9yOiBpc1NWRyA/IDEgOiBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpXHJcbiAgICB9KTtcclxuXHJcbiAgICBsZXQgcmVzdWx0O1xyXG4gICAgLy8gUmFzdGVyaXphdGlvbiBwcm9jZXNzXHJcbiAgICBzd2l0Y2ggKGV4cG9ydE9wdGlvbnMudHlwZSkge1xyXG4gICAgICBjYXNlICdzdmcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVTVkcocGFnZSk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BuZyc6XHJcbiAgICAgIGNhc2UgJ2pwZWcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVJbWFnZShcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnR5cGUsXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgICB4LFxyXG4gICAgICAgICAgICB5XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BkZic6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVBERihcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgZGVmYXVsdDpcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdDogJHtleHBvcnRPcHRpb25zLnR5cGV9LmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENsZWFyIHByZXZpb3VzbHkgaW5qZWN0ZWQgSlMgYW5kIENTUyByZXNvdXJjZXNcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIHNwZWNpZmllZCBwYWdlJ3MgY29udGVudCB3aXRoIHByb3ZpZGVkIGV4cG9ydCBpbnB1dCB3aXRoaW5cclxuICogdGhlIHdpbmRvdyBjb250ZXh0IHVzaW5nIHRoZSBgcGFnZS5zZXRDb250ZW50YCBmdW5jdGlvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0QXNTdmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIGlucHV0IGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgY29udGVudCBpcyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0QXNTdmcocGFnZSwgc3ZnKSB7XHJcbiAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKHN2ZyksIHtcclxuICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBvcHRpb25zIHdpdGggc3BlY2lmaWVkIGV4cG9ydCBpbnB1dCBhbmQgc2l6ZXMgYXMgY29uZmlndXJhdGlvbiBpbnRvXHJcbiAqIHRoZSBgY3JlYXRlQ2hhcnRgIGZ1bmN0aW9uIHdpdGhpbiB0aGUgd2luZG93IGNvbnRleHQgdXNpbmdcclxuICogdGhlIGBwYWdlLmV2YWx1YXRlYCBmdW5jdGlvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0QXNPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0IGNvbnRhaW5pbmcgY29tcGxldGUgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgY29uZmlndXJhdGlvblxyXG4gKiBpcyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0QXNPcHRpb25zKHBhZ2UsIG9wdGlvbnMpIHtcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKGNyZWF0ZUNoYXJ0LCBvcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50XHJcbiAqIHdpdGggdGhlICdjaGFydC1jb250YWluZXInIGlkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgeGAsIGB5YCwgYHdpZHRoYCwgYW5kIGBoZWlnaHRgIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZ2V0Q2xpcFJlZ2lvbihwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoJyNjaGFydC1jb250YWluZXInLCAoZWxlbWVudCkgPT4ge1xyXG4gICAgY29uc3QgeyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH0gPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgeCxcclxuICAgICAgeSxcclxuICAgICAgd2lkdGgsXHJcbiAgICAgIGhlaWdodDogTWF0aC50cnVuYyhoZWlnaHQgPiAxID8gaGVpZ2h0IDogNTAwKVxyXG4gICAgfTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gU1ZHIGJ5IGV2YWx1YXRpbmcgdGhlIGBvdXRlckhUTUxgIG9mIHRoZSBmaXJzdCAnc3ZnJyBlbGVtZW50XHJcbiAqIGluc2lkZSBhbiBlbGVtZW50IHdpdGggdGhlIGlkICdjb250YWluZXInLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVTVkdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBTVkcgc3RyaW5nLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVNWRyhwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoXHJcbiAgICAnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsXHJcbiAgICAoZWxlbWVudCkgPT4gZWxlbWVudC5vdXRlckhUTUxcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBzY3JlZW5zaG90YCBmdW5jdGlvbmFsaXR5IHdpdGhcclxuICogc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZUltYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEltYWdlIHR5cGUuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjbGlwIC0gQ2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGltYWdlIGJ1ZmZlclxyXG4gKiBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgIGZvciB0aW1lb3V0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZUltYWdlKHBhZ2UsIHR5cGUsIGNsaXAsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgcmV0dXJuIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xyXG4gICAgICB0eXBlLFxyXG4gICAgICBjbGlwLFxyXG4gICAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICAgIGZ1bGxQYWdlOiBmYWxzZSxcclxuICAgICAgb3B0aW1pemVGb3JTcGVlZDogdHJ1ZSxcclxuICAgICAgY2FwdHVyZUJleW9uZFZpZXdwb3J0OiB0cnVlLFxyXG4gICAgICAuLi4odHlwZSAhPT0gJ3BuZycgPyB7IHF1YWxpdHk6IDgwIH0gOiB7fSksXHJcbiAgICAgIC8vIEFsd2F5cyByZW5kZXIgb24gYSB0cmFuc3BhcmVudCBwYWdlIGlmIHRoZSBleHBlY3RlZCB0eXBlIGZvcm1hdCBpcyBQTkdcclxuICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZycgLy8gIzQ0NywgIzQ2M1xyXG4gICAgfSksXHJcbiAgICBuZXcgUHJvbWlzZSgoX3Jlc29sdmUsIHJlamVjdCkgPT5cclxuICAgICAgc2V0VGltZW91dChcclxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEV4cG9ydEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnLCA0MDgpKSxcclxuICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgICAgIClcclxuICAgIClcclxuICBdKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQREYgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBgcGRmYCBmdW5jdGlvbmFsaXR5IHdpdGggc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVBERlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtudW1iZXJ9IGhlaWdodCAtIFBERiBoZWlnaHQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aCAtIFBERiB3aWR0aC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBQREYgYnVmZmVyLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVBERihwYWdlLCBoZWlnaHQsIHdpZHRoLCByYXN0ZXJpemF0aW9uVGltZW91dCkge1xyXG4gIGF3YWl0IHBhZ2UuZW11bGF0ZU1lZGlhVHlwZSgnc2NyZWVuJyk7XHJcbiAgcmV0dXJuIHBhZ2UucGRmKHtcclxuICAgIC8vIFRoaXMgd2lsbCByZW1vdmUgYW4gZXh0cmEgZW1wdHkgcGFnZSBpbiBQREYgZXhwb3J0c1xyXG4gICAgaGVpZ2h0OiBoZWlnaHQgKyAxLFxyXG4gICAgd2lkdGgsXHJcbiAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICB0aW1lb3V0OiByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBwdXBwZXRlZXJFeHBvcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGEgd29ya2VyIHBvb2wgaW1wbGVtZW50YXRpb24gZm9yIG1hbmFnaW5nXHJcbiAqIHRoZSBicm93c2VyIGluc3RhbmNlIGFuZCBwYWdlcywgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIGZvciB1c2Ugd2l0aFxyXG4gKiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBvcHRpbWl6ZXMgcmVzb3VyY2VzIHVzYWdlIGFuZCBwZXJmb3JtYW5jZVxyXG4gKiBieSBtYWludGFpbmluZyBhIHBvb2wgb2Ygd29ya2VycyB0aGF0IGNhbiBoYW5kbGUgY29uY3VycmVudCBleHBvcnQgdGFza3NcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgY3JlYXRlQnJvd3NlciwgY2xvc2VCcm93c2VyLCBuZXdQYWdlLCBjbGVhclBhZ2UgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBwdXBwZXRlZXJFeHBvcnQgfSBmcm9tICcuL2V4cG9ydC5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXROZXdEYXRlVGltZSwgbWVhc3VyZVRpbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgcG9vbCBpbnN0YW5jZVxyXG5sZXQgcG9vbCA9IG51bGw7XHJcblxyXG4vLyBQb29sIHN0YXRpc3RpY3NcclxuY29uc3QgcG9vbFN0YXRzID0ge1xyXG4gIGV4cG9ydHNBdHRlbXB0ZWQ6IDAsXHJcbiAgZXhwb3J0c1BlcmZvcm1lZDogMCxcclxuICBleHBvcnRzRHJvcHBlZDogMCxcclxuICBleHBvcnRzRnJvbVN2ZzogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnM6IDAsXHJcbiAgZXhwb3J0c0Zyb21TdmdBdHRlbXB0czogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0czogMCxcclxuICB0aW1lU3BlbnQ6IDAsXHJcbiAgdGltZVNwZW50QXZlcmFnZTogMFxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNldHRpbmcgdXAgd29ya2VyIHJlc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0UG9vbFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3Bvb2xPcHRpb25zPWdldE9wdGlvbnMoKS5wb29sXSAtIE9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgZ2xvYmFsIHBvb2wgb3B0aW9ucyBvZiB0aGUgZXhwb3J0IHNlcnZlclxyXG4gKiBpbnN0YW5jZS5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gW3B1cHBldGVlckFyZ3M9W11dIC0gQWRkaXRpb25hbCBhcmd1bWVudHNcclxuICogZm9yIFB1cHBldGVlciBsYXVuY2guIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gZW5kaW5nIHRoZSBmdW5jdGlvblxyXG4gKiBleGVjdXRpb24gd2hlbiBhbiBhbHJlYWR5IGluaXRpYWxpemVkIHBvb2wgb2YgcmVzb3VyY2VzIGlzIGZvdW5kLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbFxyXG4gKiBvZiB3b3JrZXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRQb29sKFxyXG4gIHBvb2xPcHRpb25zID0gZ2V0T3B0aW9ucygpLnBvb2wsXHJcbiAgcHVwcGV0ZWVyQXJncyA9IFtdXHJcbikge1xyXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgcHVwcGV0ZWVyIGFyZ3VtZW50c1xyXG4gIGF3YWl0IGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncyk7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gSW5pdGlhbGl6aW5nIHBvb2wgd2l0aCB3b3JrZXJzOiBtaW4gJHtwb29sT3B0aW9ucy5taW5Xb3JrZXJzfSwgbWF4ICR7cG9vbE9wdGlvbnMubWF4V29ya2Vyc30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBpZiAocG9vbCkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICAnW3Bvb2xdIEFscmVhZHkgaW5pdGlhbGl6ZWQsIHBsZWFzZSBraWxsIGl0IGJlZm9yZSBjcmVhdGluZyBhIG5ldyBvbmUuJ1xyXG4gICAgICApO1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcCBhbiBleWUgb24gYSBjb3JyZWN0IG1pbiBhbmQgbWF4IHdvcmtlcnMgbnVtYmVyXHJcbiAgICBpZiAocG9vbE9wdGlvbnMubWluV29ya2VycyA+IHBvb2xPcHRpb25zLm1heFdvcmtlcnMpIHtcclxuICAgICAgcG9vbE9wdGlvbnMubWluV29ya2VycyA9IHBvb2xPcHRpb25zLm1heFdvcmtlcnM7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgcG9vbCBhbG9uZyB3aXRoIGEgbWluaW1hbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBwb29sID0gbmV3IFBvb2woe1xyXG4gICAgICAvLyBHZXQgdGhlIGBjcmVhdGVgLCBgdmFsaWRhdGVgLCBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9uc1xyXG4gICAgICAuLi5fZmFjdG9yeShwb29sT3B0aW9ucyksXHJcbiAgICAgIG1pbjogcG9vbE9wdGlvbnMubWluV29ya2VycyxcclxuICAgICAgbWF4OiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzLFxyXG4gICAgICBhY3F1aXJlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuYWNxdWlyZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmNyZWF0ZVRpbWVvdXQsXHJcbiAgICAgIGRlc3Ryb3lUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5kZXN0cm95VGltZW91dCxcclxuICAgICAgaWRsZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmlkbGVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVSZXRyeUludGVydmFsLFxyXG4gICAgICByZWFwSW50ZXJ2YWxNaWxsaXM6IHBvb2xPcHRpb25zLnJlYXBlckludGVydmFsLFxyXG4gICAgICBwcm9wYWdhdGVDcmVhdGVFcnJvcjogZmFsc2VcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFNldCBldmVudHNcclxuICAgIHBvb2wub24oJ3JlbGVhc2UnLCBhc3luYyAocmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gQ2xlYXIgcGFnZVxyXG4gICAgICBjb25zdCBjbGVhclN0YXR1cyA9IGF3YWl0IGNsZWFyUGFnZShyZXNvdXJjZSwgZmFsc2UpO1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cmVzb3VyY2UuaWR9XSAtIFJlbGVhc2luZyBhIHdvcmtlci4gQ2xlYXIgcGFnZSBzdGF0dXM6ICR7Y2xlYXJTdGF0dXN9LmBcclxuICAgICAgKTtcclxuICAgIH0pO1xyXG5cclxuICAgIHBvb2wub24oJ2Rlc3Ryb3lTdWNjZXNzJywgKF9ldmVudElkLCByZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cmVzb3VyY2UuaWR9XSAtIERlc3Ryb3llZCBhIHdvcmtlciBzdWNjZXNzZnVsbHkuYFxyXG4gICAgICApO1xyXG4gICAgICByZXNvdXJjZS5wYWdlID0gbnVsbDtcclxuICAgIH0pO1xyXG5cclxuICAgIGNvbnN0IGluaXRpYWxSZXNvdXJjZXMgPSBbXTtcclxuICAgIC8vIENyZWF0ZSBhbiBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcG9vbE9wdGlvbnMubWluV29ya2VyczsgaSsrKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG4gICAgICAgIGluaXRpYWxSZXNvdXJjZXMucHVzaChyZXNvdXJjZSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW3Bvb2xdIENvdWxkIG5vdCBjcmVhdGUgYW4gaW5pdGlhbCByZXNvdXJjZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlcyBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBpbml0aWFsUmVzb3VyY2VzLmZvckVhY2goKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIHBvb2wucmVsZWFzZShyZXNvdXJjZSk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gVGhlIHBvb2wgaXMgcmVhZHkke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RoID8gYCB3aXRoICR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGh9IGluaXRpYWwgcmVzb3VyY2VzIHdhaXRpbmcuYCA6ICcuJ31gXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcG9vbF0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgY3JlYXRlIHRoZSBwb29sIG9mIHdvcmtlcnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBLaWxscyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24ga2lsbFBvb2xcclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIGFmdGVyIHRoZSB3b3JrZXJzIGFyZVxyXG4gKiBraWxsZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBraWxsUG9vbCgpIHtcclxuICBsb2coMywgJ1twb29sXSBLaWxsaW5nIHBvb2wgd2l0aCBhbGwgd29ya2VycyBhbmQgY2xvc2luZyBicm93c2VyLicpO1xyXG5cclxuICAvLyBJZiBzdGlsbCBhbGl2ZSwgZGVzdHJveSB0aGUgcG9vbCBvZiBwYWdlcyBiZWZvcmUgY2xvc2luZyBhIGJyb3dzZXJcclxuICBpZiAocG9vbCkge1xyXG4gICAgLy8gRnJlZSB1cCBub3QgcmVsZWFzZWQgd29ya2Vyc1xyXG4gICAgZm9yIChjb25zdCB3b3JrZXIgb2YgcG9vbC51c2VkKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXIucmVzb3VyY2UpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgdGhlIHBvb2wgaWYgaXQgaXMgc3RpbGwgYXZhaWxhYmxlXHJcbiAgICBpZiAoIXBvb2wuZGVzdHJveWVkKSB7XHJcbiAgICAgIGF3YWl0IHBvb2wuZGVzdHJveSgpO1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBEZXN0cm95ZWQgdGhlIHBvb2wgb2YgcmVzb3VyY2VzLicpO1xyXG4gICAgfVxyXG4gICAgcG9vbCA9IG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGF3YWl0IGNsb3NlQnJvd3NlcigpO1xyXG59XHJcblxyXG4vKipcclxuICogUHJvY2Vzc2VzIHRoZSBleHBvcnQgd29yayB1c2luZyBhIHdvcmtlciBmcm9tIHRoZSBwb29sLiBBY3F1aXJlcyBhIHdvcmtlclxyXG4gKiBoYW5kbGUgZnJvbSB0aGUgcG9vbCwgcGVyZm9ybXMgdGhlIGV4cG9ydCB1c2luZyBwdXBwZXRlZXIsIGFuZCByZWxlYXNlc1xyXG4gKiB0aGUgd29ya2VyIGhhbmRsZSBiYWNrIHRvIHRoZSBwb29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHBvc3RXb3JrXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGV4cG9ydCByZXN1bHRcclxuICogYW5kIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0V29yayhvcHRpb25zKSB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIHN0YXJ0aW5nIHRvIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gQW4gZXhwb3J0IGF0dGVtcHQgY291bnRlZFxyXG4gICAgKytwb29sU3RhdHMuZXhwb3J0c0F0dGVtcHRlZDtcclxuXHJcbiAgICAvLyBEaXNwbGF5IHRoZSBwb29sIGluZm9ybWF0aW9uIGlmIG5lZWRlZFxyXG4gICAgaWYgKGdldE9wdGlvbnMoKS5wb29sLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGluIGNhc2Ugb2YgbGFja2luZyB0aGUgcG9vbCBpbnN0YW5jZVxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhlIGFjcXVpcmUgY291bnRlclxyXG4gICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFRyeSB0byBhY3F1aXJlIHRoZSB3b3JrZXIgYWxvbmcgd2l0aCB0aGUgaWQsIHdvcmtzIGNvdW50IGFuZCBwYWdlXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgICAgLy8gQWNxdWlyZSBhIHBvb2wgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxyXG4gICAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIG9wdGlvbnMuX3JlcXVlc3RJZFxyXG4gICAgICAgICAgICA/IGBbYmVuY2htYXJrXSBSZXF1ZXN0IFske29wdGlvbnMuX3JlcXVlc3RJZH1dIC0gYFxyXG4gICAgICAgICAgICA6ICdbYmVuY2htYXJrXSAnLFxyXG4gICAgICAgICAgYEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUgdG9vayAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdICcgK1xyXG4gICAgICAgICAgKG9wdGlvbnMuX3JlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMuX3JlcXVlc3RJZH1dIC0gYCA6ICcnKSArXHJcbiAgICAgICAgICBgRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFJlc29sdmVkIHdvcmtlciBwYWdlIGlzIGludmFsaWQ6IHRoZSBwb29sIHNldHVwIGlzIHdvbmt5LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgY29uc3Qgd29ya1N0YXJ0ID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHt3b3JrZXJIYW5kbGUuaWR9XSAtIFN0YXJ0aW5nIHdvcmsgb24gdGhpcyBwb29sIGVudHJ5LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnQgb24gYSBwdXBwZXRlZXIgbGV2ZWxcclxuICAgIGNvbnN0IGV4cG9ydENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KHdvcmtlckhhbmRsZS5wYWdlLCBvcHRpb25zKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiBpdCdzIGFuIGVycm9yXHJcbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgRXJyb3IpIHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSWYgdGhlcmUncyBhIHJhc3Rlcml6YXRpb24gdGltZW91dCwgd2Ugd2FudCBuZWVkIHRvIGZsdXNoIHRoZSBwYWdlLlxyXG4gICAgICAvLyBUaGlzIGlzIGJlY2F1c2UgdGhlIHBhZ2UgbWF5IGJlIGluIGEgc3RhdGUgd2hlcmUgaXQncyB3YWl0aW5nIGZvclxyXG4gICAgICAvLyB0aGUgc2NyZWVuc2hvdCB0byBmaW5pc2ggZXZlbiB0aG91Z2ggdGhlIHRpbWVvdXQgaGFzIG9jY3VyZWQuXHJcbiAgICAgIC8vIFdoaWNoIG9mIGNvdXJzZSBjYXVzZXMgYSBsb3Qgb2YgaXNzdWVzIHdpdGggdGhlIGV2ZW50IHN5c3RlbSxcclxuICAgICAgLy8gYW5kIHBhZ2UgY29uc2lzdGVuY3kuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIE9ubHkgcGFnZS5zY3JlZW5zaG90IHdpbGwgdGhyb3cgdGhpcywgdGltZW91dHMgZm9yIFBERidzIGFyZVxyXG4gICAgICAvLyBoYW5kbGVkIGJ5IHRoZSBwYWdlLnBkZiBmdW5jdGlvbiBpdHNlbGYuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIC4uLnllcywgdGhpcyBpcyB1Z2x5LlxyXG4gICAgICBpZiAocmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnKSB7XHJcbiAgICAgICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UgPSBudWxsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoXHJcbiAgICAgICAgcmVzdWx0Lm5hbWUgPT09ICdUaW1lb3V0RXJyb3InIHx8XHJcbiAgICAgICAgcmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnXHJcbiAgICAgICkge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbcG9vbF0gJyArXHJcbiAgICAgICAgICAgIChvcHRpb25zLl9yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLl9yZXF1ZXN0SWR9XSAtIGAgOiAnJykgK1xyXG4gICAgICAgICAgICAnUmFzdGVyaXphdGlvbiB0aW1lb3V0OiB5b3VyIGNoYXJ0IG1heSBiZSB0b28gY29tcGxleCBvciBsYXJnZSwgYW5kIGZhaWxlZCB0byByZW5kZXIgd2l0aGluIHRoZSBhbGxvdHRlZCB0aW1lLidcclxuICAgICAgICApLnNldEVycm9yKHJlc3VsdCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1twb29sXSAnICtcclxuICAgICAgICAgICAgKG9wdGlvbnMuX3JlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMuX3JlcXVlc3RJZH1dIC0gYCA6ICcnKSArXHJcbiAgICAgICAgICAgIGBFcnJvciBlbmNvdW50ZXJlZCBkdXJpbmcgZXhwb3J0OiAke2V4cG9ydENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgdGhlIFB1cHBldGVlciBleHBvcnQgdGltZVxyXG4gICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNSxcclxuICAgICAgICBvcHRpb25zLl9yZXF1ZXN0SWRcclxuICAgICAgICAgID8gYFtiZW5jaG1hcmtdIFJlcXVlc3QgWyR7b3B0aW9ucy5fcmVxdWVzdElkfV0gLSBgXHJcbiAgICAgICAgICA6ICdbYmVuY2htYXJrXSAnLFxyXG4gICAgICAgIGBFeHBvcnRpbmcgYSBjaGFydCBzdWNlc3NmdWxseSB0b29rICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXHJcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXHJcbiAgICBjb25zdCB3b3JrRW5kID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuICAgIGNvbnN0IGV4cG9ydFRpbWUgPSB3b3JrRW5kIC0gd29ya1N0YXJ0O1xyXG5cclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnQgKz0gZXhwb3J0VGltZTtcclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnRBdmVyYWdlID1cclxuICAgICAgcG9vbFN0YXRzLnRpbWVTcGVudCAvICsrcG9vbFN0YXRzLmV4cG9ydHNQZXJmb3JtZWQ7XHJcblxyXG4gICAgbG9nKDQsIGBbcG9vbF0gV29yayBjb21wbGV0ZWQgaW4gJHtleHBvcnRUaW1lfW1zLmApO1xyXG5cclxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gdGhlIHJlc3VsdFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzdWx0LFxyXG4gICAgICBvcHRpb25zXHJcbiAgICB9O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzRHJvcHBlZDtcclxuXHJcbiAgICBpZiAod29ya2VySGFuZGxlKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG4gICAgfVxyXG5cclxuICAgIHRocm93IGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZSBpZiBpbml0aWFsaXplZCwgb3IgbnVsbFxyXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sKCkge1xyXG4gIHJldHVybiBwb29sO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgc3RhdGlzdGljIG9mIGEgcG9vbCBpbnN0YWNlIGFib3V0IGV4cG9ydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sU3RhdHNcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGN1cnJlbnQgcG9vbCBzdGF0aXN0aWNzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xTdGF0cygpIHtcclxuICByZXR1cm4gcG9vbFN0YXRzO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXHJcbiAqIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZSByZXF1ZXN0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBQb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTigpIHtcclxuICByZXR1cm4ge1xyXG4gICAgbWluOiBwb29sLm1pbixcclxuICAgIG1heDogcG9vbC5tYXgsXHJcbiAgICB1c2VkOiBwb29sLm51bVVzZWQoKSxcclxuICAgIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXHJcbiAgICBhbGxDcmVhdGVkOiBwb29sLm51bVVzZWQoKSArIHBvb2wubnVtRnJlZSgpLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXM6IHBvb2wubnVtUGVuZGluZ0NyZWF0ZXMoKSxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9uczogcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSxcclxuICAgIHBlbmRpbmdEZXN0cm95czogcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoLFxyXG4gICAgYWJzb2x1dGVBbGw6XHJcbiAgICAgIHBvb2wubnVtVXNlZCgpICtcclxuICAgICAgcG9vbC5udW1GcmVlKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSArXHJcbiAgICAgIHBvb2wucGVuZGluZ0Rlc3Ryb3lzLmxlbmd0aFxyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cclxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXHJcbiAqIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mbygpIHtcclxuICBjb25zdCB7XHJcbiAgICBtaW4sXHJcbiAgICBtYXgsXHJcbiAgICB1c2VkLFxyXG4gICAgYXZhaWxhYmxlLFxyXG4gICAgYWxsQ3JlYXRlZCxcclxuICAgIHBlbmRpbmdBY3F1aXJlcyxcclxuICAgIHBlbmRpbmdDcmVhdGVzLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzLFxyXG4gICAgYWJzb2x1dGVBbGxcclxuICB9ID0gZ2V0UG9vbEluZm9KU09OKCk7XHJcblxyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21pbn0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWF4fS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHVzZWQgcmVzb3VyY2VzOiAke3VzZWR9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgZnJlZSByZXNvdXJjZXM6ICR7YXZhaWxhYmxlfS5gKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCBjcmVhdGVkICh1c2VkIGFuZCBmcmVlKSByZXNvdXJjZXM6ICR7YWxsQ3JlYXRlZH0uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBhY3F1aXJlZDogJHtwZW5kaW5nQWNxdWlyZXN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgY3JlYXRlZDogJHtwZW5kaW5nQ3JlYXRlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSB2YWxpZGF0ZWQ6ICR7cGVuZGluZ1ZhbGlkYXRpb25zfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGRlc3Ryb3llZDogJHtwZW5kaW5nRGVzdHJveXN9LmBcclxuICApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIHJlc291cmNlczogJHthYnNvbHV0ZUFsbH0uYCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGYWN0b3J5IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbiBvYmplY3Qgd2l0aCBgY3JlYXRlYCwgYHZhbGlkYXRlYCxcclxuICogYW5kIGBkZXN0cm95YCBmdW5jdGlvbnMgZm9yIHRoZSBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZhY3RvcnlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gT2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmFjdG9yeShwb29sT3B0aW9ucykge1xyXG4gIHJldHVybiB7XHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiBjcmVhdGVcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3RcclxuICAgICAqIGNvbnRhaW5pbmcgdGhlIHdvcmtlciBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIGluaXRpYWxcclxuICAgICAqIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGFuIGVycm9yIGR1cmluZ1xyXG4gICAgICogdGhlIGNyZWF0aW9uIG9mIHRoZSBuZXcgcGFnZS5cclxuICAgICAqL1xyXG4gICAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgIC8vIEluaXQgdGhlIHJlc291cmNlIHdpdGggdW5pcXVlIGlkIGFuZCB3b3JrIGNvdW50XHJcbiAgICAgIGNvbnN0IHBvb2xSZXNvdXJjZSA9IHtcclxuICAgICAgICBpZDogdXVpZCgpLFxyXG4gICAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sT3B0aW9ucy53b3JrTGltaXQgLyAyKSlcclxuICAgICAgfTtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gU3RhcnQgbWVhc3VyaW5nIGEgcGFnZSBjcmVhdGlvbiB0aW1lXHJcbiAgICAgICAgY29uc3Qgc3RhcnREYXRlID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICAgICAgLy8gQ3JlYXRlIGEgbmV3IHBhZ2VcclxuICAgICAgICBhd2FpdCBuZXdQYWdlKHBvb2xSZXNvdXJjZSk7XHJcblxyXG4gICAgICAgIC8vIE1lYXN1cmUgdGhlIHRpbWUgb2YgZnVsbCBjcmVhdGlvbiBhbmQgY29uZmlndXJhdGlvbiBvZiBhIHBhZ2VcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIsIHRvb2sgJHtcclxuICAgICAgICAgICAgZ2V0TmV3RGF0ZVRpbWUoKSAtIHN0YXJ0RGF0ZVxyXG4gICAgICAgICAgfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBSZXR1cm4gcmVhZHkgcG9vbCByZXNvdXJjZVxyXG4gICAgICAgIHJldHVybiBwb29sUmVzb3VyY2U7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIEVycm9yIGVuY291bnRlcmVkIHdoZW4gY3JlYXRpbmcgYSBuZXcgcGFnZS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIFZhbGlkYXRlcyBhIHdvcmtlciBwYWdlIGluIHRoZSBleHBvcnQgcG9vbCwgY2hlY2tpbmcgaWYgaXQgaGFzIGV4Y2VlZGVkXHJcbiAgICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiB2YWxpZGF0ZVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgaWYgdGhlIHdvcmtlclxyXG4gICAgICogaXMgdmFsaWQgYW5kIHdpdGhpbiB0aGUgd29yayBsaW1pdDsgb3RoZXJ3aXNlLCB0byBmYWxzZS5cclxuICAgICAqL1xyXG4gICAgdmFsaWRhdGU6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSW4gY2VydGFpbiBjYXNlcyBhY3F1aXJpbmcgdGhyb3dzIGEgVGFyZ2V0Q2xvc2VFcnJvciwgd2hpY2ggbWF5XHJcbiAgICAgIC8vIGJlIGNhdXNlZCBieSB0d28gdGhpbmdzOlxyXG4gICAgICAvLyAtIFRoZSBwYWdlIGlzIGNsb3NlZCBhbmQgYXR0ZW1wdGVkIHRvIGJlIHJldXNlZC5cclxuICAgICAgLy8gLSBMb3N0IGNvbnRhY3Qgd2l0aCB0aGUgYnJvd3Nlci5cclxuICAgICAgLy9cclxuICAgICAgLy8gV2hhdCB3ZSdyZSBzZWVpbmcgaW4gbG9ncyBpcyB0aGF0IHN1Y2Nlc3NpdmUgZXhwb3J0cyB0eXBpY2FsbHlcclxuICAgICAgLy8gc3VjY2VlZHMsIGFuZCB0aGUgc2VydmVyIHJlY292ZXJzLCBpbmRpY2F0aW5nIHRoYXQgaXQncyBsaWtlbHlcclxuICAgICAgLy8gdGhlIGZpcnN0IGNhc2UuIFRoaXMgaXMgYW4gYXR0ZW1wdCBhdCBhbGxpZXZhdGluZyB0aGUgaXNzdWUgYnlcclxuICAgICAgLy8gc2ltcGx5IG5vdCB2YWxpZGF0aW5nIHRoZSB3b3JrZXIgaWYgdGhlIHBhZ2UgaXMgbnVsbCBvciBjbG9zZWQuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIFRoZSBhY3R1YWwgcmVzdWx0IGZyb20gd2hlbiB0aGlzIGhhcHBlbmVkLCB3YXMgdGhhdCBhIHdvcmtlciB3b3VsZFxyXG4gICAgICAvLyBiZSBjb21wbGV0ZWx5IGxvY2tlZCwgc3RvcHBpbmcgaXQgZnJvbSBiZWluZyBhY3F1aXJlZCB1bnRpbFxyXG4gICAgICAvLyBpdHMgd29yayBjb3VudCByZWFjaGVkIHRoZSBsaW1pdC5cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgdmFsaWRcclxuICAgICAgaWYgKCFwb29sUmVzb3VyY2UucGFnZSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAobm8gdmFsaWQgcGFnZSBpcyBmb3VuZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHBhZ2VgIGlzIGNsb3NlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAocGFnZSBpcyBjbG9zZWQgb3IgaW52YWxpZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYG1haW5GcmFtZWAgaXMgZGV0YWNoZWRcclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlLm1haW5GcmFtZSgpLmRldGFjaGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlJ3MgZnJhbWUgaXMgZGV0YWNoZWQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGB3b3JrTGltaXRgIGlzIGV4Y2VlZGVkXHJcbiAgICAgIGlmIChcclxuICAgICAgICBwb29sT3B0aW9ucy53b3JrTGltaXQgJiZcclxuICAgICAgICArK3Bvb2xSZXNvdXJjZS53b3JrQ291bnQgPiBwb29sT3B0aW9ucy53b3JrTGltaXRcclxuICAgICAgKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChleGNlZWRlZCB0aGUgJHtwb29sT3B0aW9ucy53b3JrTGltaXR9IHdvcmtzIHBlciByZXNvdXJjZSBsaW1pdCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBUaGUgYHBvb2xSZXNvdXJjZWAgaXMgdmFsaWRhdGVkXHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIERlc3Ryb3lzIGEgd29ya2VyIGVudHJ5IGluIHRoZSBleHBvcnQgcG9vbCwgY2xvc2luZyBpdHMgYXNzb2NpYXRlZCBwYWdlLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGRlc3Ryb3lcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICAgKiB0aGUgd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAgICovXHJcbiAgICBkZXN0cm95OiBhc3luYyAocG9vbFJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIERlc3Ryb3lpbmcgYSB3b3JrZXIuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFJlbW92ZSBhbGwgYXR0YWNoZWQgZXZlbnQgbGlzdGVuZXJzIGZyb20gdGhlIHJlc291cmNlXHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3BhZ2VlcnJvcicpO1xyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdjb25zb2xlJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2ZyYW1lZGV0YWNoZWQnKTtcclxuXHJcbiAgICAgICAgICAvLyBXZSBuZWVkIHRvIHdhaXQgYXJvdW5kIGZvciB0aGlzXHJcbiAgICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5jbG9zZSgpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFBhZ2UgY291bGQgbm90IGJlIGNsb3NlZCB1cG9uIGRlc3Ryb3lpbmcuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH07XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBpbml0UG9vbCxcclxuICBraWxsUG9vbCxcclxuICBwb3N0V29yayxcclxuICBnZXRQb29sLFxyXG4gIGdldFBvb2xTdGF0cyxcclxuICBnZXRQb29sSW5mbyxcclxuICBnZXRQb29sSW5mb0pTT05cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFVzZWQgdG8gc2FuaXRpemUgdGhlIHN0cmluZ3MgY29taW5nIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVcclxuICogdG8gcHJldmVudCBYU1MgYXR0YWNrcyAod2l0aCB0aGUgRE9NUHVyaWZ5IGxpYnJhcnkpLlxyXG4gKi9cclxuXHJcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcclxuaW1wb3J0IHsgSlNET00gfSBmcm9tICdqc2RvbSc7XHJcblxyXG4vKipcclxuICogU2FuaXRpemVzIGEgZ2l2ZW4gSFRNTCBzdHJpbmcgYnkgcmVtb3ZpbmcgPHNjcmlwdD4gdGFncy4gVGhpcyBmdW5jdGlvbiB1c2VzXHJcbiAqIGEgcmVndWxhciBleHByZXNzaW9uIHRvIGZpbmQgYW5kIHJlbW92ZSBhbGwgb2NjdXJyZW5jZXMgb2YgPHNjcmlwdD48L3NjcmlwdD5cclxuICogdGFncyBhbmQgYW55IGNvbnRlbnQgd2l0aGluIHRoZW0uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzYW5pdGl6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgSFRNTCBzdHJpbmcgdG8gYmUgc2FuaXRpemVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2FuaXRpemVkIEhUTUwgc3RyaW5nLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplKGlucHV0KSB7XHJcbiAgLy8gR2V0IHRoZSB2aXJ0dWFsIERPTVxyXG4gIGNvbnN0IHdpbmRvdyA9IG5ldyBKU0RPTSgnJykud2luZG93O1xyXG5cclxuICAvLyBDcmVhdGUgYSBwdXJpZnlpbmcgaW5zdGFuY2VcclxuICBjb25zdCBwdXJpZnkgPSBET01QdXJpZnkod2luZG93KTtcclxuXHJcbiAgLy8gUmV0dXJuIHNhbml0aXplZCBpbnB1dFxyXG4gIHJldHVybiBwdXJpZnkuc2FuaXRpemUoaW5wdXQsIHsgQUREX1RBR1M6IFsnZm9yZWlnbk9iamVjdCddIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2FuaXRpemVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyB0byBwcmVwYXJlIGZvciB0aGUgZXhwb3J0aW5nIGNoYXJ0c1xyXG4gKiBpbnRvIHZhcmlvdXMgaW1hZ2Ugb3V0cHV0IGZvcm1hdHMgc3VjaCBhcyBKUEVHLCBQTkcsIFBERiwgYW5kIFNWR3MuXHJcbiAqIEl0IHN1cHBvcnRzIHNpbmdsZSBhbmQgYmF0Y2ggZXhwb3J0IG9wZXJhdGlvbnMgYW5kIGFsbG93cyBjdXN0b21pemF0aW9uXHJcbiAqIHRocm91Z2ggb3B0aW9ucyBwYXNzZWQgZnJvbSBjb25maWd1cmF0aW9ucyBvciBBUElzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIG1lcmdlT3B0aW9ucywgaXNBbGxvd2VkQ29uZmlnIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsga2lsbFBvb2wsIHBvc3RXb3JrLCBnZXRQb29sU3RhdHMgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHdyYXBBcm91bmRcclxufSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgZ2xvYmFsIGZsYWcgZm9yIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uXHJcbmxldCBhbGxvd0NvZGVFeGVjdXRpb24gPSBmYWxzZTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBvcHRpb25zIGFuZCBzYXZlc1xyXG4gKiB0aGUgaW1hZ2UgaW4gdGhlIHByb3ZpZGVkIG91dGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIG1heSBiZSBhIHBhcnRpYWxcclxuICogb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMuIEl0IG11c3QgY29udGFpbiBhdCBsZWFzdCBvbmUgb2YgdGhlIGZvbGxvd2luZ1xyXG4gKiBwcm9wZXJ0aWVzOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgc2luZ2xlIGV4cG9ydFxyXG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIHNpbmdsZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaW5nbGVFeHBvcnQob3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBleHBvcnQgbWFrZXMgc2Vuc2VcclxuICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLmV4cG9ydCkge1xyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnRcclxuICAgIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIGFzeW5jIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAvLyBFeGl0IHByb2Nlc3Mgd2hlbiBlcnJvciBleGlzdHNcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgIC8vIFNhdmUgdGhlIHJlc3VsdFxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgIC8vIEFzIGEgQmFzZTY0IHN0cmluZyB0byBhIHR4dCBmaWxlXHJcbiAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICBnZXRCYXNlNjQoZGF0YS5yZXN1bHQsIHR5cGUpXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAvLyBBcyBhIGNvcnJlY3QgaW1hZ2UgZm9ybWF0XHJcbiAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICBvdXRmaWxlIHx8IGBjaGFydC4ke3R5cGV9YCxcclxuICAgICAgICAgICAgdHlwZSAhPT0gJ3N2ZycgPyBCdWZmZXIuZnJvbShkYXRhLnJlc3VsdCwgJ2Jhc2U2NCcpIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEVycm9yIHdoaWxlIHNhdmluZyBhIGNoYXJ0LicsXHJcbiAgICAgICAgICA1MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gS2lsbCBwb29sIGFuZCBjbG9zZSBicm93c2VyIGFmdGVyIGZpbmlzaGluZyBzaW5nbGUgZXhwb3J0XHJcbiAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICB9KTtcclxuICB9IGVsc2Uge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NoYXJ0XSBObyBleHBlY3RlZCBgZXhwb3J0YCBvcHRpb25zIHdlcmUgZm91bmQuIFBsZWFzZSBwcm92aWRlIG9uZSBvZiB0aGUgZm9sbG93aW5nIG9wdGlvbnM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuJyxcclxuICAgICAgNDAwXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIGJhdGNoIGV4cG9ydCBwcm9jZXNzIGZvciBtdWx0aXBsZSBjaGFydHMgYmFzZWQgb24gdGhlIGluZm9ybWF0aW9uXHJcbiAqIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZyBmb3JtYXQ6XHJcbiAqIFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzIGFyZSBzYXZlZFxyXG4gKiBpbiBwcm92aWRlZCBvdXRmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBtYXkgYmUgYSBwYXJ0aWFsXHJcbiAqIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIHRvIGdlbmVyYXRlXHJcbiAqIHZhbGlkIGltYWdlcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGJhdGNoIGV4cG9ydFxyXG4gKiBwcm9jZXNzZXMgYXJlIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogYW55IG9mIHRoZSBiYXRjaCBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBiYXRjaEV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0ICYmIG9wdGlvbnMuZXhwb3J0LmJhdGNoKSB7XHJcbiAgICAvLyBBbiBhcnJheSBmb3IgY29sbGVjdGluZyBiYXRjaCBleHBvcnRzXHJcbiAgICBjb25zdCBiYXRjaEZ1bmN0aW9ucyA9IFtdO1xyXG5cclxuICAgIC8vIFNwbGl0IGFuZCBwYWlyIHRoZSBgYmF0Y2hgIGFyZ3VtZW50c1xyXG4gICAgZm9yIChsZXQgcGFpciBvZiBvcHRpb25zLmV4cG9ydC5iYXRjaC5zcGxpdCgnOycpIHx8IFtdKSB7XHJcbiAgICAgIHBhaXIgPSBwYWlyLnNwbGl0KCc9Jyk7XHJcbiAgICAgIGlmIChwYWlyLmxlbmd0aCA9PT0gMikge1xyXG4gICAgICAgIGJhdGNoRnVuY3Rpb25zLnB1c2goXHJcbiAgICAgICAgICBzdGFydEV4cG9ydChcclxuICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgIC4uLm9wdGlvbnMsXHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgY3VzdG9tT3B0aW9uc2AgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0XHJcbiAqIG1heSBiZSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zLiBUaGUgYGVuZENhbGxiYWNrYCBpcyBjYWxsZWQgd2hlblxyXG4gKiB0aGUgZXhwb3J0IGlzIGNvbXBsZXRlZCwgd2l0aCB0aGUgYGVycm9yYCBvYmplY3QgYXMgdGhlIGZpcnN0IGFyZ3VtZW50XHJcbiAqIGFuZCB0aGUgYGRhdGFgIG9iamVjdCBhcyB0aGUgc2Vjb25kLCB3aGljaCBjb250YWlucyB0aGUgQmFzZTY0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBjaGFydCBpbiB0aGUgYHJlc3VsdGAgcHJvcGVydHkgYW5kIHRoZSBmdWxsIHNldCBvZiBleHBvcnQgb3B0aW9uc1xyXG4gKiBpbiB0aGUgYG9wdGlvbnNgIHByb3BlcnR5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHN0YXJ0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21PcHRpb25zIC0gVGhlIGBjdXN0b21PcHRpb25zYCBvYmplY3QsIHdoaWNoIG1heVxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMuIElmIHRoZSBwcm92aWRlZCBvcHRpb25zIGFyZSBwYXJ0aWFsLFxyXG4gKiBtaXNzaW5nIHZhbHVlcyB3aWxsIGJlIG1lcmdlZCB3aXRoIHRoZSBkZWZhdWx0IGdlbmVyYWwgb3B0aW9ucywgcmV0cmlldmVkXHJcbiAqIHVzaW5nIHRoZSBgZ2V0T3B0aW9uc2AgZnVuY3Rpb24uXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHdvcmsgb3IgdXBvbiBlcnJvciBvY2N1cmFuY2Ugb2YgdGhlIGV4cG9ydGluZyBwcm9jZXNzLiBUaGUgZmlyc3RcclxuICogYXJndW1lbnQgaXMgYGVycm9yYCBvYmplY3QgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIHRoYXQgY29udGFpbnNcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5IGFuZCB0aGUgZnVsbFxyXG4gKiBzZXQgb2YgZXhwb3J0IG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGN1c3RvbU9wdGlvbnMsIGVuZENhbGxiYWNrKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gTWVyZ2UgdGhlIGN1c3RvbSBvcHRpb25zIGludG8gZGVmYXVsdCBvbmVzXHJcbiAgICBjb25zdCBvcHRpb25zID0gbWVyZ2VPcHRpb25zKGdldE9wdGlvbnMoZmFsc2UpLCBjdXN0b21PcHRpb25zKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGV4cG9ydCBvcHRpb25zXHJcbiAgICBjb25zdCBleHBvcnRPcHRpb25zID0gb3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSB2YWx1ZSB0byBiZSBhc3NpZ25lZCB0byB0aGUgZ2xvYmFsXHJcbiAqIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb24odmFsdWUpIHtcclxuICBhbGxvd0NvZGVFeGVjdXRpb24gPSB2YWx1ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEV4cG9ydHMgZnJvbSBhbiBTVkcgYmFzZWQgaW5wdXQgd2l0aCB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZXhwb3J0RnJvbVN2Z1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBTVkcgYmFzZWQgaW5wdXQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0IGNvbnRhaW5pbmcgY29tcGxldGUgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhIHJlc3VsdCBvZiB0aGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBub3QgYSBjb3JyZWN0XHJcbiAqIGNoYXJ0IG9wdGlvbnMgaW5wdXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZXhwb3J0RnJvbU9wdGlvbnMoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIGxvZyg0LCAnW2NoYXJ0XSBQYXJzaW5nIGlucHV0IGZyb20gb3B0aW9ucy4nKTtcclxuXHJcbiAgLy8gVHJ5IHRvIGNoZWNrLCB2YWxpZGF0ZSBhbmQgcGFyc2UgdG8gc3RyaW5naWZpZWQgb3B0aW9uc1xyXG4gIGNvbnN0IHN0cmluZ2lmaWVkT3B0aW9ucyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgIGlucHV0VG9FeHBvcnQsXHJcbiAgICB0cnVlLFxyXG4gICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICApO1xyXG5cclxuICAvLyBDaGVjayBpZiBhIGNvcnJlY3Qgc3RyaW5naWZpZWQgb3B0aW9uc1xyXG4gIGlmIChcclxuICAgIHN0cmluZ2lmaWVkT3B0aW9ucyA9PT0gbnVsbCB8fFxyXG4gICAgdHlwZW9mIHN0cmluZ2lmaWVkT3B0aW9ucyAhPT0gJ3N0cmluZycgfHxcclxuICAgICFzdHJpbmdpZmllZE9wdGlvbnMuc3RhcnRzV2l0aCgneycpIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLmVuZHNXaXRoKCd9JylcclxuICApIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gSW52YWxpZCBjb25maWd1cmF0aW9uIHByb3ZpZGVkIC0gT25seSBvcHRpb25zIGNvbmZpZ3VyYXRpb25zIGFuZCBTVkcgYXJlIGFsbG93ZWQgZm9yIHRoaXMgc2VydmVyLiBJZiB0aGlzIGlzIHlvdXIgc2VydmVyLCBKYXZhU2NyaXB0IGN1c3RvbSBjb2RlIGNhbiBiZSBlbmFibGVkIGJ5IHN0YXJ0aW5nIHRoZSBzZXJ2ZXIgd2l0aCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9ucyBzZXQgdG8gdHJ1ZS4nLFxyXG4gICAgICA0MDNcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGV4cG9ydCBpbnB1dCBhcyBhIHN0cmluZ2lmaWVkIGNoYXJ0IG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IHN0cmluZ2lmaWVkT3B0aW9ucztcclxuXHJcbiAgLy8gUmVzZXQgdGhlIHJlc3Qgb2YgdGhlIGV4cG9ydCBpbnB1dCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuc3ZnID0gbnVsbDtcclxuXHJcbiAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhIHN0cmluZ2lmaWVkIGNoYXJ0IG9wdGlvbnNcclxuICByZXR1cm4gX3ByZXBhcmVFeHBvcnQob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGdW5jdGlvbiBmb3IgZmluYWxpemluZyBvcHRpb25zIGFuZCBjb25maWd1cmF0aW9ucyBiZWZvcmUgZXhwb3J0LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9wcmVwYXJlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpIHtcclxuICBjb25zdCB7IGV4cG9ydDogZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWM6IGN1c3RvbUxvZ2ljT3B0aW9ucyB9ID0gb3B0aW9ucztcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYHR5cGVgIG9wdGlvblxyXG4gIGV4cG9ydE9wdGlvbnMudHlwZSA9IGZpeFR5cGUoZXhwb3J0T3B0aW9ucy50eXBlLCBleHBvcnRPcHRpb25zLm91dGZpbGUpO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5vdXRmaWxlID0gZml4T3V0ZmlsZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIE5vdGlmeSBhYm91dCB0aGUgY3VzdG9tIGxvZ2ljIHVzYWdlIHN0YXR1c1xyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW2NoYXJ0XSBUaGUgY3VzdG9tIGxvZ2ljIGlzICR7Y3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvbiA/ICdhbGxvd2VkJyA6ICdkaXNhbGxvd2VkJ30uYFxyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGN1c3RvbSBsb2dpYyBvcHRpb25zIChgY3VzdG9tQ29kZWAsIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgKVxyXG4gIF9oYW5kbGVDdXN0b21Mb2dpYyhjdXN0b21Mb2dpY09wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgZ2xvYmFsT3B0aW9uc2AgYW5kIGB0aGVtZU9wdGlvbnNgIG9wdGlvbnNcclxuICBfaGFuZGxlR2xvYmFsQW5kVGhlbWUoXHJcbiAgICBleHBvcnRPcHRpb25zLFxyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCwgYW5kIGBzY2FsZWAgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0ID0ge1xyXG4gICAgLi4uZXhwb3J0T3B0aW9ucyxcclxuICAgIC4uLl9maW5kQ2hhcnRTaXplKGV4cG9ydE9wdGlvbnMpXHJcbiAgfTtcclxuXHJcbiAgLy8gUG9zdCB0aGUgd29yayB0byB0aGUgcG9vbFxyXG4gIHJldHVybiBwb3N0V29yayhvcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgdGhlIGBoZWlnaHRgLCBgd2lkdGhgIGFuZCBgc2NhbGVgIGZvciBjaGFydCBleHBvcnRzIGJhc2VkXHJcbiAqIG9uIHRoZSBwcm92aWRlZCBleHBvcnQgb3B0aW9ucy5cclxuICpcclxuICogVGhlIGZ1bmN0aW9uIHByaW9yaXRpemVzIHZhbHVlcyBpbiB0aGUgZm9sbG93aW5nIG9yZGVyOlxyXG4gKiAxLiBUaGUgYGhlaWdodGAsIGB3aWR0aGAsIGBzY2FsZWAgZnJvbSB0aGUgYGV4cG9ydE9wdGlvbnNgLlxyXG4gKiAyLiBPcHRpb25zIGZyb20gdGhlIGNoYXJ0IGNvbmZpZ3VyYXRpb24gKGZyb20gYGV4cG9ydGluZ2AgYW5kIGBjaGFydGApLlxyXG4gKiAzLiBPcHRpb25zIGZyb20gdGhlIGdsb2JhbCBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogNC4gT3B0aW9ucyBmcm9tIHRoZSB0aGVtZSBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgIHNlY3Rpb25zKS5cclxuICogNS4gRmFsbGJhY2sgZGVmYXVsdCB2YWx1ZXMgKGBoZWlnaHQgPSA0MDBgLCBgd2lkdGggPSA2MDBgLCBgc2NhbGUgPSAxYCkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmluZENoYXJ0U2l6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBvYmplY3QgY29udGFpbmluZyBgY3VzdG9tTG9naWNgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY29kZSBleGVjdXRpb25cclxuICogaXMgbm90IGFsbG93ZWQgYnV0IGN1c3RvbSBsb2dpYyBvcHRpb25zIGFyZSBzdGlsbCBwcm92aWRlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVDdXN0b21Mb2dpYyhjdXN0b21Mb2dpY09wdGlvbnMsIGFsbG93Q29kZUV4ZWN1dGlvbikge1xyXG4gIC8vIEluIGNhc2Ugb2YgYWxsb3dpbmcgY29kZSBleGVjdXRpb25cclxuICBpZiAoYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgcmVzb3VyY2VzYCBvcHRpb25cclxuICAgIGlmICh0eXBlb2YgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gQ3VzdG9tIHN0cmluZ2lmaWVkIHJlc291cmNlc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmICghY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIExvYWQgdGhlIGRlZmF1bHQgb25lXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9IF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKCdyZXNvdXJjZXMuanNvbicpLCAndXRmOCcpLFxyXG4gICAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgIHRydWVcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZygyLCAnW2NoYXJ0XSBVbmFibGUgdG8gbG9hZCB0aGUgZGVmYXVsdCBgcmVzb3VyY2VzLmpzb25gIGZpbGUuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBUcnkgdG8gbG9hZCBjdXN0b20gY29kZSBhbmQgd3JhcCBhcm91bmQgaXQgaW4gYSBzZWxmIGludm9raW5nIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY3VzdG9tQ29kZWAgY2Fubm90IGJlIGxvYWRlZC4nKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUHJvY2VzcyB0aGUgYGNhbGxiYWNrYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGNhbGxiYWNrIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IHdyYXBBcm91bmQoXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW2NoYXJ0XSBUaGUgYGNhbGxiYWNrYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjdXN0b21Db2RlYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBjdXN0b21Db2RlYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjYWxsYmFja2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjaykpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGNhbGxiYWNrYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGByZXNvdXJjZXNgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgcmVzb3VyY2VzYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIElmIHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBmbGFnIGlzIHNldCB0byBmYWxzZSwgd2Ugc2hvdWxkIHJlZnVzZVxyXG4gICAgLy8gdGhlIHVzYWdlIG9mIHRoZSBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYCwgYW5kIGBjdXN0b21Db2RlYCBvcHRpb25zLlxyXG4gICAgLy8gQWRkaXRpb25hbGx5LCB0aGUgd29ya2VyIHdpbGwgcmVmdXNlIHRvIHJ1biBhcmJpdHJhcnkgSmF2YVNjcmlwdC5cclxuICAgIGlmIChcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGVcclxuICAgICkge1xyXG4gICAgICAvLyBSZXNldCBhbGwgY3VzdG9tIGNvZGUgb3B0aW9uc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gbnVsbDtcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBudWxsO1xyXG5cclxuICAgICAgLy8gU2VuZCBhIG1lc3NhZ2Ugc2F5aW5nIHRoYXQgdGhlIGV4cG9ydGVyIGRvZXMgbm90IHN1cHBvcnQgdGhlc2Ugc2V0dGluZ3NcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJywgYW5kICdjdXN0b21Db2RlJyBvcHRpb25zIGhhdmUgYmVlbiBkaXNhYmxlZCBmb3IgdGhpcyBzZXJ2ZXIuYCxcclxuICAgICAgICA0MDNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIGFuZCB2YWxpZGF0ZXMgcmVzb3VyY2VzIGZyb20gdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3IgZXhwb3J0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8c3RyaW5nfG51bGwpfSBbcmVzb3VyY2VzPW51bGxdIC0gVGhlIHJlc291cmNlcyB0byBiZSBoYW5kbGVkLlxyXG4gKiBDYW4gYmUgZWl0aGVyIGEgSlNPTiBvYmplY3QsIHN0cmluZ2lmaWVkIEpTT04sIGEgcGF0aCB0byBhIEpTT04gZmlsZSxcclxuICogb3IgbnVsbC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgbnVsbC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGxvYWRpbmdcclxuICogcmVzb3VyY2VzIGZyb20gZmlsZXMgaXMgYWxsb3dlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fG51bGwpfSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgbnVsbCBpZiBubyB2YWxpZCByZXNvdXJjZXNcclxuICogYXJlIGZvdW5kLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZVJlc291cmNlcyhcclxuICByZXNvdXJjZXMgPSBudWxsLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gTGlzdCBvZiBhbGxvd2VkIHNlY3Rpb25zIGluIHRoZSByZXNvdXJjZXMgSlNPTlxyXG4gIGNvbnN0IGFsbG93ZWRQcm9wcyA9IFsnanMnLCAnY3NzJywgJ2ZpbGVzJ107XHJcblxyXG4gIGxldCBoYW5kbGVkUmVzb3VyY2VzID0gcmVzb3VyY2VzO1xyXG4gIGxldCBjb3JyZWN0UmVzb3VyY2VzID0gZmFsc2U7XHJcblxyXG4gIC8vIFRyeSB0byBsb2FkIHJlc291cmNlcyBmcm9tIGEgZmlsZVxyXG4gIGlmIChhbGxvd0ZpbGVSZXNvdXJjZXMgJiYgcmVzb3VyY2VzLmVuZHNXaXRoKCcuanNvbicpKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBoYW5kbGVkUmVzb3VyY2VzID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgocmVzb3VyY2VzKSwgJ3V0ZjgnKSxcclxuICAgICAgICBmYWxzZSxcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2gge1xyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gVHJ5IHRvIGdldCBKU09OXHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzID0gaXNBbGxvd2VkQ29uZmlnKHJlc291cmNlcywgZmFsc2UsIGFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgZmlsZXMgc2VjdGlvblxyXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbHRlciBmcm9tIHVubmVjZXNzYXJ5IHByb3BlcnRpZXNcclxuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcclxuICAgIGlmICghYWxsb3dlZFByb3BzLmluY2x1ZGVzKHByb3BOYW1lKSkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlc1twcm9wTmFtZV07XHJcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICAgIGNvcnJlY3RSZXNvdXJjZXMgPSB0cnVlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIGFsbG93ZWQgcHJvcGVydGllcyBpcyBwcmVzZW50XHJcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcblxyXG4gIC8vIEhhbmRsZSBmaWxlcyBzZWN0aW9uXHJcbiAgaWYgKGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgPSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLm1hcCgoaXRlbSkgPT4gaXRlbS50cmltKCkpO1xyXG4gICAgaWYgKCFoYW5kbGVkUmVzb3VyY2VzLmZpbGVzIHx8IGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMubGVuZ3RoIDw9IDApIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcmVzb3VyY2VzXHJcbiAgcmV0dXJuIGhhbmRsZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBsb2FkaW5nIGFuZCB2YWxpZGF0aW9uIG9mIHRoZSBgZ2xvYmFsT3B0aW9uc2AgYW5kIGB0aGVtZU9wdGlvbnNgXHJcbiAqIGluIHRoZSBleHBvcnQgb3B0aW9ucy4gSWYgdGhlIG9wdGlvbiBpcyBhIHN0cmluZyBhbmQgcmVmZXJlbmNlcyBhIEpTT04gZmlsZVxyXG4gKiAod2hlbiB0aGUgYGFsbG93RmlsZVJlc291cmNlc2AgaXMgdHJ1ZSksIGl0IHJlYWRzIGFuZCBwYXJzZXMgdGhlIGZpbGUuXHJcbiAqIE90aGVyd2lzZSwgaXQgYXR0ZW1wdHMgdG8gcGFyc2UgdGhlIHN0cmluZyBvciBvYmplY3QgYXMgSlNPTi4gSWYgYW55IGVycm9yc1xyXG4gKiBvY2N1ciBkdXJpbmcgdGhpcyBwcm9jZXNzLCB0aGUgb3B0aW9uIGlzIHNldCB0byBudWxsLiBJZiB0aGVyZSBpcyBhbiBlcnJvclxyXG4gKiBsb2FkaW5nIG9yIHBhcnNpbmcgdGhlIGBnbG9iYWxPcHRpb25zYCBvciBgdGhlbWVPcHRpb25zYCwgdGhlIGVycm9yIGlzIGxvZ2dlZFxyXG4gKiBhbmQgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzdGFydEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgZW5hYmxpbmcgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc3BlY2lmaWVkIEV4cHJlc3MgYXBwLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IFtyYXRlTGltaXRpbmdPcHRpb25zPWdldE9wdGlvbnMoKS5zZXJ2ZXIucmF0ZUxpbWl0aW5nXSAtXHJcbiAqIE9iamVjdCBjb250YWluaW5nIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIHRoZSBnbG9iYWxcclxuICogcmF0ZSBsaW1pdGluZyBvcHRpb25zIG9mIHRoZSBleHBvcnQgc2VydmVyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0XHJcbiAqIHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiByYXRlTGltaXRpbmdNaWRkbGV3YXJlKFxyXG4gIGFwcCxcclxuICByYXRlTGltaXRpbmdPcHRpb25zID0gZ2V0T3B0aW9ucygpLnNlcnZlci5yYXRlTGltaXRpbmdcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWRcclxuICAgIGlmIChyYXRlTGltaXRpbmdPcHRpb25zLmVuYWJsZSkge1xyXG4gICAgICBjb25zdCBtc2cgPVxyXG4gICAgICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgICAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICAgICAgbWF4OiByYXRlTGltaXRpbmdPcHRpb25zLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgICAgIHdpbmRvdzogcmF0ZUxpbWl0aW5nT3B0aW9ucy53aW5kb3cgfHwgMSxcclxuICAgICAgICBkZWxheTogcmF0ZUxpbWl0aW5nT3B0aW9ucy5kZWxheSB8fCAwLFxyXG4gICAgICAgIHRydXN0UHJveHk6IHJhdGVMaW1pdGluZ09wdGlvbnMudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgICAgICBza2lwS2V5OiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBLZXkgfHwgZmFsc2UsXHJcbiAgICAgICAgc2tpcFRva2VuOiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBUb2tlbiB8fCBmYWxzZVxyXG4gICAgICB9O1xyXG5cclxuICAgICAgLy8gU2V0IGlmIGJlaGluZCBhIHByb3h5XHJcbiAgICAgIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICAgICAgYXBwLmVuYWJsZSgndHJ1c3QgcHJveHknKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gICAgICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcclxuICAgICAgICB3aW5kb3dNczogcmF0ZU9wdGlvbnMud2luZG93ICogNjAgKiAxMDAwLFxyXG4gICAgICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciB3aW5kb3dNc1xyXG4gICAgICAgIG1heDogcmF0ZU9wdGlvbnMubWF4LFxyXG4gICAgICAgIC8vIERpc2FibGUgZGVsYXlpbmcsIGZ1bGwgc3BlZWQgdW50aWwgdGhlIG1heCBsaW1pdCBpcyByZWFjaGVkXHJcbiAgICAgICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICAgICAgaGFuZGxlcjogKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5mb3JtYXQoe1xyXG4gICAgICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZCh7IG1lc3NhZ2U6IG1zZyB9KTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobXNnKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSk7XHJcbiAgICAgICAgfSxcclxuICAgICAgICBza2lwOiAocmVxdWVzdCkgPT4ge1xyXG4gICAgICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcclxuICAgICAgICAgIGlmIChcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcEtleSAhPT0gZmFsc2UgJiZcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBmYWxzZSAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmtleSA9PT0gcmF0ZU9wdGlvbnMuc2tpcEtleSAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmFjY2Vzc190b2tlbiA9PT0gcmF0ZU9wdGlvbnMuc2tpcFRva2VuXHJcbiAgICAgICAgICApIHtcclxuICAgICAgICAgICAgbG9nKDQsICdbcmF0ZSBsaW1pdGluZ10gU2tpcHBpbmcgcmF0ZSBsaW1pdGVyLicpO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgLy8gVXNlIGEgbGltaXRlciBhcyBhIG1pZGRsZXdhcmVcclxuICAgICAgYXBwLnVzZShsaW1pdGVyKTtcclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcmF0ZSBsaW1pdGluZ10gRW5hYmxlZCByYXRlIGxpbWl0aW5nIHdpdGggJHtyYXRlT3B0aW9ucy5tYXh9IHJlcXVlc3RzIHBlciAke3JhdGVPcHRpb25zLndpbmRvd30gbWludXRlIGZvciBlYWNoIElQLCB0cnVzdGluZyBwcm94eTogJHtyYXRlT3B0aW9ucy50cnVzdFByb3h5fS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tyYXRlIGxpbWl0aW5nXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBIVFRQIGVycm9yIGNsYXNzIHRoYXQgZXh0ZW5kcyB0aGUgYEV4cG9ydEVycm9yYC4gVXNlZCB0byBoYW5kbGVcclxuICogZXJyb3JzIHdpdGggSFRUUCBzdGF0dXMgY29kZXMuXHJcbiAqL1xyXG5jbGFzcyBIdHRwRXJyb3IgZXh0ZW5kcyBFeHBvcnRFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEh0dHBFcnJvcmAuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIFRoZSBlcnJvciBtZXNzYWdlIHRvIGJlIGRpc3BsYXllZC5cclxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzQ29kZSAtIE9wdGlvbmFsIEhUVFAgc3RhdHVzIGNvZGUgYXNzb2NpYXRlZFxyXG4gICAqIHdpdGggdGhlIGVycm9yIChlLmcuLCA0MDAsIDUwMCkuXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSwgc3RhdHVzQ29kZSkge1xyXG4gICAgc3VwZXIobWVzc2FnZSwgc3RhdHVzQ29kZSk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXRzIG9yIHVwZGF0ZXMgdGhlIEhUVFAgc3RhdHVzIGNvZGUgZm9yIHRoZSBlcnJvci5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gVGhlIEhUVFAgc3RhdHVzIGNvZGUgdG8gYXNzaWduIHRvIHRoZSBlcnJvci5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtIdHRwRXJyb3J9IFRoZSB1cGRhdGVkIGluc3RhbmNlIG9mIHRoZSBgSHR0cEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IEh0dHBFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciB2YWxpZGF0aW5nIGluY29taW5nIEhUVFAgcmVxdWVzdHNcclxuICogaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi4gVGhpcyBtb2R1bGUgZW5zdXJlcyB0aGF0IHJlcXVlc3RzIGNvbnRhaW5cclxuICogYXBwcm9wcmlhdGUgY29udGVudCB0eXBlcyBhbmQgdmFsaWQgcmVxdWVzdCBib2RpZXMsIGluY2x1ZGluZyBwcm9wZXIgSlNPTlxyXG4gKiBzdHJ1Y3R1cmVzIGFuZCBjaGFydCBkYXRhIGZvciBleHBvcnRzLiBJdCBjaGVja3MgZm9yIHBvdGVudGlhbCBpc3N1ZXMgc3VjaFxyXG4gKiBhcyBtaXNzaW5nIG9yIG1hbGZvcm1lZCBkYXRhLCBwcml2YXRlIHJhbmdlIFVSTHMgaW4gU1ZHIHBheWxvYWRzLCBhbmQgYWxsb3dzXHJcbiAqIGZvciBmbGV4aWJsZSBvcHRpb25zIHZhbGlkYXRpb24uIFRoZSBtaWRkbGV3YXJlIGxvZ3MgZGV0YWlsZWQgaW5mb3JtYXRpb25cclxuICogYW5kIGhhbmRsZXMgZXJyb3JzIHJlbGF0ZWQgdG8gaW5jb3JyZWN0IHBheWxvYWRzLCBjaGFydCBkYXRhLCBhbmQgcHJpdmF0ZSBVUkxcclxuICogdXNhZ2UuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uIH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeENvbnN0cixcclxuICBmaXhUeXBlLFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZFxyXG59IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBIdHRwRXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0h0dHBFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgY29udGVudC10eXBlIGhlYWRlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7SHR0cEVycm9yfSBUaHJvd3MgYW4gYEh0dHBFcnJvcmAgaWYgdGhlIGNvbnRlbnQtdHlwZVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICovXHJcbmZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZShyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIGNvbnRlbnQgdHlwZSBoZWFkZXJcclxuICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gcmVxdWVzdC5oZWFkZXJzWydjb250ZW50LXR5cGUnXSB8fCAnJztcclxuXHJcbiAgICAvLyBBbGxvdyBvbmx5IEpTT04sIFVSTC1lbmNvZGVkIGFuZCBmb3JtIGRhdGEgd2l0aG91dCBmaWxlcyB0eXBlcyBvZiBkYXRhXHJcbiAgICBpZiAoXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24vanNvbicpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJykgJiZcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdtdWx0aXBhcnQvZm9ybS1kYXRhJylcclxuICAgICkge1xyXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICdbdmFsaWRhdGlvbl0gQ29udGVudC1UeXBlIG11c3QgYmUgYXBwbGljYXRpb24vanNvbiwgYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkLCBvciBtdWx0aXBhcnQvZm9ybS1kYXRhLicsXHJcbiAgICAgICAgNDE1XHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYHJlcXVlc3RCb2R5TWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIHJlcXVlc3QncyBib2R5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtIdHRwRXJyb3J9IFRocm93cyBhbiBgSHR0cEVycm9yYCBpZiB0aGUgYm9keSBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7SHR0cEVycm9yfSBUaHJvd3MgYW4gYEh0dHBFcnJvcmAgaWYgdGhlIGNoYXJ0IGRhdGEgZnJvbSB0aGUgYm9keVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7SHR0cEVycm9yfSBUaHJvd3MgYW4gYEh0dHBFcnJvcmAgaW4gY2FzZSBvZiB0aGUgcHJpdmF0ZSByYW5nZSB1cmxcclxuICogZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGJvZHlcclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHV1aWQoKS5yZXBsYWNlKC8tL2csICcnKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgZW1wdHkuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICBcIlt2YWxpZGF0aW9uXSBUaGUgcmVxdWVzdCBib2R5IGlzIHJlcXVpcmVkLiBQbGVhc2UgZW5zdXJlIHRoYXQgeW91ciBDb250ZW50LVR5cGUgaGVhZGVyIGlzIGNvcnJlY3QuIEFjY2VwdGVkIHR5cGVzIGFyZSAnYXBwbGljYXRpb24vanNvbicgYW5kICdtdWx0aXBhcnQvZm9ybS1kYXRhJy5cIixcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBvcHRpb24gZm9yIHRoZSBzZXJ2ZXJcclxuICAgIGNvbnN0IGFsbG93Q29kZUV4ZWN1dGlvbiA9IGdldEFsbG93Q29kZUV4ZWN1dGlvbigpO1xyXG5cclxuICAgIC8vIEZpbmQgYSBjb3JyZWN0IGNoYXJ0IG9wdGlvbnNcclxuICAgIGNvbnN0IGluc3RyID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAvLyBVc2Ugb25lIG9mIHRoZSBiZWxvd1xyXG4gICAgICBib2R5Lmluc3RyIHx8IGJvZHkub3B0aW9ucyB8fCBib2R5LmluZmlsZSB8fCBib2R5LmRhdGEsXHJcbiAgICAgIC8vIFN0cmluZ2lmeSBvcHRpb25zXHJcbiAgICAgIHRydWUsXHJcbiAgICAgIC8vIEFsbG93IG9yIGRpc2FsbG93IGZ1bmN0aW9uc1xyXG4gICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICk7XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgaXMgbm8gY29ycmVjdCBjaGFydCBkYXRhXHJcbiAgICBpZiAoaW5zdHIgPT09IG51bGwgJiYgIWJvZHkuc3ZnKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgbWlzc2luZyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm9yIGV4cG9ydDogJHtKU09OLnN0cmluZ2lmeShib2R5KX0uYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICBcIlt2YWxpZGF0aW9uXSBObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLlwiLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRlc3Qgb2YgeGxpbms6aHJlZiBlbGVtZW50cyBmcm9tIHBheWxvYWQncyBTVkcgZmFpbHNcclxuICAgIGlmIChib2R5LnN2ZyAmJiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKGJvZHkuc3ZnKSkge1xyXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgIFwiW3ZhbGlkYXRpb25dIFNWRyBwb3RlbnRpYWxseSBjb250YWluIGF0IGxlYXN0IG9uZSBmb3JiaWRkZW4gVVJMIGluICd4bGluazpocmVmJyBlbGVtZW50LiBQbGVhc2UgcmV2aWV3IHRoZSBTVkcgY29udGVudCBhbmQgZW5zdXJlIHRoYXQgYWxsIHJlZmVyZW5jZWQgVVJMcyBjb21wbHkgd2l0aCBzZWN1cml0eSBwb2xpY2llcy5cIixcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBib2R5IGFuZCBzdG9yZSBwYXJzZWQgc3RydWN0dXJlIGluIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnMgPSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY3JlYXRlZCBJRCBhcyBhIGBfcmVxdWVzdElkYCBwcm9wZXJ0eSBpbiB0aGUgdmFsaWRhdGVkIG9wdGlvbnNcclxuICAgICAgX3JlcXVlc3RJZDogcmVxdWVzdElkLFxyXG4gICAgICBleHBvcnQ6IHtcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICBzdmc6IGJvZHkuc3ZnLFxyXG4gICAgICAgIG91dGZpbGU6XHJcbiAgICAgICAgICBib2R5Lm91dGZpbGUgfHxcclxuICAgICAgICAgIGAke3JlcXVlc3QucGFyYW1zLmZpbGVuYW1lIHx8ICdjaGFydCd9LiR7Zml4VHlwZShib2R5LnR5cGUpfWAsXHJcbiAgICAgICAgdHlwZTogZml4VHlwZShib2R5LnR5cGUsIGJvZHkub3V0ZmlsZSksXHJcbiAgICAgICAgY29uc3RyOiBmaXhDb25zdHIoYm9keS5jb25zdHIpLFxyXG4gICAgICAgIGI2NDogYm9keS5iNjQsXHJcbiAgICAgICAgbm9Eb3dubG9hZDogYm9keS5ub0Rvd25sb2FkLFxyXG4gICAgICAgIGhlaWdodDogYm9keS5oZWlnaHQsXHJcbiAgICAgICAgd2lkdGg6IGJvZHkud2lkdGgsXHJcbiAgICAgICAgc2NhbGU6IGJvZHkuc2NhbGUsXHJcbiAgICAgICAgZ2xvYmFsT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS5nbG9iYWxPcHRpb25zLFxyXG4gICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICksXHJcbiAgICAgICAgdGhlbWVPcHRpb25zOiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICBib2R5LnRoZW1lT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApXHJcbiAgICAgIH0sXHJcbiAgICAgIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gICAgICAgIGFsbG93RmlsZVJlc291cmNlczogZmFsc2UsXHJcbiAgICAgICAgY3VzdG9tQ29kZTogYm9keS5jdXN0b21Db2RlLFxyXG4gICAgICAgIGNhbGxiYWNrOiBib2R5LmNhbGxiYWNrLFxyXG4gICAgICAgIHJlc291cmNlczogaXNBbGxvd2VkQ29uZmlnKGJvZHkucmVzb3VyY2VzLCB0cnVlLCBhbGxvd0NvZGVFeGVjdXRpb24pXHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgbmV4dCBtaWRkbGV3YXJlXHJcbiAgICByZXR1cm4gbmV4dCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlcyB0byB0aGUgcGFzc2VkIGV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCkge1xyXG4gIC8vIEFkZCBjb250ZW50IHR5cGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgYXBwLnBvc3QoWycvJywgJy86ZmlsZW5hbWUnXSwgY29udGVudFR5cGVNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHJlcXVlc3QgYm9keSByZXF1ZXN0IHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIHJlcXVlc3RCb2R5TWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgdGhlIGV4cG9ydCByb3V0ZXMgYW5kIGxvZ2ljIGZvciBoYW5kbGluZyBjaGFydCBleHBvcnRcclxuICogcmVxdWVzdHMgaW4gYW4gRXhwcmVzcyBzZXJ2ZXIuIFRoaXMgbW9kdWxlIHByb2Nlc3NlcyBpbmNvbWluZyByZXF1ZXN0c1xyXG4gKiB0byBleHBvcnQgY2hhcnRzIGluIHZhcmlvdXMgZm9ybWF0cyAoZS5nLiBKUEVHLCBQTkcsIFBERiwgU1ZHKS4gSXQgaW50ZWdyYXRlc1xyXG4gKiB3aXRoIEhpZ2hjaGFydHMnIGNvcmUgZnVuY3Rpb25hbGl0aWVzIGFuZCBzdXBwb3J0cyBib3RoIGltbWVkaWF0ZSBkb3dubG9hZFxyXG4gKiByZXNwb25zZXMgYW5kIEJhc2U2NC1lbmNvZGVkIGNvbnRlbnQgcmV0dXJucy4gVGhlIGNvZGUgYWxzbyBmZWF0dXJlc1xyXG4gKiBiZW5jaG1hcmtpbmcgZm9yIHBlcmZvcm1hbmNlIG1vbml0b3JpbmcuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgc3RhcnRFeHBvcnQgfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldEJhc2U2NCwgbWVhc3VyZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgSHR0cEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9IdHRwRXJyb3IuanMnO1xyXG5cclxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xyXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XHJcbiAgcG5nOiAnaW1hZ2UvcG5nJyxcclxuICBqcGVnOiAnaW1hZ2UvanBlZycsXHJcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcclxuICBwZGY6ICdhcHBsaWNhdGlvbi9wZGYnLFxyXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXHJcbn07XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgZXhwb3J0IHJlcXVlc3RzIGZyb20gdGhlIGNsaWVudC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0KHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0IGNvdW50aW5nIHRpbWUgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgcmVxdWVzdENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIEluIGNhc2UgdGhlIGNvbm5lY3Rpb24gaXMgY2xvc2VkLCBmb3JjZSB0byBhYm9ydCBmdXJ0aGVyIGFjdGlvbnNcclxuICAgIGxldCBjb25uZWN0aW9uQWJvcnRlZCA9IGZhbHNlO1xyXG4gICAgcmVxdWVzdC5zb2NrZXQub24oJ2Nsb3NlJywgKGhhZEVycm9ycykgPT4ge1xyXG4gICAgICBpZiAoaGFkRXJyb3JzKSB7XHJcbiAgICAgICAgY29ubmVjdGlvbkFib3J0ZWQgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIG9wdGlvbnMgcHJldmlvdXNseSB2YWxpZGF0ZWQgaW4gdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gICAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGlkXHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSByZXF1ZXN0T3B0aW9ucy5fcmVxdWVzdElkO1xyXG5cclxuICAgIC8vIEluZm8gYWJvdXQgYW4gaW5jb21pbmcgcmVxdWVzdCB3aXRoIGNvcnJlY3QgZGF0YVxyXG4gICAgbG9nKDQsIGBbZXhwb3J0XSBHb3QgYW4gaW5jb21pbmcgSFRUUCByZXF1ZXN0IHdpdGggSUQgJHtyZXF1ZXN0SWR9LmApO1xyXG5cclxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQocmVxdWVzdE9wdGlvbnMsIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAvLyBSZW1vdmUgdGhlIGNsb3NlIGV2ZW50IGZyb20gdGhlIHNvY2tldFxyXG4gICAgICByZXF1ZXN0LnNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2Nsb3NlJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkLCBkbyBub3RoaW5nXHJcbiAgICAgIGlmIChjb25uZWN0aW9uQWJvcnRlZCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSBjbGllbnQgY2xvc2VkIHRoZSBjb25uZWN0aW9uIGJlZm9yZSB0aGUgY2hhcnQgZmluaXNoZWQgcHJvY2Vzc2luZy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGVycm9yLCBsb2cgaXQgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGRhdGEgaXMgbWlzc2luZywgbG9nIHRoZSBtZXNzYWdlIGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmICghZGF0YSB8fCAhZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBSZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fFxyXG4gICAgICAgICAgICByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCByZXN1bHQgaXMgJHtkYXRhLnJlc3VsdH0uYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAnW2V4cG9ydF0gVW5leHBlY3RlZCByZXR1cm4gb2YgdGhlIGV4cG9ydCByZXN1bHQgZnJvbSB0aGUgY2hhcnQgZ2VuZXJhdGlvbi4gUGxlYXNlIGNoZWNrIHlvdXIgcmVxdWVzdCBkYXRhLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBSZXR1cm4gdGhlIHJlc3VsdCBpbiBhbiBhcHByb3ByaWF0ZSBmb3JtYXRcclxuICAgICAgaWYgKGRhdGEucmVzdWx0KSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzIHRvb2sgJHtyZXF1ZXN0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIGB0eXBlYCwgYGI2NGAsIGBub0Rvd25sb2FkYCwgYW5kIGBvdXRmaWxlYCBmcm9tIG9wdGlvbnNcclxuICAgICAgICBjb25zdCB7IHR5cGUsIGI2NCwgbm9Eb3dubG9hZCwgb3V0ZmlsZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgLy8gSWYgb25seSBCYXNlNjQgaXMgcmVxdWlyZWQsIHJldHVybiBpdFxyXG4gICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gU2V0IGNvcnJlY3QgY29udGVudCB0eXBlXHJcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVyKCdDb250ZW50LVR5cGUnLCByZXZlcnNlZE1pbWVbdHlwZV0gfHwgJ2ltYWdlL3BuZycpO1xyXG5cclxuICAgICAgICAvLyBEZWNpZGUgd2hldGhlciB0byBkb3dubG9hZCBvciBub3QgY2hhcnQgZmlsZVxyXG4gICAgICAgIGlmICghbm9Eb3dubG9hZCkge1xyXG4gICAgICAgICAgcmVzcG9uc2UuYXR0YWNobWVudChvdXRmaWxlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIElmIFNWRywgcmV0dXJuIHBsYWluIGNvbnRlbnQsIG90aGVyd2lzZSBhIGI2NCBzdHJpbmcgZnJvbSBhIGJ1ZmZlclxyXG4gICAgICAgIHJldHVybiB0eXBlID09PSAnc3ZnJ1xyXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGRhdGEucmVzdWx0KVxyXG4gICAgICAgICAgOiByZXNwb25zZS5zZW5kKEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JykpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBleHBvcnRgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4cG9ydFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGV4cG9ydFJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3RcclxuICAgKiBlbmRwb2ludC5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLycsIHJlcXVlc3RFeHBvcnQpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvOmZpbGVuYW1lJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgd2l0aFxyXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLzpmaWxlbmFtZScsIHJlcXVlc3RFeHBvcnQpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHNlcnZlciBoZWFsdGggbW9uaXRvcmluZywgaW5jbHVkaW5nXHJcbiAqIHVwdGltZSwgc3VjY2VzcyByYXRlcywgYW5kIG90aGVyIHNlcnZlciBzdGF0aXN0aWNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldFBvb2xTdGF0cywgZ2V0UG9vbEluZm9KU09OIH0gZnJvbSAnLi4vLi4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGFkZFRpbWVyIH0gZnJvbSAnLi4vLi4vdGltZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGdldE5ld0RhdGVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLy8gU2V0IHRoZSBzdGFydCBkYXRlIG9mIHRoZSBzZXJ2ZXJcclxuY29uc3Qgc2VydmVyU3RhcnRUaW1lID0gbmV3IERhdGUoKTtcclxuXHJcbi8vIEdldCB0aGUgYHBhY2thZ2UuanNvbmAgY29udGVudFxyXG5jb25zdCBwYWNrYWdlRmlsZSA9IEpTT04ucGFyc2UocmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpKTtcclxuXHJcbi8vIEFuIGFycmF5IGZvciBzdWNjZXNzIHJhdGUgcmF0aW9zXHJcbmNvbnN0IHN1Y2Nlc3NSYXRlcyA9IFtdO1xyXG5cclxuLy8gUmVjb3JkIGV2ZXJ5IG1pbnV0ZVxyXG5jb25zdCByZWNvcmRJbnRlcnZhbCA9IDYwICogMTAwMDtcclxuXHJcbi8vIDMwIG1pbnV0ZXNcclxuY29uc3Qgd2luZG93U2l6ZSA9IDMwO1xyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgbW92aW5nIGF2ZXJhZ2UgaW5kaWNhdG9yIGJhc2VkIG9uIHRoZSBkYXRhIGZyb20gdGhlIGBzdWNjZXNzUmF0ZXNgXHJcbiAqIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2VcclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gQSBtb3ZpbmcgYXZlcmFnZSBmb3Igc3VjY2VzcyByYXRpbyBvZiB0aGUgc2VydmVyIGV4cG9ydHMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpIHtcclxuICByZXR1cm4gc3VjY2Vzc1JhdGVzLnJlZHVjZSgoYSwgYikgPT4gYSArIGIsIDApIC8gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyB0aGUgaW50ZXJ2YWwgcmVzcG9uc2libGUgZm9yIGNhbGN1bGF0aW5nIGN1cnJlbnQgc3VjY2VzcyByYXRlIHJhdGlvXHJcbiAqIGFuZCBjb2xsZWN0cyByZWNvcmRzIHRvIHRoZSBgc3VjY2Vzc1JhdGVzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtOb2RlSlMuVGltZW91dH0gSWQgb2YgYW4gaW50ZXJ2YWwuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc3RhcnRTdWNjZXNzUmF0ZSgpIHtcclxuICByZXR1cm4gc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgIGNvbnN0IHN1Y2Nlc3NSYXRpbyA9XHJcbiAgICAgIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQgPT09IDBcclxuICAgICAgICA/IDFcclxuICAgICAgICA6IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDA7XHJcblxyXG4gICAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcclxuICAgIGlmIChzdWNjZXNzUmF0ZXMubGVuZ3RoID4gd2luZG93U2l6ZSkge1xyXG4gICAgICBzdWNjZXNzUmF0ZXMuc2hpZnQoKTtcclxuICAgIH1cclxuICB9LCByZWNvcmRJbnRlcnZhbCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgaGVhbHRoYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBoZWFsdGhSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBoZWFsdGhSb3V0ZXMoYXBwKSB7XHJcbiAgLy8gU3RhcnQgcHJvY2Vzc2luZyBzdWNjZXNzIHJhdGUgcmF0aW8gaW50ZXJ2YWwgYW5kIHNhdmUgaXRzIGlkIHRvIHRoZSBhcnJheVxyXG4gIC8vIGZvciB0aGUgZ3JhY2VmdWwgY2xlYXJpbmcgb24gc2h1dGRvd24gd2l0aCBpbmplY3RlZCBgYWRkVGltZXJgIGZ1bnRpb25cclxuICBhZGRUaW1lcihfc3RhcnRTdWNjZXNzUmF0ZSgpKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgR0VUICcvaGVhbHRoJyAtIEEgcm91dGUgZm9yIGdldHRpbmcgdGhlIGJhc2ljIHN0YXRzIG9mIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldCgnL2hlYWx0aCcsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbaGVhbHRoXSBSZXR1cm5pbmcgc2VydmVyIGhlYWx0aC4nKTtcclxuXHJcbiAgICAgIGNvbnN0IHN0YXRzID0gZ2V0UG9vbFN0YXRzKCk7XHJcbiAgICAgIGNvbnN0IHBlcmlvZCA9IHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbiAgICAgIGNvbnN0IG1vdmluZ0F2ZXJhZ2UgPSBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpO1xyXG5cclxuICAgICAgLy8gU2VuZCB0aGUgc2VydmVyJ3Mgc3RhdGlzdGljc1xyXG4gICAgICByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAvLyBTdGF0dXMgYW5kIHRpbWVzXHJcbiAgICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICAgIGJvb3RUaW1lOiBzZXJ2ZXJTdGFydFRpbWUsXHJcbiAgICAgICAgdXB0aW1lOiBgJHtNYXRoLmZsb29yKChnZXROZXdEYXRlVGltZSgpIC0gc2VydmVyU3RhcnRUaW1lLmdldFRpbWUoKSkgLyAxMDAwIC8gNjApfSBtaW51dGVzYCxcclxuXHJcbiAgICAgICAgLy8gVmVyc2lvbnNcclxuICAgICAgICBzZXJ2ZXJWZXJzaW9uOiBwYWNrYWdlRmlsZS52ZXJzaW9uLFxyXG4gICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG5cclxuICAgICAgICAvLyBFeHBvcnRzXHJcbiAgICAgICAgYXZlcmFnZUV4cG9ydFRpbWU6IHN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UsXHJcbiAgICAgICAgYXR0ZW1wdGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCxcclxuICAgICAgICBwZXJmb3JtZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzUGVyZm9ybWVkLFxyXG4gICAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNEcm9wcGVkLFxyXG4gICAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCAvIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQpICogMTAwLFxyXG5cclxuICAgICAgICAvLyBQb29sXHJcbiAgICAgICAgcG9vbDogZ2V0UG9vbEluZm9KU09OKCksXHJcblxyXG4gICAgICAgIC8vIE1vdmluZyBhdmVyYWdlXHJcbiAgICAgICAgcGVyaW9kLFxyXG4gICAgICAgIG1vdmluZ0F2ZXJhZ2UsXHJcbiAgICAgICAgbWVzc2FnZTpcclxuICAgICAgICAgIGlzTmFOKG1vdmluZ0F2ZXJhZ2UpIHx8ICFzdWNjZXNzUmF0ZXMubGVuZ3RoXHJcbiAgICAgICAgICAgID8gJ1RvbyBlYXJseSB0byByZXBvcnQuIE5vIGV4cG9ydHMgbWFkZSB5ZXQuIFBsZWFzZSBjaGVjayBiYWNrIHNvb24uJ1xyXG4gICAgICAgICAgICA6IGBMYXN0ICR7cGVyaW9kfSBtaW51dGVzIGhhZCBhIHN1Y2Nlc3MgcmF0ZSBvZiAke21vdmluZ0F2ZXJhZ2UudG9GaXhlZCgyKX0lLmAsXHJcblxyXG4gICAgICAgIC8vIFNWRyBhbmQgSlNPTiBleHBvcnRzXHJcbiAgICAgICAgc3ZnRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21TdmcsXHJcbiAgICAgICAganNvbkV4cG9ydHM6IHN0YXRzLmV4cG9ydHNGcm9tT3B0aW9ucyxcclxuICAgICAgICBzdmdFeHBvcnRzQXR0ZW1wdHM6IHN0YXRzLmV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMsXHJcbiAgICAgICAganNvbkV4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHNcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmluZyB0aGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyXHJcbiAqIHdoZW4gZW5hYmxlZC5cclxuICovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB1aWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdWlSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB1aVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy8nIC0gQSByb3V0ZSBmb3IgYSBVSSB3aGVuIGVuYWJsZWQgb24gdGhlIGV4cG9ydCBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldChnZXRPcHRpb25zKCkudWkucm91dGUgfHwgJy8nLCAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwge1xyXG4gICAgICAgIGFjY2VwdFJhbmdlczogZmFsc2VcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3IgdXBkYXRpbmcgdGhlIEhpZ2hjaGFydHMgdmVyc2lvblxyXG4gKiBvbiB0aGUgc2VydmVyLCB3aXRoIGF1dGhlbnRpY2F0aW9uIGFuZCB2YWxpZGF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLCBnZXRIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5cclxuaW1wb3J0IEh0dHBFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvSHR0cEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgdmVyc2lvbl9jaGFuZ2VgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIHZlcnNpb25DaGFuZ2VSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicgLSBBIHJvdXRlIGZvciBjaGFuZ2luZ1xyXG4gICAqIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gb24gdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnL3ZlcnNpb25fY2hhbmdlLzpuZXdWZXJzaW9uJywgYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBHZXQgdGhlIHRva2VuIGRpcmVjdGx5IGZyb20gZW52c1xyXG4gICAgICBjb25zdCBhZG1pblRva2VuID0gZW52cy5ISUdIQ0hBUlRTX0FETUlOX1RPS0VOO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGV4aXN0ZW5jZSBvZiB0aGUgdG9rZW5cclxuICAgICAgaWYgKCFhZG1pblRva2VuIHx8ICFhZG1pblRva2VuLmxlbmd0aCkge1xyXG4gICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIFRoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBmcm9tIHRoZSBoYy1hdXRoIGhlYWRlclxyXG4gICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIEludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdmVyc2lvbnNcclxuICAgICAgY29uc3QgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XHJcbiAgICAgIGlmIChuZXdWZXJzaW9uKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFVwZGF0ZSB2ZXJzaW9uXHJcbiAgICAgICAgICBhd2FpdCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAgICAgYFt2ZXJzaW9uXSBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgIDQwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXHJcbiAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcignW3ZlcnNpb25dIE5vIG5ldyB2ZXJzaW9uIHN1cHBsaWVkLicsIDQwMCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQSBtb2R1bGUgdGhhdCBzZXRzIHVwIGFuZCBtYW5hZ2VzIEhUVFAgYW5kIEhUVFBTIHNlcnZlcnNcclxuICogZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuIEl0IGhhbmRsZXMgc2VydmVyIGluaXRpYWxpemF0aW9uLFxyXG4gKiBjb25maWd1cmF0aW9uLCBlcnJvciBoYW5kbGluZywgbWlkZGxld2FyZSBzZXR1cCwgcm91dGUgZGVmaW5pdGlvbiwgYW5kIHJhdGVcclxuICogbGltaXRpbmcuIFRoZSBtb2R1bGUgZXhwb3J0cyBmdW5jdGlvbnMgdG8gc3RhcnQsIHN0b3AsIGFuZCBtYW5hZ2Ugc2VydmVyXHJcbiAqIGluc3RhbmNlcywgYXMgd2VsbCBhcyB1dGlsaXR5IGZ1bmN0aW9ucyBmb3IgZGVmaW5pbmcgcm91dGVzIGFuZCBhdHRhY2hpbmdcclxuICogbWlkZGxld2FyZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGUgfSBmcm9tICdmcy9wcm9taXNlcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBjb3JzIGZyb20gJ2NvcnMnO1xyXG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcbmltcG9ydCBtdWx0ZXIgZnJvbSAnbXVsdGVyJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IGVycm9yTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL2Vycm9yLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdGluZ01pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy9yYXRlTGltaXRpbmcuanMnO1xyXG5pbXBvcnQgdmFsaWRhdGlvbk1pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzJztcclxuXHJcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcclxuaW1wb3J0IGhlYWx0aFJvdXRlcyBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xyXG5pbXBvcnQgdmVyc2lvbkNoYW5nZVJvdXRlcyBmcm9tICcuL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcclxuY29uc3QgYWN0aXZlU2VydmVycyA9IG5ldyBNYXAoKTtcclxuXHJcbi8vIENyZWF0ZSBleHByZXNzIGFwcFxyXG5jb25zdCBhcHAgPSBleHByZXNzKCk7XHJcblxyXG4vKipcclxuICogU3RhcnRzIEhUVFAgb3IvYW5kIEhUVFBTIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi5cclxuICogVGhlIGBzZXJ2ZXJPcHRpb25zYCBvYmplY3QgY29udGFpbnMgYWxsIHNlcnZlciByZWxhdGVkIHByb3BlcnRpZXMgKHNlZVxyXG4gKiB0aGUgYHNlcnZlcmAgc2VjdGlvbiBpbiB0aGUgYGxpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgYSByZWZlcmVuY2UpLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHN0YXJ0U2VydmVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbc2VydmVyT3B0aW9ucz1nZXRPcHRpb25zKCkuc2VydmVyXSAtIE9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXJgIG9wdGlvbnMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIHRoZSBnbG9iYWwgc2VydmVyIG9wdGlvbnNcclxuICogb2YgdGhlIGV4cG9ydCBzZXJ2ZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBlbmRpbmcgdGhlIGZ1bmN0aW9uXHJcbiAqIGV4ZWN1dGlvbiB3aGVuIHRoZSBzZXJ2ZXIgc2hvdWxkIG5vdCBiZSBlbmFibGVkIG9yIHdoZW4gbm8gdmFsaWQgRXhwcmVzcyBhcHBcclxuICogaXMgZm91bmQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgc2VydmVyIGNhbm5vdFxyXG4gKiBiZSBjb25maWd1cmVkIGFuZCBzdGFydGVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0U2VydmVyKHNlcnZlck9wdGlvbnMgPSBnZXRPcHRpb25zKCkuc2VydmVyKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0b3AgaWYgbm90IGVuYWJsZWRcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5lbmFibGUgfHwgIWFwcCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tzZXJ2ZXJdIFNlcnZlciBjYW5ub3QgYmUgc3RhcnRlZCAobm90IGVuYWJsZWQgb3Igbm8gY29ycmVjdCBFeHByZXNzIGFwcCBmb3VuZCkuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUb28gYmlnIGxpbWl0cyBsZWFkIHRvIHRpbWVvdXRzIGluIHRoZSBleHBvcnQgcHJvY2VzcyB3aGVuXHJcbiAgICAvLyB0aGUgcmFzdGVyaXphdGlvbiB0aW1lb3V0IGlzIHNldCB0b28gbG93XHJcbiAgICBjb25zdCB1cGxvYWRMaW1pdEJ5dGVzID0gc2VydmVyT3B0aW9ucy51cGxvYWRMaW1pdCAqIDEwMjQgKiAxMDI0O1xyXG5cclxuICAgIC8vIE1lbW9yeSBzdG9yYWdlIGZvciBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XHJcblxyXG4gICAgLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3QgdXBsb2FkID0gbXVsdGVyKHtcclxuICAgICAgc3RvcmFnZSxcclxuICAgICAgbGltaXRzOiB7XHJcbiAgICAgICAgZmllbGRTaXplOiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIERpc2FibGUgdGhlIFgtUG93ZXJlZC1CeSBoZWFkZXJcclxuICAgIGFwcC5kaXNhYmxlKCd4LXBvd2VyZWQtYnknKTtcclxuXHJcbiAgICAvLyBFbmFibGUgQ09SUyBzdXBwb3J0XHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBjb3JzKHtcclxuICAgICAgICBtZXRob2RzOiBbJ1BPU1QnLCAnR0VUJywgJ09QVElPTlMnXVxyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXR0aW5nIGEgbG90IG9mIGBSYW5nZU5vdFNhdGlzZmlhYmxlRXJyb3JgIGV4Y2VwdGlvbnMgKGV2ZW4gdGhvdWdoIHRoaXNcclxuICAgIC8vIGlzIGEgZGVwcmVjYXRlZCBvcHRpb25zLCBsZXQncyB0cnkgdG8gc2V0IGl0IHRvIGZhbHNlKVxyXG4gICAgYXBwLnVzZSgocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgICAgcmVzcG9uc2Uuc2V0KCdBY2NlcHQtUmFuZ2VzJywgJ25vbmUnKTtcclxuICAgICAgbmV4dCgpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBKU09OIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MuanNvbih7XHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBVUkwtZW5jb2RlZCBmb3JtIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MudXJsZW5jb2RlZCh7XHJcbiAgICAgICAgZXh0ZW5kZWQ6IHRydWUsXHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gVXNlIG9ubHkgbm9uLWZpbGUgbXVsdGlwYXJ0IGZvcm0gZmllbGRzXHJcbiAgICBhcHAudXNlKHVwbG9hZC5ub25lKCkpO1xyXG5cclxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcclxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQIHNlcnZlclxyXG4gICAgaWYgKCFzZXJ2ZXJPcHRpb25zLnNzbC5mb3JjZSkge1xyXG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcCk7XHJcblxyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKGh0dHBTZXJ2ZXIpO1xyXG5cclxuICAgICAgLy8gTGlzdGVuXHJcbiAgICAgIGh0dHBTZXJ2ZXIubGlzdGVuKHNlcnZlck9wdGlvbnMucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFAgc2VydmVyXHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5zZXQoc2VydmVyT3B0aW9ucy5wb3J0LCBodHRwU2VydmVyKTtcclxuXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyT3B0aW9ucy5ob3N0fToke3NlcnZlck9wdGlvbnMucG9ydH0uYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQUyBzZXJ2ZXJcclxuICAgIGlmIChzZXJ2ZXJPcHRpb25zLnNzbC5lbmFibGUpIHtcclxuICAgICAgLy8gU2V0IHVwIGFuIFNTTCBzZXJ2ZXIgYWxzb1xyXG4gICAgICBsZXQga2V5LCBjZXJ0O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBrZXlcclxuICAgICAgICBrZXkgPSBhd2FpdCByZWFkRmlsZShcclxuICAgICAgICAgIGpvaW4oZ2V0QWJzb2x1dGVQYXRoKHNlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRoKSwgJ3NlcnZlci5rZXknKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgU1NMIGNlcnRpZmljYXRlXHJcbiAgICAgICAgY2VydCA9IGF3YWl0IHJlYWRGaWxlKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmNydCcpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFVuYWJsZSB0byBsb2FkIGtleS9jZXJ0aWZpY2F0ZSBmcm9tIHRoZSAnJHtzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChrZXkgJiYgY2VydCkge1xyXG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcclxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xyXG5cclxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgICAgX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAvLyBMaXN0ZW5cclxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyT3B0aW9ucy5zc2wucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUFMgc2VydmVyXHJcbiAgICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAzLFxyXG4gICAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQUyBzZXJ2ZXIgb24gJHtzZXJ2ZXJPcHRpb25zLmhvc3R9OiR7c2VydmVyT3B0aW9ucy5zc2wucG9ydH0uYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgc2VydmVyT3B0aW9ucy5yYXRlTGltaXRpbmcpO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgdmFsaWRhdGlvbiBoYW5kbGVyXHJcbiAgICB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGhlYWx0aFJvdXRlcyhhcHApO1xyXG4gICAgZXhwb3J0Um91dGVzKGFwcCk7XHJcbiAgICB1aVJvdXRlcyhhcHApO1xyXG4gICAgdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgY2VudHJhbGl6ZWQgZXJyb3IgaGFuZGxlclxyXG4gICAgZXJyb3JNaWRkbGV3YXJlKGFwcCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tzZXJ2ZXJdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHN0YXJ0IHRoZSBzZXJ2ZXIuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbG9zZXMgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzKCkge1xyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBzZXJ2ZXJzIHdvcmtpbmdcclxuICBpZiAoYWN0aXZlU2VydmVycy5zaXplID4gMCkge1xyXG4gICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zaW5nIGFsbCBzZXJ2ZXJzLmApO1xyXG5cclxuICAgIC8vIENsb3NlIGVhY2ggb25lIG9mIHNlcnZlcnNcclxuICAgIGZvciAoY29uc3QgW3BvcnQsIHNlcnZlcl0gb2YgYWN0aXZlU2VydmVycykge1xyXG4gICAgICBzZXJ2ZXIuY2xvc2UoKCkgPT4ge1xyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuZGVsZXRlKHBvcnQpO1xyXG4gICAgICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2VkIHNlcnZlciBvbiBwb3J0OiAke3BvcnR9LmApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0U2VydmVyc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPE9iamVjdD59IFNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFNlcnZlcnMoKSB7XHJcbiAgcmV0dXJuIGFjdGl2ZVNlcnZlcnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRFeHByZXNzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRFeHByZXNzKCkge1xyXG4gIHJldHVybiBleHByZXNzO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEFwcFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RXhwcmVzc30gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEFwcCgpIHtcclxuICByZXR1cm4gYXBwO1xyXG59XHJcblxyXG4vKipcclxuICogRW5hYmxlIHJhdGUgbGltaXRpbmcgZm9yIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVSYXRlTGltaXRpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHJhdGVMaW1pdGluZ09wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBgcmF0ZUxpbWl0aW5nYFxyXG4gKiBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZyhyYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZShhcHAsIHJhdGVMaW1pdGluZ09wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogQXBwbHkgbWlkZGxld2FyZShzKSB0byBhIHNwZWNpZmljIHBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1c2VcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHVzZShwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC51c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBHRVQgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAuZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggUE9TVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLnBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoIGVycm9yIGhhbmRsZXJzIHRvIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVyc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhodHRwLlNlcnZlcnxodHRwcy5TZXJ2ZXIpfSBzZXJ2ZXIgLSBUaGUgSFRUUC9IVFRQUyBzZXJ2ZXIgaW5zdGFuY2UuXHJcbiAqL1xyXG5mdW5jdGlvbiBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhzZXJ2ZXIpIHtcclxuICBzZXJ2ZXIub24oJ2NsaWVudEVycm9yJywgKGVycm9yLCBzb2NrZXQpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgMSxcclxuICAgICAgZXJyb3IsXHJcbiAgICAgIGBbc2VydmVyXSBDbGllbnQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX0sIGRlc3Ryb3lpbmcgc29ja2V0LmBcclxuICAgICk7XHJcbiAgICBzb2NrZXQuZGVzdHJveSgpO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTZXJ2ZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdjb25uZWN0aW9uJywgKHNvY2tldCkgPT4ge1xyXG4gICAgc29ja2V0Lm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTb2NrZXQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc3RhcnRTZXJ2ZXIsXHJcbiAgY2xvc2VTZXJ2ZXJzLFxyXG4gIGdldFNlcnZlcnMsXHJcbiAgZ2V0RXhwcmVzcyxcclxuICBnZXRBcHAsXHJcbiAgZW5hYmxlUmF0ZUxpbWl0aW5nLFxyXG4gIHVzZSxcclxuICBnZXQsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgSGFuZGxlcyBncmFjZWZ1bCBzaHV0ZG93biBvZiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLCBlbnN1cmluZ1xyXG4gKiBwcm9wZXIgY2xlYW51cCBvZiByZXNvdXJjZXMgc3VjaCBhcyBicm93c2VyLCBwYWdlcywgc2VydmVycywgYW5kIHRpbWVycy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBraWxsUG9vbCB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGNsZWFyQWxsVGltZXJzIH0gZnJvbSAnLi90aW1lci5qcyc7XHJcbmltcG9ydCB7IGNsb3NlU2VydmVycyB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogQ2xlYW5zIHVwIGZ1bmN0aW9uIHRvIHRyaWdnZXIgYmVmb3JlIGVuZGluZyBwcm9jZXNzIGZvciB0aGUgZ3JhY2VmdWxcclxuICogc2h1dGRvd24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXBcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGV4aXRDb2RlIC0gQW4gZXhpdCBjb2RlIGZvciB0aGUgYHByb2Nlc3MuZXhpdCgpYCBmdW5jdGlvbi5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXAoZXhpdENvZGUpIHtcclxuICAvLyBBd2FpdCBmcmVlaW5nIGFsbCByZXNvdXJjZXNcclxuICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW1xyXG4gICAgLy8gQ2xlYXIgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzXHJcbiAgICBjbGVhckFsbFRpbWVycygpLFxyXG5cclxuICAgIC8vIEdldCBhdmFpbGFibGUgc2VydmVyIGluc3RhbmNlcyAoSFRUUC9IVFRQUykgYW5kIGNsb3NlIHRoZW1cclxuICAgIGNsb3NlU2VydmVycygpLFxyXG5cclxuICAgIC8vIENsb3NlIGFuIGFjdGl2ZSBwb29sIGFsb25nIHdpdGggaXRzIHdvcmtlcnMgYW5kIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgICBraWxsUG9vbCgpXHJcbiAgXSk7XHJcblxyXG4gIC8vIEV4aXQgcHJvY2VzcyB3aXRoIGEgY29ycmVjdCBjb2RlXHJcbiAgcHJvY2Vzcy5leGl0KGV4aXRDb2RlKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNodXRkb3duQ2xlYW5VcFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29yZSBtb2R1bGUgZm9yIGluaXRpYWxpemluZyBhbmQgbWFuYWdpbmcgdGhlIEhpZ2hjaGFydHMgRXhwb3J0XHJcbiAqIFNlcnZlci4gUHJvdmlkZXMgZnVuY3Rpb25hbGl0aWVzIGZvciBjb25maWd1cmluZyBleHBvcnRzLCBzZXR0aW5nIHVwIHNlcnZlclxyXG4gKiBvcGVyYXRpb25zLCBsb2dnaW5nLCBzY3JpcHRzIGNhY2hpbmcsIHJlc291cmNlIHBvb2xpbmcsIGFuZCBncmFjZWZ1bCBwcm9jZXNzXHJcbiAqIGNsZWFudXAuXHJcbiAqL1xyXG5cclxuaW1wb3J0ICdjb2xvcnMnO1xyXG5cclxuaW1wb3J0IHsgY2hlY2tBbmRVcGRhdGVDYWNoZSB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQge1xyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBiYXRjaEV4cG9ydCxcclxuICBzdGFydEV4cG9ydCxcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxufSBmcm9tICcuL2NoYXJ0LmpzJztcclxuaW1wb3J0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHNldE9wdGlvbnMsXHJcbiAgbWVyZ2VPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9uc1xyXG59IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHtcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIGluaXRMb2dnaW5nLFxyXG4gIHNldExvZ0xldmVsLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nXHJcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpbml0UG9vbCwga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzaHV0ZG93bkNsZWFuVXAgfSBmcm9tICcuL3Jlc291cmNlUmVsZWFzZS5qcyc7XHJcbmltcG9ydCBzZXJ2ZXIsIHsgc3RhcnRTZXJ2ZXIgfSBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiB0aGUgY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHJlc291cmNlIHBvb2wgb2NjdXIgZHVyaW5nIHRoaXNcclxuICogc3RhZ2UuIFRoaXMgZnVuY3Rpb24gbXVzdCBiZSBjYWxsZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXRcclxuICogdXAgYSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tT3B0aW9ucyAtIFRoZSBgY3VzdG9tT3B0aW9uc2Agb2JqZWN0LCB3aGljaCBtYXlcclxuICogYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zLiBJZiB0aGUgcHJvdmlkZWQgb3B0aW9ucyBhcmUgcGFydGlhbCxcclxuICogbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgZGVmYXVsdCBnZW5lcmFsIG9wdGlvbnMsIHJldHJpZXZlZFxyXG4gKiB1c2luZyB0aGUgYGdldE9wdGlvbnNgIGZ1bmN0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRFeHBvcnQoY3VzdG9tT3B0aW9ucykge1xyXG4gIC8vIEdldCB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IGNvcHkgYW5kIGV4dGVuZCBpdCB3aXRoIHRoZSBpbmNvbWluZyBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IG1lcmdlT3B0aW9ucyhnZXRPcHRpb25zKGZhbHNlKSwgY3VzdG9tT3B0aW9ucyk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcblxyXG4gIC8vIEluaXQgdGhlIHBvb2xcclxuICBhd2FpdCBpbml0UG9vbChvcHRpb25zLnBvb2wsIG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoZXMgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MsIGVuc3VyaW5nIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlc1xyXG4gKiBhbmQgdGVybWluYXRpb24gb24gZXhpdCBzaWduYWxzLiBIYW5kbGVzICdleGl0JywgJ1NJR0lOVCcsICdTSUdURVJNJ1xyXG4gKiBhbmQgJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnNcclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LmApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0lOVCdcclxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR1RFUk0nXHJcbiAgcHJvY2Vzcy5vbignU0lHVEVSTScsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoMCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoMCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAndW5jYXVnaHRFeGNlcHRpb24nXHJcbiAgcHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCBhc3luYyAoZXJyb3IsIG5hbWUpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIHNlcnZlcixcclxuICBzdGFydFNlcnZlcixcclxuXHJcbiAgLy8gT3B0aW9uc1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgc2V0T3B0aW9ucyxcclxuICBtZXJnZU9wdGlvbnMsXHJcbiAgbWFwVG9OZXdPcHRpb25zLFxyXG5cclxuICAvLyBFeHBvcnRpbmdcclxuICBpbml0RXhwb3J0LFxyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBiYXRjaEV4cG9ydCxcclxuICBzdGFydEV4cG9ydCxcclxuXHJcbiAgLy8gQ2FjaGVcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG5cclxuICAvLyBQb29sXHJcbiAgaW5pdFBvb2wsXHJcbiAga2lsbFBvb2wsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG5cclxuICAvLyBVdGlsc1xyXG4gIHNodXRkb3duQ2xlYW5VcFxyXG59O1xyXG4iXSwibmFtZXMiOlsiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJiYXNlVVJJIiwiZGVlcENvcHkiLCJvYmpBcnIiLCJvYmpBcnJDb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwiT2JqZWN0IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiZml4Q29uc3RyIiwiY29uc3RyIiwiZml4ZWRDb25zdHIiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJpbmNsdWRlcyIsImZpeE91dGZpbGUiLCJ0eXBlIiwib3V0ZmlsZSIsImdldEFic29sdXRlUGF0aCIsInNwbGl0Iiwic2hpZnQiLCJmaXhUeXBlIiwibWltZVR5cGVzIiwiZm9ybWF0cyIsInZhbHVlcyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsInBhdGgiLCJpc0Fic29sdXRlIiwiam9pbiIsImdldEJhc2U2NCIsImlucHV0IiwiQnVmZmVyIiwiZnJvbSIsInRvU3RyaW5nIiwiZ2V0TmV3RGF0ZSIsIkRhdGUiLCJ0cmltIiwiZ2V0TmV3RGF0ZVRpbWUiLCJnZXRUaW1lIiwiaXNPYmplY3QiLCJpdGVtIiwiaXNPYmplY3RFbXB0eSIsImtleXMiLCJsZW5ndGgiLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwic29tZSIsInBhdHRlcm4iLCJ0ZXN0IiwibWVhc3VyZVRpbWUiLCJzdGFydCIsInByb2Nlc3MiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJyb3VuZE51bWJlciIsInZhbHVlIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsIk1hdGgiLCJwb3ciLCJyb3VuZCIsIndyYXBBcm91bmQiLCJjdXN0b21Db2RlIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiaXNDYWxsYmFjayIsImVuZHNXaXRoIiwicmVhZEZpbGVTeW5jIiwic3RhcnRzV2l0aCIsImNvbG9ycyIsImxvZ2dpbmciLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsInBhdGhUb0xvZyIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibG9nIiwiYXJncyIsIm5ld0xldmVsIiwidGV4dHMiLCJsZXZlbCIsInByZWZpeCIsIl9sb2dUb0ZpbGUiLCJjb25zb2xlIiwiYXBwbHkiLCJ1bmRlZmluZWQiLCJjb25jYXQiLCJsb2dXaXRoU3RhY2siLCJlcnJvciIsImN1c3RvbU1lc3NhZ2UiLCJtYWluTWVzc2FnZSIsIm1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInB1c2giLCJpbml0TG9nZ2luZyIsImxvZ2dpbmdPcHRpb25zIiwiZGVzdCIsImZpbGUiLCJzZXRMb2dMZXZlbCIsImVuYWJsZUNvbnNvbGVMb2dnaW5nIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJleGlzdHNTeW5jIiwibWtkaXJTeW5jIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiX2luaXRHbG9iYWxPcHRpb25zIiwiZ2V0T3B0aW9ucyIsImdldFJlZmVyZW5jZSIsInNldE9wdGlvbnMiLCJjdXN0b21PcHRpb25zIiwiY2xpQXJncyIsIm1vZGlmeUdsb2JhbCIsImNvbmZpZ09wdGlvbnMiLCJjbGlPcHRpb25zIiwiX2xvYWRDb25maWdGaWxlIiwiX3BhaXJBcmd1bWVudFZhbHVlIiwiZ2VuZXJhbE9wdGlvbnMiLCJfdXBkYXRlT3B0aW9ucyIsIm1lcmdlT3B0aW9ucyIsIm9yaWdpbmFsT3B0aW9ucyIsIm5ld09wdGlvbnMiLCJlbnRyaWVzIiwibWFwVG9OZXdPcHRpb25zIiwib2xkT3B0aW9ucyIsInByb3BlcnRpZXNDaGFpbiIsInJlZHVjZSIsIm9iaiIsInByb3AiLCJpbmRleCIsImlzQWxsb3dlZENvbmZpZyIsImFsbG93RnVuY3Rpb25zIiwib2JqZWN0Q29uZmlnIiwiZXZhbCIsIkpTT04iLCJzdHJpbmdpZmllZE9wdGlvbnMiLCJfb3B0aW9uc1N0cmluZ2lmeSIsInBhcnNlZE9wdGlvbnMiLCJfIiwibmFtZSIsImNvbmZpZ09wdCIsImN1c3RvbU9wdCIsImNsaU9wdCIsImNvbmZpZ1ZhbCIsImN1c3RvbVZhbCIsImNsaVZhbCIsImVudlZhbCIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJFcnJvciIsImNvbmZpZ0luZGV4IiwiZmluZEluZGV4IiwiYXJnIiwiY29uZmlnRmlsZU5hbWUiLCJpIiwib3B0aW9uIiwiYXN5bmMiLCJmZXRjaCIsInVybCIsInJlcXVlc3RPcHRpb25zIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJfZ2V0UHJvdG9jb2xNb2R1bGUiLCJnZXQiLCJyZXNwb25zZSIsInJlc3BvbnNlRGF0YSIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiaHR0cHMiLCJodHRwIiwiRXhwb3J0RXJyb3IiLCJjb25zdHJ1Y3RvciIsInN0YXR1c0NvZGUiLCJzdXBlciIsInRoaXMiLCJzZXRFcnJvciIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsInNldHVwSGlnaGNoYXJ0cyIsIkhpZ2hjaGFydHMiLCJhbmltT2JqZWN0IiwiZHVyYXRpb24iLCJjcmVhdGVDaGFydCIsIm1lcmdlIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJpc1JlbmRlckNvbXBsZXRlIiwiQ2hhcnQiLCJwcm9jZWVkIiwidXNlck9wdGlvbnMiLCJjYiIsImV4cG9ydGluZyIsImVuYWJsZWQiLCJwbG90T3B0aW9ucyIsInNlcmllcyIsImxhYmVsIiwidG9vbHRpcCIsImFuaW1hdGlvbiIsIm9uSGlnaGNoYXJ0c1JlbmRlciIsImFkZEV2ZW50IiwiU2VyaWVzIiwiY2hhcnQiLCJhZGRpdGlvbmFsT3B0aW9ucyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImRlZmF1bHRPcHRpb25zIiwidGVtcGxhdGUiLCJicm93c2VyIiwiY3JlYXRlQnJvd3NlciIsInB1cHBldGVlckFyZ3MiLCJlbmFibGVkRGVidWciLCJkZWJ1Z09wdGlvbnMiLCJsYXVuY2hPcHRpb25zIiwidXNlckRhdGFEaXIiLCJoYW5kbGVTSUdJTlQiLCJoYW5kbGVTSUdURVJNIiwiaGFuZGxlU0lHSFVQIiwid2FpdEZvckluaXRpYWxQYWdlIiwiZGVmYXVsdFZpZXdwb3J0IiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwic2V0VGltZW91dCIsImNsb3NlQnJvd3NlciIsImNvbm5lY3RlZCIsImNsb3NlIiwibmV3UGFnZSIsInBvb2xSZXNvdXJjZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJfc2V0UGFnZUNvbnRlbnQiLCJfc2V0UGFnZUV2ZW50cyIsImlzQ2xvc2VkIiwiY2xlYXJQYWdlIiwiaGFyZFJlc2V0IiwiZ290byIsIndhaXRVbnRpbCIsImV2YWx1YXRlIiwiYm9keSIsImlubmVySFRNTCIsImlkIiwid29ya0NvdW50IiwiYWRkUGFnZVJlc291cmNlcyIsImN1c3RvbUxvZ2ljT3B0aW9ucyIsImluamVjdGVkUmVzb3VyY2VzIiwiaW5qZWN0ZWRKcyIsImpzIiwiY29udGVudCIsImZpbGVzIiwiaXNMb2NhbCIsImpzUmVzb3VyY2UiLCJhZGRTY3JpcHRUYWciLCJpbmplY3RlZENzcyIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJjc3NSZXNvdXJjZSIsImFkZFN0eWxlVGFnIiwiY2xlYXJQYWdlUmVzb3VyY2VzIiwicmVzb3VyY2UiLCJkaXNwb3NlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJlbGVtZW50IiwicmVtb3ZlIiwic2V0Q29udGVudCIsImNzc1RlbXBsYXRlIiwic3ZnVGVtcGxhdGUiLCJwdXBwZXRlZXJFeHBvcnQiLCJleHBvcnRPcHRpb25zIiwiaXNTVkciLCJfc2V0QXNTdmciLCJfc2V0QXNPcHRpb25zIiwic2l6ZSIsInN2Z0VsZW1lbnQiLCJxdWVyeVNlbGVjdG9yIiwiY2hhcnRIZWlnaHQiLCJiYXNlVmFsIiwiY2hhcnRXaWR0aCIsInN0eWxlIiwiem9vbSIsIm1hcmdpbiIsIngiLCJ5IiwiX2dldENsaXBSZWdpb24iLCJ2aWV3cG9ydEhlaWdodCIsImFicyIsImNlaWwiLCJ2aWV3cG9ydFdpZHRoIiwicmVzdWx0Iiwic2V0Vmlld3BvcnQiLCJkZXZpY2VTY2FsZUZhY3RvciIsIl9jcmVhdGVTVkciLCJfY3JlYXRlSW1hZ2UiLCJfY3JlYXRlUERGIiwiJGV2YWwiLCJnZXRCb3VuZGluZ0NsaWVudFJlY3QiLCJ0cnVuYyIsIm91dGVySFRNTCIsImNsaXAiLCJyYWNlIiwic2NyZWVuc2hvdCIsImVuY29kaW5nIiwiZnVsbFBhZ2UiLCJvcHRpbWl6ZUZvclNwZWVkIiwiY2FwdHVyZUJleW9uZFZpZXdwb3J0IiwicXVhbGl0eSIsIm9taXRCYWNrZ3JvdW5kIiwiX3Jlc29sdmUiLCJlbXVsYXRlTWVkaWFUeXBlIiwicGRmIiwicG9vbFN0YXRzIiwiZXhwb3J0c0F0dGVtcHRlZCIsImV4cG9ydHNQZXJmb3JtZWQiLCJleHBvcnRzRHJvcHBlZCIsImV4cG9ydHNGcm9tU3ZnIiwiZXhwb3J0c0Zyb21PcHRpb25zIiwiZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyIsImV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzIiwidGltZVNwZW50IiwidGltZVNwZW50QXZlcmFnZSIsImluaXRQb29sIiwicG9vbE9wdGlvbnMiLCJQb29sIiwiX2ZhY3RvcnkiLCJhY3F1aXJlVGltZW91dE1pbGxpcyIsImNyZWF0ZVRpbWVvdXRNaWxsaXMiLCJkZXN0cm95VGltZW91dE1pbGxpcyIsImlkbGVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpcyIsInJlYXBJbnRlcnZhbE1pbGxpcyIsInByb3BhZ2F0ZUNyZWF0ZUVycm9yIiwiY2xlYXJTdGF0dXMiLCJfZXZlbnRJZCIsImluaXRpYWxSZXNvdXJjZXMiLCJhY3F1aXJlIiwicHJvbWlzZSIsInJlbGVhc2UiLCJraWxsUG9vbCIsIndvcmtlciIsInVzZWQiLCJkZXN0cm95ZWQiLCJwb3N0V29yayIsIndvcmtlckhhbmRsZSIsImdldFBvb2xJbmZvIiwiYWNxdWlyZUNvdW50ZXIiLCJfcmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsImV4cG9ydFRpbWUiLCJnZXRQb29sU3RhdHMiLCJnZXRQb29sSW5mb0pTT04iLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwibnVtRnJlZSIsImFsbENyZWF0ZWQiLCJwZW5kaW5nQWNxdWlyZXMiLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwZW5kaW5nQ3JlYXRlcyIsIm51bVBlbmRpbmdDcmVhdGVzIiwicGVuZGluZ1ZhbGlkYXRpb25zIiwibnVtUGVuZGluZ1ZhbGlkYXRpb25zIiwicGVuZGluZ0Rlc3Ryb3lzIiwiYWJzb2x1dGVBbGwiLCJjcmVhdGUiLCJ1dWlkIiwicmFuZG9tIiwic3RhcnREYXRlIiwidmFsaWRhdGUiLCJtYWluRnJhbWUiLCJkZXRhY2hlZCIsInJlbW92ZUFsbExpc3RlbmVycyIsInNhbml0aXplIiwiSlNET00iLCJET01QdXJpZnkiLCJBRERfVEFHUyIsInNpbmdsZUV4cG9ydCIsInN0YXJ0RXhwb3J0IiwiZGF0YSIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiYmF0Y2hSZXN1bHRzIiwiYWxsU2V0dGxlZCIsInJlYXNvbiIsImVuZENhbGxiYWNrIiwiZmlsZUNvbnRlbnQiLCJfZXhwb3J0RnJvbVN2ZyIsIl9leHBvcnRGcm9tT3B0aW9ucyIsImdldEFsbG93Q29kZUV4ZWN1dGlvbiIsInNldEFsbG93Q29kZUV4ZWN1dGlvbiIsImlucHV0VG9FeHBvcnQiLCJfcHJlcGFyZUV4cG9ydCIsIl9oYW5kbGVDdXN0b21Mb2dpYyIsIl9oYW5kbGVHbG9iYWxBbmRUaGVtZSIsIl9maW5kQ2hhcnRTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidGltZXJJZHMiLCJhZGRUaW1lciIsImNsZWFyQWxsVGltZXJzIiwiY2xlYXJJbnRlcnZhbCIsImNsZWFyVGltZW91dCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcXVlc3QiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RhdHVzIiwianNvbiIsImVycm9yTWlkZGxld2FyZSIsImFwcCIsInVzZSIsInJhdGVMaW1pdGluZ01pZGRsZXdhcmUiLCJyYXRlTGltaXRpbmdPcHRpb25zIiwibXNnIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwicmF0ZUxpbWl0Iiwid2luZG93TXMiLCJkZWxheU1zIiwiaGFuZGxlciIsImZvcm1hdCIsInNlbmQiLCJkZWZhdWx0Iiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwiSHR0cEVycm9yIiwic2V0U3RhdHVzIiwiY29udGVudFR5cGVNaWRkbGV3YXJlIiwiY29udGVudFR5cGUiLCJoZWFkZXJzIiwicmVxdWVzdEJvZHlNaWRkbGV3YXJlIiwicmVxdWVzdElkIiwiY29ubmVjdGlvbiIsInJlbW90ZUFkZHJlc3MiLCJ2YWxpZGF0ZWRPcHRpb25zIiwicGFyYW1zIiwiZmlsZW5hbWUiLCJ2YWxpZGF0aW9uTWlkZGxld2FyZSIsInBvc3QiLCJyZXZlcnNlZE1pbWUiLCJwbmciLCJqcGVnIiwiZ2lmIiwicmVxdWVzdEV4cG9ydCIsInJlcXVlc3RDb3VudGVyIiwiY29ubmVjdGlvbkFib3J0ZWQiLCJzb2NrZXQiLCJoYWRFcnJvcnMiLCJoZWFkZXIiLCJhdHRhY2htZW50IiwiZXhwb3J0Um91dGVzIiwic2VydmVyU3RhcnRUaW1lIiwicGFja2FnZUZpbGUiLCJzdWNjZXNzUmF0ZXMiLCJyZWNvcmRJbnRlcnZhbCIsIndpbmRvd1NpemUiLCJfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSIsImEiLCJiIiwiX3N0YXJ0U3VjY2Vzc1JhdGUiLCJzZXRJbnRlcnZhbCIsInN0YXRzIiwic3VjY2Vzc1JhdGlvIiwiaGVhbHRoUm91dGVzIiwicGVyaW9kIiwibW92aW5nQXZlcmFnZSIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJzZXJ2ZXJWZXJzaW9uIiwiaGlnaGNoYXJ0c1ZlcnNpb24iLCJhdmVyYWdlRXhwb3J0VGltZSIsImF0dGVtcHRlZEV4cG9ydHMiLCJwZXJmb3JtZWRFeHBvcnRzIiwiZmFpbGVkRXhwb3J0cyIsInN1Y2Vzc1JhdGlvIiwidG9GaXhlZCIsInN2Z0V4cG9ydHMiLCJqc29uRXhwb3J0cyIsInN2Z0V4cG9ydHNBdHRlbXB0cyIsImpzb25FeHBvcnRzQXR0ZW1wdHMiLCJ1aVJvdXRlcyIsInNlbmRGaWxlIiwiYWNjZXB0UmFuZ2VzIiwidmVyc2lvbkNoYW5nZVJvdXRlcyIsImFkbWluVG9rZW4iLCJ0b2tlbiIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJPcHRpb25zIiwidXBsb2FkTGltaXRCeXRlcyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRTaXplIiwiZGlzYWJsZSIsImNvcnMiLCJtZXRob2RzIiwic2V0IiwibGltaXQiLCJ1cmxlbmNvZGVkIiwiZXh0ZW5kZWQiLCJub25lIiwic3RhdGljIiwiaHR0cFNlcnZlciIsImNyZWF0ZVNlcnZlciIsIl9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwibGlzdGVuIiwiY2VydCIsInJlYWRGaWxlIiwiaHR0cHNTZXJ2ZXIiLCJjbG9zZVNlcnZlcnMiLCJkZWxldGUiLCJnZXRTZXJ2ZXJzIiwiZ2V0RXhwcmVzcyIsImdldEFwcCIsImVuYWJsZVJhdGVMaW1pdGluZyIsIm1pZGRsZXdhcmVzIiwic2h1dGRvd25DbGVhblVwIiwiZXhpdENvZGUiLCJleGl0IiwiaW5pdEV4cG9ydCIsIl9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyIsImNvZGUiXSwibWFwcGluZ3MiOiJ3bkJBMkJPLE1BQU1BLFlBQVlDLElBQWFBLGNBQUMsSUFBSUMsSUFBSSxPQUFRLG9CQUFBQyxTQUFBQyxRQUFBLE9BQUFDLGNBQUFDLFlBQUFDLEtBQUFDLHdCQUFBLFdBQUFBLHVCQUFBQyxRQUFBQyxlQUFBRix1QkFBQUcsS0FBQSxJQUFBVCxJQUFBLFlBQUFDLFNBQUFTLFNBQUFMLE9BK0JoRCxTQUFTTSxTQUFTQyxHQUV2QixHQUFlLE9BQVhBLEdBQXFDLGlCQUFYQSxFQUM1QixPQUFPQSxFQUlULE1BQU1DLEVBQWFDLE1BQU1DLFFBQVFILEdBQVUsR0FBSyxHQUdoRCxJQUFLLE1BQU1JLEtBQU9KLEVBQ1pLLE9BQU9DLFVBQVVDLGVBQWVDLEtBQUtSLEVBQVFJLEtBQy9DSCxFQUFXRyxHQUFPTCxTQUFTQyxFQUFPSSxLQUt0QyxPQUFPSCxDQUNULENBMkRPLFNBQVNRLFVBQVVDLEdBQ3hCLElBRUUs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 +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),promises=require("fs/promises"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?e:path.join(__dirname$1,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env);class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 globalOptions=_initGlobalOptions(defaultConfig),instanceOptions=deepCopy(globalOptions);function getOptions(e=!0){return e?instanceOptions:globalOptions}function setGlobalOptions(e={},t=[]){let o={},r={};return t&&Array.isArray(t)&&t.length&&(o=_loadConfigFile(t,globalOptions.customLogic),r=_pairArgumentValue(nestedProps,t)),_updateGlobalOptions(defaultConfig,globalOptions,o,r,e),globalOptions}function updateOptions(e,t=!1){return t&&(Object.keys(instanceOptions).forEach((e=>{delete instanceOptions[e]})),mergeOptions(instanceOptions,deepCopy(globalOptions))),mergeOptions(instanceOptions,e),instanceOptions}function mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initGlobalOptions(e){const t={};for(const[o,r]of Object.entries(e))if(Object.prototype.hasOwnProperty.call(r,"value")){const e=envs[r.envLink];t[o]=null!=e?e:r.value}else t[o]=_initGlobalOptions(r);return t}function _updateGlobalOptions(e,t,o,r,n){Object.keys(e).forEach((i=>{const s=e[i],a=o&&o[i],l=r&&r[i],c=n&&n[i];if(void 0===s.value)_updateGlobalOptions(s,t[i],a,l,c);else{null!=a&&(t[i]=a);const e=envs[s.envLink];s.envLink in envs&&null!=e&&(t[i]=e),null!=l&&(t[i]=l),null!=c&&(t[i]=c)}}))}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _loadConfigFile(e,t){const o=e.findIndex((e=>"loadConfig"===e.replace(/-/g,""))),r=o>-1&&e[o+1];if(r&&t.allowFileResources)try{return isAllowedConfig(fs.readFileSync(getAbsolutePath(r),"utf8"),!1,t.allowCodeExecution)}catch(e){logWithStack(2,e,`[config] Unable to load the configuration from the ${r} file.`)}return{}}function _pairArgumentValue(e,t){const o={};for(let r=0;r{if(i.length-1===s){const i=t[++r];i||log(2,`[config] Missing value for the CLI '--${n}' argument. Using the default value.`),e[o]=i||null}else void 0===e[o]&&(e[o]={});return e[o]}),o)}return o}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}const cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=getOptions();t.highcharts.version=e,await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:path.join(__dirname$1,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.",400);const o=mergeOptions(deepCopy(getOptions()),{export:e.export,customLogic:e.customLogic}),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e={}){const t=updateOptions(e,!0);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,setGlobalOptions:setGlobalOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvZXJyb3JzL0V4cG9ydEVycm9yLmpzIiwiLi4vbGliL2NvbmZpZy5qcyIsIi4uL2xpYi9mZXRjaC5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIGpvaW4gfSBmcm9tICdwYXRoJztcclxuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCc7XHJcblxyXG5jb25zdCBNQVhfQkFDS09GRl9BVFRFTVBUUyA9IDY7XHJcblxyXG4vLyBUaGUgZGlyZWN0b3J5IHBhdGhcclxuZXhwb3J0IGNvbnN0IF9fZGlybmFtZSA9IGZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLi4vLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbmQgc3RhbmRhcmRpemVzIHRleHQgYnkgcmVwbGFjaW5nIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2VcclxuICogY2hhcmFjdGVycyB3aXRoIGEgc2luZ2xlIHNwYWNlIGFuZCB0cmltbWluZyBhbnkgbGVhZGluZyBvciB0cmFpbGluZ1xyXG4gKiB3aGl0ZXNwYWNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJUZXh0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IC0gVGhlIGlucHV0IHRleHQgdG8gYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtSZWdFeHB9IFtydWxlPS9cXHNcXHMrL2ddIC0gVGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBydWxlIHRvIG1hdGNoXHJcbiAqIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgdGhlICcvXFxzXFxzKy9nJyBSZWdFeHAuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcmVwbGFjZXI9JyAnXSAtIFRoZSBzdHJpbmcgdXNlZCB0byByZXBsYWNlIG11bHRpcGxlXHJcbiAqIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgdGhlICcgJyBzdHJpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjbGVhcmVkIGFuZCBzdGFuZGFyZGl6ZWQgdGV4dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhclRleHQodGV4dCwgcnVsZSA9IC9cXHNcXHMrL2csIHJlcGxhY2VyID0gJyAnKSB7XHJcbiAgcmV0dXJuIHRleHQucmVwbGFjZUFsbChydWxlLCByZXBsYWNlcikudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIGRlZXAgY29weSBvZiB0aGUgZ2l2ZW4gb2JqZWN0IG9yIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZGVlcENvcHlcclxuICpcclxuICogQHBhcmFtIHsoT2JqZWN0fEFycmF5KX0gb2JqQXJyIC0gVGhlIG9iamVjdCBvciBhcnJheSB0byBiZSBkZWVwbHkgY29waWVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxBcnJheSl9IFRoZSBkZWVwIGNvcHkgb2YgdGhlIHByb3ZpZGVkIG9iamVjdCBvciBhcnJheS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBkZWVwQ29weShvYmpBcnIpIHtcclxuICAvLyBJZiB0aGUgYG9iakFycmAgaXMgbnVsbCBvciBub3Qgb2YgdGhlIGBvYmplY3RgIHR5cGUsIHJldHVybiBpdFxyXG4gIGlmIChvYmpBcnIgPT09IG51bGwgfHwgdHlwZW9mIG9iakFyciAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmpBcnI7XHJcbiAgfVxyXG5cclxuICAvLyBQcmVwYXJlIGVpdGhlciBhIG5ldyBhcnJheSBvciBhIG5ldyBvYmplY3RcclxuICBjb25zdCBvYmpBcnJDb3B5ID0gQXJyYXkuaXNBcnJheShvYmpBcnIpID8gW10gOiB7fTtcclxuXHJcbiAgLy8gUmVjdXJzaXZlbHkgY29weSBlYWNoIHByb3BlcnR5XHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqQXJyKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iakFyciwga2V5KSkge1xyXG4gICAgICBvYmpBcnJDb3B5W2tleV0gPSBkZWVwQ29weShvYmpBcnJba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNvcGllZCBvYmplY3RcclxuICByZXR1cm4gb2JqQXJyQ29weTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEltcGxlbWVudHMgYW4gZXhwb25lbnRpYWwgYmFja29mZiBzdHJhdGVneSBmb3IgcmV0cnlpbmcgYSBmdW5jdGlvbiB1bnRpbFxyXG4gKiBhIGNlcnRhaW4gbnVtYmVyIG9mIGF0dGVtcHRzIGFyZSByZWFjaGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGV4cEJhY2tvZmZcclxuICpcclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gLSBUaGUgZnVuY3Rpb24gdG8gYmUgcmV0cmllZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IFthdHRlbXB0PTBdIC0gVGhlIGN1cnJlbnQgYXR0ZW1wdCBudW1iZXIuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGAwYC5cclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0XHJcbiAqIG9mIHRoZSBmdW5jdGlvbiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwQmFja29mZihmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlcGVhdCwgdGhyb3cgYW4gZXJyb3JcclxuICAgIGlmICgrK2F0dGVtcHQgPj0gTUFYX0JBQ0tPRkZfQVRURU1QVFMpIHtcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxyXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc3BvbnNlKSA9PiBzZXRUaW1lb3V0KHJlc3BvbnNlLCBkZWxheUluTXMpKTtcclxuXHJcbiAgICAvLy8gVE8gRE86IENvcnJlY3RcclxuICAgIC8vIC8vIEluZm9ybWF0aW9uIGFib3V0IHRoZSByZXNvdXJjZSB0aW1lb3V0XHJcbiAgICAvLyBsb2coXHJcbiAgICAvLyAgIDMsXHJcbiAgICAvLyAgIGBbdXRpbHNdIFdhaXRlZCAke2RlbGF5SW5Nc31tcyB1bnRpbCBuZXh0IGNhbGwgZm9yIHRoZSByZXNvdXJjZSBvZiBJRDogJHthcmdzWzBdfS5gXHJcbiAgICAvLyApO1xyXG5cclxuICAgIC8vIFRyeSBhZ2FpblxyXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkanVzdHMgdGhlIGNvbnN0cnVjdG9yIG5hbWUgYnkgdHJhbnNmb3JtaW5nIGFuZCBub3JtYWxpemluZyBpdCBiYXNlZFxyXG4gKiBvbiBjb21tb24gY2hhcnQgdHlwZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhDb25zdHJcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnN0ciAtIFRoZSBvcmlnaW5hbCBjb25zdHJ1Y3RvciBuYW1lIHRvIGJlIGZpeGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGNvbnN0cnVjdG9yIG5hbWUsIG9yICdjaGFydCcgaWYgdGhlIGlucHV0XHJcbiAqIGlzIG5vdCByZWNvZ25pemVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeENvbnN0cihjb25zdHIpIHtcclxuICB0cnkge1xyXG4gICAgLy8gRml4IHRoZSBjb25zdHJ1Y3RvciBieSBsb3dlcmluZyBjYXNpbmdcclxuICAgIGNvbnN0IGZpeGVkQ29uc3RyID0gYCR7Y29uc3RyLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnY2hhcnQnLCAnJyl9Q2hhcnRgO1xyXG5cclxuICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgcmVzdWx0IGlzIGp1c3QgJ0NoYXJ0J1xyXG4gICAgaWYgKGZpeGVkQ29uc3RyID09PSAnQ2hhcnQnKSB7XHJcbiAgICAgIGZpeGVkQ29uc3RyLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IsIG90aGVyd2lzZSBkZWZhdWx0IHRvICdjaGFydCdcclxuICAgIHJldHVybiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddLmluY2x1ZGVzKFxyXG4gICAgICBmaXhlZENvbnN0clxyXG4gICAgKVxyXG4gICAgICA/IGZpeGVkQ29uc3RyXHJcbiAgICAgIDogJ2NoYXJ0JztcclxuICB9IGNhdGNoIHtcclxuICAgIC8vIERlZmF1bHQgdG8gJ2NoYXJ0JyBpbiBjYXNlIG9mIGFueSBlcnJvclxyXG4gICAgcmV0dXJuICdjaGFydCc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIG91dGZpbGUgYmFzZWQgb24gcHJvdmlkZWQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeE91dGZpbGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBvdXRmaWxlIC0gVGhlIGZpbGUgcGF0aCBvciBuYW1lLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIG91dGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4T3V0ZmlsZSh0eXBlLCBvdXRmaWxlKSB7XHJcbiAgLy8gR2V0IHRoZSBmaWxlIG5hbWUgZnJvbSB0aGUgYG91dGZpbGVgIG9wdGlvblxyXG4gIGNvbnN0IGZpbGVOYW1lID0gZ2V0QWJzb2x1dGVQYXRoKG91dGZpbGUgfHwgJ2NoYXJ0JylcclxuICAgIC5zcGxpdCgnLicpXHJcbiAgICAuc2hpZnQoKTtcclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCBvdXRmaWxlXHJcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke3R5cGV9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhUeXBlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW291dGZpbGU9bnVsbF0gLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGBudWxsYC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNvcnJlY3RlZCBleHBvcnQgdHlwZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhUeXBlKHR5cGUsIG91dGZpbGUgPSBudWxsKSB7XHJcbiAgLy8gTUlNRSB0eXBlc1xyXG4gIGNvbnN0IG1pbWVUeXBlcyA9IHtcclxuICAgICdpbWFnZS9wbmcnOiAncG5nJyxcclxuICAgICdpbWFnZS9qcGVnJzogJ2pwZWcnLFxyXG4gICAgJ2FwcGxpY2F0aW9uL3BkZic6ICdwZGYnLFxyXG4gICAgJ2ltYWdlL3N2Zyt4bWwnOiAnc3ZnJ1xyXG4gIH07XHJcblxyXG4gIC8vIEdldCBmb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IE9iamVjdC52YWx1ZXMobWltZVR5cGVzKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdHlwZSBhbmQgb3V0ZmlsZSdzIGV4dGVuc2lvbnMgYXJlIHRoZSBzYW1lXHJcbiAgaWYgKG91dGZpbGUpIHtcclxuICAgIGNvbnN0IG91dFR5cGUgPSBvdXRmaWxlLnNwbGl0KCcuJykucG9wKCk7XHJcblxyXG4gICAgLy8gU3VwcG9ydCB0aGUgSlBHIHR5cGVcclxuICAgIGlmIChvdXRUeXBlID09PSAnanBnJykge1xyXG4gICAgICB0eXBlID0gJ2pwZWcnO1xyXG4gICAgfSBlbHNlIGlmIChmb3JtYXRzLmluY2x1ZGVzKG91dFR5cGUpICYmIHR5cGUgIT09IG91dFR5cGUpIHtcclxuICAgICAgdHlwZSA9IG91dFR5cGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IHR5cGVcclxuICByZXR1cm4gbWltZVR5cGVzW3R5cGVdIHx8IGZvcm1hdHMuZmluZCgodCkgPT4gdCA9PT0gdHlwZSkgfHwgJ3BuZyc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIHBhdGggaXMgcmVsYXRpdmUgb3IgYWJzb2x1dGUgYW5kIHJldHVybnMgdGhlIGNvcnJlY3RlZCxcclxuICogYWJzb2x1dGUgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzQWJzb2x1dGVQYXRoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gYmUgY2hlY2tlZCBvbi5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGguXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWJzb2x1dGVQYXRoKHBhdGgpIHtcclxuICByZXR1cm4gaXNBYnNvbHV0ZShwYXRoKSA/IHBhdGggOiBqb2luKF9fZGlybmFtZSwgcGF0aCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBpbnB1dCBkYXRhIHRvIGEgQmFzZTY0IHN0cmluZyBiYXNlZCBvbiB0aGUgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRCYXNlNjRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IC0gVGhlIGlucHV0IHRvIGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmb3JtYXQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQmFzZTY0IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QmFzZTY0KGlucHV0LCB0eXBlKSB7XHJcbiAgLy8gRm9yIHBkZiBhbmQgc3ZnIHR5cGVzIHRoZSBpbnB1dCBtdXN0IGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmcm9tIGEgYnVmZmVyXHJcbiAgaWYgKHR5cGUgPT09ICdwZGYnIHx8IHR5cGUgPT0gJ3N2ZycpIHtcclxuICAgIHJldHVybiBCdWZmZXIuZnJvbShpbnB1dCwgJ3V0ZjgnKS50b1N0cmluZygnYmFzZTY0Jyk7XHJcbiAgfVxyXG5cclxuICAvLyBGb3IgcG5nIGFuZCBqcGVnIGlucHV0IGlzIGFscmVhZHkgYSBCYXNlNjQgc3RyaW5nXHJcbiAgcmV0dXJuIGlucHV0O1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyBzdHJpbmdpZmllZCBkYXRlIHdpdGhvdXQgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGUoKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgR01UIHRleHQgaW5mb3JtYXRpb25cclxuICByZXR1cm4gbmV3IERhdGUoKS50b1N0cmluZygpLnNwbGl0KCcoJylbMF0udHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgc3RvcmVkIHRpbWUgdmFsdWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVRpbWVcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXROZXdEYXRlVGltZSgpIHtcclxuICByZXR1cm4gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIGl0ZW0gaXMgYW4gb2JqZWN0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNPYmplY3RcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIGl0ZW0gdG8gYmUgY2hlY2tlZC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIGl0ZW0gaXMgYW4gb2JqZWN0LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3QoaXRlbSkge1xyXG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaXRlbSkgPT09ICdbb2JqZWN0IE9iamVjdF0nO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdEVtcHR5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGVtcHR5LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3RFbXB0eShpdGVtKSB7XHJcbiAgcmV0dXJuIChcclxuICAgIHR5cGVvZiBpdGVtID09PSAnb2JqZWN0JyAmJlxyXG4gICAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICAgIGl0ZW0gIT09IG51bGwgJiZcclxuICAgIE9iamVjdC5rZXlzKGl0ZW0pLmxlbmd0aCA9PT0gMFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCBpbiB0aGUgZ2l2ZW4gc3RyaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaXRlbSAtIFRoZSBzdHJpbmcgdG8gYmUgY2hlY2tlZCBmb3IgYSBwcml2YXRlIElQIHJhbmdlIFVSTC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoaXRlbSkge1xyXG4gIGNvbnN0IHJlZ2V4UGF0dGVybnMgPSBbXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/bG9jYWxob3N0XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTI3XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xNzJcXC4oMVs2LTldfDJbMC05XXwzWzAtMV0pXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTkyXFwuMTY4XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi9cclxuICBdO1xyXG5cclxuICByZXR1cm4gcmVnZXhQYXR0ZXJucy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3QoaXRlbSkpO1xyXG59XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIGVsYXBzZWQgdGltZSB1c2luZyB0aGUgTm9kZS5qcyBgcHJvY2Vzcy5ocnRpbWUoKWAgbWV0aG9kLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbWVhc3VyZVRpbWVcclxuICpcclxuICogQHJldHVybnMge0Z1bmN0aW9ufSBBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZWxhcHNlZCB0aW1lIGluIG1pbGxpc2Vjb25kcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBtZWFzdXJlVGltZSgpIHtcclxuICBjb25zdCBzdGFydCA9IHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO1xyXG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xyXG59XHJcblxyXG4vKipcclxuICogUm91bmRzIGEgbnVtYmVyIHRvIHRoZSBzcGVjaWZpZWQgcHJlY2lzaW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcm91bmROdW1iZXJcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IHZhbHVlIC0gVGhlIG51bWJlciB0byBiZSByb3VuZGVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcHJlY2lzaW9uIC0gVGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyB0byByb3VuZCB0by5cclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gVGhlIHJvdW5kZWQgbnVtYmVyLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHJvdW5kTnVtYmVyKHZhbHVlLCBwcmVjaXNpb24gPSAxKSB7XHJcbiAgY29uc3QgbXVsdGlwbGllciA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24gfHwgMCk7XHJcbiAgcmV0dXJuIE1hdGgucm91bmQoK3ZhbHVlICogbXVsdGlwbGllcikgLyBtdWx0aXBsaWVyO1xyXG59XHJcblxyXG4vKipcclxuICogQ29udmVydHMgYSB2YWx1ZSB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB0b0Jvb2xlYW5cclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUaGUgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQgdmFsdWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdG9Cb29sZWFuKGl0ZW0pIHtcclxuICByZXR1cm4gWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBXcmFwcyBjdXN0b20gY29kZSB0byBleGVjdXRlIGl0IHNhZmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIHdyYXBBcm91bmRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tIGNvZGUgdG8gYmUgd3JhcHBlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBGbGFnIHRvIGFsbG93IGxvYWRpbmcgY29kZSBmcm9tIGEgZmlsZS5cclxuICogQHBhcmFtIHtib29sZWFufSBbaXNDYWxsYmFjaz1mYWxzZV0gLSBGbGFnIHRoYXQgaW5kaWNhdGVzIHRoZSByZXR1cm5lZCBjb2RlXHJcbiAqIG11c3QgYmUgaW4gYSBjYWxsYmFjayBmb3JtYXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoc3RyaW5nfG51bGwpfSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBudWxsIGlmIHdyYXBwaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHdyYXBBcm91bmQoY3VzdG9tQ29kZSwgYWxsb3dGaWxlUmVzb3VyY2VzLCBpc0NhbGxiYWNrID0gZmFsc2UpIHtcclxuICBpZiAoY3VzdG9tQ29kZSAmJiB0eXBlb2YgY3VzdG9tQ29kZSA9PT0gJ3N0cmluZycpIHtcclxuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcclxuXHJcbiAgICBpZiAoY3VzdG9tQ29kZS5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgICAgLy8gTG9hZCBhIGZpbGUgaWYgdGhlIGZpbGUgcmVzb3VyY2VzIGFyZSBhbGxvd2VkXHJcbiAgICAgIHJldHVybiBhbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgICA/IHdyYXBBcm91bmQoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY3VzdG9tQ29kZSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgICAgaXNDYWxsYmFja1xyXG4gICAgICAgICAgKVxyXG4gICAgICAgIDogbnVsbDtcclxuICAgIH0gZWxzZSBpZiAoXHJcbiAgICAgICFpc0NhbGxiYWNrICYmXHJcbiAgICAgIChjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24gKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFRyZWF0IGEgZnVuY3Rpb24gYXMgYSBzZWxmLWludm9raW5nIGV4cHJlc3Npb25cclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gT3IgcmV0dXJuIGFzIGEgc3RyaW5naWZpZWQgY29kZVxyXG4gICAgcmV0dXJuIGN1c3RvbUNvZGUucmVwbGFjZSgvOyQvLCAnJyk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgX19kaXJuYW1lLFxyXG4gIGNsZWFyVGV4dCxcclxuICBkZWVwQ29weSxcclxuICBleHBCYWNrb2ZmLFxyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBnZXROZXdEYXRlLFxyXG4gIGdldE5ld0RhdGVUaW1lLFxyXG4gIGlzT2JqZWN0LFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBtZWFzdXJlVGltZSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQSBtb2R1bGUgZm9yIG1hbmFnaW5nIGxvZ2dpbmcgZnVuY3Rpb25hbGl0eSB3aXRoIGN1c3RvbWl6YWJsZVxyXG4gKiBsb2cgbGV2ZWxzLCBjb25zb2xlIGFuZCBmaWxlIGxvZ2dpbmcgb3B0aW9ucywgYW5kIGVycm9yIGhhbmRsaW5nIHN1cHBvcnQuXHJcbiAqIFRoZSBtb2R1bGUgYWxzbyBlbnN1cmVzIHRoYXQgZmlsZS1iYXNlZCBsb2dzIGFyZSBzdG9yZWQgaW4gYSBzdHJ1Y3R1cmVkXHJcbiAqIGRpcmVjdG9yeSwgY3JlYXRpbmcgdGhlIG5lY2Vzc2FyeSBwYXRocyBhdXRvbWF0aWNhbGx5IGlmIHRoZXkgZG8gbm90IGV4aXN0LlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWJzb2x1dGVQYXRoLCBnZXROZXdEYXRlIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG4vLyBUaGUgYXZhaWxhYmxlIGNvbG9yc1xyXG5jb25zdCBjb2xvcnMgPSBbJ3JlZCcsICd5ZWxsb3cnLCAnYmx1ZScsICdncmF5JywgJ2dyZWVuJ107XHJcblxyXG4vLyBUaGUgZGVmYXVsdCBsb2dnaW5nIGNvbmZpZ1xyXG5jb25zdCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBGdWxsIHBhdGggdG8gdGhlIGxvZyBmaWxlXHJcbiAgcGF0aFRvTG9nOiAnJyxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdXHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhIG1lc3NhZ2Ugd2l0aCBhIHNwZWNpZmllZCBsb2cgbGV2ZWwuIEFjY2VwdHMgYSB2YXJpYWJsZSBudW1iZXJcclxuICogb2YgYXJndW1lbnRzLiBUaGUgYXJndW1lbnRzIGFmdGVyIHRoZSBgbGV2ZWxgIGFyZSBwYXNzZWQgdG8gYGNvbnNvbGUubG9nYFxyXG4gKiBhbmQvb3IgdXNlZCB0byBjb25zdHJ1Y3QgYW5kIGFwcGVuZCBtZXNzYWdlcyB0byBhIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Li4udW5rbm93bn0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZ1xyXG4gKiBsZXZlbCBhbmQgdGhlIHJlbWFpbmluZyBhcmUgc3RyaW5ncyB1c2VkIHRvIGJ1aWxkIHRoZSBsb2cgbWVzc2FnZS5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZyguLi5hcmdzKSB7XHJcbiAgY29uc3QgW25ld0xldmVsLCAuLi50ZXh0c10gPSBhcmdzO1xyXG5cclxuICAvLyBDdXJyZW50IGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgbGV2ZWxzRGVzYywgbGV2ZWwgfSA9IGxvZ2dpbmc7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZSBvciBpcyBpdCBhIGJlbmNobWFyayBsb2dcclxuICBpZiAoXHJcbiAgICBuZXdMZXZlbCAhPT0gNSAmJlxyXG4gICAgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aClcclxuICApIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtnZXROZXdEYXRlKCl9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgYW4gZXJyb3IgbWVzc2FnZSBhbG9uZyB3aXRoIGl0cyBzdGFjayB0cmFjZS4gT3B0aW9uYWxseSwgYSBjdXN0b20gbWVzc2FnZVxyXG4gKiBjYW4gYmUgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dXaXRoU3RhY2tcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IG5ld0xldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHN0YWNrIHRyYWNlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGluY2x1ZGVkXHJcbiAqIGluIHRoZSBsb2cgYWxvbmdzaWRlIHRoZSBlcnJvci5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZ1dpdGhTdGFjayhuZXdMZXZlbCwgZXJyb3IsIGN1c3RvbU1lc3NhZ2UpIHtcclxuICAvLyBHZXQgdGhlIG1haW4gbWVzc2FnZVxyXG4gIGNvbnN0IG1haW5NZXNzYWdlID0gY3VzdG9tTWVzc2FnZSB8fCAoZXJyb3IgJiYgZXJyb3IubWVzc2FnZSkgfHwgJyc7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlXHJcbiAgaWYgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aCkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIEFkZCB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9IGVycm9yICYmIGVycm9yLnN0YWNrO1xyXG5cclxuICAvLyBDb21iaW5lIGN1c3RvbSBtZXNzYWdlIG9yIGVycm9yIG1lc3NhZ2Ugd2l0aCBlcnJvciBzdGFjayBtZXNzYWdlLCBpZiBleGlzdHNcclxuICBjb25zdCB0ZXh0cyA9IFttYWluTWVzc2FnZV07XHJcbiAgaWYgKHN0YWNrTWVzc2FnZSkge1xyXG4gICAgdGV4dHMucHVzaCgnXFxuJywgc3RhY2tNZXNzYWdlKTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgdGV4dHMuc2hpZnQoKVtjb2xvcnNbbmV3TGV2ZWwgLSAxXV0sXHJcbiAgICAgICAgLi4udGV4dHNcclxuICAgICAgXSlcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgbG9nZ2luZyBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaW5pdExvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGxvZ2dpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGxvZ2dpbmdgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaW5pdExvZ2dpbmcobG9nZ2luZ09wdGlvbnMpIHtcclxuICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBgbG9nZ2luZ09wdGlvbnNgIG9iamVjdFxyXG4gIGNvbnN0IHsgbGV2ZWwsIGRlc3QsIGZpbGUsIHRvQ29uc29sZSwgdG9GaWxlIH0gPSBsb2dnaW5nT3B0aW9ucztcclxuXHJcbiAgLy8gUmVzZXQgZmxhZ3MgdG8gdGhlIGRlZmF1bHQgdmFsdWVzXHJcbiAgbG9nZ2luZy5wYXRoQ3JlYXRlZCA9IGZhbHNlO1xyXG4gIGxvZ2dpbmcucGF0aFRvTG9nID0gJyc7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nZ2luZyBsZXZlbFxyXG4gIHNldExvZ0xldmVsKGxldmVsKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb25zb2xlIGxvZ2dpbmdcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpO1xyXG5cclxuICAvLyBTZXQgdGhlIGZpbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKGAwYCA9IG5vIGxvZ2dpbmcsXHJcbiAqIGAxYCA9IGVycm9yLCBgMmAgPSB3YXJuaW5nLCBgM2AgPSBub3RpY2UsIGA0YCA9IHZlcmJvc2UsIG9yIGA1YCA9IGJlbmNobWFyaykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRMb2dMZXZlbFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgbG9nIGxldmVsIHRvIGJlIHNldC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRMb2dMZXZlbChsZXZlbCkge1xyXG4gIGlmIChcclxuICAgIE51bWJlci5pc0ludGVnZXIobGV2ZWwpICYmXHJcbiAgICBsZXZlbCA+PSAwICYmXHJcbiAgICBsZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoXHJcbiAgKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYGxldmVsYCBvcHRpb25cclxuICAgIGxvZ2dpbmcubGV2ZWwgPSBsZXZlbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGNvbnNvbGUgbG9nZ2luZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUNvbnNvbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9Db25zb2xlIC0gVGhlIGZsYWcgZm9yIHNldHRpbmcgdGhlIGxvZ2dpbmcgdG8gdGhlIGNvbnNvbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmcodG9Db25zb2xlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0NvbnNvbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9Db25zb2xlID0gISF0b0NvbnNvbGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlIG5hbWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVGaWxlTG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZGVzdCAtIFRoZSBkZXN0aW5hdGlvbiBwYXRoIHdoZXJlIHRoZSBsb2cgZmlsZSBzaG91bGRcclxuICogYmUgc2F2ZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlIC0gVGhlIG5hbWUgb2YgdGhlIGxvZyBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvRmlsZSAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9nZ2luZyBzaG91bGRcclxuICogYmUgZGlyZWN0ZWQgdG8gYSBmaWxlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgdG9GaWxlYCBvcHRpb25cclxuICBsb2dnaW5nLnRvRmlsZSA9ICEhdG9GaWxlO1xyXG5cclxuICAvLyBTZXQgdGhlIGBkZXN0YCBhbmQgYGZpbGVgIG9wdGlvbnMgb25seSBpZiB0aGUgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWRcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIGxvZ2dpbmcuZGVzdCA9IGRlc3QgfHwgJyc7XHJcbiAgICBsb2dnaW5nLmZpbGUgPSBmaWxlIHx8ICcnO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgdGhlIHByb3ZpZGVkIHRleHRzIHRvIGEgZmlsZSwgaWYgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWQuIEl0IGNyZWF0ZXNcclxuICogdGhlIG5lY2Vzc2FyeSBkaXJlY3Rvcnkgc3RydWN0dXJlIGlmIG5vdCBhbHJlYWR5IGNyZWF0ZWQgYW5kIGFwcGVuZHNcclxuICogdGhlIGNvbnRlbnQsIGluY2x1ZGluZyBhbiBvcHRpb25hbCBwcmVmaXgsIHRvIHRoZSBzcGVjaWZpZWQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9nVG9GaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHRleHRzIC0gQW4gYXJyYXkgb2YgdGV4dHMgdG8gYmUgbG9nZ2VkLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4IC0gQW4gb3B0aW9uYWwgcHJlZml4IHRvIGJlIGFkZGVkIHRvIGVhY2ggbG9nIGVudHJ5LlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KSB7XHJcbiAgaWYgKCFsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAvLyBDcmVhdGUgaWYgZG9lcyBub3QgZXhpc3RcclxuICAgICFleGlzdHNTeW5jKGdldEFic29sdXRlUGF0aChsb2dnaW5nLmRlc3QpKSAmJlxyXG4gICAgICBta2RpclN5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpO1xyXG5cclxuICAgIC8vIENyZWF0ZSB0aGUgZnVsbCBwYXRoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyA9IGdldEFic29sdXRlUGF0aChqb2luKGxvZ2dpbmcuZGVzdCwgbG9nZ2luZy5maWxlKSk7XHJcblxyXG4gICAgLy8gV2Ugbm93IGFzc3VtZSB0aGUgcGF0aCBpcyBhdmFpbGFibGUsIGUuZy4gaXQncyB0aGUgcmVzcG9uc2liaWxpdHlcclxuICAgIC8vIG9mIHRoZSB1c2VyIHRvIGNyZWF0ZSB0aGUgcGF0aCB3aXRoIHRoZSBjb3JyZWN0IGFjY2VzcyByaWdodHMuXHJcbiAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICB9XHJcblxyXG4gIC8vIEFkZCB0aGUgY29udGVudCB0byBhIGZpbGVcclxuICBhcHBlbmRGaWxlKFxyXG4gICAgbG9nZ2luZy5wYXRoVG9Mb2csXHJcbiAgICBbcHJlZml4XS5jb25jYXQodGV4dHMpLmpvaW4oJyAnKSArICdcXG4nLFxyXG4gICAgKGVycm9yKSA9PiB7XHJcbiAgICAgIGlmIChlcnJvciAmJiBsb2dnaW5nLnRvRmlsZSAmJiBsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmcsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmdcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvbmZpZ3VyYXRpb24gbWFuYWdlbWVudCBtb2R1bGUgZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuXHJcbiAqIFByb3ZpZGVzIGRlZmF1bHQgY29uZmlndXJhdGlvbnMgdGhhdCBzdXBwb3J0IGVudmlyb25tZW50IHZhcmlhYmxlcywgQ0xJXHJcbiAqIGFyZ3VtZW50cywgYW5kIGludGVyYWN0aXZlIHByb21wdHMgZm9yIGN1c3RvbWl6YXRpb24gb2Ygb3B0aW9ucyBhbmQgZmVhdHVyZXMuXHJcbiAqIEFkZGl0aW9uYWxseSwgaXQgbWFwcyBsZWdhY3kgb3B0aW9ucyB0byBtb2Rlcm4gc3RydWN0dXJlcywgZ2VuZXJhdGVzIG5lc3RlZFxyXG4gKiBhcmd1bWVudCBtYXBwaW5ncywgYW5kIGRpc3BsYXlzIENMSSB1c2FnZSBpbmZvcm1hdGlvbi5cclxuICovXHJcblxyXG4vKipcclxuICogVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYWxsIGF2YWlsYWJsZSBvcHRpb25zLCBvcmdhbml6ZWRcclxuICogYnkgc2VjdGlvbnMuXHJcbiAqXHJcbiAqIFRoaXMgb2JqZWN0IGluY2x1ZGVzOlxyXG4gKiAtIERlZmF1bHQgdmFsdWVzIGZvciBlYWNoIG9wdGlvblxyXG4gKiAtIERhdGEgdHlwZXMgZm9yIHZhbGlkYXRpb25cclxuICogLSBOYW1lcyBvZiBjb3JyZXNwb25kaW5nIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiAtIERlc2NyaXB0aW9ucyBvZiBlYWNoIHByb3BlcnR5XHJcbiAqIC0gSW5mb3JtYXRpb24gdXNlZCBmb3IgcHJvbXB0cyBpbiBpbnRlcmFjdGl2ZSBjb25maWd1cmF0aW9uXHJcbiAqIC0gW09wdGlvbmFsXSBDb3JyZXNwb25kaW5nIENMSSBhcmd1bWVudCBuYW1lcyBmb3IgQ0xJIHVzYWdlXHJcbiAqIC0gW09wdGlvbmFsXSBMZWdhY3kgbmFtZXMgZnJvbSB0aGUgcHJldmlvdXMgUGhhbnRvbUpTLWJhc2VkIHNlcnZlclxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZmF1bHRDb25maWcgPSB7XHJcbiAgcHVwcGV0ZWVyOiB7XHJcbiAgICBhcmdzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJy0tYWxsb3ctcnVubmluZy1pbnNlY3VyZS1jb250ZW50JyxcclxuICAgICAgICAnLS1hc2gtbm8tbnVkZ2VzJyxcclxuICAgICAgICAnLS1hdXRvcGxheS1wb2xpY3k9dXNlci1nZXN0dXJlLXJlcXVpcmVkJyxcclxuICAgICAgICAnLS1ibG9jay1uZXctd2ViLWNvbnRlbnRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWFjY2VsZXJhdGVkLTJkLWNhbnZhcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLW5ldHdvcmtpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1icmVha3BhZCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jaGVja2VyLWltYWdpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC1leHRlbnNpb25zLXdpdGgtYmFja2dyb3VuZC1wYWdlcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtdXBkYXRlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRlZmF1bHQtYXBwcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZXYtc2htLXVzYWdlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRvbWFpbi1yZWxpYWJpbGl0eScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1leHRlbnNpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWZlYXR1cmVzPUNhbGN1bGF0ZU5hdGl2ZVdpbk9jY2x1c2lvbixJbnRlcmVzdEZlZWRDb250ZW50U3VnZ2VzdGlvbnMsV2ViT1RQJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1sb2dnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXBvcHVwLWJsb2NraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1yZW5kZXJlci1iYWNrZ3JvdW5kaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlYXJjaC1lbmdpbmUtY2hvaWNlLXNjcmVlbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNpdGUtaXNvbGF0aW9uLXRyaWFscycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zcGVlY2gtYXBpJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXN5bmMnLFxyXG4gICAgICAgICctLWVuYWJsZS11bnNhZmUtd2ViZ3B1JyxcclxuICAgICAgICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcclxuICAgICAgICAnLS1oaWRlLXNjcm9sbGJhcnMnLFxyXG4gICAgICAgICctLW1ldHJpY3MtcmVjb3JkaW5nLW9ubHknLFxyXG4gICAgICAgICctLW11dGUtYXVkaW8nLFxyXG4gICAgICAgICctLW5vLWRlZmF1bHQtYnJvd3Nlci1jaGVjaycsXHJcbiAgICAgICAgJy0tbm8tZmlyc3QtcnVuJyxcclxuICAgICAgICAnLS1uby1waW5ncycsXHJcbiAgICAgICAgJy0tbm8tc2FuZGJveCcsXHJcbiAgICAgICAgJy0tbm8tc3RhcnR1cC13aW5kb3cnLFxyXG4gICAgICAgICctLW5vLXp5Z290ZScsXHJcbiAgICAgICAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxyXG4gICAgICAgICctLXByb2Nlc3MtcGVyLXRhYicsXHJcbiAgICAgICAgJy0tdXNlLW1vY2sta2V5Y2hhaW4nXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdQVVBQRVRFRVJfQVJHUycsXHJcbiAgICAgIGNsaU5hbWU6ICdwdXBwZXRlZXJBcmdzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBcnJheSBvZiBQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgICBzZXBhcmF0b3I6ICc7J1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBoaWdoY2hhcnRzOiB7XHJcbiAgICB2ZXJzaW9uOiB7XHJcbiAgICAgIHZhbHVlOiAnbGF0ZXN0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgdmVyc2lvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNkblVybDoge1xyXG4gICAgICB2YWx1ZTogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmb3JjZUZldGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmxhZyB0byByZWZldGNoIHNjcmlwdHMgYWZ0ZXIgZWFjaCBzZXJ2ZXIgcmVydW4nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhY2hlUGF0aDoge1xyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DQUNIRV9QQVRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEaXJlY3RvcnkgcGF0aCBmb3IgY2FjaGVkIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2hpZ2hjaGFydHMnLCAnaGlnaGNoYXJ0cy1tb3JlJywgJ2hpZ2hjaGFydHMtM2QnXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbW9kdWxlU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdzdG9jaycsXHJcbiAgICAgICAgJ21hcCcsXHJcbiAgICAgICAgJ2dhbnR0JyxcclxuICAgICAgICAnZXhwb3J0aW5nJyxcclxuICAgICAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgICAgICdhY2Nlc3NpYmlsaXR5JyxcclxuICAgICAgICAvLyAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgICAgICdib29zdCcsXHJcbiAgICAgICAgJ2RhdGEnLFxyXG4gICAgICAgICdkYXRhLXRvb2xzJyxcclxuICAgICAgICAnZHJhZ2dhYmxlLXBvaW50cycsXHJcbiAgICAgICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAgICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICAgICAnaGVhdG1hcCcsXHJcbiAgICAgICAgJ3RpbGVtYXAnLFxyXG4gICAgICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAgICAgJ3RpbWVsaW5lJyxcclxuICAgICAgICAndHJlZW1hcCcsXHJcbiAgICAgICAgJ3RyZWVncmFwaCcsXHJcbiAgICAgICAgJ2l0ZW0tc2VyaWVzJyxcclxuICAgICAgICAnZHJpbGxkb3duJyxcclxuICAgICAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAgICAgJ2J1bGxldCcsXHJcbiAgICAgICAgJ2Z1bm5lbCcsXHJcbiAgICAgICAgJ2Z1bm5lbDNkJyxcclxuICAgICAgICAnZ2VvaGVhdG1hcCcsXHJcbiAgICAgICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAgICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAgICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzZXJpZXMtb24tcG9pbnQnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgLy8gJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaScsXHJcbiAgICAgICAgJ2Zsb3dtYXAnLFxyXG4gICAgICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAgICAgJ25hdmlnYXRvcicsXHJcbiAgICAgICAgJ3RleHRwYXRoJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBtb2R1bGUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogWydpbmRpY2F0b3JzLWFsbCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBpbmRpY2F0b3Igc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQuanMvMi4zMC4xL21vbWVudC5taW4uanMnLFxyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQtdGltZXpvbmUvMC41LjQ1L21vbWVudC10aW1lem9uZS13aXRoLWRhdGEubWluLmpzJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DVVNUT01fU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBjdXN0b20gc2NyaXB0cyBvciBkZXBlbmRlbmNpZXMgdG8gZmV0Y2gnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGV4cG9ydDoge1xyXG4gICAgaW5maWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0lORklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnB1dCBmaWxlbmFtZSB3aXRoIHR5cGUsIGZvcm1hdHRlZCBjb3JyZWN0bHkgYXMgSlNPTiBvciBTVkcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbnN0cjoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5TVFInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3ZlcnJpZGVzIHRoZSBgaW5maWxlYCB3aXRoIEpTT04sIHN0cmluZ2lmaWVkIEpTT04sIG9yIFNWRyBpbnB1dCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09QVElPTlMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FsaWFzIGZvciB0aGUgYGluc3RyYCBvcHRpb24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzdmc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU1ZHJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTVkcgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCB0byByZW5kZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CQVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdCYXRjaCBqb2Igc3RyaW5nIHdpdGggaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09VVEZJTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3V0cHV0IGZpbGVuYW1lIHdpdGggdHlwZS4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcgYW5kIGlnbm9yZXMgYHR5cGVgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIGV4cG9ydCBmb3JtYXQuIENhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBwbmcnLFxyXG4gICAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0NPTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDaGFydCBjb25zdHJ1Y3Rvci4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgICBoaW50OiAnRGVmYXVsdDogY2hhcnQnLFxyXG4gICAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGI2NDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CNjQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gdGhlIGNoYXJ0IHNob3VsZCBiZSByZWNlaXZlZCBpbiBCYXNlNjQgZm9ybWF0IGluc3RlYWQgb2YgYmluYXJ5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBub0Rvd25sb2FkOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX05PX0RPV05MT0FEJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZXRoZXIgb3Igbm90IHRvIGluY2x1ZGUgb3IgZXhjbHVkZSBhdHRhY2htZW50IGhlYWRlcnMgaW4gdGhlIHJlc3BvbnNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSEVJR0hUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1NDQUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0RlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgbWluOiAwLjEsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9HTE9CQUxfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggZ2xvYmFsIG9wdGlvbnMgZm9yIEhpZ2hjaGFydHMuc2V0T3B0aW9ucycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVEhFTUVfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHdlYnBhZ2UgcmVuZGVyaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgZHVyaW5nIGV4cG9ydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FsbG93cyBvciBkaXNhbGxvd3MgaW5qZWN0aW9uIG9mIGZpbGVzeXN0ZW0gcmVzb3VyY2VzIChkaXNhYmxlZCBpbiBzZXJ2ZXIgbW9kZSknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DQUxMQkFDSycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIENhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgLmpzIGZpbGVuYW1lJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZXMgYXMgSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgZmlsZW5hbWUsIGNvbnRhaW5pbmcgZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSB3aXRoIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdQcm9tcHQtYmFzZWQgb3B0aW9uIHNldHRpbmcsIHNhdmVkIHRvIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVTZXJ2ZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0cyB0aGUgc2VydmVyIHdoZW4gdHJ1ZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaG9zdDoge1xyXG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIb3N0bmFtZSBvZiB0aGUgc2VydmVyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUG9ydCBudW1iZXIgZm9yIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHVwbG9hZExpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiAzLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9VUExPQURfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVxdWVzdCBib2R5IHNpemUgaW4gTUInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAnc2VydmVyQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Rpc3BsYXlzIG9yIG5vdCBhY3Rpb24gZHVyYXRpb25zIGluIG1pbGxpc2Vjb25kcyBkdXJpbmcgc2VydmVyIHJlcXVlc3RzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwcm94eToge1xyXG4gICAgICBob3N0OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eUhvc3QnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnSG9zdCBvZiB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB3aW5kb3c6IHtcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RpbWUgd2luZG93IGluIG1pbnV0ZXMgZm9yIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnRGVsYXkgZHVyYXRpb24gYmV0d2VlbiBzdWNjZXNzaXZlIHJlcXVlc3RzIGJlZm9yZSByZWFjaGluZyB0aGUgbGltaXQnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcEtleToge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0tleSB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwVG9rZW5gJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBUb2tlbjoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVG9rZW4gdG8gYnlwYXNzIHRoZSByYXRlIGxpbWl0ZXIsIHVzZWQgd2l0aCBgc2tpcEtleWAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdGb3JjZXMgdGhlIHNlcnZlciB0byB1c2UgSFRUUFMgb25seSB3aGVuIHRydWUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IGZvciB0aGUgU1NMIHNlcnZlcicsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xDZXJ0UGF0aCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWluaW11bSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICB3b3JrTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDQwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTnVtYmVyIG9mIHRhc2tzIGEgd29ya2VyIGNhbiBoYW5kbGUgYmVmb3JlIHJlc3RhcnRpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpZGxlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMzAwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGlkbGUgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAyMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGJlZm9yZSByZXRyeWluZyByZXNvdXJjZSBjcmVhdGlvbiBvbiBmYWlsdXJlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgdG8gY2hlY2sgYW5kIGRlc3Ryb3kgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1Nob3dzIHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdMb2dnaW5nIHZlcmJvc2l0eSBsZXZlbCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICByb3VuZDogMCxcclxuICAgICAgICBtaW46IDAsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnTG9nIGZpbGUgbmFtZS4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCB0byBiZSBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZXN0OiB7XHJcbiAgICAgIHZhbHVlOiAnbG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFJlcXVpcmVzIGBsb2dUb0ZpbGVgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBjb25zb2xlIGxvZ2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvRmlsZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICB1aToge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVVpJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGVuZHBvaW50IHJvdXRlIGZvciB0aGUgVUknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBOb2RlLmpzIGVudmlyb25tZW50IHR5cGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0Rpc3BsYXkgb3Igc2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoYXJkUmVzZXRQYWdlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfSEFSRF9SRVNFVF9QQUdFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaGV0aGVyIG9yIG5vdCB0byByZXNldCB0aGUgcGFnZSBjb250ZW50IGVudGlyZWx5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBicm93c2VyU2hlbGxNb2RlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHNldCB0aGUgYnJvd3NlciB0byBydW4gaW4gc2hlbGwgbW9kZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBkZWJ1Zzoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZURlYnVnJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSB1bmRlcmx5aW5nIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlYWRsZXNzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBoZWFkbGVzcyBtb2RlIGR1cmluZyBkZWJ1Z2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRldnRvb2xzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgRGV2VG9vbHMgaW4gaGVhZGZ1bCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGxpc3RlbmluZyB0byBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGR1bXBpbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RVTVBJTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdSZWRpcmVjdHMgb3Igbm90IGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gcHJvY2Vzcy5zdGRvdXQgYW5kIHByb2Nlc3Muc3RkZXJyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzbG93TW86IHtcclxuICAgICAgdmFsdWU6IDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVsYXlzIFB1cHBldGVlciBvcGVyYXRpb25zIGJ5IHRoZSBzcGVjaWZpZWQgbWlsbGlzZWNvbmRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IHVzZWQgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLy8gUHJvcGVydGllcyBuZXN0aW5nIGxldmVsIG9mIGFsbCBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRQcm9wcyA9IF9jcmVhdGVOZXN0ZWRQcm9wcyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIFByb3BlcnRpZXMgbmFtZXMgdGhhdCBzaG91bGQgbm90IGJlIHJlY3Vyc2l2ZWx5IG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IF9jcmVhdGVBYnNvbHV0ZVByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdlbmVyYXRlcyBhIG1hcHBpbmcgb2YgbmVzdGVkIGFyZ3VtZW50IGNoYWlucyBmcm9tIGEgbmVzdGVkXHJcbiAqIGNvbmZpZyBvYmplY3QuIFRoaXMgZnVuY3Rpb24gdHJhdmVyc2VzIGEgbmVzdGVkIG9iamVjdCBhbmQgY3JlYXRlcyBhIG1hcHBpbmdcclxuICogd2hlcmUgZWFjaCBrZXkgaXMgYW4gYXJndW1lbnQgbmFtZSAoZWl0aGVyIGZyb20gYGNsaU5hbWVgLCBgbGVnYWN5TmFtZWAsXHJcbiAqIG9yIHRoZSBvcmlnaW5hbCBrZXkpIGFuZCBlYWNoIHZhbHVlIGlzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY2hhaW5cclxuICogb2YgbmVzdGVkIHByb3BlcnRpZXMgbGVhZGluZyB0byB0aGF0IGFyZ3VtZW50LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbbmVzdGVkUHJvcHM9e31dIC0gVGhlIGFjY3VtdWxhdG9yIG9iamVjdCBmb3Igc3RvcmluZ1xyXG4gKiB0aGUgcmVzdWx0aW5nIGFyZ3VtZW50cyBjaGFpbnMuICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcHJvcENoYWluPScnXSAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzLFxyXG4gKiB1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbi4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gb2JqZWN0IG1hcHBpbmcgYXJndW1lbnQgbmFtZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZ1xyXG4gKiBuZXN0ZWQgcHJvcGVydHkgY2hhaW5zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzKGNvbmZpZywgbmVzdGVkUHJvcHMgPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgX2NyZWF0ZU5lc3RlZFByb3BzKGVudHJ5LCBuZXN0ZWRQcm9wcywgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBuZXN0ZWRQcm9wc1tlbnRyeS5jbGlOYW1lIHx8IGtleV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgaWYgKGVudHJ5LmxlZ2FjeU5hbWUgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2tleX1gLnN1YnN0cmluZygxKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIG9iamVjdCB3aXRoIG5lc3RlZCBhcmd1bWVudCBjaGFpbnNcclxuICByZXR1cm4gbmVzdGVkUHJvcHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSBnYXRoZXJzIHRoZSBuYW1lcyBvZiBwcm9wZXJ0aWVzIGZyb20gYSBjb25maWd1cmF0aW9uIG9iamVjdCB0aGF0XHJcbiAqIGNhbiBiZSB0cmVhdGVkIGFzIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZXNlIHByb3BlcnRpZXMgaGF2ZSB2YWx1ZXMgdGhhdFxyXG4gKiBhcmUgb2JqZWN0cyBhbmQgZG8gbm90IGNvbnRhaW4gZnVydGhlciBuZXN0ZWQgZGVwdGggd2hlbiBtZXJnaW5nIGFuIG9iamVjdFxyXG4gKiBjb250YWluaW5nIHRoZXNlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbYWJzb2x1dGVQcm9wcz1bXV0gLSBBbiBhcnJheSB0byBjb2xsZWN0IHRoZSBuYW1lc1xyXG4gKiBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxzdHJpbmc+fSBBbiBhcnJheSBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZVxyXG4gKiBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZUFic29sdXRlUHJvcHMoY29uZmlnLCBhYnNvbHV0ZVByb3BzID0gW10pIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudHlwZXMgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzXHJcbiAgICAgIF9jcmVhdGVBYnNvbHV0ZVByb3BzKGVudHJ5LCBhYnNvbHV0ZVByb3BzKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIHRoZSBvcHRpb24gY2FuIGJlIGFuIG9iamVjdCwgc2F2ZSBpdHMgdHlwZSBpbiB0aGUgYXJyYXlcclxuICAgICAgaWYgKGVudHJ5LnR5cGVzLmluY2x1ZGVzKCdPYmplY3QnKSkge1xyXG4gICAgICAgIGFic29sdXRlUHJvcHMucHVzaChrZXkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgYXJyYXkgd2l0aCB0aGUgbmFtZXMgb2YgYWJzb2x1dGUgcHJvcGVydGllc1xyXG4gIHJldHVybiBhYnNvbHV0ZVByb3BzO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRQcm9wcyxcclxuICBhYnNvbHV0ZVByb3BzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZFxyXG4gKiB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBgZW52c2AuIFdlIHNob3VsZCBub3QgdXNlIHRoZSBgcHJvY2Vzcy5lbnZgXHJcbiAqIGRpcmVjdGx5IGluIHRoZSBhcHBsaWNhdGlvbiBhcyB0aGVzZSB3b3VsZCBub3QgYmUgcGFyc2VkIHByb3Blcmx5LlxyXG4gKlxyXG4gKiBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSBwYXJzZWQgYW5kIHZhbGlkYXRlZCBvbmx5IG9uY2Ugd2hlblxyXG4gKiB0aGUgYXBwbGljYXRpb24gc3RhcnRzLiBXZSBzaG91bGQgd3JpdGUgYSBjdXN0b20gdmFsaWRhdG9yIG9yIGEgdHJhbnNmb3JtZXJcclxuICogZm9yIGVhY2ggb2YgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gcHVwcGV0ZWVyXHJcbiAgUFVQUEVURUVSX0FSR1M6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIGhpZ2hjaGFydHNcclxuICBISUdIQ0hBUlRTX1ZFUlNJT046IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PiAvXihsYXRlc3R8XFxkKyhcXC5cXGQrKXswLDJ9KSQvLnRlc3QodmFsdWUpIHx8IHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vJykgfHxcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcclxuICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgSElHSENIQVJUU19DRE5fVVJMLiBJdCBzaG91bGQgc3RhcnQgd2l0aCBodHRwOi8vIG9yIGh0dHBzOi8vLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTOiB2LmFycmF5KGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jb3JlU2NyaXB0cy52YWx1ZSksXHJcbiAgSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5tb2R1bGVTY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9JTkZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0lOU1RSOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9TVkc6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0JBVENIOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PVVRGSUxFOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9UWVBFOiB2LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLFxyXG4gIEVYUE9SVF9DT05TVFI6IHYuZW51bShbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddKSxcclxuICBFWFBPUlRfQjY0OiB2LmJvb2xlYW4oKSxcclxuICBFWFBPUlRfTk9fRE9XTkxPQUQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0dMT0JBTF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9USEVNRV9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19DVVNUT01fQ09ERTogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ0FMTEJBQ0s6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX1JFU09VUkNFUzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfTE9BRF9DT05GSUc6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUc6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlclxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1VQTE9BRF9MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgTE9HR0lOR19UT19GSUxFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gdWlcclxuICBVSV9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PREVfRU5WOiB2LmVudW0oWydkZXZlbG9wbWVudCcsICdwcm9kdWN0aW9uJywgJ3Rlc3QnXSksXHJcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0hBUkRfUkVTRVRfUEFHRTogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBlcnJvciBjbGFzcyBmb3IgaGFuZGxpbmcgZXhwb3J0LXJlbGF0ZWQgZXJyb3JzLiBFeHRlbmRzIHRoZSBuYXRpdmVcclxuICogYEVycm9yYCBjbGFzcyB0byBpbmNsdWRlIGFkZGl0aW9uYWwgcHJvcGVydGllcyBsaWtlIHN0YXR1cyBjb2RlIGFuZCBzdGFja1xyXG4gKiB0cmFjZSBkZXRhaWxzLlxyXG4gKi9cclxuY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gVGhlIGVycm9yIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkLlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gT3B0aW9uYWwgSFRUUCBzdGF0dXMgY29kZSBhc3NvY2lhdGVkXHJcbiAgICogd2l0aCB0aGUgZXJyb3IgKGUuZy4sIDQwMCwgNTAwKS5cclxuICAgKi9cclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXNDb2RlKSB7XHJcbiAgICBzdXBlcigpO1xyXG5cclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcblxyXG4gICAgaWYgKHN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgb3IgdXBkYXRlcyB0aGUgSFRUUCBzdGF0dXMgY29kZSBmb3IgdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBUaGUgSFRUUCBzdGF0dXMgY29kZSB0byBhc3NpZ24gdG8gdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgYWRkaXRpb25hbCBlcnJvciBkZXRhaWxzIGJhc2VkIG9uIGFuIGV4aXN0aW5nIGVycm9yIG9iamVjdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gQW4gZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgZGV0YWlscyB0byBwb3B1bGF0ZVxyXG4gICAqIHRoZSBgRXhwb3J0RXJyb3JgIGluc3RhbmNlLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG5cclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IE1hbmFnZXMgY29uZmlndXJhdGlvbiBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBieSBsb2FkaW5nXHJcbiAqIGFuZCBtZXJnaW5nIG9wdGlvbnMgZnJvbSBtdWx0aXBsZSBzb3VyY2VzLCBzdWNoIGFzIGRlZmF1bHQgc2V0dGluZ3MsXHJcbiAqIGVudmlyb25tZW50IHZhcmlhYmxlcywgdXNlci1wcm92aWRlZCBvcHRpb25zLCBhbmQgY29tbWFuZC1saW5lIGFyZ3VtZW50cy5cclxuICogRW5zdXJlcyB0aGUgZ2xvYmFsIG9wdGlvbnMgYXJlIHVwLXRvLWRhdGUgd2l0aCB0aGUgaGlnaGVzdCBwcmlvcml0eSB2YWx1ZXMuXHJcbiAqIFByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgYWNjZXNzaW5nIGFuZCB1cGRhdGluZyBjb25maWd1cmF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuL2VudnMuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGRlZXBDb3B5LCBnZXRBYnNvbHV0ZVBhdGgsIGlzT2JqZWN0IH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgeyBhYnNvbHV0ZVByb3BzLCBuZXN0ZWRQcm9wcywgZGVmYXVsdENvbmZpZyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0R2xvYmFsT3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIEFuIG9iamVjdCBmb3IgdGhlIGluc3RhbmNlIG9wdGlvbnMsIGNyZWF0ZWQgZWFjaCB0aW1lIHRoZSBgaW5pdEV4cG9ydGAgb2NjdXJzXHJcbmNvbnN0IGluc3RhbmNlT3B0aW9ucyA9IGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpO1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhIHJlZmVyZW5jZSB0byB0aGUgb3B0aW9ucyBvYmplY3QuIERlcGVuZGluZyBvbiB0aGUgYGdldEluc3RhbmNlYFxyXG4gKiBwYXJhbWV0ZXIsIGl0IHJldHVybnMgZWl0aGVyIHRoZSBnbG9iYWwgb3B0aW9ucyBvciB0aGUgaW5zdGFuY2Utc3BlY2lmaWNcclxuICogb3B0aW9ucyBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldEluc3RhbmNlPXRydWVdIC0gT3B0aW9uYWwgcGFyYW1ldGVyIHRoYXQgZGVjaWRlcyB3aGV0aGVyXHJcbiAqIHRvIHJldHVybiB0aGUgaW5zdGFuY2Utc3BlY2lmaWMgb3B0aW9ucyAod2hlbiBgdHJ1ZWApIG9yIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gKiAod2hlbiBgZmFsc2VgKS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYHRydWVgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBIHJlZmVyZW5jZSB0byBlaXRoZXIgdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9yIHRoZSBpbnN0YW5jZS1zcGVjaWZpYyBvcHRpb25zLCBiYXNlZCBvbiB0aGUgYGdldEluc3RhbmNlYCBwYXJhbWV0ZXIuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0T3B0aW9ucyhnZXRJbnN0YW5jZSA9IHRydWUpIHtcclxuICByZXR1cm4gZ2V0SW5zdGFuY2UgPyBpbnN0YW5jZU9wdGlvbnMgOiBnbG9iYWxPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgZ2xvYmFsIG9wdGlvbnMgb2YgdGhlIGV4cG9ydCBzZXJ2ZXIsIGtlZXBpbmcgdGhlIHByaW5jaXBsZVxyXG4gKiBvZiB0aGUgb3B0aW9ucyBsb2FkIHByaW9yaXR5IGZyb20gYWxsIGF2YWlsYWJsZSBzb3VyY2VzLiBJdCBhY2NlcHRzIG9wdGlvbmFsXHJcbiAqIGBjdXN0b21PcHRpb25zYCBvYmplY3QgYW5kIGBjbGlBcmdzYCBhcnJheSB3aXRoIGFyZ3VtZW50cyBmcm9tIHRoZSBDTEkuIFRoZXNlXHJcbiAqIG9wdGlvbnMgd2lsbCBiZSB2YWxpZGF0ZWQgYW5kIGFwcGxpZWQgaWYgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIFRoZSBwcmlvcml0eSBvcmRlciBvZiBzZXR0aW5nIHZhbHVlcyBpczpcclxuICpcclxuICogMS4gT3B0aW9ucyBmcm9tIHRoZSBgbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIChkZWZhdWx0IHZhbHVlcykuXHJcbiAqIDIuIE9wdGlvbnMgZnJvbSBhIGN1c3RvbSBKU09OIGZpbGUgKGxvYWRlZCBieSB0aGUgYGxvYWRDb25maWdgIG9wdGlvbikuXHJcbiAqIDMuIE9wdGlvbnMgZnJvbSB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzICh0aGUgYC5lbnZgIGZpbGUpLlxyXG4gKiA0LiBPcHRpb25zIGZyb20gdGhlIGNvbW1hbmQgbGluZSBpbnRlcmZhY2UgKENMSSkuXHJcbiAqIDUuIE9wdGlvbnMgZnJvbSB0aGUgZmlyc3QgcGFyYW1ldGVyICh0aGUgYGN1c3RvbU9wdGlvbnNgIGlzIGJ5IGRlZmF1bHRcclxuICogYW4gZW1wdHkgb2JqZWN0KS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldEdsb2JhbE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IFtjdXN0b21PcHRpb25zPXt9XSAtIE9wdGlvbmFsIGN1c3RvbSBvcHRpb25zIGZvciBhZGRpdGlvbmFsXHJcbiAqIGNvbmZpZ3VyYXRpb24uIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gW2NsaUFyZ3M9W11dIC0gT3B0aW9uYWwgY29tbWFuZCBsaW5lIGFyZ3VtZW50c1xyXG4gKiBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIHVwZGF0ZWQgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCByZWZsZWN0aW5nIHRoZSBtZXJnZWRcclxuICogY29uZmlndXJhdGlvbiBmcm9tIGFsbCBhdmFpbGFibGUgc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRHbG9iYWxPcHRpb25zKGN1c3RvbU9wdGlvbnMgPSB7fSwgY2xpQXJncyA9IFtdKSB7XHJcbiAgLy8gT2JqZWN0IGZvciBvcHRpb25zIGxvYWRlZCB2aWEgdGhlIGBsb2FkQ29uZmlnYCBvcHRpb25cclxuICBsZXQgY29uZmlnT3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBPYmplY3QgZm9yIG9wdGlvbnMgZnJvbSB0aGUgQ0xJXHJcbiAgbGV0IGNsaU9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzICYmIEFycmF5LmlzQXJyYXkoY2xpQXJncykgJiYgY2xpQXJncy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGxvYWRlZCB2aWEgdGhlIGBsb2FkQ29uZmlnYFxyXG4gICAgY29uZmlnT3B0aW9ucyA9IF9sb2FkQ29uZmlnRmlsZShjbGlBcmdzLCBnbG9iYWxPcHRpb25zLmN1c3RvbUxvZ2ljKTtcclxuXHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBDTElcclxuICAgIGNsaU9wdGlvbnMgPSBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpO1xyXG4gIH1cclxuXHJcbiAgLy8gVXBkYXRlIHZhbHVlcyBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCB2YWx1ZXMgZnJvbSBlYWNoIHNvdXJjZSBwb3NzaWJsZVxyXG4gIF91cGRhdGVHbG9iYWxPcHRpb25zKFxyXG4gICAgZGVmYXVsdENvbmZpZyxcclxuICAgIGdsb2JhbE9wdGlvbnMsXHJcbiAgICBjb25maWdPcHRpb25zLFxyXG4gICAgY2xpT3B0aW9ucyxcclxuICAgIGN1c3RvbU9wdGlvbnNcclxuICApO1xyXG5cclxuICAvLyBSZXR1cm4gdXBkYXRlZCBnbG9iYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnbG9iYWxPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgaW5zdGFuY2Ugb3B0aW9ucyB3aXRoIGFkZGl0aW9uYWwgb3B0aW9ucy4gSXQgb3B0aW9uYWxseSBhbGxvd3NcclxuICogdG8gcmVpbml0aWFsaXplIHRoZSBpbnN0YW5jZSBvcHRpb25zIHdpdGggdGhlIHZhbHVlcyBvZiBhIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gdXBkYXRlT3B0aW9ucyAtIFRoZSB1cGRhdGUgb3B0aW9ucyB0byBtZXJnZSBpbnRvIHRoZSBpbnN0YW5jZVxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtuZXdJbnN0YW5jZT1mYWxzZV0gLSBBIGZsYWcgdG8gaW5kaWNhdGUgd2hldGhlciB0byBpbml0XHJcbiAqIG9wdGlvbnMgZm9yIGEgbmV3IGluc3RhbmNlLiBJZiBgdHJ1ZWAsIHRoZSBleGlzdGluZyBpbnN0YW5jZSBvcHRpb25zIHdpbGxcclxuICogYmUgY2xlYXJlZCBhbmQgcmVpbml0aWFsaXplZCBiYXNlZCBvbiB0aGUgZ2xvYmFsIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IC0gVGhlIHVwZGF0ZWQgaW5zdGFuY2Ugb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVPcHRpb25zKHVwZGF0ZU9wdGlvbnMsIG5ld0luc3RhbmNlID0gZmFsc2UpIHtcclxuICAvLyBDaGVjayBpZiBvcHRpb25zIG5lZWQgdG8gYmUgY3JlYXRlZCBmb3IgYSBuZXcgaW5zdGFuY2VcclxuICBpZiAobmV3SW5zdGFuY2UpIHtcclxuICAgIC8vIEdldCByaWQgb2YgdGhlIG9sZCBpbnN0YW5jZSBvcHRpb25zXHJcbiAgICBPYmplY3Qua2V5cyhpbnN0YW5jZU9wdGlvbnMpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgICBkZWxldGUgaW5zdGFuY2VPcHRpb25zW2tleV07XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBJbml0IHRoZSBuZXcgaW5zdGFuY2Ugb3B0aW9ucyBiYXNlZCBvbiB0aGUgZ2xvYmFsIG9wdGlvbnNcclxuICAgIG1lcmdlT3B0aW9ucyhpbnN0YW5jZU9wdGlvbnMsIGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpKTtcclxuICB9XHJcblxyXG4gIC8vIE1lcmdlIGFkZGl0aW9uYWwgb3B0aW9ucyB0byB0aGUgaW5zdGFuY2Ugb3B0aW9uc1xyXG4gIG1lcmdlT3B0aW9ucyhpbnN0YW5jZU9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMpO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIHJlZmVyZW5jZSB0byB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICByZXR1cm4gaW5zdGFuY2VPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogTWVyZ2VzIHR3byBzZXRzIG9mIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgY29uc2lkZXJpbmcgYWJzb2x1dGUgcHJvcGVydGllcy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1lcmdlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3JpZ2luYWxPcHRpb25zIC0gT3JpZ2luYWwgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIE5ldyBjb25maWd1cmF0aW9uIG9wdGlvbnMgdG8gYmUgbWVyZ2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBNZXJnZWQgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlT3B0aW9ucyhvcmlnaW5hbE9wdGlvbnMsIG5ld09wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgYG9yaWdpbmFsT3B0aW9uc2AgYW5kIGBuZXdPcHRpb25zYCBhcmUgY29ycmVjdCBvYmplY3RzXHJcbiAgaWYgKGlzT2JqZWN0KG9yaWdpbmFsT3B0aW9ucykgJiYgaXNPYmplY3QobmV3T3B0aW9ucykpIHtcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICAgIG9yaWdpbmFsT3B0aW9uc1trZXldID1cclxuICAgICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXHJcbiAgICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgPyBtZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zW2tleV0sIHZhbHVlKVxyXG4gICAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgICAgOiBvcmlnaW5hbE9wdGlvbnNba2V5XSB8fCBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBvcmlnaW5hbCAobW9kaWZpZWQgb3Igbm90KSBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG9sZE9wdGlvbnMpKSB7XHJcbiAgICAvLyBJdGVyYXRlIG92ZXIgZWFjaCBrZXktdmFsdWUgcGFpciBpbiB0aGUgb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgdGhlcmUgaXMgYSBuZXN0ZWQgbWFwcGluZywgc3BsaXQgaXQgaW50byBhIHByb3BlcnRpZXMgY2hhaW5cclxuICAgICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNba2V5XVxyXG4gICAgICAgID8gbmVzdGVkUHJvcHNba2V5XS5zcGxpdCgnLicpXHJcbiAgICAgICAgOiBbXTtcclxuXHJcbiAgICAgIC8vIElmIGl0IGlzIHRoZSBsYXN0IHByb3BlcnR5IGluIHRoZSBjaGFpbiwgYXNzaWduIHRoZSB2YWx1ZSwgb3RoZXJ3aXNlLFxyXG4gICAgICAvLyBjcmVhdGUgb3IgcmV1c2UgdGhlIG5lc3RlZCBvYmplY3RcclxuICAgICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cclxuICAgICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgICAgbmV3T3B0aW9uc1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgICdbY29uZmlnXSBObyBjb3JyZWN0IG9iamVjdCB3aXRoIG9wdGlvbnMgd2FzIHByb3ZpZGVkLiBSZXR1cm5pbmcgYW4gZW1wdHkgYXJyYXkuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgbmV3LCBzdHJ1Y3R1cmVkIG9wdGlvbnMgb2JqZWN0XHJcbiAgcmV0dXJuIG5ld09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBWYWxpZGF0ZXMsIHBhcnNlcywgYW5kIGNoZWNrcyBpZiB0aGUgcHJvdmlkZWQgY29uZmlnIGlzIGFsbG93ZWQgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0FsbG93ZWRDb25maWdcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBjb25maWcgLSBUaGUgY29uZmlnIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkIGFzIGEgc2V0XHJcbiAqIG9mIG9wdGlvbnMuIE11c3QgYmUgZWl0aGVyIGFuIG9iamVjdCBvciBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBbdG9TdHJpbmc9ZmFsc2VdIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCB2ZXJzaW9uXHJcbiAqIG9mIHRoZSBwYXJzZWQgY29uZmlnLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFthbGxvd0Z1bmN0aW9ucz1mYWxzZV0gLSBXaGV0aGVyIHRvIGFsbG93IGZ1bmN0aW9uc1xyXG4gKiBpbiB0aGUgcGFyc2VkIGNvbmZpZy4gSWYgdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWQuIE90aGVyd2lzZSwgd2hlblxyXG4gKiBhIGZ1bmN0aW9uIGlzIGZvdW5kLCBudWxsIGlzIHJldHVybmVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFZlcnNpb24obm9Mb2dvID0gZmFsc2UpIHtcclxuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGAuZW52YCBvciBmcm9tIGBwYWNrYWdlLmpzb25gXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuICApLnZlcnNpb247XHJcblxyXG4gIC8vIFByaW50IHRleHQgb25seVxyXG4gIGlmIChub0xvZ28pIHtcclxuICAgIGNvbnNvbGUubG9nKGBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259YCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFByaW50IHRoZSBsb2dvXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAnbXNnJywgJ3N0YXJ0dXAubXNnJyksICd1dGY4JykudG9TdHJpbmcoKVxyXG4gICAgICAgIC5ib2xkLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyBnbG9iYWwgb3B0aW9ucyBvYmplY3QgYmFzZWQgb24gcHJvdmlkZWRcclxuICogY29uZmlndXJhdGlvbiwgc2V0dGluZyB2YWx1ZXMgZnJvbSBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9pbml0R2xvYmFsT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvIGJlIHVzZWQgZm9yIGluaXRpYWxpemluZ1xyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBJbml0aWFsaXplZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmZ1bmN0aW9uIF9pbml0R2xvYmFsT3B0aW9ucyhjb25maWcpIHtcclxuICBjb25zdCBvcHRpb25zID0ge307XHJcblxyXG4gIC8vIFN0YXJ0IGluaXRpYWxpemluZyB0aGUgYG9wdGlvbnNgIG9iamVjdCByZWN1cnNpdmVseVxyXG4gIGZvciAoY29uc3QgW25hbWUsIGl0ZW1dIG9mIE9iamVjdC5lbnRyaWVzKGNvbmZpZykpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoaXRlbSwgJ3ZhbHVlJykpIHtcclxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcyBleGlzdHMsIGl0IHRha2VzIHByZWNlZGVuY2VcclxuICAgICAgY29uc3QgZW52VmFsID0gZW52c1tpdGVtLmVudkxpbmtdO1xyXG4gICAgICBpZiAoZW52VmFsICE9PSB1bmRlZmluZWQgJiYgZW52VmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1tuYW1lXSA9IGVudlZhbDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gaXRlbS52YWx1ZTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgb3B0aW9uc1tuYW1lXSA9IF9pbml0R2xvYmFsT3B0aW9ucyhpdGVtKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY3JlYXRlZCBgb3B0aW9uc2Agb2JqZWN0XHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIGdsb2JhbCBvcHRpb25zIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIHZhcmlvdXMgc291cmNlcywgZm9sbG93aW5nXHJcbiAqIGEgc3BlY2lmaWMgcHJpb3JpdGl6YXRpb24gb3JkZXIuIFRoZSBmdW5jdGlvbiBjaGVja3MgZm9yIHZhbHVlcyBpbiB0aGUgb3JkZXJcclxuICogb2YgcHJlY2VkZW5jZTogdGhlIGBsb2FkQ29uZmlnYCBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGVudmlyb25tZW50IHZhcmlhYmxlcyxcclxuICogY3VzdG9tIG9wdGlvbnMsIGFuZCBDTEkgb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIF91cGRhdGVHbG9iYWxPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QsIHdoaWNoIGluY2x1ZGVzIHRoZSBpbml0aWFsXHJcbiAqIHNldHRpbmdzIGFuZCBtZXRhZGF0YSBmb3IgZWFjaCBvcHRpb24uIFRoaXMgb2JqZWN0IGlzIHVzZWQgdG8gZGV0ZXJtaW5lXHJcbiAqIHRoZSBzdHJ1Y3R1cmUgYW5kIGRlZmF1bHQgdmFsdWVzIGZvciB0aGUgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IHRoYXQgd2lsbCBiZSB1cGRhdGVkXHJcbiAqIHdpdGggdmFsdWVzIGZyb20gb3RoZXIgc291cmNlcy5cclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZ09wdCAtIFRoZSBjb25maWd1cmF0aW9uIG9wdGlvbnMgb2JqZWN0LCBsb2FkZWQgd2l0aFxyXG4gKiB0aGUgYGxvYWRDb25maWdgIG9wdGlvbiwgd2hpY2ggbWF5IHByb3ZpZGUgdmFsdWVzIHRvIG92ZXJyaWRlIGRlZmF1bHRzLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2xpT3B0IC0gVGhlIENMSSBvcHRpb25zIG9iamVjdCwgd2hpY2ggbWF5IGluY2x1ZGUgdmFsdWVzXHJcbiAqIHByb3ZpZGVkIHRocm91Z2ggY29tbWFuZC1saW5lIGFyZ3VtZW50cyBhbmQgbWF5IG92ZXJyaWRlIGNvbmZpZ3VyYXRpb25cclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbU9wdCAtIFRoZSBjdXN0b20gb3B0aW9ucyBvYmplY3QsIHR5cGljYWxseSBjb250YWluaW5nXHJcbiAqIGFkZGl0aW9uYWwgYW5kIHVzZXItZGVmaW5lZCB2YWx1ZXMsIHdoaWNoIGhhcyB0aGUgaGlnaGVzdCBwcmVjZWRlbmNlIGFtb25nXHJcbiAqIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfdXBkYXRlR2xvYmFsT3B0aW9ucyhjb25maWcsIG9wdGlvbnMsIGNvbmZpZ09wdCwgY2xpT3B0LCBjdXN0b21PcHQpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBjb25maWcgZW50cnkgb2YgYSBzcGVjaWZpYyBvcHRpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gR2F0aGVyIHZhbHVlcyBmb3IgdGhlIG9wdGlvbnMgZnJvbSBldmVyeSBwb3NzaWJsZSBzb3VyY2UsIGlmIGV4aXN0c1xyXG4gICAgY29uc3QgY29uZmlnVmFsID0gY29uZmlnT3B0ICYmIGNvbmZpZ09wdFtrZXldO1xyXG4gICAgY29uc3QgY2xpVmFsID0gY2xpT3B0ICYmIGNsaU9wdFtrZXldO1xyXG4gICAgY29uc3QgY3VzdG9tVmFsID0gY3VzdG9tT3B0ICYmIGN1c3RvbU9wdFtrZXldO1xyXG5cclxuICAgIC8vIElmIHRoZSB2YWx1ZSBub3QgZm91bmQsIG5lZWQgdG8gZ28gZGVlcGVyXHJcbiAgICBpZiAodHlwZW9mIGVudHJ5LnZhbHVlID09PSAndW5kZWZpbmVkJykge1xyXG4gICAgICBfdXBkYXRlR2xvYmFsT3B0aW9ucyhlbnRyeSwgb3B0aW9uc1trZXldLCBjb25maWdWYWwsIGNsaVZhbCwgY3VzdG9tVmFsKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIGEgdmFsdWUgZnJvbSBjdXN0b20gSlNPTiBvcHRpb25zIGV4aXN0cywgaXQgdGFrZXMgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY29uZmlnVmFsICE9PSB1bmRlZmluZWQgJiYgY29uZmlnVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY29uZmlnVmFsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzIGV4aXN0cywgaXQgdGFrZXMgcHJlY2VkZW5jZVxyXG4gICAgICBjb25zdCBlbnZWYWwgPSBlbnZzW2VudHJ5LmVudkxpbmtdO1xyXG4gICAgICBpZiAoZW50cnkuZW52TGluayBpbiBlbnZzICYmIGVudlZhbCAhPT0gdW5kZWZpbmVkICYmIGVudlZhbCAhPT0gbnVsbCkge1xyXG4gICAgICAgIG9wdGlvbnNba2V5XSA9IGVudlZhbDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIENMSSBvcHRpb25zIGV4aXN0cywgaXQgdGFrZXMgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY2xpVmFsICE9PSB1bmRlZmluZWQgJiYgY2xpVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY2xpVmFsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gdXNlciBvcHRpb25zIGV4aXN0cywgaXQgdGFrZXMgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY3VzdG9tVmFsICE9PSB1bmRlZmluZWQgJiYgY3VzdG9tVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY3VzdG9tVmFsO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmdcclxuICogd2l0aCB0aGUgb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy4gSW4gb3JkZXIgZm9yIGEgZnVuY3Rpb25cclxuICogdG8gYmUgcHJlc2VydmVkLCBpdCBuZWVkcyB0byBmb2xsb3cgdGhlIGZvcm1hdCBgZnVuY3Rpb24gKC4uLikgey4uLn1gLlxyXG4gKiBTdWNoIGEgZnVuY3Rpb24gY2FuIGFsc28gYmUgc3RyaW5naWZpZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWRcclxuICogaW4gdGhlIG91dHB1dC4gT3RoZXJ3aXNlIGFuIGVycm9yIGlzIHRocm93bi5cclxuICogQHBhcmFtIHtib29sZWFufSBzdHJpbmdpZnlGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBzYXZlZFxyXG4gKiBhcyBzdHJpbmdzLiBUaGUgYGFsbG93RnVuY3Rpb25zYCBtdXN0IGJlIHNldCB0byB0cnVlIGFzIHdlbGwgZm9yIHRoaXMgdG8gdGFrZVxyXG4gKiBhbiBlZmZlY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgd2hlbiBmdW5jdGlvbnMgYXJlIG5vdCBhbGxvd2VkIGJ1dCBhcmVcclxuICogZm91bmQgaW4gcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX29wdGlvbnNTdHJpbmdpZnkob3B0aW9ucywgYWxsb3dGdW5jdGlvbnMsIHN0cmluZ2lmeUZ1bmN0aW9ucykge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAoXywgdmFsdWUpID0+IHtcclxuICAgIC8vIFRyaW0gc3RyaW5nIHZhbHVlc1xyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgdmFsdWUgaXMgYSBmdW5jdGlvbiBvciBzdHJpbmdpZmllZCBmdW5jdGlvblxyXG4gICAgaWYgKFxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgfHxcclxuICAgICAgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpICYmXHJcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBJZiBhbGxvd0Z1bmN0aW9ucyBpcyBzZXQgdG8gdHJ1ZSwgcHJlc2VydmUgZnVuY3Rpb25zXHJcbiAgICAgIGlmIChhbGxvd0Z1bmN0aW9ucykge1xyXG4gICAgICAgIC8vIEJhc2VkIG9uIHRoZSBgc3RyaW5naWZ5RnVuY3Rpb25zYCBvcHRpb25zLCBzZXQgZnVuY3Rpb24gdmFsdWVzXHJcbiAgICAgICAgcmV0dXJuIHN0cmluZ2lmeUZ1bmN0aW9uc1xyXG4gICAgICAgICAgPyAvLyBBcyBzdHJpbmdpZmllZCBmdW5jdGlvbnNcclxuICAgICAgICAgICAgYFwiRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOXCJgXHJcbiAgICAgICAgICA6IC8vIEFzIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOYDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaHJvdyBhbiBlcnJvciBvdGhlcndpc2VcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgc2ltcGx5IHJldHVybiB0aGUgdmFsdWVcclxuICAgIHJldHVybiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIHN0cmluZ2lmeUZ1bmN0aW9ucyA/IC9cXFxcXCJFWFBfRlVOfEVYUF9GVU5cXFxcXCIvZyA6IC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHByb3ZpZGVkIHZpYVxyXG4gKiB0aGUgYGxvYWRDb25maWdgIG9wdGlvbiBpbiB0aGUgY29tbWFuZC1saW5lIGFyZ3VtZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2FkQ29uZmlnRmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjbGlBcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyB0byBzZWFyY2hcclxuICogZm9yIHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGBsb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGBsb2FkQ29uZmlnYCBvcHRpb24gdmFsdWVcclxuICBjb25zdCBjb25maWdGaWxlTmFtZSA9IGNvbmZpZ0luZGV4ID4gLTEgJiYgY2xpQXJnc1tjb25maWdJbmRleCArIDFdO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgYGxvYWRDb25maWdgIGlzIHByZXNlbnQgYW5kIGhhcyBhIGNvcnJlY3QgdmFsdWVcclxuICBpZiAoY29uZmlnRmlsZU5hbWUgJiYgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICByZXR1cm4gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGVycm9yLFxyXG4gICAgICAgIGBbY29uZmlnXSBVbmFibGUgdG8gbG9hZCB0aGUgY29uZmlndXJhdGlvbiBmcm9tIHRoZSAke2NvbmZpZ0ZpbGVOYW1lfSBmaWxlLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIE5vIGFkZGl0aW9uYWwgb3B0aW9ucyB0byByZXR1cm5cclxuICByZXR1cm4ge307XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQYXJzZXMgY29tbWFuZC1saW5lIGFyZ3VtZW50cyBhbmQgcGFpcnMgZWFjaCBhcmd1bWVudCB3aXRoIGl0cyBjb3JyZXNwb25kaW5nXHJcbiAqIG9wdGlvbiBpbiB0aGUgY29uZmlndXJhdGlvbi4gVGhlIHZhbHVlcyBhcmUgc3RydWN0dXJlZCBpbnRvIGEgbmVzdGVkIG9wdGlvbnNcclxuICogb2JqZWN0LCBiYXNlZCBvbiBwcmVkZWZpbmVkIG1hcHBpbmdzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3BhaXJBcmd1bWVudFZhbHVlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG5lc3RlZFByb3BzIC0gQW4gYXJyYXkgb2YgbmVzdGluZyBsZXZlbCBmb3IgYWxsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBBbiBhcnJheSBvZiBjb21tYW5kLWxpbmUgYXJndW1lbnRzXHJcbiAqIGNvbnRhaW5pbmcgb3B0aW9ucyBhbmQgdGhlaXIgYXNzb2NpYXRlZCB2YWx1ZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFuIHVwZGF0ZWQgb3B0aW9ucyBvYmplY3Qgd2hlcmUgZWFjaCBvcHRpb24gZnJvbVxyXG4gKiB0aGUgY29tbWFuZC1saW5lIGlzIHBhaXJlZCB3aXRoIGl0cyB2YWx1ZSwgc3RydWN0dXJlZCBpbnRvIG5lc3RlZCBvYmplY3RzXHJcbiAqIGFzIGRlZmluZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpIHtcclxuICAvLyBBbiBlbXB0eSBvYmplY3QgdG8gY29sbGVjdCBhbmQgc3RydWN0dXJpemUgZGF0YSBmcm9tIHRoZSBhcmdzXHJcbiAgY29uc3QgY2xpT3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDeWNsZSB0aHJvdWdoIGFsbCBDTEkgYXJncyBhbmQgZmlsdGVyIHRoZW1cclxuICBmb3IgKGxldCBpID0gMDsgaSA8IGNsaUFyZ3MubGVuZ3RoOyBpKyspIHtcclxuICAgIGNvbnN0IG9wdGlvbiA9IGNsaUFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW29wdGlvbl1cclxuICAgICAgPyBuZXN0ZWRQcm9wc1tvcHRpb25dLnNwbGl0KCcuJylcclxuICAgICAgOiBbXTtcclxuXHJcbiAgICAvLyBDcmVhdGUgb3B0aW9ucyBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSBDTEkgZm9yIGxhdGVyIHBhcnNpbmcgYW5kIG1lcmdpbmdcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcclxuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xyXG4gICAgICAgIGNvbnN0IHZhbHVlID0gY2xpQXJnc1srK2ldO1xyXG4gICAgICAgIGlmICghdmFsdWUpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSBDTEkgJy0tJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgb2JqW3Byb3BdID0gdmFsdWUgfHwgbnVsbDtcclxuICAgICAgfSBlbHNlIGlmIChvYmpbcHJvcF0gPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG9ialtwcm9wXSA9IHt9O1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBvYmpbcHJvcF07XHJcbiAgICB9LCBjbGlPcHRpb25zKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBwYXJzZWQgQ0xJIG9wdGlvbnNcclxuICByZXR1cm4gY2xpT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IHRyYXZlcnNlcyB0aGUgb3B0aW9ucyBvYmplY3QgdG8gcHJpbnQgdGhlIHVzYWdlIGluZm9ybWF0aW9uXHJcbiAqIGZvciBlYWNoIG9wdGlvbiBjYXRlZ29yeSBhbmQgaW5kaXZpZHVhbCBvcHRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3ljbGVDYXRlZ29yaWVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgQ0xJIG9wdGlvbnMuIEl0IG1heVxyXG4gKiBpbmNsdWRlIG5lc3RlZCBjYXRlZ29yaWVzIGFuZCBpbmRpdmlkdWFsIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3ljbGVDYXRlZ29yaWVzKG9wdGlvbnMpIHtcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKG9wdGlvbnMpKSB7XHJcbiAgICAvLyBJZiB0aGUgY3VycmVudCBlbnRyeSBpcyBhIGNhdGVnb3J5IGFuZCBub3QgYSBsZWFmIG9wdGlvbiwgcmVjdXJzZSBpbnRvIGl0XHJcbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvcHRpb24sICd2YWx1ZScpKSB7XHJcbiAgICAgIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9uKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIFByZXBhcmUgZGVzY3JpcHRpb25cclxuICAgICAgY29uc3QgZGVzY05hbWUgPSBgIC0tJHtvcHRpb24uY2xpTmFtZSB8fCBuYW1lfWA7XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIHZhbHVlXHJcbiAgICAgIGxldCBvcHRpb25WYWx1ZSA9IG9wdGlvbi52YWx1ZTtcclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhcnJheSBvZiBzdHJpbmdzXHJcbiAgICAgIGlmIChvcHRpb25WYWx1ZSAhPT0gbnVsbCAmJiBvcHRpb24udHlwZXMuaW5jbHVkZXMoJ3N0cmluZ1tdJykpIHtcclxuICAgICAgICBvcHRpb25WYWx1ZSA9XHJcbiAgICAgICAgICAnWycgKyBvcHRpb25WYWx1ZS5tYXAoKGl0ZW0pID0+IGAnJHtpdGVtfSdgKS5qb2luKCcsICcpICsgJ10nO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBQcmVwYXJlIHZhbHVlIGZvciBvcHRpb24gdGhhdCBpcyBub3QgbnVsbCBhbmQgaXMgYSBzdHJpbmdcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nJykpIHtcclxuICAgICAgICBvcHRpb25WYWx1ZSA9IGAnJHtvcHRpb25WYWx1ZX0nYDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRGlzcGxheSBjb3JyZWN0bHkgYWxpZ25lZCBtZXNzYWdlc1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICBkZXNjTmFtZS5ncmVlbixcclxuICAgICAgICBgJHsoJzwnICsgb3B0aW9uLnR5cGVzLmpvaW4oJ3wnKSArICc+JykueWVsbG93fWAsXHJcbiAgICAgICAgYCR7U3RyaW5nKG9wdGlvblZhbHVlKS5ib2xkfWAuYmx1ZSxcclxuICAgICAgICBgLSAke29wdGlvbi5kZXNjcmlwdGlvbn0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgc2V0R2xvYmFsT3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIG1lcmdlT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcbiAgaXNBbGxvd2VkQ29uZmlnLFxyXG4gIHByaW50TGljZW5zZSxcclxuICBwcmludFVzYWdlLFxyXG4gIHByaW50VmVyc2lvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgSFRUUCB1dGlsaXR5IG1vZHVsZSBmb3IgZmV0Y2hpbmcgYW5kIHBvc3RpbmcgZGF0YS4gU3VwcG9ydHMgYm90aFxyXG4gKiBIVFRQIGFuZCBIVFRQUyBwcm90b2NvbHMsIHByb3ZpZGluZyBtZXRob2RzIHRvIG1ha2UgR0VUIGFuZCBQT1NUIHJlcXVlc3RzXHJcbiAqIHdpdGggY3VzdG9taXphYmxlIG9wdGlvbnMuIEluY2x1ZGVzIHByb3RvY29sIGRldGVybWluYXRpb24gYmFzZWQgb24gVVJMXHJcbiAqIGFuZCBhdWdtZW50cyByZXNwb25zZSBvYmplY3RzIHdpdGggYSAndGV4dCcgcHJvcGVydHkgZm9yIGVhc2llciBkYXRhIGFjY2Vzcy5cclxuICovXHJcblxyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGRhdGEgZnJvbSB0aGUgc3BlY2lmaWVkIFVSTCB1c2luZyBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBmZXRjaFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBmZXRjaCBkYXRhIGZyb20uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmV0Y2godXJsLCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpXHJcbiAgICAgIC5nZXQodXJsLCByZXF1ZXN0T3B0aW9ucywgKHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2VEYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFyZXNwb25zZURhdGEpIHtcclxuICAgICAgICAgICAgcmVqZWN0KCdOb3RoaW5nIHdhcyBmZXRjaGVkIGZyb20gdGhlIFVSTC4nKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogU2VuZHMgYSBQT1NUIHJlcXVlc3QgdG8gdGhlIHNwZWNpZmllZCBVUkwgd2l0aCB0aGUgcHJvdmlkZWQgSlNPTiBib2R5IHVzaW5nXHJcbiAqIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHBvc3RcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gc2VuZCB0aGUgUE9TVCByZXF1ZXN0IHRvLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2JvZHk9e31dIC0gVGhlIEpTT04gYm9keSB0byBpbmNsdWRlIGluIHRoZSBQT1NUIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtyZXF1ZXN0T3B0aW9ucz17fV0gLSBPcHRpb25zIGZvciB0aGUgSFRUUC9IVFRQUyByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBIVFRQL0hUVFBTIHJlc3BvbnNlXHJcbiAqIG9iamVjdCB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0KHVybCwgYm9keSA9IHt9LCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIGNvbnN0IGRhdGEgPSBKU09OLnN0cmluZ2lmeShib2R5KTtcclxuXHJcbiAgICAvLyBTZXQgZGVmYXVsdCBoZWFkZXJzIGFuZCBtZXJnZSB3aXRoIHJlcXVlc3RPcHRpb25zXHJcbiAgICBjb25zdCBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihcclxuICAgICAge1xyXG4gICAgICAgIG1ldGhvZDogJ1BPU1QnLFxyXG4gICAgICAgIGhlYWRlcnM6IHtcclxuICAgICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXHJcbiAgICAgICAgICAnQ29udGVudC1MZW5ndGgnOiBkYXRhLmxlbmd0aFxyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcmVxdWVzdE9wdGlvbnNcclxuICAgICk7XHJcblxyXG4gICAgY29uc3QgcmVxdWVzdCA9IF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpXHJcbiAgICAgIC5yZXF1ZXN0KHVybCwgb3B0aW9ucywgKHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2VEYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgLy8gV3JpdGUgdGhlIHJlcXVlc3QgYm9keSBhbmQgZW5kIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LndyaXRlKGRhdGEpO1xyXG4gICAgcmVxdWVzdC5lbmQoKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgdGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBVUkwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZGV0ZXJtaW5lIHRoZSBwcm90b2NvbC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIChodHRwIG9yIGh0dHBzKS5cclxuICovXHJcbmZ1bmN0aW9uIF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpIHtcclxuICByZXR1cm4gdXJsLnN0YXJ0c1dpdGgoJ2h0dHBzJykgPyBodHRwcyA6IGh0dHA7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBmZXRjaCxcclxuICBwb3N0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgY2FjaGUgbWFuYWdlciBpcyByZXNwb25zaWJsZSBmb3IgaGFuZGxpbmcgYW5kIG1hbmFnaW5nXHJcbiAqIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYWxvbmcgd2l0aCBpdHMgZGVwZW5kZW5jaWVzLiBJdCBlbnN1cmVzIHRoYXQgdGhlc2VcclxuICogcmVzb3VyY2VzIGFyZSBzdG9yZWQgYW5kIHJldHJpZXZlZCBlZmZpY2llbnRseSB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZVxyXG4gKiBhbmQgcmVkdWNlIHJlZHVuZGFudCBuZXR3b3JrIHJlcXVlc3RzLiBUaGUgY2FjaGUgaXMgc3RvcmVkIGluIHRoZSBgLmNhY2hlYFxyXG4gKiBkaXJlY3RvcnkgYnkgZGVmYXVsdCwgd2hpY2ggc2VydmVzIGFzIGEgZGVkaWNhdGVkIGZvbGRlciBmb3Iga2VlcGluZyBjYWNoZWRcclxuICogZmlsZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IEh0dHBzUHJveHlBZ2VudCB9IGZyb20gJ2h0dHBzLXByb3h5LWFnZW50JztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGZldGNoIH0gZnJvbSAnLi9mZXRjaC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnNcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNhY2hlIHBhdGhcclxuICAgIGNvbnN0IGNhY2hlUGF0aCA9IGdldENhY2hlUGF0aCgpO1xyXG5cclxuICAgIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgICBjb25zdCBtYW5pZmVzdFBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKTtcclxuICAgIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ3NvdXJjZXMuanMnKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcclxuXHJcbiAgICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIHRoZSBgbWFuaWZlc3QuanNvbmAgZG9lcyBub3QgZXhpc3RcclxuICAgIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICAgIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGhpZ2hjaGFydHNPcHRpb25zLmZvcmNlRmV0Y2gpIHtcclxuICAgICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAgIC8vIFJlYWQgdGhlIG1hbmlmZXN0IEpTT05cclxuICAgICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpLCAndXRmOCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG1vZHVsZXMgaXMgYW4gYXJyYXksIGlmIHNvLCB3ZSByZXdyaXRlIGl0IHRvIGEgbWFwIHRvIG1ha2VcclxuICAgICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIGFjdHVhbCBudW1iZXIgb2Ygc2NyaXB0cyB0byBiZSBmZXRjaGVkXHJcbiAgICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9XHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnM7XHJcbiAgICAgIGNvbnN0IG51bWJlck9mTW9kdWxlcyA9XHJcbiAgICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcclxuICAgICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24pIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAgIE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzXHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgX3VwZGF0ZUNhY2hlKFxyXG4gICAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgICBzb3VyY2VQYXRoXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xyXG5cclxuICAgICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEZpbmFsbHksIHNhdmUgdGhlIG5ldyBtYW5pZmVzdCwgd2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjdXJyZW50IGNvbmZpZ1xyXG4gICAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgICBhd2FpdCBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY29uZmlndXJlIGNhY2hlIGFuZCBjcmVhdGUgb3IgdXBkYXRlIHRoZSBjb25maWcgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIEdldCB0aGUgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyB0byB1cGRhdGUgdG8gdGhlIG5ldyB2ZXJzaW9uXHJcbiAgY29uc3Qgb3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRvIHRoZSBuZXcgdmVyc2lvblxyXG4gIG9wdGlvbnMuaGlnaGNoYXJ0cy52ZXJzaW9uID0gbmV3VmVyc2lvbjtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgsICd1dGY4Jyk7IC8vICM1NjJcclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBgZmV0Y2hlZE1vZHVsZXNgIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxyXG4gKiB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxyXG4gKiBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuIFRoZSBkZWZhdWx0XHJcbiAqIHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbVxyXG4gKiB3aXRoIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxyXG4gIHNjcmlwdCxcclxuICByZXF1ZXN0T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlcyxcclxuICBzaG91bGRUaHJvd0Vycm9yID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgLmpzIGZyb20gdGhlIGN1c3RvbSBzdHJpbmdzXHJcbiAgaWYgKHNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgIHNjcmlwdCA9IHNjcmlwdC5zdWJzdHJpbmcoMCwgc2NyaXB0Lmxlbmd0aCAtIDMpO1xyXG4gIH1cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gIH1cclxuXHJcbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYCxcclxuICAgICAgNDA0XHJcbiAgICApLnNldEVycm9yKHJlc3BvbnNlKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uLmBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3RcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbZmV0Y2hlZE1vZHVsZXM9e31dIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzT3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMgPSB7fSkge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzXHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihnZXRDYWNoZVBhdGgoKSwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgYHNjcmlwdHNgIGFuZCBgY3VzdG9tU2NyaXB0c2AgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNvcmVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBjb3JlIHNjcmlwdHMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG1vZHVsZVNjcmlwdHMgLSBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGN1c3RvbVNjcmlwdHMgLSBDdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsXHJcbiAqIFVSTHMpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmZXRjaGVkIHNjcmlwdHNcclxuICogY29udGVudCBqb2luZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogc2V0dGluZyBhbiBIVFRQIEFnZW50IGZvciBwcm94eS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHMoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pIHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gc2VydmVyUHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gc2VydmVyUHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiBleGlzdHMsIGFkZCBwcm94eSBhZ2VudCB0byByZXF1ZXN0IG9wdGlvbnNcclxuICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHByb3h5QWdlbnRcclxuICAgID8ge1xyXG4gICAgICAgIGFnZW50OiBwcm94eUFnZW50LFxyXG4gICAgICAgIHRpbWVvdXQ6IHNlcnZlclByb3h5T3B0aW9ucy50aW1lb3V0XHJcbiAgICAgIH1cclxuICAgIDoge307XHJcblxyXG4gIGNvbnN0IGFsbEZldGNoUHJvbWlzZXMgPSBbXHJcbiAgICAuLi5jb3JlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcylcclxuICAgICksXHJcbiAgICAuLi5jdXN0b21TY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucylcclxuICAgIClcclxuICBdO1xyXG5cclxuICBjb25zdCBmZXRjaGVkU2NyaXB0cyA9IGF3YWl0IFByb21pc2UuYWxsKGFsbEZldGNoUHJvbWlzZXMpO1xyXG4gIHJldHVybiBmZXRjaGVkU2NyaXB0cy5qb2luKCc7XFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgcmVwcmVzZW50aW5nXHJcbiAqIHRoZSBmZXRjaGVkIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF91cGRhdGVDYWNoZShoaWdoY2hhcnRzT3B0aW9ucywgc2VydmVyUHJveHlPcHRpb25zLCBzb3VyY2VQYXRoKSB7XHJcbiAgLy8gR2V0IEhpZ2hjaGFydHMgdmVyc2lvbiBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGhjVmVyc2lvbiA9XHJcbiAgICBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uID09PSAnbGF0ZXN0J1xyXG4gICAgICA/IG51bGxcclxuICAgICAgOiBgJHtoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9ufWA7XHJcblxyXG4gIC8vIEdldCB0aGUgQ0ROIHVybCBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGNkblVybCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVybCB8fCBjYWNoZS5jZG5Vcmw7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5zb3VyY2VzID0gYXdhaXQgX2ZldGNoU2NyaXB0cyhcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoYykgPT5cclxuICAgICAgICAgIGhjVmVyc2lvbiA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259LyR7Y31gIDogYCR7Y2RuVXJsfS8ke2N9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vbWFwcy8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tYXBzL21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgOiBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICksXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGkpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb25cclxuICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L3N0b2NrLyR7aGNWZXJzaW9ufS9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9zdG9jay9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldEhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIGV4dHJhY3RWZXJzaW9uLFxyXG4gIGV4dHJhY3RNb2R1bGVOYW1lLFxyXG4gIGdldENhY2hlLFxyXG4gIGdldENhY2hlUGF0aFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWV0aG9kcyBmb3IgaW5pdGlhbGl6aW5nIEhpZ2hjaGFydHMgd2l0aCBjdXN0b21pemVkXHJcbiAqIGFuaW1hdGlvbiBzZXR0aW5ncyBhbmQgdHJpZ2dlcmluZyB0aGUgY3JlYXRpb24gb2YgSGlnaGNoYXJ0cyBjaGFydHMgd2l0aFxyXG4gKiBleHBvcnQtc3BlY2lmaWMgY29uZmlndXJhdGlvbnMgaW4gdGhlIHBhZ2UgY29udGV4dC4gU3VwcG9ydHMgZHluYW1pYyBvcHRpb25cclxuICogbWVyZ2luZywgY3VzdG9tIGxvZ2ljIGluamVjdGlvbiwgYW5kIGNvbnRyb2wgb3ZlciByZW5kZXJpbmcgYmVoYXZpb3JzLiBVc2VkXHJcbiAqIGJ5IHRoZSBQdXBwZXRlZXIgcGFnZS5cclxuICovXHJcblxyXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xyXG5cclxuLyoqXHJcbiAqIFNldHRpbmcgdGhlIGBIaWdoY2hhcnRzLmFuaW1PYmplY3RgIGZ1bmN0aW9uLiBDYWxsZWQgd2hlbiBpbml0aW5nIHRoZSBwYWdlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xyXG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcclxuICAgIHJldHVybiB7IGR1cmF0aW9uOiAwIH07XHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBIaWdoY2hhcnRzIGNoYXJ0IG9uIGEgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVDaGFydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQ2hhcnQoZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgc2V0T3B0aW9ucywgbWVyZ2UsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHRoZSBgdXNlck9wdGlvbnNgIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB0aGUgYHVzZXJPcHRpb25zYCB3aGVuIGBmb3JFeHBvcnRgIGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBTb21lIG1hbmRhdG9yeSBhZGRpdGlvbmFsIGBjaGFydGAgYW5kIGBleHBvcnRpbmdgIG9wdGlvbnNcclxuICBjb25zdCBhZGRpdGlvbmFsT3B0aW9ucyA9IHtcclxuICAgIGNoYXJ0OiB7XHJcbiAgICAgIC8vIEJ5IGRlZmF1bHQgYW5pbWF0aW9uIGlzIGRpc2FibGVkXHJcbiAgICAgIGFuaW1hdGlvbjogZmFsc2UsXHJcbiAgICAgIC8vIEdldCB0aGUgcmlnaHQgc2l6ZSB2YWx1ZXNcclxuICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcclxuICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmluc3RyfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYHRoZW1lT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgdGhlbWVPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9uc31gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb25cclxuICBjb25zdCBnbG9iYWxPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gTWVyZ2UgdGhlIGZvbGxvd2luZyBvcHRpb25zIG9iamVjdHMgdG8gY3JlYXRlIGZpbmFsIG9wdGlvbnNcclxuICBjb25zdCBmaW5hbE9wdGlvbnMgPSBtZXJnZShcclxuICAgIGZhbHNlLFxyXG4gICAgdGhlbWVPcHRpb25zLFxyXG4gICAgdXNlck9wdGlvbnMsXHJcbiAgICAvLyBQbGFjZWQgaXQgaGVyZSBpbnN0ZWFkIGluIHRoZSBpbml0IGJlY2F1c2Ugb2YgdGhlIHNpemUgaXNzdWVzXHJcbiAgICBhZGRpdGlvbmFsT3B0aW9uc1xyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjYWxsYmFja2Agb3B0aW9uXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke2N1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja31gKSgpXHJcbiAgICA6IG51bGw7XHJcblxyXG4gIC8vIFRyaWdnZXIgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbignb3B0aW9ucycsIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbZXhwb3J0T3B0aW9ucy5jb25zdHJdKCdjb250YWluZXInLCBmaW5hbE9wdGlvbnMsIGZpbmFsQ2FsbGJhY2spO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBgc2V0T3B0aW9uc2Agd2FzIHVzZWQgaW4gdGhlIGBjdXN0b21Db2RlYClcclxuICBmb3IgKGNvbnN0IHByb3AgaW4gZGVmYXVsdE9wdGlvbnMpIHtcclxuICAgIGlmICh0eXBlb2YgZGVmYXVsdE9wdGlvbnNbcHJvcF0gIT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgZGVsZXRlIGRlZmF1bHRPcHRpb25zW3Byb3BdO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBkZWZhdWx0IG9wdGlvbnMgYmFja1xyXG4gIHNldE9wdGlvbnMoSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqKTtcclxuXHJcbiAgLy8gRW1wdHkgdGhlIGN1c3RvbSBnbG9iYWwgb3B0aW9ucyBvYmplY3RcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSB7fTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNldHVwSGlnaGNoYXJ0cyxcclxuICBjcmVhdGVDaGFydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZSwgY3JlYXRpbmcgYW5kIGNsZWFyaW5nIHBhZ2VzLCBpbmplY3RpbmcgY3VzdG9tIHJlc291cmNlcyxcclxuICogYW5kIHNldHRpbmcgdXAgSGlnaGNoYXJ0cyBmb3Igc2VydmVyLXNpZGUgcmVuZGVyaW5nLiBUaGUgbW9kdWxlIGVuc3VyZXNcclxuICogdGhhdCByZXNvdXJjZXMgYXJlIGNvcnJlY3RseSBtYW5hZ2VkIGFuZCBjYW4gaGFuZGxlIGZhaWx1cmVzIGR1cmluZ1xyXG4gKiBvcGVyYXRpb25zIGxpa2UgbGF1bmNoaW5nIHRoZSBicm93c2VyIG9yIGNyZWF0aW5nIG5ldyBwYWdlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciBwYWdlc1xyXG5jb25zdCB0ZW1wbGF0ZSA9IHJlYWRGaWxlU3luYyhcclxuICBqb2luKF9fZGlybmFtZSwgJ3RlbXBsYXRlcycsICd0ZW1wbGF0ZS5odG1sJyksXHJcbiAgJ3V0ZjgnXHJcbik7XHJcblxyXG4vLyBUbyBzYXZlIHRoZSBicm93c2VyXHJcbmxldCBicm93c2VyID0gbnVsbDtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QnJvd3NlclxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBubyB2YWxpZCBicm93c2VyXHJcbiAqIGhhcyBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QnJvd3NlcigpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nLCA1MDApO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXJcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgY3JlYXRlZCBQdXBwZXRlZXJcclxuICogYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG1heCByZXRyaWVzIHRvIG9wZW5cclxuICogYSBicm93c2VyIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyXHJcbiAqIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2AgYW5kIGBvdGhlcmAgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWdPcHRpb25zIH0gPSBkZWJ1ZztcclxuXHJcbiAgLy8gTGF1bmNoIG9wdGlvbnMgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgY29uc3QgbGF1bmNoT3B0aW9ucyA9IHtcclxuICAgIGhlYWRsZXNzOiBvdGhlci5icm93c2VyU2hlbGxNb2RlID8gJ3NoZWxsJyA6IHRydWUsXHJcbiAgICB1c2VyRGF0YURpcjogJ3RtcCcsXHJcbiAgICBhcmdzOiBwdXBwZXRlZXJBcmdzIHx8IFtdLFxyXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHSFVQOiBmYWxzZSxcclxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXHJcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXHJcbiAgICAuLi4oZW5hYmxlZERlYnVnICYmIGRlYnVnT3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICAvLyBBIGNvdW50ZXIgZm9yIHRoZSBicm93c2VyJ3MgbGF1bmNoIHJldHJpZXNcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFticm93c2VyXSBBdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeSAkeysrdHJ5Q291bnR9KS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gTGF1bmNoIHRoZSBicm93c2VyXHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgJ1ticm93c2VyXSBGYWlsZWQgdG8gbGF1bmNoIGEgYnJvd3NlciBpbnN0YW5jZS4nXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0cnkgdG8gbGF1bmNoIGJyb3dzZXIgdW50aWwgcmVhY2hpbmcgbWF4IGF0dGVtcHRzXHJcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcclxuICAgICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIFJldHJ5IHRvIG9wZW4gYSBicm93c2VyICgke3RyeUNvdW50fSBvdXQgb2YgMjUpLmApO1xyXG5cclxuICAgICAgICAgIC8vIFdhaXQgZm9yIGEgNCBzZWNvbmRzIGJlZm9yZSB0cnlpbmcgYWdhaW5cclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG5cclxuICAgICAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGxhdW5jaE9wdGlvbnMuaGVhZGxlc3MgPT09ICdzaGVsbCcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIHNoZWxsIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERlYnVnIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChlbmFibGVkRGVidWcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIGRlYnVnIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2Jyb3dzZXJdIE1heGltdW0gcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlciBpbnN0YW5jZSByZWFjaGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nLCA1MDApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xvc2VCcm93c2VyXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2VCcm93c2VyKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIgJiYgYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgYnJvd3NlciA9IG51bGw7XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgcGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHRoZSBgX3NldFBhZ2VDb250ZW50KClgLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBuZXdQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIGBpZGAsXHJcbiAqIGB3b3JrQ291bnRgLCBhbmQgYHBhZ2VgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjb25uZWN0ZWQgb3IgaWYgYSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG5ld1BhZ2UocG9vbFJlc291cmNlKSB7XHJcbiAgLy8gRXJyb3IgaW4gY2FzZSBvZiBubyBjb25uZWN0ZWQgYnJvd3NlclxyXG4gIGlmICghYnJvd3NlciB8fCAhYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW2Jyb3dzZXJdIEJyb3dzZXIgaXMgbm90IHlldCBjb25uZWN0ZWQuYCwgNTAwKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBwb29sUmVzb3VyY2UucGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBEaXNhYmxlIGNhY2hlXHJcbiAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXHJcbiAgX3NldFBhZ2VFdmVudHMocG9vbFJlc291cmNlLnBhZ2UpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgcGFnZSBpcyBjb3JyZWN0bHkgY3JlYXRlZFxyXG4gIGlmICghcG9vbFJlc291cmNlLnBhZ2UgfHwgcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgdGhlIGNvbnRlbnQgb2YgYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG1vZGUuIExvZ3NcclxuICogdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIG9mIGEgcGFnZSdzIGNvbnRlbnQgZmFpbHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIHBhZ2UgYW5kIGlkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtoYXJkUmVzZXQ9ZmFsc2VdIC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gYGFib3V0OmJsYW5rYCBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgd2hlbiBwYWdlXHJcbiAqIGlzIGNvcnJlY3RseSBjbGVhcmVkIGFuZCBmYWxzZSB3aGVuIGl0IGlzIG5vdC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2UocG9vbFJlc291cmNlLCBoYXJkUmVzZXQgPSBmYWxzZSkge1xyXG4gIHRyeSB7XHJcbiAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGBhYm91dDpibGFua2BcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHtcclxuICAgICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBfc2V0UGFnZUNvbnRlbnQocG9vbFJlc291cmNlLnBhZ2UpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gQ29udGVudCBvZiB0aGUgcGFnZSBjb3VsZCBub3QgYmUgY2xlYXJlZC5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICBwb29sUmVzb3VyY2Uud29ya0NvdW50ID0gZ2V0T3B0aW9ucygpLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICB9XHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyBjdXN0b20gSlMgYW5kIENTUyByZXNvdXJjZXMgdG8gYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYWRkUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9ICFmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRydWUgOiBmYWxzZTtcclxuXHJcbiAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICBpbmplY3RlZEpzLnB1c2goXHJcbiAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgID8ge1xyXG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChmaWxlKSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZvciAoY29uc3QganNSZXNvdXJjZSBvZiBpbmplY3RlZEpzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhqc1Jlc291cmNlKSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIFRoZSBKUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpbmplY3RlZEpzLmxlbmd0aCA9IDA7XHJcblxyXG4gICAgLy8gTG9hZCBDU1NcclxuICAgIGNvbnN0IGluamVjdGVkQ3NzID0gW107XHJcbiAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgcGF0aDogam9pbihfX2Rpcm5hbWUsIGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoY3NzUmVzb3VyY2UpKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICAgYFticm93c2VyXSBUaGUgQ1NTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaW5qZWN0ZWRDc3MubGVuZ3RoID0gMDtcclxuICAgIH1cclxuICB9XHJcbiAgcmV0dXJuIGluamVjdGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIG91dCBhbGwgc3RhdGUgc2V0IG9uIHRoZSBwYWdlIHdpdGggYGFkZFNjcmlwdFRhZ2AgYW5kIGBhZGRTdHlsZVRhZ2AuXHJcbiAqIFJlbW92ZXMgaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS5cclxuICogQWRkaXRpb25hbGx5LCBpdCBkZXN0cm95cyBwcmV2aW91c2x5IGV4aXN0aW5nIGNoYXJ0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtBcnJheS48T2JqZWN0Pn0gaW5qZWN0ZWRSZXNvdXJjZXMgLSBBcnJheSBvZiBpbmplY3RlZCByZXNvdXJjZXNcclxuICogdG8gYmUgY2xlYXJlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICB0cnkge1xyXG4gICAgZm9yIChjb25zdCByZXNvdXJjZSBvZiBpbmplY3RlZFJlc291cmNlcykge1xyXG4gICAgICBhd2FpdCByZXNvdXJjZS5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzIGFmdGVyIGV4cG9ydCBpcyBkb25lIGFuZCByZXNldCBhbGwgQ1NTIGFuZCBzY3JpcHQgdGFnc1xyXG4gICAgYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCB3aGVuIGRvaW5nIFNWRyBleHBvcnRzXHJcbiAgICAgIGlmICh0eXBlb2YgSGlnaGNoYXJ0cyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcclxuXHJcbiAgICAgICAgLy8gQ2hlY2sgaW4gYW55IGFscmVhZHkgZXhpc3RpbmcgY2hhcnRzXHJcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XHJcbiAgICAgICAgICAvLyBEZXN0cm95IG9sZCBjaGFydHNcclxuICAgICAgICAgIGZvciAoY29uc3Qgb2xkQ2hhcnQgb2Ygb2xkQ2hhcnRzKSB7XHJcbiAgICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcclxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICAgIEhpZ2hjaGFydHMuY2hhcnRzLnNoaWZ0KCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLnNjcmlwdHNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLCAuLi5zdHlsZXNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc3R5bGUnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5saW5rc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdsaW5rJyk7XHJcblxyXG4gICAgICAvLyBSZW1vdmUgdGFnc1xyXG4gICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgW1xyXG4gICAgICAgIC4uLnNjcmlwdHNUb1JlbW92ZSxcclxuICAgICAgICAuLi5zdHlsZXNUb1JlbW92ZSxcclxuICAgICAgICAuLi5saW5rc1RvUmVtb3ZlXHJcbiAgICAgIF0pIHtcclxuICAgICAgICBlbGVtZW50LnJlbW92ZSgpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciBwYWdlJ3MgcmVzb3VyY2VzLmApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvbnRlbnQgZm9yIGEgUHVwcGV0ZWVyIFBhZ2UgdXNpbmcgYSBwcmVkZWZpbmVkIHRlbXBsYXRlXHJcbiAqIGFuZCBhZGRpdGlvbmFsIHNjcmlwdHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VDb250ZW50XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgY29udGVudFxyXG4gKiBpcyBiZWluZyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnQocGFnZSkge1xyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBwYWdlIGNvbnRlbnRcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gIC8vIEFkZCBhbGwgcmVnaXN0ZXJlZCBIaWdjaGFydHMgc2NyaXB0cywgcXVpdGUgZGVtYW5kaW5nXHJcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBqb2luKGdldENhY2hlUGF0aCgpLCAnc291cmNlcy5qcycpIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGluaXRpYWwgYGFuaW1PYmplY3RgIGZvciBIaWdoY2hhcnRzXHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IGV2ZW50cyAobGlrZSBgcGFnZWVycm9yYCBhbmQgYGNvbnNvbGVgKSBmb3IgYSBQdXBwZXRlZXIgUGFnZSBpbiBvcmRlclxyXG4gKiB0byBjYXRjaCBhbmQgZGlzcGxheSBlcnJvcnMgYW5kIGNvbnNvbGUgbG9ncyBmcm9tIHRoZSB3aW5kb3cgY29udGV4dC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgbGlzdGVuZXJzXHJcbiAqIGFyZSBiZWluZyBzZXQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc2V0UGFnZUV2ZW50cyhwYWdlKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgcGFnZWVycm9yYCBsaXN0ZW5lclxyXG4gIHBhZ2Uub24oJ3BhZ2VlcnJvcicsIGFzeW5jICgpID0+IHtcclxuICAgIC8vIEl0IHdvdWxkIHNlZW0gbGlrZSB0aGlzIG1heSBmaXJlIGF0IHRoZSBzYW1lIHRpbWUgb3Igc2hvcnRseSBiZWZvcmVcclxuICAgIC8vIGEgcGFnZSBpcyBjbG9zZWQuXHJcbiAgICBpZiAocGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgY29uc29sZWAgbGlzdGVuZXIsIGlmIG5lZWRlZFxyXG4gIGlmIChkZWJ1Zy5lbmFibGUgJiYgZGVidWcubGlzdGVuVG9Db25zb2xlKSB7XHJcbiAgICBwYWdlLm9uKCdjb25zb2xlJywgKG1lc3NhZ2UpID0+IHtcclxuICAgICAgY29uc29sZS5sb2coYFtkZWJ1Z10gJHttZXNzYWdlLnRleHQoKX1gKTtcclxuICAgIH0pO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldEJyb3dzZXIsXHJcbiAgY3JlYXRlQnJvd3NlcixcclxuICBjbG9zZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSxcclxuICBjbGVhclBhZ2UsXHJcbiAgYWRkUGFnZVJlc291cmNlcyxcclxuICBjbGVhclBhZ2VSZXNvdXJjZXNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogVGhlIENTUyB0byBiZSB1c2VkIG9uIHRoZSBleHBvcnRlZCBwYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQ1NTIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoKSA9PiBgXHJcblxyXG5odG1sLCBib2R5IHtcclxuICBtYXJnaW46IDA7XHJcbiAgcGFkZGluZzogMDtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG59XHJcblxyXG4jdGFibGUtZGl2LCAjc2xpZGVycywgI2RhdGF0YWJsZSwgI2NvbnRyb2xzLCAubGQtcm93IHtcclxuICBkaXNwbGF5OiBub25lO1xyXG4gIGhlaWdodDogMDtcclxufVxyXG5cclxuI2NoYXJ0LWNvbnRhaW5lciB7XHJcbiAgYm94LXNpemluZzogYm9yZGVyLWJveDtcclxuICBtYXJnaW46IDA7XHJcbiAgb3ZlcmZsb3c6IGF1dG87XHJcbiAgZm9udC1zaXplOiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyID4gZmlndXJlLCBkaXYge1xyXG4gIG1hcmdpbi10b3A6IDAgIWltcG9ydGFudDtcclxuICBtYXJnaW4tYm90dG9tOiAwICFpbXBvcnRhbnQ7XHJcbn1cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNzc1RlbXBsYXRlIGZyb20gJy4vY3NzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBUaGUgU1ZHIHRlbXBsYXRlIHRvIHVzZSB3aGVuIGxvYWRpbmcgU1ZHIGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIGlucHV0IGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBTVkcgdGVtcGxhdGUuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoc3ZnKSA9PiBgXHJcbjwhRE9DVFlQRSBodG1sPlxyXG48aHRtbCBsYW5nPSdlbi1VUyc+XHJcbiAgPGhlYWQ+XHJcbiAgICA8bWV0YSBodHRwLWVxdWl2PVwiQ29udGVudC1UeXBlXCIgY29udGVudD1cInRleHQvaHRtbDsgY2hhcnNldD11dGYtOFwiPlxyXG4gICAgPHRpdGxlPkhpZ2hjaGFydHMgRXhwb3J0PC90aXRsZT5cclxuICA8L2hlYWQ+XHJcbiAgPHN0eWxlPlxyXG4gICAgJHtjc3NUZW1wbGF0ZSgpfVxyXG4gIDwvc3R5bGU+XHJcbiAgPGJvZHk+XHJcbiAgICA8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+XHJcbiAgICAgICR7c3ZnfVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgaGFuZGxlcyBjaGFydCBleHBvcnQgZnVuY3Rpb25hbGl0eSB1c2luZyBQdXBwZXRlZXIuXHJcbiAqIEl0IHN1cHBvcnRzIGV4cG9ydGluZyBjaGFydHMgYXMgU1ZHLCBQTkcsIEpQRUcsIGFuZCBQREYgZm9ybWF0cy4gVGhlIG1vZHVsZVxyXG4gKiBtYW5hZ2VzIHBhZ2UgcmVzb3VyY2VzLCBzZXRzIHVwIHRoZSBleHBvcnQgZW52aXJvbm1lbnQsIGFuZCBwcm9jZXNzZXMgY2hhcnRcclxuICogY29uZmlndXJhdGlvbnMgb3IgU1ZHIGlucHV0cyBmb3IgcmVuZGVyaW5nLiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2VcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFkZFBhZ2VSZXNvdXJjZXMsIGNsZWFyUGFnZVJlc291cmNlcyB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IGNyZWF0ZUNoYXJ0IH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IHN2Z1RlbXBsYXRlIGZyb20gJy4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8KHN0cmluZ3xCdWZmZXJ8RXhwb3J0RXJyb3IpPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXNcclxuICogdG8gdGhlIGV4cG9ydGVkIGRhdGEgb3IgcmVqZWN0aW5nIHdpdGggYW4gYEV4cG9ydEVycm9yYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGV4cG9ydCB0byBhbiB1bnN1cHBvcnRlZFxyXG4gKiBvdXRwdXQgZm9ybWF0IG9jY3Vycy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnQocGFnZSwgZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5IChhZGRpdGlvbmFsIEpTIGFuZCBDU1MpXHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxldCBpc1NWRyA9IGZhbHNlO1xyXG5cclxuICAgIC8vIERlY2lkZSBvbiB0aGUgZXhwb3J0IG1ldGhvZFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnKSB7XHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgU1ZHIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gSWYgdGhlIGB0eXBlYCBpcyBhbHNvIFNWRywgcmV0dXJuIHRoZSBpbnB1dFxyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAgIHJldHVybiBleHBvcnRPcHRpb25zLnN2ZztcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gTWFyayBhcyBTVkcgZXhwb3J0IGZvciB0aGUgbGF0ZXIgc2l6ZSBjb3JyZWN0aW9uc1xyXG4gICAgICBpc1NWRyA9IHRydWU7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2Uuc2V0Q29udGVudChzdmdUZW1wbGF0ZShleHBvcnRPcHRpb25zLnN2ZyksIHtcclxuICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xyXG4gICAgICB9KTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgSlNPTiBjb25maWcuJyk7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGV4cG9ydFxyXG4gICAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKGNyZWF0ZUNoYXJ0LCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXBzIHRyYWNrIG9mIGFsbCByZXNvdXJjZXMgYWRkZWQgb24gdGhlIHBhZ2Ugd2l0aCBhZGRYWFhUYWcuIGV0Y1xyXG4gICAgLy8gSXQncyBWSVRBTCB0aGF0IGFsbCBhZGRlZCByZXNvdXJjZXMgZW5kcyB1cCBoZXJlIHNvIHdlIGNhbiBjbGVhciB0aGluZ3NcclxuICAgIC8vIG91dCB3aGVuIGRvaW5nIGEgbmV3IGV4cG9ydCBpbiB0aGUgc2FtZSBwYWdlIVxyXG4gICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgLi4uKGF3YWl0IGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZWFsIGNoYXJ0IHNpemUgYW5kIHNldCB0aGUgem9vbSBhY2NvcmRpbmdseVxyXG4gICAgY29uc3Qgc2l6ZSA9IGlzU1ZHXHJcbiAgICAgID8gYXdhaXQgcGFnZS5ldmFsdWF0ZSgoc2NhbGUpID0+IHtcclxuICAgICAgICAgIGNvbnN0IHN2Z0VsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFxyXG4gICAgICAgICAgICAnI2NoYXJ0LWNvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZSdcclxuICAgICAgICAgICk7XHJcblxyXG4gICAgICAgICAgLy8gR2V0IHRoZSB2YWx1ZXMgY29ycmVjdGx5IHNjYWxlZFxyXG4gICAgICAgICAgY29uc3QgY2hhcnRIZWlnaHQgPSBzdmdFbGVtZW50LmhlaWdodC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcbiAgICAgICAgICBjb25zdCBjaGFydFdpZHRoID0gc3ZnRWxlbWVudC53aWR0aC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gSW4gY2FzZSBvZiBTVkcgdGhlIHpvb20gbXVzdCBiZSBzZXQgZGlyZWN0bHkgZm9yIGJvZHkgYXMgc2NhbGVcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUubWFyZ2luID0gJzBweCc7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSwgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKSlcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG5cclxuICAgICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXNcclxuICAgICAgICAgIC8vIG9mIGV4cG9ydHMuIFJlc2V0IHRoZSB6b29tIGZvciBvdGhlciBleHBvcnRzIHRoYW4gdG8gU1ZHc1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY2xpcCByZWdpb24gZm9yIHRoZSBwYWdlXHJcbiAgICBjb25zdCB7IHgsIHkgfSA9IGF3YWl0IF9nZXRDbGlwUmVnaW9uKHBhZ2UpO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgaGVpZ2h0YCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0SGVpZ2h0ID0gTWF0aC5hYnMoXHJcbiAgICAgIE1hdGguY2VpbChzaXplLmNoYXJ0SGVpZ2h0IHx8IGV4cG9ydE9wdGlvbnMuaGVpZ2h0KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgZmluYWwgYHdpZHRoYCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0V2lkdGggPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRXaWR0aCB8fCBleHBvcnRPcHRpb25zLndpZHRoKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGZpbmFsIHZpZXdwb3J0IG5vdyB0aGF0IHdlIGhhdmUgdGhlIHJlYWwgaGVpZ2h0XHJcbiAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcclxuICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgIGRldmljZVNjYWxlRmFjdG9yOiBpc1NWRyA/IDEgOiBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpXHJcbiAgICB9KTtcclxuXHJcbiAgICBsZXQgcmVzdWx0O1xyXG4gICAgLy8gUmFzdGVyaXphdGlvbiBwcm9jZXNzXHJcbiAgICBzd2l0Y2ggKGV4cG9ydE9wdGlvbnMudHlwZSkge1xyXG4gICAgICBjYXNlICdzdmcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVTVkcocGFnZSk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BuZyc6XHJcbiAgICAgIGNhc2UgJ2pwZWcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVJbWFnZShcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnR5cGUsXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgICB4LFxyXG4gICAgICAgICAgICB5XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BkZic6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVBERihcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgZGVmYXVsdDpcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdDogJHtleHBvcnRPcHRpb25zLnR5cGV9LmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENsZWFyIHByZXZpb3VzbHkgaW5qZWN0ZWQgSlMgYW5kIENTUyByZXNvdXJjZXNcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50XHJcbiAqIHdpdGggdGhlICdjaGFydC1jb250YWluZXInIGlkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgeGAsIGB5YCwgYHdpZHRoYCwgYW5kIGBoZWlnaHRgIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZ2V0Q2xpcFJlZ2lvbihwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoJyNjaGFydC1jb250YWluZXInLCAoZWxlbWVudCkgPT4ge1xyXG4gICAgY29uc3QgeyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH0gPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgeCxcclxuICAgICAgeSxcclxuICAgICAgd2lkdGgsXHJcbiAgICAgIGhlaWdodDogTWF0aC50cnVuYyhoZWlnaHQgPiAxID8gaGVpZ2h0IDogNTAwKVxyXG4gICAgfTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gU1ZHIGJ5IGV2YWx1YXRpbmcgdGhlIGBvdXRlckhUTUxgIG9mIHRoZSBmaXJzdCAnc3ZnJyBlbGVtZW50XHJcbiAqIGluc2lkZSBhbiBlbGVtZW50IHdpdGggdGhlIGlkICdjb250YWluZXInLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVTVkdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBTVkcgc3RyaW5nLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVNWRyhwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoXHJcbiAgICAnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsXHJcbiAgICAoZWxlbWVudCkgPT4gZWxlbWVudC5vdXRlckhUTUxcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBzY3JlZW5zaG90YCBmdW5jdGlvbmFsaXR5IHdpdGhcclxuICogc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZUltYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEltYWdlIHR5cGUuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjbGlwIC0gQ2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGltYWdlIGJ1ZmZlclxyXG4gKiBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgIGZvciB0aW1lb3V0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZUltYWdlKHBhZ2UsIHR5cGUsIGNsaXAsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgcmV0dXJuIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xyXG4gICAgICB0eXBlLFxyXG4gICAgICBjbGlwLFxyXG4gICAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICAgIGZ1bGxQYWdlOiBmYWxzZSxcclxuICAgICAgb3B0aW1pemVGb3JTcGVlZDogdHJ1ZSxcclxuICAgICAgY2FwdHVyZUJleW9uZFZpZXdwb3J0OiB0cnVlLFxyXG4gICAgICAuLi4odHlwZSAhPT0gJ3BuZycgPyB7IHF1YWxpdHk6IDgwIH0gOiB7fSksXHJcbiAgICAgIC8vIEFsd2F5cyByZW5kZXIgb24gYSB0cmFuc3BhcmVudCBwYWdlIGlmIHRoZSBleHBlY3RlZCB0eXBlIGZvcm1hdCBpcyBQTkdcclxuICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZycgLy8gIzQ0NywgIzQ2M1xyXG4gICAgfSksXHJcbiAgICBuZXcgUHJvbWlzZSgoX3Jlc29sdmUsIHJlamVjdCkgPT5cclxuICAgICAgc2V0VGltZW91dChcclxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEV4cG9ydEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnLCA0MDgpKSxcclxuICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgICAgIClcclxuICAgIClcclxuICBdKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQREYgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBgcGRmYCBmdW5jdGlvbmFsaXR5IHdpdGggc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVBERlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtudW1iZXJ9IGhlaWdodCAtIFBERiBoZWlnaHQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aCAtIFBERiB3aWR0aC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBQREYgYnVmZmVyLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVBERihwYWdlLCBoZWlnaHQsIHdpZHRoLCByYXN0ZXJpemF0aW9uVGltZW91dCkge1xyXG4gIGF3YWl0IHBhZ2UuZW11bGF0ZU1lZGlhVHlwZSgnc2NyZWVuJyk7XHJcbiAgcmV0dXJuIHBhZ2UucGRmKHtcclxuICAgIC8vIFRoaXMgd2lsbCByZW1vdmUgYW4gZXh0cmEgZW1wdHkgcGFnZSBpbiBQREYgZXhwb3J0c1xyXG4gICAgaGVpZ2h0OiBoZWlnaHQgKyAxLFxyXG4gICAgd2lkdGgsXHJcbiAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICB0aW1lb3V0OiByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBwdXBwZXRlZXJFeHBvcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGEgd29ya2VyIHBvb2wgaW1wbGVtZW50YXRpb24gZm9yIG1hbmFnaW5nXHJcbiAqIHRoZSBicm93c2VyIGluc3RhbmNlIGFuZCBwYWdlcywgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIGZvciB1c2Ugd2l0aFxyXG4gKiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBvcHRpbWl6ZXMgcmVzb3VyY2VzIHVzYWdlIGFuZCBwZXJmb3JtYW5jZVxyXG4gKiBieSBtYWludGFpbmluZyBhIHBvb2wgb2Ygd29ya2VycyB0aGF0IGNhbiBoYW5kbGUgY29uY3VycmVudCBleHBvcnQgdGFza3NcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgY3JlYXRlQnJvd3NlciwgY2xvc2VCcm93c2VyLCBuZXdQYWdlLCBjbGVhclBhZ2UgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBwdXBwZXRlZXJFeHBvcnQgfSBmcm9tICcuL2V4cG9ydC5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXROZXdEYXRlVGltZSwgbWVhc3VyZVRpbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgcG9vbCBpbnN0YW5jZVxyXG5sZXQgcG9vbCA9IG51bGw7XHJcblxyXG4vLyBQb29sIHN0YXRpc3RpY3NcclxuY29uc3QgcG9vbFN0YXRzID0ge1xyXG4gIGV4cG9ydHNBdHRlbXB0ZWQ6IDAsXHJcbiAgZXhwb3J0c1BlcmZvcm1lZDogMCxcclxuICBleHBvcnRzRHJvcHBlZDogMCxcclxuICBleHBvcnRzRnJvbVN2ZzogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnM6IDAsXHJcbiAgZXhwb3J0c0Zyb21TdmdBdHRlbXB0czogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0czogMCxcclxuICB0aW1lU3BlbnQ6IDAsXHJcbiAgdGltZVNwZW50QXZlcmFnZTogMFxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNldHRpbmcgdXAgd29ya2VyIHJlc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0UG9vbFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gZW5kaW5nIHRoZSBmdW5jdGlvblxyXG4gKiBleGVjdXRpb24gd2hlbiBhbiBhbHJlYWR5IGluaXRpYWxpemVkIHBvb2wgb2YgcmVzb3VyY2VzIGlzIGZvdW5kLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbFxyXG4gKiBvZiB3b3JrZXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRQb29sKHBvb2xPcHRpb25zLCBwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBwdXBwZXRlZXIgYXJndW1lbnRzXHJcbiAgYXdhaXQgY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBJbml0aWFsaXppbmcgcG9vbCB3aXRoIHdvcmtlcnM6IG1pbiAke3Bvb2xPcHRpb25zLm1pbldvcmtlcnN9LCBtYXggJHtwb29sT3B0aW9ucy5tYXhXb3JrZXJzfS5gXHJcbiAgICApO1xyXG5cclxuICAgIGlmIChwb29sKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgICdbcG9vbF0gQWxyZWFkeSBpbml0aWFsaXplZCwgcGxlYXNlIGtpbGwgaXQgYmVmb3JlIGNyZWF0aW5nIGEgbmV3IG9uZS4nXHJcbiAgICAgICk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBLZWVwIGFuIGV5ZSBvbiBhIGNvcnJlY3QgbWluIGFuZCBtYXggd29ya2VycyBudW1iZXJcclxuICAgIGlmIChwb29sT3B0aW9ucy5taW5Xb3JrZXJzID4gcG9vbE9wdGlvbnMubWF4V29ya2Vycykge1xyXG4gICAgICBwb29sT3B0aW9ucy5taW5Xb3JrZXJzID0gcG9vbE9wdGlvbnMubWF4V29ya2VycztcclxuICAgIH1cclxuXHJcbiAgICAvLyBDcmVhdGUgYSBwb29sIGFsb25nIHdpdGggYSBtaW5pbWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIHBvb2wgPSBuZXcgUG9vbCh7XHJcbiAgICAgIC8vIEdldCB0aGUgYGNyZWF0ZWAsIGB2YWxpZGF0ZWAsIGFuZCBgZGVzdHJveWAgZnVuY3Rpb25zXHJcbiAgICAgIC4uLl9mYWN0b3J5KHBvb2xPcHRpb25zKSxcclxuICAgICAgbWluOiBwb29sT3B0aW9ucy5taW5Xb3JrZXJzLFxyXG4gICAgICBtYXg6IHBvb2xPcHRpb25zLm1heFdvcmtlcnMsXHJcbiAgICAgIGFjcXVpcmVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5hY3F1aXJlVGltZW91dCxcclxuICAgICAgY3JlYXRlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlVGltZW91dCxcclxuICAgICAgZGVzdHJveVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmRlc3Ryb3lUaW1lb3V0LFxyXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuaWRsZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXM6IHBvb2xPcHRpb25zLmNyZWF0ZVJldHJ5SW50ZXJ2YWwsXHJcbiAgICAgIHJlYXBJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMucmVhcGVySW50ZXJ2YWwsXHJcbiAgICAgIHByb3BhZ2F0ZUNyZWF0ZUVycm9yOiBmYWxzZVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGV2ZW50c1xyXG4gICAgcG9vbC5vbigncmVsZWFzZScsIGFzeW5jIChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBDbGVhciBwYWdlXHJcbiAgICAgIGNvbnN0IGNsZWFyU3RhdHVzID0gYXdhaXQgY2xlYXJQYWdlKHJlc291cmNlLCBmYWxzZSk7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gUmVsZWFzaW5nIGEgd29ya2VyLiBDbGVhciBwYWdlIHN0YXR1czogJHtjbGVhclN0YXR1c30uYFxyXG4gICAgICApO1xyXG4gICAgfSk7XHJcblxyXG4gICAgcG9vbC5vbignZGVzdHJveVN1Y2Nlc3MnLCAoX2V2ZW50SWQsIHJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gRGVzdHJveWVkIGEgd29ya2VyIHN1Y2Nlc3NmdWxseS5gXHJcbiAgICAgICk7XHJcbiAgICAgIHJlc291cmNlLnBhZ2UgPSBudWxsO1xyXG4gICAgfSk7XHJcblxyXG4gICAgY29uc3QgaW5pdGlhbFJlc291cmNlcyA9IFtdO1xyXG4gICAgLy8gQ3JlYXRlIGFuIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwb29sT3B0aW9ucy5taW5Xb3JrZXJzOyBpKyspIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcbiAgICAgICAgaW5pdGlhbFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSBhbiBpbml0aWFsIHJlc291cmNlLicpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHJlc291cmNlKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGggPyBgIHdpdGggJHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aH0gaW5pdGlhbCByZXNvdXJjZXMgd2FpdGluZy5gIDogJy4nfWBcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1twb29sXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBjcmVhdGUgdGhlIHBvb2wgb2Ygd29ya2Vycy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFRlcm1pbmF0ZXMgYWxsIHdvcmtlcnMgaW4gdGhlIHBvb2wsIGRlc3Ryb3lzIHRoZSBwb29sLCBhbmQgY2xvc2VzIHRoZSBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGtpbGxQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIGFsbCB3b3JrZXJzIGFyZVxyXG4gKiB0ZXJtaW5hdGVkLCB0aGUgcG9vbCBpcyBkZXN0cm95ZWQsIGFuZCB0aGUgYnJvd3NlciBpcyBzdWNjZXNzZnVsbHkgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGtpbGxQb29sKCkge1xyXG4gIGxvZygzLCAnW3Bvb2xdIEtpbGxpbmcgcG9vbCB3aXRoIGFsbCB3b3JrZXJzIGFuZCBjbG9zaW5nIGJyb3dzZXIuJyk7XHJcblxyXG4gIC8vIElmIHN0aWxsIGFsaXZlLCBkZXN0cm95IHRoZSBwb29sIG9mIHBhZ2VzIGJlZm9yZSBjbG9zaW5nIGEgYnJvd3NlclxyXG4gIGlmIChwb29sKSB7XHJcbiAgICAvLyBGcmVlIHVwIG5vdCByZWxlYXNlZCB3b3JrZXJzXHJcbiAgICBmb3IgKGNvbnN0IHdvcmtlciBvZiBwb29sLnVzZWQpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlci5yZXNvdXJjZSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSB0aGUgcG9vbCBpZiBpdCBpcyBzdGlsbCBhdmFpbGFibGVcclxuICAgIGlmICghcG9vbC5kZXN0cm95ZWQpIHtcclxuICAgICAgYXdhaXQgcG9vbC5kZXN0cm95KCk7XHJcbiAgICAgIGxvZyg0LCAnW3Bvb2xdIERlc3Ryb3llZCB0aGUgcG9vbCBvZiByZXNvdXJjZXMuJyk7XHJcbiAgICB9XHJcbiAgICBwb29sID0gbnVsbDtcclxuICB9XHJcblxyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgYXdhaXQgY2xvc2VCcm93c2VyKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcm9jZXNzZXMgdGhlIGV4cG9ydCB3b3JrIHVzaW5nIGEgd29ya2VyIGZyb20gdGhlIHBvb2wuIEFjcXVpcmVzIGEgd29ya2VyXHJcbiAqIGhhbmRsZSBmcm9tIHRoZSBwb29sLCBwZXJmb3JtcyB0aGUgZXhwb3J0IHVzaW5nIHB1cHBldGVlciwgYW5kIHJlbGVhc2VzXHJcbiAqIHRoZSB3b3JrZXIgaGFuZGxlIGJhY2sgdG8gdGhlIHBvb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFdvcmtcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGV4cG9ydCByZXN1bHRcclxuICogYW5kIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0V29yayhvcHRpb25zKSB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIHN0YXJ0aW5nIHRvIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gQW4gZXhwb3J0IGF0dGVtcHQgY291bnRlZFxyXG4gICAgKytwb29sU3RhdHMuZXhwb3J0c0F0dGVtcHRlZDtcclxuXHJcbiAgICAvLyBEaXNwbGF5IHRoZSBwb29sIGluZm9ybWF0aW9uIGlmIG5lZWRlZFxyXG4gICAgaWYgKG9wdGlvbnMucG9vbC5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgZ2V0UG9vbEluZm8oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpbiBjYXNlIG9mIGxhY2tpbmcgdGhlIHBvb2wgaW5zdGFuY2VcclxuICAgIGlmICghcG9vbCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1twb29sXSBXb3JrIHJlY2VpdmVkLCBidXQgcG9vbCBoYXMgbm90IGJlZW4gc3RhcnRlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRoZSBhY3F1aXJlIGNvdW50ZXJcclxuICAgIGNvbnN0IGFjcXVpcmVDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBUcnkgdG8gYWNxdWlyZSB0aGUgd29ya2VyIGFsb25nIHdpdGggdGhlIGlkLCB3b3JrcyBjb3VudCBhbmQgcGFnZVxyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuXHJcbiAgICAgIC8vIEFjcXVpcmUgYSBwb29sIHJlc291cmNlXHJcbiAgICAgIHdvcmtlckhhbmRsZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcblxyXG4gICAgICAvLyBDaGVjayB0aGUgcGFnZSBhY3F1aXJlIHRpbWVcclxuICAgICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDUsXHJcbiAgICAgICAgICBgW2JlbmNobWFya10gJHtvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJyd9YCxcclxuICAgICAgICAgIGBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlIHRvb2sgJHthY3F1aXJlQ291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgfUVycm9yIGVuY291bnRlcmVkIHdoZW4gYWNxdWlyaW5nIGFuIGF2YWlsYWJsZSBlbnRyeTogJHthY3F1aXJlQ291bnRlcigpfW1zLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyZWQgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgIGlmICghd29ya2VySGFuZGxlLnBhZ2UpIHtcclxuICAgICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1twb29sXSBSZXNvbHZlZCB3b3JrZXIgcGFnZSBpcyBpbnZhbGlkOiB0aGUgcG9vbCBzZXR1cCBpcyB3b25reS4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgdGhlIHN0YXJ0IHRpbWVcclxuICAgIGNvbnN0IHdvcmtTdGFydCA9IGdldE5ld0RhdGVUaW1lKCk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICA0LFxyXG4gICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7d29ya2VySGFuZGxlLmlkfV0gLSBTdGFydGluZyB3b3JrIG9uIHRoaXMgcG9vbCBlbnRyeS5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFN0YXJ0IG1lYXN1cmluZyBleHBvcnQgdGltZVxyXG4gICAgY29uc3QgZXhwb3J0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnQgb24gYSBwdXBwZXRlZXIgbGV2ZWxcclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHB1cHBldGVlckV4cG9ydChcclxuICAgICAgd29ya2VySGFuZGxlLnBhZ2UsXHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICApO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIGl0J3MgYW4gZXJyb3JcclxuICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBFcnJvcikge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJZiB0aGVyZSdzIGEgcmFzdGVyaXphdGlvbiB0aW1lb3V0LCB3ZSB3YW50IG5lZWQgdG8gZmx1c2ggdGhlIHBhZ2UuXHJcbiAgICAgIC8vIFRoaXMgaXMgYmVjYXVzZSB0aGUgcGFnZSBtYXkgYmUgaW4gYSBzdGF0ZSB3aGVyZSBpdCdzIHdhaXRpbmcgZm9yXHJcbiAgICAgIC8vIHRoZSBzY3JlZW5zaG90IHRvIGZpbmlzaCBldmVuIHRob3VnaCB0aGUgdGltZW91dCBoYXMgb2NjdXJlZC5cclxuICAgICAgLy8gV2hpY2ggb2YgY291cnNlIGNhdXNlcyBhIGxvdCBvZiBpc3N1ZXMgd2l0aCB0aGUgZXZlbnQgc3lzdGVtLFxyXG4gICAgICAvLyBhbmQgcGFnZSBjb25zaXN0ZW5jeS5cclxuICAgICAgLy9cclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gT25seSBwYWdlLnNjcmVlbnNob3Qgd2lsbCB0aHJvdyB0aGlzLCB0aW1lb3V0cyBmb3IgUERGJ3MgYXJlXHJcbiAgICAgIC8vIGhhbmRsZWQgYnkgdGhlIHBhZ2UucGRmIGZ1bmN0aW9uIGl0c2VsZi5cclxuICAgICAgLy9cclxuICAgICAgLy8gLi4ueWVzLCB0aGlzIGlzIHVnbHkuXHJcbiAgICAgIGlmIChyZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpIHtcclxuICAgICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICAgIHdvcmtlckhhbmRsZS53b3JrQ291bnQgPSBvcHRpb25zLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICAgICAgICB3b3JrZXJIYW5kbGUucGFnZSA9IG51bGw7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChcclxuICAgICAgICByZXN1bHQubmFtZSA9PT0gJ1RpbWVvdXRFcnJvcicgfHxcclxuICAgICAgICByZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCdcclxuICAgICAgKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICAgIH1SYXN0ZXJpemF0aW9uIHRpbWVvdXQ6IHlvdXIgY2hhcnQgbWF5IGJlIHRvbyBjb21wbGV4IG9yIGxhcmdlLCBhbmQgZmFpbGVkIHRvIHJlbmRlciB3aXRoaW4gdGhlIGFsbG90dGVkIHRpbWUuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfUVycm9yIGVuY291bnRlcmVkIGR1cmluZyBleHBvcnQ6ICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApLnNldEVycm9yKHJlc3VsdCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayB0aGUgUHVwcGV0ZWVyIGV4cG9ydCB0aW1lXHJcbiAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA1LFxyXG4gICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgIGBFeHBvcnRpbmcgYSBjaGFydCBzdWNlc3NmdWxseSB0b29rICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXHJcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXHJcbiAgICBjb25zdCB3b3JrRW5kID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuICAgIGNvbnN0IGV4cG9ydFRpbWUgPSB3b3JrRW5kIC0gd29ya1N0YXJ0O1xyXG5cclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnQgKz0gZXhwb3J0VGltZTtcclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnRBdmVyYWdlID1cclxuICAgICAgcG9vbFN0YXRzLnRpbWVTcGVudCAvICsrcG9vbFN0YXRzLmV4cG9ydHNQZXJmb3JtZWQ7XHJcblxyXG4gICAgbG9nKDQsIGBbcG9vbF0gV29yayBjb21wbGV0ZWQgaW4gJHtleHBvcnRUaW1lfW1zLmApO1xyXG5cclxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gdGhlIHJlc3VsdFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzdWx0LFxyXG4gICAgICBvcHRpb25zXHJcbiAgICB9O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzRHJvcHBlZDtcclxuXHJcbiAgICBpZiAod29ya2VySGFuZGxlKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG4gICAgfVxyXG5cclxuICAgIHRocm93IGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZSBpZiBpbml0aWFsaXplZCwgb3IgbnVsbFxyXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sKCkge1xyXG4gIHJldHVybiBwb29sO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgc3RhdGlzdGljIG9mIGEgcG9vbCBpbnN0YWNlIGFib3V0IGV4cG9ydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sU3RhdHNcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGN1cnJlbnQgcG9vbCBzdGF0aXN0aWNzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xTdGF0cygpIHtcclxuICByZXR1cm4gcG9vbFN0YXRzO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXHJcbiAqIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZSByZXF1ZXN0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBQb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTigpIHtcclxuICByZXR1cm4ge1xyXG4gICAgbWluOiBwb29sLm1pbixcclxuICAgIG1heDogcG9vbC5tYXgsXHJcbiAgICB1c2VkOiBwb29sLm51bVVzZWQoKSxcclxuICAgIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXHJcbiAgICBhbGxDcmVhdGVkOiBwb29sLm51bVVzZWQoKSArIHBvb2wubnVtRnJlZSgpLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXM6IHBvb2wubnVtUGVuZGluZ0NyZWF0ZXMoKSxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9uczogcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSxcclxuICAgIHBlbmRpbmdEZXN0cm95czogcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoLFxyXG4gICAgYWJzb2x1dGVBbGw6XHJcbiAgICAgIHBvb2wubnVtVXNlZCgpICtcclxuICAgICAgcG9vbC5udW1GcmVlKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSArXHJcbiAgICAgIHBvb2wucGVuZGluZ0Rlc3Ryb3lzLmxlbmd0aFxyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cclxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXHJcbiAqIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mbygpIHtcclxuICBjb25zdCB7XHJcbiAgICBtaW4sXHJcbiAgICBtYXgsXHJcbiAgICB1c2VkLFxyXG4gICAgYXZhaWxhYmxlLFxyXG4gICAgYWxsQ3JlYXRlZCxcclxuICAgIHBlbmRpbmdBY3F1aXJlcyxcclxuICAgIHBlbmRpbmdDcmVhdGVzLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzLFxyXG4gICAgYWJzb2x1dGVBbGxcclxuICB9ID0gZ2V0UG9vbEluZm9KU09OKCk7XHJcblxyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21pbn0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWF4fS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHVzZWQgcmVzb3VyY2VzOiAke3VzZWR9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgZnJlZSByZXNvdXJjZXM6ICR7YXZhaWxhYmxlfS5gKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCBjcmVhdGVkICh1c2VkIGFuZCBmcmVlKSByZXNvdXJjZXM6ICR7YWxsQ3JlYXRlZH0uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBhY3F1aXJlZDogJHtwZW5kaW5nQWNxdWlyZXN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgY3JlYXRlZDogJHtwZW5kaW5nQ3JlYXRlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSB2YWxpZGF0ZWQ6ICR7cGVuZGluZ1ZhbGlkYXRpb25zfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGRlc3Ryb3llZDogJHtwZW5kaW5nRGVzdHJveXN9LmBcclxuICApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIHJlc291cmNlczogJHthYnNvbHV0ZUFsbH0uYCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGYWN0b3J5IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbiBvYmplY3Qgd2l0aCBgY3JlYXRlYCwgYHZhbGlkYXRlYCxcclxuICogYW5kIGBkZXN0cm95YCBmdW5jdGlvbnMgZm9yIHRoZSBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZhY3RvcnlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgXHJcbiAqIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmFjdG9yeShwb29sT3B0aW9ucykge1xyXG4gIHJldHVybiB7XHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiBjcmVhdGVcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3RcclxuICAgICAqIGNvbnRhaW5pbmcgdGhlIHdvcmtlciBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIGluaXRpYWxcclxuICAgICAqIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGFuIGVycm9yIGR1cmluZ1xyXG4gICAgICogdGhlIGNyZWF0aW9uIG9mIHRoZSBuZXcgcGFnZS5cclxuICAgICAqL1xyXG4gICAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgIC8vIEluaXQgdGhlIHJlc291cmNlIHdpdGggdW5pcXVlIGlkIGFuZCB3b3JrIGNvdW50XHJcbiAgICAgIGNvbnN0IHBvb2xSZXNvdXJjZSA9IHtcclxuICAgICAgICBpZDogdXVpZCgpLFxyXG4gICAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sT3B0aW9ucy53b3JrTGltaXQgLyAyKSlcclxuICAgICAgfTtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gU3RhcnQgbWVhc3VyaW5nIGEgcGFnZSBjcmVhdGlvbiB0aW1lXHJcbiAgICAgICAgY29uc3Qgc3RhcnREYXRlID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICAgICAgLy8gQ3JlYXRlIGEgbmV3IHBhZ2VcclxuICAgICAgICBhd2FpdCBuZXdQYWdlKHBvb2xSZXNvdXJjZSk7XHJcblxyXG4gICAgICAgIC8vIE1lYXN1cmUgdGhlIHRpbWUgb2YgZnVsbCBjcmVhdGlvbiBhbmQgY29uZmlndXJhdGlvbiBvZiBhIHBhZ2VcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIsIHRvb2sgJHtcclxuICAgICAgICAgICAgZ2V0TmV3RGF0ZVRpbWUoKSAtIHN0YXJ0RGF0ZVxyXG4gICAgICAgICAgfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBSZXR1cm4gcmVhZHkgcG9vbCByZXNvdXJjZVxyXG4gICAgICAgIHJldHVybiBwb29sUmVzb3VyY2U7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIEVycm9yIGVuY291bnRlcmVkIHdoZW4gY3JlYXRpbmcgYSBuZXcgcGFnZS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIFZhbGlkYXRlcyBhIHdvcmtlciBwYWdlIGluIHRoZSBleHBvcnQgcG9vbCwgY2hlY2tpbmcgaWYgaXQgaGFzIGV4Y2VlZGVkXHJcbiAgICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiB2YWxpZGF0ZVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgaWYgdGhlIHdvcmtlclxyXG4gICAgICogaXMgdmFsaWQgYW5kIHdpdGhpbiB0aGUgd29yayBsaW1pdDsgb3RoZXJ3aXNlLCB0byBmYWxzZS5cclxuICAgICAqL1xyXG4gICAgdmFsaWRhdGU6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSW4gY2VydGFpbiBjYXNlcyBhY3F1aXJpbmcgdGhyb3dzIGEgVGFyZ2V0Q2xvc2VFcnJvciwgd2hpY2ggbWF5XHJcbiAgICAgIC8vIGJlIGNhdXNlZCBieSB0d28gdGhpbmdzOlxyXG4gICAgICAvLyAtIFRoZSBwYWdlIGlzIGNsb3NlZCBhbmQgYXR0ZW1wdGVkIHRvIGJlIHJldXNlZC5cclxuICAgICAgLy8gLSBMb3N0IGNvbnRhY3Qgd2l0aCB0aGUgYnJvd3Nlci5cclxuICAgICAgLy9cclxuICAgICAgLy8gV2hhdCB3ZSdyZSBzZWVpbmcgaW4gbG9ncyBpcyB0aGF0IHN1Y2Nlc3NpdmUgZXhwb3J0cyB0eXBpY2FsbHlcclxuICAgICAgLy8gc3VjY2VlZHMsIGFuZCB0aGUgc2VydmVyIHJlY292ZXJzLCBpbmRpY2F0aW5nIHRoYXQgaXQncyBsaWtlbHlcclxuICAgICAgLy8gdGhlIGZpcnN0IGNhc2UuIFRoaXMgaXMgYW4gYXR0ZW1wdCBhdCBhbGxpZXZhdGluZyB0aGUgaXNzdWUgYnlcclxuICAgICAgLy8gc2ltcGx5IG5vdCB2YWxpZGF0aW5nIHRoZSB3b3JrZXIgaWYgdGhlIHBhZ2UgaXMgbnVsbCBvciBjbG9zZWQuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIFRoZSBhY3R1YWwgcmVzdWx0IGZyb20gd2hlbiB0aGlzIGhhcHBlbmVkLCB3YXMgdGhhdCBhIHdvcmtlciB3b3VsZFxyXG4gICAgICAvLyBiZSBjb21wbGV0ZWx5IGxvY2tlZCwgc3RvcHBpbmcgaXQgZnJvbSBiZWluZyBhY3F1aXJlZCB1bnRpbFxyXG4gICAgICAvLyBpdHMgd29yayBjb3VudCByZWFjaGVkIHRoZSBsaW1pdC5cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgdmFsaWRcclxuICAgICAgaWYgKCFwb29sUmVzb3VyY2UucGFnZSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAobm8gdmFsaWQgcGFnZSBpcyBmb3VuZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHBhZ2VgIGlzIGNsb3NlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAocGFnZSBpcyBjbG9zZWQgb3IgaW52YWxpZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYG1haW5GcmFtZWAgaXMgZGV0YWNoZWRcclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlLm1haW5GcmFtZSgpLmRldGFjaGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlJ3MgZnJhbWUgaXMgZGV0YWNoZWQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGB3b3JrTGltaXRgIGlzIGV4Y2VlZGVkXHJcbiAgICAgIGlmIChcclxuICAgICAgICBwb29sT3B0aW9ucy53b3JrTGltaXQgJiZcclxuICAgICAgICArK3Bvb2xSZXNvdXJjZS53b3JrQ291bnQgPiBwb29sT3B0aW9ucy53b3JrTGltaXRcclxuICAgICAgKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChleGNlZWRlZCB0aGUgJHtwb29sT3B0aW9ucy53b3JrTGltaXR9IHdvcmtzIHBlciByZXNvdXJjZSBsaW1pdCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBUaGUgYHBvb2xSZXNvdXJjZWAgaXMgdmFsaWRhdGVkXHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIERlc3Ryb3lzIGEgd29ya2VyIGVudHJ5IGluIHRoZSBleHBvcnQgcG9vbCwgY2xvc2luZyBpdHMgYXNzb2NpYXRlZCBwYWdlLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGRlc3Ryb3lcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICAgKiB0aGUgd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAgICovXHJcbiAgICBkZXN0cm95OiBhc3luYyAocG9vbFJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIERlc3Ryb3lpbmcgYSB3b3JrZXIuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFJlbW92ZSBhbGwgYXR0YWNoZWQgZXZlbnQgbGlzdGVuZXJzIGZyb20gdGhlIHJlc291cmNlXHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3BhZ2VlcnJvcicpO1xyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdjb25zb2xlJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2ZyYW1lZGV0YWNoZWQnKTtcclxuXHJcbiAgICAgICAgICAvLyBXZSBuZWVkIHRvIHdhaXQgYXJvdW5kIGZvciB0aGlzXHJcbiAgICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5jbG9zZSgpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFBhZ2UgY291bGQgbm90IGJlIGNsb3NlZCB1cG9uIGRlc3Ryb3lpbmcuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH07XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBpbml0UG9vbCxcclxuICBraWxsUG9vbCxcclxuICBwb3N0V29yayxcclxuICBnZXRQb29sLFxyXG4gIGdldFBvb2xTdGF0cyxcclxuICBnZXRQb29sSW5mbyxcclxuICBnZXRQb29sSW5mb0pTT05cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFVzZWQgdG8gc2FuaXRpemUgdGhlIHN0cmluZ3MgY29taW5nIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVcclxuICogdG8gcHJldmVudCBYU1MgYXR0YWNrcyAod2l0aCB0aGUgRE9NUHVyaWZ5IGxpYnJhcnkpLlxyXG4gKi9cclxuXHJcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcclxuaW1wb3J0IHsgSlNET00gfSBmcm9tICdqc2RvbSc7XHJcblxyXG4vKipcclxuICogU2FuaXRpemVzIGEgZ2l2ZW4gSFRNTCBzdHJpbmcgYnkgcmVtb3ZpbmcgPHNjcmlwdD4gdGFncy4gVGhpcyBmdW5jdGlvbiB1c2VzXHJcbiAqIGEgcmVndWxhciBleHByZXNzaW9uIHRvIGZpbmQgYW5kIHJlbW92ZSBhbGwgb2NjdXJyZW5jZXMgb2YgPHNjcmlwdD48L3NjcmlwdD5cclxuICogdGFncyBhbmQgYW55IGNvbnRlbnQgd2l0aGluIHRoZW0uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzYW5pdGl6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgSFRNTCBzdHJpbmcgdG8gYmUgc2FuaXRpemVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2FuaXRpemVkIEhUTUwgc3RyaW5nLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplKGlucHV0KSB7XHJcbiAgLy8gR2V0IHRoZSB2aXJ0dWFsIERPTVxyXG4gIGNvbnN0IHdpbmRvdyA9IG5ldyBKU0RPTSgnJykud2luZG93O1xyXG5cclxuICAvLyBDcmVhdGUgYSBwdXJpZnlpbmcgaW5zdGFuY2VcclxuICBjb25zdCBwdXJpZnkgPSBET01QdXJpZnkod2luZG93KTtcclxuXHJcbiAgLy8gUmV0dXJuIHNhbml0aXplZCBpbnB1dFxyXG4gIHJldHVybiBwdXJpZnkuc2FuaXRpemUoaW5wdXQsIHsgQUREX1RBR1M6IFsnZm9yZWlnbk9iamVjdCddIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2FuaXRpemVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyB0byBwcmVwYXJlIGZvciB0aGUgZXhwb3J0aW5nIGNoYXJ0c1xyXG4gKiBpbnRvIHZhcmlvdXMgaW1hZ2Ugb3V0cHV0IGZvcm1hdHMgc3VjaCBhcyBKUEVHLCBQTkcsIFBERiwgYW5kIFNWR3MuXHJcbiAqIEl0IHN1cHBvcnRzIHNpbmdsZSBhbmQgYmF0Y2ggZXhwb3J0IG9wZXJhdGlvbnMgYW5kIGFsbG93cyBjdXN0b21pemF0aW9uXHJcbiAqIHRocm91Z2ggb3B0aW9ucyBwYXNzZWQgZnJvbSBjb25maWd1cmF0aW9ucyBvciBBUElzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIGlzQWxsb3dlZENvbmZpZywgbWVyZ2VPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGRlZXBDb3B5LFxyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBpc09iamVjdCxcclxuICByb3VuZE51bWJlcixcclxuICB3cmFwQXJvdW5kXHJcbn0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGdsb2JhbCBmbGFnIGZvciB0aGUgY29kZSBleGVjdXRpb24gcGVybWlzc2lvblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucyBhbmQgc2F2ZXNcclxuICogdGhlIHJlc3VsdGluZyBpbWFnZSB0byB0aGUgcHJvdmlkZWQgb3V0cHV0IGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIFRoZSBvYmplY3QgbXVzdCBjb250YWluIGF0IGxlYXN0IG9uZVxyXG4gKiBvZiB0aGUgZm9sbG93aW5nIGBleHBvcnRgIHByb3BlcnRpZXM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgXHJcbiAqIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpbmdsZUV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0KSB7XHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQoXHJcbiAgICAgIHsgZXhwb3J0OiBvcHRpb25zLmV4cG9ydCwgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWMgfSxcclxuICAgICAgYXN5bmMgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSBvbmUgb2YgdGhlIGZvbGxvd2luZyBvcHRpb25zOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIGluZm9ybWF0aW9uXHJcbiAqIHByb3ZpZGVkIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZ1xyXG4gKiBmb3JtYXQ6IFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzXHJcbiAqIGFyZSBzYXZlZCB0byB0aGUgc3BlY2lmaWVkIG91dHB1dCBmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIGZyb21cclxuICogdGhlIGBleHBvcnRgIHNlY3Rpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3NlcyBhcmUgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJhdGNoRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQgJiYgb3B0aW9ucy5leHBvcnQuYmF0Y2gpIHtcclxuICAgIC8vIEFuIGFycmF5IGZvciBjb2xsZWN0aW5nIGJhdGNoIGV4cG9ydHNcclxuICAgIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIGBiYXRjaGAgYXJndW1lbnRzXHJcbiAgICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykgfHwgW10pIHtcclxuICAgICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgZXhwb3J0aW5nT3B0aW9uc2AgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0XHJcbiAqIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW5cclxuICogYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIElmIHBhcnRpYWxcclxuICogb3B0aW9ucyBhcmUgcHJvdmlkZWQsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBgZW5kQ2FsbGJhY2tgIGZ1bmN0aW9uIGlzIGludm9rZWQgdXBvbiB0aGUgY29tcGxldGlvbiBvZiB0aGUgZXhwb3J0LFxyXG4gKiBlaXRoZXIgc3VjY2Vzc2Z1bGx5IG9yIHdpdGggYW4gZXJyb3IuIFRoZSBgZXJyb3JgIG9iamVjdCBpcyBwcm92aWRlZFxyXG4gKiBhcyB0aGUgZmlyc3QgYXJndW1lbnQsIGFuZCB0aGUgYGRhdGFgIG9iamVjdCBpcyB0aGUgc2Vjb25kLCBjb250YWluaW5nXHJcbiAqIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YCBwcm9wZXJ0eVxyXG4gKiBhbmQgdGhlIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGluIHRoZSBgb3B0aW9uc2AgcHJvcGVydHkuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydGluZ09wdGlvbnMgLSBUaGUgYGV4cG9ydGluZ09wdGlvbnNgIG9iamVjdCwgd2hpY2hcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgdGhlIHByb3ZpZGVkXHJcbiAqIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHRoZSBleHBvcnQgcHJvY2VzcyBvciB1cG9uIGVuY291bnRlcmluZyBhbiBlcnJvci4gVGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGlzIHRoZSBgZXJyb3JgIG9iamVjdCwgYW5kIHRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIGBkYXRhYCBvYmplY3QsXHJcbiAqIHdoaWNoIGluY2x1ZGVzIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YFxyXG4gKiBwcm9wZXJ0eSBhbmQgdGhlIGZ1bGwgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGV4cG9ydGluZ09wdGlvbnMsIGVuZENhbGxiYWNrKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHByb3ZpZGVkIG9wdGlvbnMgaXMgYW4gb2JqZWN0XHJcbiAgICBpZiAoIWlzT2JqZWN0KGV4cG9ydGluZ09wdGlvbnMpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NoYXJ0XSBJbmNvcnJlY3QgdmFsdWUgb2YgdGhlIHByb3ZpZGVkIGBleHBvcnRpbmdPcHRpb25zYC4gTmVlZHMgdG8gYmUgYW4gb2JqZWN0LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTWVyZ2UgYWRkaXRpb25hbCBvcHRpb25zIHRvIHRoZSBjb3B5IG9mIHRoZSBpbnN0YW5jZSBvcHRpb25zXHJcbiAgICBjb25zdCBvcHRpb25zID0gbWVyZ2VPcHRpb25zKGRlZXBDb3B5KGdldE9wdGlvbnMoKSksIHtcclxuICAgICAgZXhwb3J0OiBleHBvcnRpbmdPcHRpb25zLmV4cG9ydCxcclxuICAgICAgY3VzdG9tTG9naWM6IGV4cG9ydGluZ09wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICByZXR1cm4gcG9zdFdvcmsob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBmb3IgY2hhcnQgZXhwb3J0cyBiYXNlZFxyXG4gKiBvbiB0aGUgcHJvdmlkZWQgZXhwb3J0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICogMS4gVGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBgc2NhbGVgIGZyb20gdGhlIGBleHBvcnRPcHRpb25zYC5cclxuICogMi4gT3B0aW9ucyBmcm9tIHRoZSBjaGFydCBjb25maWd1cmF0aW9uIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBnbG9iYWwgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgdGhlbWUgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCBzZWN0aW9ucykuXHJcbiAqIDUuIEZhbGxiYWNrIGRlZmF1bHQgdmFsdWVzIChgaGVpZ2h0ID0gNDAwYCwgYHdpZHRoID0gNjAwYCwgYHNjYWxlID0gMWApLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZpbmRDaGFydFNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb2RlIGV4ZWN1dGlvblxyXG4gKiBpcyBub3QgYWxsb3dlZCBidXQgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYXJlIHN0aWxsIHByb3ZpZGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgLy8gSW4gY2FzZSBvZiBhbGxvd2luZyBjb2RlIGV4ZWN1dGlvblxyXG4gIGlmIChhbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIC8vIFByb2Nlc3MgdGhlIGByZXNvdXJjZXNgIG9wdGlvblxyXG4gICAgaWYgKHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID09PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBDdXN0b20gc3RyaW5naWZpZWQgcmVzb3VyY2VzXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gTG9hZCB0aGUgZGVmYXVsdCBvbmVcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoJ3Jlc291cmNlcy5qc29uJyksICd1dGY4JyksXHJcbiAgICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IGByZXNvdXJjZXMuanNvbmAgZmlsZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGN1c3RvbSBjb2RlIGFuZCB3cmFwIGFyb3VuZCBpdCBpbiBhIHNlbGYgaW52b2tpbmcgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1tjaGFydF0gVGhlIGBjdXN0b21Db2RlYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gVHJ5IHRvIGxvYWQgY2FsbGJhY2sgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2ssXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY2FsbGJhY2tgIGNhbm5vdCBiZSBsb2FkZWQuJyk7XHJcblxyXG4gICAgICAvLyBJbiBjYXNlIG9mIGFuIGVycm9yLCBzZXQgdGhlIG9wdGlvbiB3aXRoIG51bGxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGN1c3RvbUNvZGVgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGN1c3RvbUNvZGVgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGNhbGxiYWNrYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgY2FsbGJhY2tgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHJlc291cmNlc2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gSWYgdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIGZsYWcgaXMgc2V0IHRvIGZhbHNlLCB3ZSBzaG91bGQgcmVmdXNlXHJcbiAgICAvLyB0aGUgdXNhZ2Ugb2YgdGhlIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgLCBhbmQgYGN1c3RvbUNvZGVgIG9wdGlvbnMuXHJcbiAgICAvLyBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbCByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LlxyXG4gICAgaWYgKFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFJlc2V0IGFsbCBjdXN0b20gY29kZSBvcHRpb25zXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcblxyXG4gICAgICAvLyBTZW5kIGEgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydCB0aGVzZSBzZXR0aW5nc1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnLCBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gLFxyXG4gICAgICAgIDQwM1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZnJvbSB0aGUgYHJlc291cmNlc2Agb3B0aW9uIGZvciBleHBvcnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFtyZXNvdXJjZXM9bnVsbF0gLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuXHJcbiAqIENhbiBiZSBlaXRoZXIgYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiwgYSBwYXRoIHRvIGEgSlNPTiBmaWxlLFxyXG4gKiBvciBudWxsLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgbnVsbGAuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2FkaW5nXHJcbiAqIHJlc291cmNlcyBmcm9tIGZpbGVzIGlzIGFsbG93ZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGhhbmRsZWQgcmVzb3VyY2VzIG9yIG51bGwgaWYgbm8gdmFsaWQgcmVzb3VyY2VzXHJcbiAqIGFyZSBmb3VuZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgcmVzb3VyY2VzID0gbnVsbCxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIExpc3Qgb2YgYWxsb3dlZCBzZWN0aW9ucyBpbiB0aGUgcmVzb3VyY2VzIEpTT05cclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKHJlc291cmNlcyksICd1dGY4JyksXHJcbiAgICAgICAgZmFsc2UsXHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxyXG4gICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhyZXNvdXJjZXMsIGZhbHNlLCBhbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIGZpbGVzIHNlY3Rpb25cclxuICAgIGlmIChoYW5kbGVkUmVzb3VyY2VzICYmICFhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaWx0ZXIgZnJvbSB1bm5lY2Vzc2FyeSBwcm9wZXJ0aWVzXHJcbiAgZm9yIChjb25zdCBwcm9wTmFtZSBpbiBoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXNbcHJvcE5hbWVdO1xyXG4gICAgfSBlbHNlIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiBhbGxvd2VkIHByb3BlcnRpZXMgaXMgcHJlc2VudFxyXG4gIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgbG9hZGluZyBhbmQgdmFsaWRhdGlvbiBvZiB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYFxyXG4gKiBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIElmIHRoZSBvcHRpb24gaXMgYSBzdHJpbmcgYW5kIHJlZmVyZW5jZXMgYSBKU09OIGZpbGVcclxuICogKHdoZW4gdGhlIGBhbGxvd0ZpbGVSZXNvdXJjZXNgIGlzIHRydWUpLCBpdCByZWFkcyBhbmQgcGFyc2VzIHRoZSBmaWxlLlxyXG4gKiBPdGhlcndpc2UsIGl0IGF0dGVtcHRzIHRvIHBhcnNlIHRoZSBzdHJpbmcgb3Igb2JqZWN0IGFzIEpTT04uIElmIGFueSBlcnJvcnNcclxuICogb2NjdXIgZHVyaW5nIHRoaXMgcHJvY2VzcywgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC4gSWYgdGhlcmUgaXMgYW4gZXJyb3JcclxuICogbG9hZGluZyBvciBwYXJzaW5nIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3IgYHRoZW1lT3B0aW9uc2AsIHRoZSBlcnJvciBpcyBsb2dnZWRcclxuICogYW5kIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBlbmFibGluZyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzcGVjaWZpZWQgRXhwcmVzcyBhcHAuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXRcclxuICogdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCByYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWRcclxuICAgIGlmIChyYXRlTGltaXRpbmdPcHRpb25zLmVuYWJsZSkge1xyXG4gICAgICBjb25zdCBtZXNzYWdlID1cclxuICAgICAgICAnVG9vIG1hbnkgcmVxdWVzdHMsIHlvdSBoYXZlIGJlZW4gcmF0ZSBsaW1pdGVkLiBQbGVhc2UgdHJ5IGFnYWluIGxhdGVyLic7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICAgIGNvbnN0IHJhdGVPcHRpb25zID0ge1xyXG4gICAgICAgIHdpbmRvdzogcmF0ZUxpbWl0aW5nT3B0aW9ucy53aW5kb3cgfHwgMSxcclxuICAgICAgICBtYXhSZXF1ZXN0czogcmF0ZUxpbWl0aW5nT3B0aW9ucy5tYXhSZXF1ZXN0cyB8fCAzMCxcclxuICAgICAgICBkZWxheTogcmF0ZUxpbWl0aW5nT3B0aW9ucy5kZWxheSB8fCAwLFxyXG4gICAgICAgIHRydXN0UHJveHk6IHJhdGVMaW1pdGluZ09wdGlvbnMudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgICAgICBza2lwS2V5OiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBLZXkgfHwgbnVsbCxcclxuICAgICAgICBza2lwVG9rZW46IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcFRva2VuIHx8IG51bGxcclxuICAgICAgfTtcclxuXHJcbiAgICAgIC8vIFNldCBpZiBiZWhpbmQgYSBwcm94eVxyXG4gICAgICBpZiAocmF0ZU9wdGlvbnMudHJ1c3RQcm94eSkge1xyXG4gICAgICAgIGFwcC5lbmFibGUoJ3RydXN0IHByb3h5Jyk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENyZWF0ZSBhIGxpbWl0ZXJcclxuICAgICAgY29uc3QgbGltaXRlciA9IHJhdGVMaW1pdCh7XHJcbiAgICAgICAgLy8gVGltZSBmcmFtZSBmb3Igd2hpY2ggcmVxdWVzdHMgYXJlIGNoZWNrZWQgYW5kIHJlbWVtYmVyZWRcclxuICAgICAgICB3aW5kb3dNczogcmF0ZU9wdGlvbnMud2luZG93ICogNjAgKiAxMDAwLFxyXG4gICAgICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciBgd2luZG93TXNgXHJcbiAgICAgICAgbGltaXQ6IHJhdGVPcHRpb25zLm1heFJlcXVlc3RzLFxyXG4gICAgICAgIC8vIERpc2FibGUgZGVsYXlpbmcsIGZ1bGwgc3BlZWQgdW50aWwgdGhlIG1heCBsaW1pdCBpcyByZWFjaGVkXHJcbiAgICAgICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICAgICAgaGFuZGxlcjogKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5mb3JtYXQoe1xyXG4gICAgICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZCh7IG1lc3NhZ2UgfSk7XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIGRlZmF1bHQ6ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKG1lc3NhZ2UpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICB9LFxyXG4gICAgICAgIHNraXA6IChyZXF1ZXN0KSA9PiB7XHJcbiAgICAgICAgICAvLyBBbGxvdyBieXBhc3NpbmcgdGhlIGxpbWl0ZXIgaWYgYSB2YWxpZCBrZXkvdG9rZW4gaGFzIGJlZW4gc2VudFxyXG4gICAgICAgICAgaWYgKFxyXG4gICAgICAgICAgICByYXRlT3B0aW9ucy5za2lwS2V5ICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBUb2tlbiAhPT0gbnVsbCAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmtleSA9PT0gcmF0ZU9wdGlvbnMuc2tpcEtleSAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmFjY2Vzc190b2tlbiA9PT0gcmF0ZU9wdGlvbnMuc2tpcFRva2VuXHJcbiAgICAgICAgICApIHtcclxuICAgICAgICAgICAgbG9nKDQsICdbcmF0ZSBsaW1pdGluZ10gU2tpcHBpbmcgcmF0ZSBsaW1pdGVyLicpO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgLy8gVXNlIGEgbGltaXRlciBhcyBhIG1pZGRsZXdhcmVcclxuICAgICAgYXBwLnVzZShsaW1pdGVyKTtcclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcmF0ZSBsaW1pdGluZ10gRW5hYmxlZCByYXRlIGxpbWl0aW5nIHdpdGggJHtyYXRlT3B0aW9ucy5tYXhSZXF1ZXN0c30gcmVxdWVzdHMgcGVyICR7cmF0ZU9wdGlvbnMud2luZG93fSBtaW51dGUgZm9yIGVhY2ggSVAsIHRydXN0aW5nIHByb3h5OiAke3JhdGVPcHRpb25zLnRydXN0UHJveHl9LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3JhdGUgbGltaXRpbmddIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHNldCB0aGUgcmF0ZSBsaW1pdGluZyBvcHRpb25zLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtaWRkbGV3YXJlIGZ1bmN0aW9ucyBmb3IgdmFsaWRhdGluZyBpbmNvbWluZyBIVFRQIHJlcXVlc3RzXHJcbiAqIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uIFRoaXMgbW9kdWxlIGVuc3VyZXMgdGhhdCByZXF1ZXN0cyBjb250YWluXHJcbiAqIGFwcHJvcHJpYXRlIGNvbnRlbnQgdHlwZXMgYW5kIHZhbGlkIHJlcXVlc3QgYm9kaWVzLCBpbmNsdWRpbmcgcHJvcGVyIEpTT05cclxuICogc3RydWN0dXJlcyBhbmQgY2hhcnQgZGF0YSBmb3IgZXhwb3J0cy4gSXQgY2hlY2tzIGZvciBwb3RlbnRpYWwgaXNzdWVzIHN1Y2hcclxuICogYXMgbWlzc2luZyBvciBtYWxmb3JtZWQgZGF0YSwgcHJpdmF0ZSByYW5nZSBVUkxzIGluIFNWRyBwYXlsb2FkcywgYW5kIGFsbG93c1xyXG4gKiBmb3IgZmxleGlibGUgb3B0aW9ucyB2YWxpZGF0aW9uLiBUaGUgbWlkZGxld2FyZSBsb2dzIGRldGFpbGVkIGluZm9ybWF0aW9uXHJcbiAqIGFuZCBoYW5kbGVzIGVycm9ycyByZWxhdGVkIHRvIGluY29ycmVjdCBwYXlsb2FkcywgY2hhcnQgZGF0YSwgYW5kIHByaXZhdGUgVVJMXHJcbiAqIHVzYWdlLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7IGdldEFsbG93Q29kZUV4ZWN1dGlvbiB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaXNPYmplY3RFbXB0eSwgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIGNvbnRlbnQtdHlwZSBoZWFkZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9IFRoZSBjYWxsIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY29udGVudC10eXBlXHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gY29udGVudFR5cGVNaWRkbGV3YXJlKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgY29udGVudCB0eXBlIGhlYWRlclxyXG4gICAgY29uc3QgY29udGVudFR5cGUgPSByZXF1ZXN0LmhlYWRlcnNbJ2NvbnRlbnQtdHlwZSddIHx8ICcnO1xyXG5cclxuICAgIC8vIEFsbG93IG9ubHkgSlNPTiwgVVJMLWVuY29kZWQgYW5kIGZvcm0gZGF0YSB3aXRob3V0IGZpbGVzIHR5cGVzIG9mIGRhdGFcclxuICAgIGlmIChcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdhcHBsaWNhdGlvbi9qc29uJykgJiZcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ211bHRpcGFydC9mb3JtLWRhdGEnKVxyXG4gICAgKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3ZhbGlkYXRpb25dIENvbnRlbnQtVHlwZSBtdXN0IGJlIGFwcGxpY2F0aW9uL2pzb24sIGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCwgb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YS4nLFxyXG4gICAgICAgIDQxNVxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENhbGwgdGhlIGByZXF1ZXN0Qm9keU1pZGRsZXdhcmVgIG1pZGRsZXdhcmVcclxuICAgIHJldHVybiBuZXh0KCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciB2YWxpZGF0aW5nIHRoZSByZXF1ZXN0J3MgYm9keS5cclxuICpcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RCb2R5TWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBib2R5IGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGNoYXJ0IGRhdGEgZnJvbSB0aGUgYm9keVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGluIGNhc2Ugb2YgdGhlIHByaXZhdGUgcmFuZ2VcclxuICogdXJsIGVycm9yLlxyXG4gKi9cclxuZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgcmVxdWVzdCBib2R5XHJcbiAgICBjb25zdCBib2R5ID0gcmVxdWVzdC5ib2R5O1xyXG5cclxuICAgIC8vIENyZWF0ZSBhIHVuaXF1ZSBJRCBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSB1dWlkKCk7XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgaXMgbm8gY29ycmVjdCBib2R5XHJcbiAgICBpZiAoIWJvZHkgfHwgaXNPYmplY3RFbXB0eShib2R5KSkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIGVtcHR5LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBib2R5IGlzIHJlcXVpcmVkLiBQbGVhc2UgZW5zdXJlIHRoYXQgeW91ciBDb250ZW50LVR5cGUgaGVhZGVyIGlzIGNvcnJlY3QuIEFjY2VwdGVkIHR5cGVzIGFyZSAnYXBwbGljYXRpb24vanNvbicgYW5kICdtdWx0aXBhcnQvZm9ybS1kYXRhJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIG9wdGlvbiBmb3IgdGhlIHNlcnZlclxyXG4gICAgY29uc3QgYWxsb3dDb2RlRXhlY3V0aW9uID0gZ2V0QWxsb3dDb2RlRXhlY3V0aW9uKCk7XHJcblxyXG4gICAgLy8gRmluZCBhIGNvcnJlY3QgY2hhcnQgb3B0aW9uc1xyXG4gICAgY29uc3QgaW5zdHIgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgIC8vIFVzZSBvbmUgb2YgdGhlIGJlbG93XHJcbiAgICAgIGJvZHkuaW5zdHIgfHwgYm9keS5vcHRpb25zIHx8IGJvZHkuaW5maWxlIHx8IGJvZHkuZGF0YSxcclxuICAgICAgLy8gU3RyaW5naWZ5IG9wdGlvbnNcclxuICAgICAgdHJ1ZSxcclxuICAgICAgLy8gQWxsb3cgb3IgZGlzYWxsb3cgZnVuY3Rpb25zXHJcbiAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGNoYXJ0IGRhdGFcclxuICAgIGlmIChpbnN0ciA9PT0gbnVsbCAmJiAhYm9keS5zdmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fCByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcGF5bG9hZCBpcyBtaXNzaW5nIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3IgZXhwb3J0OiAke0pTT04uc3RyaW5naWZ5KGJvZHkpfS5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBbdmFsaWRhdGlvbl0gTm8gY29ycmVjdCBjaGFydCBkYXRhIGZvdW5kLiBFbnN1cmUgdGhhdCB5b3UgYXJlIHVzaW5nIGVpdGhlciBhcHBsaWNhdGlvbi9qc29uIG9yIG11bHRpcGFydC9mb3JtLWRhdGEgaGVhZGVycy4gSWYgc2VuZGluZyBKU09OLCBtYWtlIHN1cmUgdGhlIGNoYXJ0IGRhdGEgaXMgaW4gdGhlICdpbmZpbGUnLCAnb3B0aW9ucycsIG9yICdkYXRhJyBhdHRyaWJ1dGUuIElmIHNlbmRpbmcgU1ZHLCBlbnN1cmUgaXQgaXMgaW4gdGhlICdzdmcnIGF0dHJpYnV0ZS5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRlc3Qgb2YgeGxpbms6aHJlZiBlbGVtZW50cyBmcm9tIHBheWxvYWQncyBTVkcgZmFpbHNcclxuICAgIGlmIChib2R5LnN2ZyAmJiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKGJvZHkuc3ZnKSkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBbdmFsaWRhdGlvbl0gU1ZHIHBvdGVudGlhbGx5IGNvbnRhaW4gYXQgbGVhc3Qgb25lIGZvcmJpZGRlbiBVUkwgaW4gJ3hsaW5rOmhyZWYnIGVsZW1lbnQuIFBsZWFzZSByZXZpZXcgdGhlIFNWRyBjb250ZW50IGFuZCBlbnN1cmUgdGhhdCBhbGwgcmVmZXJlbmNlZCBVUkxzIGNvbXBseSB3aXRoIHNlY3VyaXR5IHBvbGljaWVzLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IG9wdGlvbnMgYW5kIHN0b3JlIHBhcnNlZCBzdHJ1Y3R1cmUgaW4gdGhlIHJlcXVlc3RcclxuICAgIHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucyA9IHtcclxuICAgICAgLy8gU2V0IHRoZSBjcmVhdGVkIElEIGFzIGEgYHJlcXVlc3RJZGAgcHJvcGVydHkgaW4gdGhlIG9wdGlvbnNcclxuICAgICAgcmVxdWVzdElkLFxyXG4gICAgICBleHBvcnQ6IHtcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICBzdmc6IGJvZHkuc3ZnLFxyXG4gICAgICAgIG91dGZpbGU6XHJcbiAgICAgICAgICBib2R5Lm91dGZpbGUgfHxcclxuICAgICAgICAgIGAke3JlcXVlc3QucGFyYW1zLmZpbGVuYW1lIHx8ICdjaGFydCd9LiR7Ym9keS50eXBlIHx8ICdwbmcnfWAsXHJcbiAgICAgICAgdHlwZTogYm9keS50eXBlLFxyXG4gICAgICAgIGNvbnN0cjogYm9keS5jb25zdHIsXHJcbiAgICAgICAgYjY0OiBib2R5LmI2NCxcclxuICAgICAgICBub0Rvd25sb2FkOiBib2R5Lm5vRG93bmxvYWQsXHJcbiAgICAgICAgaGVpZ2h0OiBib2R5LmhlaWdodCxcclxuICAgICAgICB3aWR0aDogYm9keS53aWR0aCxcclxuICAgICAgICBzY2FsZTogYm9keS5zY2FsZSxcclxuICAgICAgICBnbG9iYWxPcHRpb25zOiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICBib2R5Lmdsb2JhbE9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKSxcclxuICAgICAgICB0aGVtZU9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkudGhlbWVPcHRpb25zLFxyXG4gICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgIClcclxuICAgICAgfSxcclxuICAgICAgY3VzdG9tTG9naWM6IHtcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzOiBmYWxzZSxcclxuICAgICAgICBjdXN0b21Db2RlOiBib2R5LmN1c3RvbUNvZGUsXHJcbiAgICAgICAgY2FsbGJhY2s6IGJvZHkuY2FsbGJhY2ssXHJcbiAgICAgICAgcmVzb3VyY2VzOiBpc0FsbG93ZWRDb25maWcoYm9keS5yZXNvdXJjZXMsIHRydWUsIGFsbG93Q29kZUV4ZWN1dGlvbilcclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBuZXh0IG1pZGRsZXdhcmVcclxuICAgIHJldHVybiBuZXh0KCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVzIHRvIHRoZSBwYXNzZWQgZXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmFsaWRhdGlvbk1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGNvbnRlbnQgdHlwZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCBjb250ZW50VHlwZU1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgcmVxdWVzdCBib2R5IHJlcXVlc3QgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgYXBwLnBvc3QoWycvJywgJy86ZmlsZW5hbWUnXSwgcmVxdWVzdEJvZHlNaWRkbGV3YXJlKTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyB0aGUgZXhwb3J0IHJvdXRlcyBhbmQgbG9naWMgZm9yIGhhbmRsaW5nIGNoYXJ0IGV4cG9ydFxyXG4gKiByZXF1ZXN0cyBpbiBhbiBFeHByZXNzIHNlcnZlci4gVGhpcyBtb2R1bGUgcHJvY2Vzc2VzIGluY29taW5nIHJlcXVlc3RzXHJcbiAqIHRvIGV4cG9ydCBjaGFydHMgaW4gdmFyaW91cyBmb3JtYXRzIChlLmcuIEpQRUcsIFBORywgUERGLCBTVkcpLiBJdCBpbnRlZ3JhdGVzXHJcbiAqIHdpdGggSGlnaGNoYXJ0cycgY29yZSBmdW5jdGlvbmFsaXRpZXMgYW5kIHN1cHBvcnRzIGJvdGggaW1tZWRpYXRlIGRvd25sb2FkXHJcbiAqIHJlc3BvbnNlcyBhbmQgQmFzZTY0LWVuY29kZWQgY29udGVudCByZXR1cm5zLiBUaGUgY29kZSBhbHNvIGZlYXR1cmVzXHJcbiAqIGJlbmNobWFya2luZyBmb3IgcGVyZm9ybWFuY2UgbW9uaXRvcmluZy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBzdGFydEV4cG9ydCB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0QmFzZTY0LCBtZWFzdXJlVGltZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xyXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XHJcbiAgcG5nOiAnaW1hZ2UvcG5nJyxcclxuICBqcGVnOiAnaW1hZ2UvanBlZycsXHJcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcclxuICBwZGY6ICdhcHBsaWNhdGlvbi9wZGYnLFxyXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXHJcbn07XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgZXhwb3J0IHJlcXVlc3RzIGZyb20gdGhlIGNsaWVudC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0KHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0IGNvdW50aW5nIHRpbWUgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgcmVxdWVzdENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIEluIGNhc2UgdGhlIGNvbm5lY3Rpb24gaXMgY2xvc2VkLCBmb3JjZSB0byBhYm9ydCBmdXJ0aGVyIGFjdGlvbnNcclxuICAgIGxldCBjb25uZWN0aW9uQWJvcnRlZCA9IGZhbHNlO1xyXG4gICAgcmVxdWVzdC5zb2NrZXQub24oJ2Nsb3NlJywgKGhhZEVycm9ycykgPT4ge1xyXG4gICAgICBpZiAoaGFkRXJyb3JzKSB7XHJcbiAgICAgICAgY29ubmVjdGlvbkFib3J0ZWQgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIG9wdGlvbnMgcHJldmlvdXNseSB2YWxpZGF0ZWQgaW4gdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gICAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGlkXHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSByZXF1ZXN0T3B0aW9ucy5yZXF1ZXN0SWQ7XHJcblxyXG4gICAgLy8gSW5mbyBhYm91dCBhbiBpbmNvbWluZyByZXF1ZXN0IHdpdGggY29ycmVjdCBkYXRhXHJcbiAgICBsb2coNCwgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBHb3QgYW4gaW5jb21pbmcgSFRUUCByZXF1ZXN0LmApO1xyXG5cclxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQocmVxdWVzdE9wdGlvbnMsIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAvLyBSZW1vdmUgdGhlIGNsb3NlIGV2ZW50IGZyb20gdGhlIHNvY2tldFxyXG4gICAgICByZXF1ZXN0LnNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2Nsb3NlJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkLCBkbyBub3RoaW5nXHJcbiAgICAgIGlmIChjb25uZWN0aW9uQWJvcnRlZCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSBjbGllbnQgY2xvc2VkIHRoZSBjb25uZWN0aW9uIGJlZm9yZSB0aGUgY2hhcnQgZmluaXNoZWQgcHJvY2Vzc2luZy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGVycm9yLCBsb2cgaXQgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGRhdGEgaXMgbWlzc2luZywgbG9nIHRoZSBtZXNzYWdlIGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmICghZGF0YSB8fCAhZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBSZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fFxyXG4gICAgICAgICAgICByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCByZXN1bHQgaXMgJHtkYXRhLnJlc3VsdH0uYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVW5leHBlY3RlZCByZXR1cm4gb2YgdGhlIGV4cG9ydCByZXN1bHQgZnJvbSB0aGUgY2hhcnQgZ2VuZXJhdGlvbi4gUGxlYXNlIGNoZWNrIHlvdXIgcmVxdWVzdCBkYXRhLmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBSZXR1cm4gdGhlIHJlc3VsdCBpbiBhbiBhcHByb3ByaWF0ZSBmb3JtYXRcclxuICAgICAgaWYgKGRhdGEucmVzdWx0KSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzIHRvb2sgJHtyZXF1ZXN0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIGB0eXBlYCwgYGI2NGAsIGBub0Rvd25sb2FkYCwgYW5kIGBvdXRmaWxlYCBmcm9tIG9wdGlvbnNcclxuICAgICAgICBjb25zdCB7IHR5cGUsIGI2NCwgbm9Eb3dubG9hZCwgb3V0ZmlsZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgLy8gSWYgb25seSBCYXNlNjQgaXMgcmVxdWlyZWQsIHJldHVybiBpdFxyXG4gICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gU2V0IGNvcnJlY3QgY29udGVudCB0eXBlXHJcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVyKCdDb250ZW50LVR5cGUnLCByZXZlcnNlZE1pbWVbdHlwZV0gfHwgJ2ltYWdlL3BuZycpO1xyXG5cclxuICAgICAgICAvLyBEZWNpZGUgd2hldGhlciB0byBkb3dubG9hZCBvciBub3QgY2hhcnQgZmlsZVxyXG4gICAgICAgIGlmICghbm9Eb3dubG9hZCkge1xyXG4gICAgICAgICAgcmVzcG9uc2UuYXR0YWNobWVudChvdXRmaWxlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIElmIFNWRywgcmV0dXJuIHBsYWluIGNvbnRlbnQsIG90aGVyd2lzZSBhIGI2NCBzdHJpbmcgZnJvbSBhIGJ1ZmZlclxyXG4gICAgICAgIHJldHVybiB0eXBlID09PSAnc3ZnJ1xyXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGRhdGEucmVzdWx0KVxyXG4gICAgICAgICAgOiByZXNwb25zZS5zZW5kKEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JykpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBleHBvcnRgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4cG9ydFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGV4cG9ydFJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3RcclxuICAgKiBlbmRwb2ludC5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLycsIHJlcXVlc3RFeHBvcnQpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvOmZpbGVuYW1lJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgd2l0aFxyXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLzpmaWxlbmFtZScsIHJlcXVlc3RFeHBvcnQpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHNlcnZlciBoZWFsdGggbW9uaXRvcmluZywgaW5jbHVkaW5nXHJcbiAqIHVwdGltZSwgc3VjY2VzcyByYXRlcywgYW5kIG90aGVyIHNlcnZlciBzdGF0aXN0aWNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldFBvb2xTdGF0cywgZ2V0UG9vbEluZm9KU09OIH0gZnJvbSAnLi4vLi4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGFkZFRpbWVyIH0gZnJvbSAnLi4vLi4vdGltZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGdldE5ld0RhdGVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLy8gU2V0IHRoZSBzdGFydCBkYXRlIG9mIHRoZSBzZXJ2ZXJcclxuY29uc3Qgc2VydmVyU3RhcnRUaW1lID0gbmV3IERhdGUoKTtcclxuXHJcbi8vIEdldCB0aGUgYHBhY2thZ2UuanNvbmAgY29udGVudFxyXG5jb25zdCBwYWNrYWdlRmlsZSA9IEpTT04ucGFyc2UoXHJcbiAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuKTtcclxuXHJcbi8vIEFuIGFycmF5IGZvciBzdWNjZXNzIHJhdGUgcmF0aW9zXHJcbmNvbnN0IHN1Y2Nlc3NSYXRlcyA9IFtdO1xyXG5cclxuLy8gUmVjb3JkIGV2ZXJ5IG1pbnV0ZVxyXG5jb25zdCByZWNvcmRJbnRlcnZhbCA9IDYwICogMTAwMDtcclxuXHJcbi8vIDMwIG1pbnV0ZXNcclxuY29uc3Qgd2luZG93U2l6ZSA9IDMwO1xyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgbW92aW5nIGF2ZXJhZ2UgaW5kaWNhdG9yIGJhc2VkIG9uIHRoZSBkYXRhIGZyb20gdGhlIGBzdWNjZXNzUmF0ZXNgXHJcbiAqIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2VcclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gQSBtb3ZpbmcgYXZlcmFnZSBmb3Igc3VjY2VzcyByYXRpbyBvZiB0aGUgc2VydmVyIGV4cG9ydHMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpIHtcclxuICByZXR1cm4gc3VjY2Vzc1JhdGVzLnJlZHVjZSgoYSwgYikgPT4gYSArIGIsIDApIC8gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyB0aGUgaW50ZXJ2YWwgcmVzcG9uc2libGUgZm9yIGNhbGN1bGF0aW5nIGN1cnJlbnQgc3VjY2VzcyByYXRlIHJhdGlvXHJcbiAqIGFuZCBjb2xsZWN0cyByZWNvcmRzIHRvIHRoZSBgc3VjY2Vzc1JhdGVzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtOb2RlSlMuVGltZW91dH0gSWQgb2YgYW4gaW50ZXJ2YWwuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc3RhcnRTdWNjZXNzUmF0ZSgpIHtcclxuICByZXR1cm4gc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgIGNvbnN0IHN1Y2Nlc3NSYXRpbyA9XHJcbiAgICAgIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQgPT09IDBcclxuICAgICAgICA/IDFcclxuICAgICAgICA6IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDA7XHJcblxyXG4gICAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcclxuICAgIGlmIChzdWNjZXNzUmF0ZXMubGVuZ3RoID4gd2luZG93U2l6ZSkge1xyXG4gICAgICBzdWNjZXNzUmF0ZXMuc2hpZnQoKTtcclxuICAgIH1cclxuICB9LCByZWNvcmRJbnRlcnZhbCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgaGVhbHRoYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBoZWFsdGhSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBoZWFsdGhSb3V0ZXMoYXBwKSB7XHJcbiAgLy8gU3RhcnQgcHJvY2Vzc2luZyBzdWNjZXNzIHJhdGUgcmF0aW8gaW50ZXJ2YWwgYW5kIHNhdmUgaXRzIGlkIHRvIHRoZSBhcnJheVxyXG4gIC8vIGZvciB0aGUgZ3JhY2VmdWwgY2xlYXJpbmcgb24gc2h1dGRvd24gd2l0aCBpbmplY3RlZCBgYWRkVGltZXJgIGZ1bnRpb25cclxuICBhZGRUaW1lcihfc3RhcnRTdWNjZXNzUmF0ZSgpKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgR0VUICcvaGVhbHRoJyAtIEEgcm91dGUgZm9yIGdldHRpbmcgdGhlIGJhc2ljIHN0YXRzIG9mIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldCgnL2hlYWx0aCcsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbaGVhbHRoXSBSZXR1cm5pbmcgc2VydmVyIGhlYWx0aC4nKTtcclxuXHJcbiAgICAgIGNvbnN0IHN0YXRzID0gZ2V0UG9vbFN0YXRzKCk7XHJcbiAgICAgIGNvbnN0IHBlcmlvZCA9IHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbiAgICAgIGNvbnN0IG1vdmluZ0F2ZXJhZ2UgPSBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpO1xyXG5cclxuICAgICAgLy8gU2VuZCB0aGUgc2VydmVyJ3Mgc3RhdGlzdGljc1xyXG4gICAgICByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAvLyBTdGF0dXMgYW5kIHRpbWVzXHJcbiAgICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICAgIGJvb3RUaW1lOiBzZXJ2ZXJTdGFydFRpbWUsXHJcbiAgICAgICAgdXB0aW1lOiBgJHtNYXRoLmZsb29yKChnZXROZXdEYXRlVGltZSgpIC0gc2VydmVyU3RhcnRUaW1lLmdldFRpbWUoKSkgLyAxMDAwIC8gNjApfSBtaW51dGVzYCxcclxuXHJcbiAgICAgICAgLy8gVmVyc2lvbnNcclxuICAgICAgICBzZXJ2ZXJWZXJzaW9uOiBwYWNrYWdlRmlsZS52ZXJzaW9uLFxyXG4gICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG5cclxuICAgICAgICAvLyBFeHBvcnRzXHJcbiAgICAgICAgYXZlcmFnZUV4cG9ydFRpbWU6IHN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UsXHJcbiAgICAgICAgYXR0ZW1wdGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCxcclxuICAgICAgICBwZXJmb3JtZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzUGVyZm9ybWVkLFxyXG4gICAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNEcm9wcGVkLFxyXG4gICAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCAvIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQpICogMTAwLFxyXG5cclxuICAgICAgICAvLyBQb29sXHJcbiAgICAgICAgcG9vbDogZ2V0UG9vbEluZm9KU09OKCksXHJcblxyXG4gICAgICAgIC8vIE1vdmluZyBhdmVyYWdlXHJcbiAgICAgICAgcGVyaW9kLFxyXG4gICAgICAgIG1vdmluZ0F2ZXJhZ2UsXHJcbiAgICAgICAgbWVzc2FnZTpcclxuICAgICAgICAgIGlzTmFOKG1vdmluZ0F2ZXJhZ2UpIHx8ICFzdWNjZXNzUmF0ZXMubGVuZ3RoXHJcbiAgICAgICAgICAgID8gJ1RvbyBlYXJseSB0byByZXBvcnQuIE5vIGV4cG9ydHMgbWFkZSB5ZXQuIFBsZWFzZSBjaGVjayBiYWNrIHNvb24uJ1xyXG4gICAgICAgICAgICA6IGBMYXN0ICR7cGVyaW9kfSBtaW51dGVzIGhhZCBhIHN1Y2Nlc3MgcmF0ZSBvZiAke21vdmluZ0F2ZXJhZ2UudG9GaXhlZCgyKX0lLmAsXHJcblxyXG4gICAgICAgIC8vIFNWRyBhbmQgSlNPTiBleHBvcnRzXHJcbiAgICAgICAgc3ZnRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21TdmcsXHJcbiAgICAgICAganNvbkV4cG9ydHM6IHN0YXRzLmV4cG9ydHNGcm9tT3B0aW9ucyxcclxuICAgICAgICBzdmdFeHBvcnRzQXR0ZW1wdHM6IHN0YXRzLmV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMsXHJcbiAgICAgICAganNvbkV4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHNcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmluZyB0aGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyXHJcbiAqIHdoZW4gZW5hYmxlZC5cclxuICovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB1aWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdWlSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB1aVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy8nIC0gQSByb3V0ZSBmb3IgYSBVSSB3aGVuIGVuYWJsZWQgb24gdGhlIGV4cG9ydCBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldChnZXRPcHRpb25zKCkudWkucm91dGUgfHwgJy8nLCAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3VpXSBSZXR1cm5pbmcgVUkgZm9yIHRoZSBleHBvcnQuJyk7XHJcblxyXG4gICAgICByZXNwb25zZS5zZW5kRmlsZShqb2luKF9fZGlybmFtZSwgJ3B1YmxpYycsICdpbmRleC5odG1sJyksIHtcclxuICAgICAgICBhY2NlcHRSYW5nZXM6IGZhbHNlXHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHVwZGF0aW5nIHRoZSBIaWdoY2hhcnRzIHZlcnNpb25cclxuICogb24gdGhlIHNlcnZlciwgd2l0aCBhdXRoZW50aWNhdGlvbiBhbmQgdmFsaWRhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiwgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuLi8uLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB2ZXJzaW9uX2NoYW5nZWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHZlcnNpb25DaGFuZ2VSb3V0ZXMoYXBwKSB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnL3ZlcnNpb25fY2hhbmdlLzpuZXdWZXJzaW9uJyAtIEEgcm91dGUgZm9yIGNoYW5naW5nXHJcbiAgICogdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBvbiB0aGUgc2VydmVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nLCBhc3luYyAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3ZlcnNpb25dIENoYW5naW5nIEhpZ2hjaGFydHMgdmVyc2lvbi4nKTtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZGlyZWN0bHkgZnJvbSBlbnZzXHJcbiAgICAgIGNvbnN0IGFkbWluVG9rZW4gPSBlbnZzLkhJR0hDSEFSVFNfQURNSU5fVE9LRU47XHJcblxyXG4gICAgICAvLyBDaGVjayB0aGUgZXhpc3RlbmNlIG9mIHRoZSB0b2tlblxyXG4gICAgICBpZiAoIWFkbWluVG9rZW4gfHwgIWFkbWluVG9rZW4ubGVuZ3RoKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1t2ZXJzaW9uXSBUaGUgc2VydmVyIGlzIG5vdCBjb25maWd1cmVkIHRvIHBlcmZvcm0gcnVuLXRpbWUgdmVyc2lvbiBjaGFuZ2VzOiBISUdIQ0hBUlRTX0FETUlOX1RPS0VOIGlzIG5vdCBzZXQuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZnJvbSB0aGUgaGMtYXV0aCBoZWFkZXJcclxuICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXHJcbiAgICAgIGlmICghdG9rZW4gfHwgdG9rZW4gIT09IGFkbWluVG9rZW4pIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIEludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdmVyc2lvbnNcclxuICAgICAgbGV0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG4gICAgICBpZiAobmV3VmVyc2lvbikge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAvLyBVcGRhdGUgdmVyc2lvblxyXG4gICAgICAgICAgYXdhaXQgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbik7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgYFt2ZXJzaW9uXSBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgIDQwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbdmVyc2lvbl0gTm8gbmV3IHZlcnNpb24gc3VwcGxpZWQuJywgNDAwKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSB0aGF0IHNldHMgdXAgYW5kIG1hbmFnZXMgSFRUUCBhbmQgSFRUUFMgc2VydmVyc1xyXG4gKiBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci4gSXQgaGFuZGxlcyBzZXJ2ZXIgaW5pdGlhbGl6YXRpb24sXHJcbiAqIGNvbmZpZ3VyYXRpb24sIGVycm9yIGhhbmRsaW5nLCBtaWRkbGV3YXJlIHNldHVwLCByb3V0ZSBkZWZpbml0aW9uLCBhbmQgcmF0ZVxyXG4gKiBsaW1pdGluZy4gVGhlIG1vZHVsZSBleHBvcnRzIGZ1bmN0aW9ucyB0byBzdGFydCwgc3RvcCwgYW5kIG1hbmFnZSBzZXJ2ZXJcclxuICogaW5zdGFuY2VzLCBhcyB3ZWxsIGFzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBkZWZpbmluZyByb3V0ZXMgYW5kIGF0dGFjaGluZ1xyXG4gKiBtaWRkbGV3YXJlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZSB9IGZyb20gJ2ZzL3Byb21pc2VzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xyXG5cclxuaW1wb3J0IHsgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgZXJyb3JNaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvZXJyb3IuanMnO1xyXG5pbXBvcnQgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3JhdGVMaW1pdGluZy5qcyc7XHJcbmltcG9ydCB2YWxpZGF0aW9uTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3ZhbGlkYXRpb24uanMnO1xyXG5cclxuaW1wb3J0IGV4cG9ydFJvdXRlcyBmcm9tICcuL3JvdXRlcy9leHBvcnQuanMnO1xyXG5pbXBvcnQgaGVhbHRoUm91dGVzIGZyb20gJy4vcm91dGVzL2hlYWx0aC5qcyc7XHJcbmltcG9ydCB1aVJvdXRlcyBmcm9tICcuL3JvdXRlcy91aS5qcyc7XHJcbmltcG9ydCB2ZXJzaW9uQ2hhbmdlUm91dGVzIGZyb20gJy4vcm91dGVzL3ZlcnNpb25DaGFuZ2UuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBBcnJheSBvZiBhbiBhY3RpdmUgc2VydmVyc1xyXG5jb25zdCBhY3RpdmVTZXJ2ZXJzID0gbmV3IE1hcCgpO1xyXG5cclxuLy8gQ3JlYXRlIGV4cHJlc3MgYXBwXHJcbmNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gSFRUUCBhbmQvb3IgSFRUUFMgc2VydmVyIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLlxyXG4gKiBUaGUgYHNlcnZlck9wdGlvbnNgIG9iamVjdCBjb250YWlucyBzZXJ2ZXItcmVsYXRlZCBwcm9wZXJ0aWVzIChyZWZlclxyXG4gKiB0byB0aGUgYHNlcnZlcmAgc2VjdGlvbiBpbiB0aGUgYGxpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgZGV0YWlscykuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRTZXJ2ZXJcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlck9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIHRoZSBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBkZWZhdWx0XHJcbiAqIHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGVpdGhlclxyXG4gKiBub3QgZW5hYmxlZCBvciBubyB2YWxpZCBFeHByZXNzIGFwcCBpcyBmb3VuZCwgc2lnbmFsaW5nIHRoZSBlbmQgb2YgdGhlXHJcbiAqIGZ1bmN0aW9uJ3MgZXhlY3V0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIHNlcnZlciBjYW5ub3RcclxuICogYmUgY29uZmlndXJlZCBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFNlcnZlcihzZXJ2ZXJPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgc2VydmVyOiBzZXJ2ZXJPcHRpb25zXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBVc2UgdmFsaWRhdGVkIG9wdGlvbnNcclxuICAgIHNlcnZlck9wdGlvbnMgPSBvcHRpb25zLnNlcnZlcjtcclxuXHJcbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXHJcbiAgICBpZiAoIXNlcnZlck9wdGlvbnMuZW5hYmxlIHx8ICFhcHApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbc2VydmVyXSBTZXJ2ZXIgY2Fubm90IGJlIHN0YXJ0ZWQgKG5vdCBlbmFibGVkIG9yIG5vIGNvcnJlY3QgRXhwcmVzcyBhcHAgZm91bmQpLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVG9vIGJpZyBsaW1pdHMgbGVhZCB0byB0aW1lb3V0cyBpbiB0aGUgZXhwb3J0IHByb2Nlc3Mgd2hlblxyXG4gICAgLy8gdGhlIHJhc3Rlcml6YXRpb24gdGltZW91dCBpcyBzZXQgdG9vIGxvd1xyXG4gICAgY29uc3QgdXBsb2FkTGltaXRCeXRlcyA9IHNlcnZlck9wdGlvbnMudXBsb2FkTGltaXQgKiAxMDI0ICogMTAyNDtcclxuXHJcbiAgICAvLyBNZW1vcnkgc3RvcmFnZSBmb3IgbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHN0b3JhZ2UgPSBtdWx0ZXIubWVtb3J5U3RvcmFnZSgpO1xyXG5cclxuICAgIC8vIEVuYWJsZSBwYXJzaW5nIG9mIGZvcm0gZGF0YSAoZmlsZXMpIHdpdGggbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHVwbG9hZCA9IG11bHRlcih7XHJcbiAgICAgIHN0b3JhZ2UsXHJcbiAgICAgIGxpbWl0czoge1xyXG4gICAgICAgIGZpZWxkU2l6ZTogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXHJcbiAgICBhcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XHJcblxyXG4gICAgLy8gRW5hYmxlIENPUlMgc3VwcG9ydFxyXG4gICAgYXBwLnVzZShcclxuICAgICAgY29ycyh7XHJcbiAgICAgICAgbWV0aG9kczogWydQT1NUJywgJ0dFVCcsICdPUFRJT05TJ11cclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0dGluZyBhIGxvdCBvZiBgUmFuZ2VOb3RTYXRpc2ZpYWJsZUVycm9yYCBleGNlcHRpb25zIChldmVuIHRob3VnaCB0aGlzXHJcbiAgICAvLyBpcyBhIGRlcHJlY2F0ZWQgb3B0aW9ucywgbGV0J3MgdHJ5IHRvIHNldCBpdCB0byBmYWxzZSlcclxuICAgIGFwcC51c2UoKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgIHJlc3BvbnNlLnNldCgnQWNjZXB0LVJhbmdlcycsICdub25lJyk7XHJcbiAgICAgIG5leHQoKTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgSlNPTiBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLmpzb24oe1xyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgVVJMLWVuY29kZWQgZm9ybSBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLnVybGVuY29kZWQoe1xyXG4gICAgICAgIGV4dGVuZGVkOiB0cnVlLFxyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xyXG4gICAgYXBwLnVzZSh1cGxvYWQubm9uZSgpKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgc3RhdGljIGZvbGRlcidzIHJvdXRlXHJcbiAgICBhcHAudXNlKGV4cHJlc3Muc3RhdGljKGpvaW4oX19kaXJuYW1lLCAncHVibGljJykpKTtcclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5zc2wuZm9yY2UpIHtcclxuICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFApXHJcbiAgICAgIGNvbnN0IGh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApO1xyXG5cclxuICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIC8vIExpc3RlblxyXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnBvcnQsIHNlcnZlck9wdGlvbnMuaG9zdCwgKCkgPT4ge1xyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMucG9ydCwgaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnBvcnR9LmBcclxuICAgICAgICApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgICBpZiAoc2VydmVyT3B0aW9ucy5zc2wuZW5hYmxlKSB7XHJcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cclxuICAgICAgbGV0IGtleSwgY2VydDtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XHJcbiAgICAgICAga2V5ID0gYXdhaXQgcmVhZEZpbGUoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIua2V5JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBjZXJ0aWZpY2F0ZVxyXG4gICAgICAgIGNlcnQgPSBhd2FpdCByZWFkRmlsZShcclxuICAgICAgICAgIGpvaW4oZ2V0QWJzb2x1dGVQYXRoKHNlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRoKSwgJ3NlcnZlci5jcnQnKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgIGBbc2VydmVyXSBVbmFibGUgdG8gbG9hZCBrZXkvY2VydGlmaWNhdGUgZnJvbSB0aGUgJyR7c2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGh9JyBwYXRoLiBDb3VsZCBub3QgcnVuIHNlY3VyZWQgbGF5ZXIgc2VydmVyLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoa2V5ICYmIGNlcnQpIHtcclxuICAgICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUFMpXHJcbiAgICAgICAgY29uc3QgaHR0cHNTZXJ2ZXIgPSBodHRwcy5jcmVhdGVTZXJ2ZXIoeyBrZXksIGNlcnQgfSwgYXBwKTtcclxuXHJcbiAgICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICAgIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgLy8gTGlzdGVuXHJcbiAgICAgICAgaHR0cHNTZXJ2ZXIubGlzdGVuKHNlcnZlck9wdGlvbnMuc3NsLnBvcnQsIHNlcnZlck9wdGlvbnMuaG9zdCwgKCkgPT4ge1xyXG4gICAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFBTIHNlcnZlclxyXG4gICAgICAgICAgYWN0aXZlU2VydmVycy5zZXQoc2VydmVyT3B0aW9ucy5zc2wucG9ydCwgaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFtzZXJ2ZXJdIFN0YXJ0ZWQgSFRUUFMgc2VydmVyIG9uICR7c2VydmVyT3B0aW9ucy5ob3N0fToke3NlcnZlck9wdGlvbnMuc3NsLnBvcnR9LmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBTZXQgdXAgdGhlIHJhdGUgbGltaXRlclxyXG4gICAgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZShhcHAsIHNlcnZlck9wdGlvbnMucmF0ZUxpbWl0aW5nKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgdGhlIHZhbGlkYXRpb24gaGFuZGxlclxyXG4gICAgdmFsaWRhdGlvbk1pZGRsZXdhcmUoYXBwKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgcm91dGVzXHJcbiAgICBleHBvcnRSb3V0ZXMoYXBwKTtcclxuICAgIGhlYWx0aFJvdXRlcyhhcHApO1xyXG4gICAgdWlSb3V0ZXMoYXBwKTtcclxuICAgIHZlcnNpb25DaGFuZ2VSb3V0ZXMoYXBwKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgdGhlIGNlbnRyYWxpemVkIGVycm9yIGhhbmRsZXJcclxuICAgIGVycm9yTWlkZGxld2FyZShhcHApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbc2VydmVyXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBzdGFydCB0aGUgc2VydmVyLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIGFsbCBzZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGNsb3NlU2VydmVyc1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsb3NlU2VydmVycygpIHtcclxuICAvLyBDaGVjayBpZiB0aGVyZSBhcmUgc2VydmVycyB3b3JraW5nXHJcbiAgaWYgKGFjdGl2ZVNlcnZlcnMuc2l6ZSA+IDApIHtcclxuICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2luZyBhbGwgc2VydmVycy5gKTtcclxuXHJcbiAgICAvLyBDbG9zZSBlYWNoIG9uZSBvZiBzZXJ2ZXJzXHJcbiAgICBmb3IgKGNvbnN0IFtwb3J0LCBzZXJ2ZXJdIG9mIGFjdGl2ZVNlcnZlcnMpIHtcclxuICAgICAgc2VydmVyLmNsb3NlKCgpID0+IHtcclxuICAgICAgICBhY3RpdmVTZXJ2ZXJzLmRlbGV0ZShwb3J0KTtcclxuICAgICAgICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NlZCBzZXJ2ZXIgb24gcG9ydDogJHtwb3J0fS5gKTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogR2V0IGFsbCBzZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFNlcnZlcnNcclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxPYmplY3Q+fSBTZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRTZXJ2ZXJzKCkge1xyXG4gIHJldHVybiBhY3RpdmVTZXJ2ZXJzO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0RXhwcmVzc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7RXhwcmVzc30gVGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0RXhwcmVzcygpIHtcclxuICByZXR1cm4gZXhwcmVzcztcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBcHBcclxuICpcclxuICogQHJldHVybnMge0V4cHJlc3N9IFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBcHAoKSB7XHJcbiAgcmV0dXJuIGFwcDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZSByYXRlIGxpbWl0aW5nIGZvciB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlUmF0ZUxpbWl0aW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSByYXRlTGltaXRpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHJhdGVMaW1pdGluZ2Agb3B0aW9ucy4gVGhpcyBvYmplY3QgbWF5IGluY2x1ZGUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldFxyXG4gKiBvZiB0aGUgYHJhdGVMaW1pdGluZ2Agb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzXHJcbiAqIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVSYXRlTGltaXRpbmcocmF0ZUxpbWl0aW5nT3B0aW9ucykge1xyXG4gIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICBzZXJ2ZXI6IHtcclxuICAgICAgcmF0ZUxpbWl0aW5nOiByYXRlTGltaXRpbmdPcHRpb25zXHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgcmF0ZSBsaW1pdGluZyBvcHRpb25zXHJcbiAgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZShhcHAsIG9wdGlvbnMuc2VydmVyLnJhdGVMaW1pdGluZ09wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogQXBwbHkgbWlkZGxld2FyZShzKSB0byBhIHNwZWNpZmljIHBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1c2VcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHVzZShwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC51c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBHRVQgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAuZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggUE9TVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLnBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoIGVycm9yIGhhbmRsZXJzIHRvIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVyc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhodHRwLlNlcnZlcnxodHRwcy5TZXJ2ZXIpfSBzZXJ2ZXIgLSBUaGUgSFRUUC9IVFRQUyBzZXJ2ZXIgaW5zdGFuY2UuXHJcbiAqL1xyXG5mdW5jdGlvbiBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhzZXJ2ZXIpIHtcclxuICBzZXJ2ZXIub24oJ2NsaWVudEVycm9yJywgKGVycm9yLCBzb2NrZXQpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgMSxcclxuICAgICAgZXJyb3IsXHJcbiAgICAgIGBbc2VydmVyXSBDbGllbnQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX0sIGRlc3Ryb3lpbmcgc29ja2V0LmBcclxuICAgICk7XHJcbiAgICBzb2NrZXQuZGVzdHJveSgpO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTZXJ2ZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdjb25uZWN0aW9uJywgKHNvY2tldCkgPT4ge1xyXG4gICAgc29ja2V0Lm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTb2NrZXQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc3RhcnRTZXJ2ZXIsXHJcbiAgY2xvc2VTZXJ2ZXJzLFxyXG4gIGdldFNlcnZlcnMsXHJcbiAgZ2V0RXhwcmVzcyxcclxuICBnZXRBcHAsXHJcbiAgZW5hYmxlUmF0ZUxpbWl0aW5nLFxyXG4gIHVzZSxcclxuICBnZXQsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgSGFuZGxlcyBncmFjZWZ1bCBzaHV0ZG93biBvZiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLCBlbnN1cmluZ1xyXG4gKiBwcm9wZXIgY2xlYW51cCBvZiByZXNvdXJjZXMgc3VjaCBhcyBicm93c2VyLCBwYWdlcywgc2VydmVycywgYW5kIHRpbWVycy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBraWxsUG9vbCB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGNsZWFyQWxsVGltZXJzIH0gZnJvbSAnLi90aW1lci5qcyc7XHJcblxyXG5pbXBvcnQgeyBjbG9zZVNlcnZlcnMgfSBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIFBlcmZvcm1zIGNsZWFudXAgb3BlcmF0aW9ucyB0byBlbnN1cmUgYSBncmFjZWZ1bCBzaHV0ZG93biBvZiB0aGUgcHJvY2Vzcy5cclxuICogVGhpcyBpbmNsdWRlcyBjbGVhcmluZyBhbGwgcmVnaXN0ZXJlZCB0aW1lb3V0cy9pbnRlcnZhbHMsIGNsb3NpbmcgYWN0aXZlXHJcbiAqIHNlcnZlcnMsIHRlcm1pbmF0aW5nIHJlc291cmNlcyAocGFnZXMpIG9mIHRoZSBwb29sLCBwb29sIGl0c2VsZiwgYW5kIGNsb3NpbmdcclxuICogdGhlIGJyb3dzZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXBcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IFtleGl0Q29kZT0wXSAtIFRoZSBleGl0IGNvZGUgdG8gdXNlIHdpdGggYHByb2Nlc3MuZXhpdCgpYC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYDBgLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNodXRkb3duQ2xlYW5VcChleGl0Q29kZSA9IDApIHtcclxuICAvLyBBd2FpdCBmcmVlaW5nIGFsbCByZXNvdXJjZXNcclxuICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW1xyXG4gICAgLy8gQ2xlYXIgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzXHJcbiAgICBjbGVhckFsbFRpbWVycygpLFxyXG5cclxuICAgIC8vIEdldCBhdmFpbGFibGUgc2VydmVyIGluc3RhbmNlcyAoSFRUUC9IVFRQUykgYW5kIGNsb3NlIHRoZW1cclxuICAgIGNsb3NlU2VydmVycygpLFxyXG5cclxuICAgIC8vIENsb3NlIGFuIGFjdGl2ZSBwb29sIGFsb25nIHdpdGggaXRzIHdvcmtlcnMgYW5kIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgICBraWxsUG9vbCgpXHJcbiAgXSk7XHJcblxyXG4gIC8vIEV4aXQgcHJvY2VzcyB3aXRoIGEgY29ycmVjdCBjb2RlXHJcbiAgcHJvY2Vzcy5leGl0KGV4aXRDb2RlKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNodXRkb3duQ2xlYW5VcFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29yZSBtb2R1bGUgZm9yIGluaXRpYWxpemluZyBhbmQgbWFuYWdpbmcgdGhlIEhpZ2hjaGFydHMgRXhwb3J0XHJcbiAqIFNlcnZlci4gUHJvdmlkZXMgZnVuY3Rpb25hbGl0aWVzIGZvciBjb25maWd1cmluZyBleHBvcnRzLCBzZXR0aW5nIHVwIHNlcnZlclxyXG4gKiBvcGVyYXRpb25zLCBsb2dnaW5nLCBzY3JpcHRzIGNhY2hpbmcsIHJlc291cmNlIHBvb2xpbmcsIGFuZCBncmFjZWZ1bCBwcm9jZXNzXHJcbiAqIGNsZWFudXAuXHJcbiAqL1xyXG5cclxuaW1wb3J0ICdjb2xvcnMnO1xyXG5cclxuaW1wb3J0IHsgY2hlY2tBbmRVcGRhdGVDYWNoZSB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQge1xyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBiYXRjaEV4cG9ydCxcclxuICBzdGFydEV4cG9ydCxcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxufSBmcm9tICcuL2NoYXJ0LmpzJztcclxuaW1wb3J0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHVwZGF0ZU9wdGlvbnMsXHJcbiAgc2V0R2xvYmFsT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnNcclxufSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbFxyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2h1dGRvd25DbGVhblVwIH0gZnJvbSAnLi9yZXNvdXJjZVJlbGVhc2UuanMnO1xyXG5cclxuaW1wb3J0IHNlcnZlciBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiB0aGUgY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHJlc291cmNlIHBvb2wgb2NjdXIgZHVyaW5nIHRoaXNcclxuICogc3RhZ2UuXHJcbiAqXHJcbiAqIFRoaXMgZnVuY3Rpb24gbXVzdCBiZSBjYWxsZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXRcclxuICogdXAgYSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2luaXRPcHRpb25zPXt9XSAtIFRoZSBgaW5pdE9wdGlvbnNgIG9iamVjdCwgd2hpY2ggbWF5XHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmdcclxuICogdmFsdWVzIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRFeHBvcnQoaW5pdE9wdGlvbnMgPSB7fSkge1xyXG4gIC8vIEluaXQgYW5kIHVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyhpbml0T3B0aW9ucywgdHJ1ZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcblxyXG4gIC8vIEluaXQgdGhlIHBvb2xcclxuICBhd2FpdCBpbml0UG9vbChvcHRpb25zLnBvb2wsIG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoZXMgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MsIGVuc3VyaW5nIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlc1xyXG4gKiBhbmQgdGVybWluYXRpb24gb24gZXhpdCBzaWduYWxzLiBIYW5kbGVzICdleGl0JywgJ1NJR0lOVCcsICdTSUdURVJNJ1xyXG4gKiBhbmQgJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnNcclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LmApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0lOVCdcclxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHVEVSTSdcclxuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0hVUCdcclxuICBwcm9jZXNzLm9uKCdTSUdIVVAnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAndW5jYXVnaHRFeGNlcHRpb24nXHJcbiAgcHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCBhc3luYyAoZXJyb3IsIG5hbWUpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIC4uLnNlcnZlcixcclxuXHJcbiAgLy8gT3B0aW9uc1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgc2V0R2xvYmFsT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcblxyXG4gIC8vIEV4cG9ydGluZ1xyXG4gIGluaXRFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG5cclxuICAvLyBSZWxlYXNlXHJcbiAga2lsbFBvb2wsXHJcbiAgc2h1dGRvd25DbGVhblVwLFxyXG5cclxuICAvLyBMb2dzXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbDogZnVuY3Rpb24gKGxldmVsKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICBsZXZlbFxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgc2V0TG9nTGV2ZWwob3B0aW9ucy5sb2dnaW5nLmxldmVsKTtcclxuICB9LFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nOiBmdW5jdGlvbiAodG9Db25zb2xlKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICB0b0NvbnNvbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUNvbnNvbGVMb2dnaW5nKG9wdGlvbnMubG9nZ2luZy50b0NvbnNvbGUpO1xyXG4gIH0sXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmc6IGZ1bmN0aW9uIChkZXN0LCBmaWxlLCB0b0ZpbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGRlc3QsXHJcbiAgICAgICAgZmlsZSxcclxuICAgICAgICB0b0ZpbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUZpbGVMb2dnaW5nKFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZGVzdCxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLmZpbGUsXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy50b0ZpbGVcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG4iXSwibmFtZXMiOlsiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJiYXNlVVJJIiwiZGVlcENvcHkiLCJvYmpBcnIiLCJvYmpBcnJDb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwiT2JqZWN0IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiZml4Q29uc3RyIiwiY29uc3RyIiwiZml4ZWRDb25zdHIiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJpbmNsdWRlcyIsImZpeE91dGZpbGUiLCJ0eXBlIiwib3V0ZmlsZSIsImdldEFic29sdXRlUGF0aCIsInNwbGl0Iiwic2hpZnQiLCJmaXhUeXBlIiwibWltZVR5cGVzIiwiZm9ybWF0cyIsInZhbHVlcyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsInBhdGgiLCJpc0Fic29sdXRlIiwiam9pbiIsImdldEJhc2U2NCIsImlucHV0IiwiQnVmZmVyIiwiZnJvbSIsInRvU3RyaW5nIiwiZ2V0TmV3RGF0ZSIsIkRhdGUiLCJ0cmltIiwiZ2V0TmV3RGF0ZVRpbWUiLCJnZXRUaW1lIiwiaXNPYmplY3QiLCJpdGVtIiwiaXNPYmplY3RFbXB0eSIsImtleXMiLCJsZW5ndGgiLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwic29tZSIsInBhdHRlcm4iLCJ0ZXN0IiwibWVhc3VyZVRpbWUiLCJzdGFydCIsInByb2Nlc3MiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJyb3VuZE51bWJlciIsInZhbHVlIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsIk1hdGgiLCJwb3ciLCJyb3VuZCIsIndyYXBBcm91bmQiLCJjdXN0b21Db2RlIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiaXNDYWxsYmFjayIsImVuZHNXaXRoIiwicmVhZEZpbGVTeW5jIiwic3RhcnRzV2l0aCIsImNvbG9ycyIsImxvZ2dpbmciLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsInBhdGhUb0xvZyIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibG9nIiwiYXJncyIsIm5ld0xldmVsIiwidGV4dHMiLCJsZXZlbCIsInByZWZpeCIsIl9sb2dUb0ZpbGUiLCJjb25zb2xlIiwiYXBwbHkiLCJ1bmRlZmluZWQiLCJjb25jYXQiLCJsb2dXaXRoU3RhY2siLCJlcnJvciIsImN1c3RvbU1lc3NhZ2UiLCJtYWluTWVzc2FnZSIsIm1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInB1c2giLCJpbml0TG9nZ2luZyIsImxvZ2dpbmdPcHRpb25zIiwiZGVzdCIsImZpbGUiLCJzZXRMb2dMZXZlbCIsImVuYWJsZUNvbnNvbGVMb2dnaW5nIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJpc0ludGVnZXIiLCJleGlzdHNTeW5jIiwibWtkaXJTeW5jIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiRXhwb3J0RXJyb3IiLCJFcnJvciIsImNvbnN0cnVjdG9yIiwic3RhdHVzQ29kZSIsInN1cGVyIiwidGhpcyIsInNldFN0YXR1cyIsInNldEVycm9yIiwibmFtZSIsIl9pbml0R2xvYmFsT3B0aW9ucyIsImluc3RhbmNlT3B0aW9ucyIsImdldE9wdGlvbnMiLCJnZXRJbnN0YW5jZSIsInNldEdsb2JhbE9wdGlvbnMiLCJjdXN0b21PcHRpb25zIiwiY2xpQXJncyIsImNvbmZpZ09wdGlvbnMiLCJjbGlPcHRpb25zIiwiX2xvYWRDb25maWdGaWxlIiwiX3BhaXJBcmd1bWVudFZhbHVlIiwiX3VwZGF0ZUdsb2JhbE9wdGlvbnMiLCJ1cGRhdGVPcHRpb25zIiwibmV3SW5zdGFuY2UiLCJtZXJnZU9wdGlvbnMiLCJvcmlnaW5hbE9wdGlvbnMiLCJuZXdPcHRpb25zIiwiZW50cmllcyIsIm1hcFRvTmV3T3B0aW9ucyIsIm9sZE9wdGlvbnMiLCJwcm9wZXJ0aWVzQ2hhaW4iLCJyZWR1Y2UiLCJvYmoiLCJwcm9wIiwiaW5kZXgiLCJpc0FsbG93ZWRDb25maWciLCJhbGxvd0Z1bmN0aW9ucyIsIm9iamVjdENvbmZpZyIsImV2YWwiLCJKU09OIiwic3RyaW5naWZpZWRPcHRpb25zIiwiX29wdGlvbnNTdHJpbmdpZnkiLCJwYXJzZWRPcHRpb25zIiwiXyIsImVudlZhbCIsImNvbmZpZ09wdCIsImNsaU9wdCIsImN1c3RvbU9wdCIsImNvbmZpZ1ZhbCIsImNsaVZhbCIsImN1c3RvbVZhbCIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJjdXN0b21Mb2dpY09wdGlvbnMiLCJjb25maWdJbmRleCIsImZpbmRJbmRleCIsImFyZyIsImNvbmZpZ0ZpbGVOYW1lIiwiaSIsIm9wdGlvbiIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwiX2dldFByb3RvY29sTW9kdWxlIiwiZ2V0IiwicmVzcG9uc2UiLCJyZXNwb25zZURhdGEiLCJvbiIsImNodW5rIiwidGV4dCIsImh0dHBzIiwiaHR0cCIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsInNldHVwSGlnaGNoYXJ0cyIsIkhpZ2hjaGFydHMiLCJhbmltT2JqZWN0IiwiZHVyYXRpb24iLCJjcmVhdGVDaGFydCIsImV4cG9ydE9wdGlvbnMiLCJzZXRPcHRpb25zIiwibWVyZ2UiLCJ3cmFwIiwic2V0T3B0aW9uc09iaiIsImlzUmVuZGVyQ29tcGxldGUiLCJDaGFydCIsInByb2NlZWQiLCJ1c2VyT3B0aW9ucyIsImNiIiwiZXhwb3J0aW5nIiwiZW5hYmxlZCIsInBsb3RPcHRpb25zIiwic2VyaWVzIiwibGFiZWwiLCJ0b29sdGlwIiwiYW5pbWF0aW9uIiwib25IaWdoY2hhcnRzUmVuZGVyIiwiYWRkRXZlbnQiLCJTZXJpZXMiLCJjaGFydCIsImFkZGl0aW9uYWxPcHRpb25zIiwiRnVuY3Rpb24iLCJmaW5hbE9wdGlvbnMiLCJmaW5hbENhbGxiYWNrIiwiZGVmYXVsdE9wdGlvbnMiLCJ0ZW1wbGF0ZSIsImJyb3dzZXIiLCJjcmVhdGVCcm93c2VyIiwicHVwcGV0ZWVyQXJncyIsImVuYWJsZWREZWJ1ZyIsImRlYnVnT3B0aW9ucyIsImxhdW5jaE9wdGlvbnMiLCJ1c2VyRGF0YURpciIsImhhbmRsZVNJR0lOVCIsImhhbmRsZVNJR1RFUk0iLCJoYW5kbGVTSUdIVVAiLCJ3YWl0Rm9ySW5pdGlhbFBhZ2UiLCJkZWZhdWx0Vmlld3BvcnQiLCJ0cnlDb3VudCIsIm9wZW4iLCJsYXVuY2giLCJzZXRUaW1lb3V0IiwiY2xvc2VCcm93c2VyIiwiY29ubmVjdGVkIiwiY2xvc2UiLCJuZXdQYWdlIiwicG9vbFJlc291cmNlIiwicGFnZSIsInNldENhY2hlRW5hYmxlZCIsIl9zZXRQYWdlQ29udGVudCIsIl9zZXRQYWdlRXZlbnRzIiwiaXNDbG9zZWQiLCJjbGVhclBhZ2UiLCJoYXJkUmVzZXQiLCJnb3RvIiwid2FpdFVudGlsIiwiZXZhbHVhdGUiLCJib2R5IiwiaW5uZXJIVE1MIiwiaWQiLCJ3b3JrQ291bnQiLCJhZGRQYWdlUmVzb3VyY2VzIiwiaW5qZWN0ZWRSZXNvdXJjZXMiLCJpbmplY3RlZEpzIiwianMiLCJjb250ZW50IiwiZmlsZXMiLCJpc0xvY2FsIiwianNSZXNvdXJjZSIsImFkZFNjcmlwdFRhZyIsImluamVjdGVkQ3NzIiwiY3NzIiwiY3NzSW1wb3J0cyIsIm1hdGNoIiwiY3NzSW1wb3J0UGF0aCIsImNzc1Jlc291cmNlIiwiYWRkU3R5bGVUYWciLCJjbGVhclBhZ2VSZXNvdXJjZXMiLCJyZXNvdXJjZSIsImRpc3Bvc2UiLCJvbGRDaGFydHMiLCJjaGFydHMiLCJvbGRDaGFydCIsImRlc3Ryb3kiLCJzY3JpcHRzVG9SZW1vdmUiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsInN0eWxlc1RvUmVtb3ZlIiwibGlua3NUb1JlbW92ZSIsImVsZW1lbnQiLCJyZW1vdmUiLCJzZXRDb250ZW50IiwiY3NzVGVtcGxhdGUiLCJzdmdUZW1wbGF0ZSIsInB1cHBldGVlckV4cG9ydCIsImlzU1ZHIiwic2l6ZSIsInN2Z0VsZW1lbnQiLCJxdWVyeVNlbGVjdG9yIiwiY2hhcnRIZWlnaHQiLCJiYXNlVmFsIiwiY2hhcnRXaWR0aCIsInN0eWxlIiwiem9vbSIsIm1hcmdpbiIsIngiLCJ5IiwiX2dldENsaXBSZWdpb24iLCJ2aWV3cG9ydEhlaWdodCIsImFicyIsImNlaWwiLCJ2aWV3cG9ydFdpZHRoIiwicmVzdWx0Iiwic2V0Vmlld3BvcnQiLCJkZXZpY2VTY2FsZUZhY3RvciIsIl9jcmVhdGVTVkciLCJfY3JlYXRlSW1hZ2UiLCJfY3JlYXRlUERGIiwiJGV2YWwiLCJnZXRCb3VuZGluZ0NsaWVudFJlY3QiLCJ0cnVuYyIsIm91dGVySFRNTCIsImNsaXAiLCJyYWNlIiwic2NyZWVuc2hvdCIsImVuY29kaW5nIiwiZnVsbFBhZ2UiLCJvcHRpbWl6ZUZvclNwZWVkIiwiY2FwdHVyZUJleW9uZFZpZXdwb3J0IiwicXVhbGl0eSIsIm9taXRCYWNrZ3JvdW5kIiwiX3Jlc29sdmUiLCJlbXVsYXRlTWVkaWFUeXBlIiwicGRmIiwicG9vbFN0YXRzIiwiZXhwb3J0c0F0dGVtcHRlZCIsImV4cG9ydHNQZXJmb3JtZWQiLCJleHBvcnRzRHJvcHBlZCIsImV4cG9ydHNGcm9tU3ZnIiwiZXhwb3J0c0Zyb21PcHRpb25zIiwiZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyIsImV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzIiwidGltZVNwZW50IiwidGltZVNwZW50QXZlcmFnZSIsImluaXRQb29sIiwicG9vbE9wdGlvbnMiLCJQb29sIiwiX2ZhY3RvcnkiLCJhY3F1aXJlVGltZW91dE1pbGxpcyIsImNyZWF0ZVRpbWVvdXRNaWxsaXMiLCJkZXN0cm95VGltZW91dE1pbGxpcyIsImlkbGVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpcyIsInJlYXBJbnRlcnZhbE1pbGxpcyIsInByb3BhZ2F0ZUNyZWF0ZUVycm9yIiwiY2xlYXJTdGF0dXMiLCJfZXZlbnRJZCIsImluaXRpYWxSZXNvdXJjZXMiLCJhY3F1aXJlIiwicHJvbWlzZSIsInJlbGVhc2UiLCJraWxsUG9vbCIsIndvcmtlciIsInVzZWQiLCJkZXN0cm95ZWQiLCJwb3N0V29yayIsIndvcmtlckhhbmRsZSIsImdldFBvb2xJbmZvIiwiYWNxdWlyZUNvdW50ZXIiLCJyZXF1ZXN0SWQiLCJ3b3JrU3RhcnQiLCJleHBvcnRDb3VudGVyIiwiZXhwb3J0VGltZSIsImdldFBvb2xTdGF0cyIsImdldFBvb2xJbmZvSlNPTiIsIm51bVVzZWQiLCJhdmFpbGFibGUiLCJudW1GcmVlIiwiYWxsQ3JlYXRlZCIsInBlbmRpbmdBY3F1aXJlcyIsIm51bVBlbmRpbmdBY3F1aXJlcyIsInBlbmRpbmdDcmVhdGVzIiwibnVtUGVuZGluZ0NyZWF0ZXMiLCJwZW5kaW5nVmFsaWRhdGlvbnMiLCJudW1QZW5kaW5nVmFsaWRhdGlvbnMiLCJwZW5kaW5nRGVzdHJveXMiLCJhYnNvbHV0ZUFsbCIsImNyZWF0ZSIsInV1aWQiLCJyYW5kb20iLCJzdGFydERhdGUiLCJ2YWxpZGF0ZSIsIm1haW5GcmFtZSIsImRldGFjaGVkIiwicmVtb3ZlQWxsTGlzdGVuZXJzIiwic2FuaXRpemUiLCJKU0RPTSIsIkRPTVB1cmlmeSIsIkFERF9UQUdTIiwic2luZ2xlRXhwb3J0Iiwic3RhcnRFeHBvcnQiLCJkYXRhIiwiYmF0Y2hFeHBvcnQiLCJiYXRjaEZ1bmN0aW9ucyIsInBhaXIiLCJiYXRjaFJlc3VsdHMiLCJhbGxTZXR0bGVkIiwicmVhc29uIiwiZXhwb3J0aW5nT3B0aW9ucyIsImVuZENhbGxiYWNrIiwiZmlsZUNvbnRlbnQiLCJfZXhwb3J0RnJvbVN2ZyIsIl9leHBvcnRGcm9tT3B0aW9ucyIsImdldEFsbG93Q29kZUV4ZWN1dGlvbiIsInNldEFsbG93Q29kZUV4ZWN1dGlvbiIsImlucHV0VG9FeHBvcnQiLCJfcHJlcGFyZUV4cG9ydCIsIl9oYW5kbGVDdXN0b21Mb2dpYyIsIl9oYW5kbGVHbG9iYWxBbmRUaGVtZSIsIl9maW5kQ2hhcnRTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidGltZXJJZHMiLCJhZGRUaW1lciIsImNsZWFyQWxsVGltZXJzIiwiY2xlYXJJbnRlcnZhbCIsImNsZWFyVGltZW91dCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcXVlc3QiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RhdHVzIiwianNvbiIsImVycm9yTWlkZGxld2FyZSIsImFwcCIsInVzZSIsInJhdGVMaW1pdGluZ01pZGRsZXdhcmUiLCJyYXRlTGltaXRpbmdPcHRpb25zIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwicmF0ZUxpbWl0Iiwid2luZG93TXMiLCJsaW1pdCIsImRlbGF5TXMiLCJoYW5kbGVyIiwiZm9ybWF0Iiwic2VuZCIsImRlZmF1bHQiLCJza2lwIiwicXVlcnkiLCJhY2Nlc3NfdG9rZW4iLCJjb250ZW50VHlwZU1pZGRsZXdhcmUiLCJjb250ZW50VHlwZSIsImhlYWRlcnMiLCJyZXF1ZXN0Qm9keU1pZGRsZXdhcmUiLCJjb25uZWN0aW9uIiwicmVtb3RlQWRkcmVzcyIsInZhbGlkYXRlZE9wdGlvbnMiLCJwYXJhbXMiLCJmaWxlbmFtZSIsInZhbGlkYXRpb25NaWRkbGV3YXJlIiwicG9zdCIsInJldmVyc2VkTWltZSIsInBuZyIsImpwZWciLCJnaWYiLCJyZXF1ZXN0RXhwb3J0IiwicmVxdWVzdENvdW50ZXIiLCJjb25uZWN0aW9uQWJvcnRlZCIsInNvY2tldCIsImhhZEVycm9ycyIsImhlYWRlciIsImF0dGFjaG1lbnQiLCJleHBvcnRSb3V0ZXMiLCJzZXJ2ZXJTdGFydFRpbWUiLCJwYWNrYWdlRmlsZSIsInN1Y2Nlc3NSYXRlcyIsInJlY29yZEludGVydmFsIiwid2luZG93U2l6ZSIsIl9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlIiwiYSIsImIiLCJfc3RhcnRTdWNjZXNzUmF0ZSIsInNldEludGVydmFsIiwic3RhdHMiLCJzdWNjZXNzUmF0aW8iLCJoZWFsdGhSb3V0ZXMiLCJwZXJpb2QiLCJtb3ZpbmdBdmVyYWdlIiwiYm9vdFRpbWUiLCJ1cHRpbWUiLCJmbG9vciIsInNlcnZlclZlcnNpb24iLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VFeHBvcnRUaW1lIiwiYXR0ZW1wdGVkRXhwb3J0cyIsInBlcmZvcm1lZEV4cG9ydHMiLCJmYWlsZWRFeHBvcnRzIiwic3VjZXNzUmF0aW8iLCJ0b0ZpeGVkIiwic3ZnRXhwb3J0cyIsImpzb25FeHBvcnRzIiwic3ZnRXhwb3J0c0F0dGVtcHRzIiwianNvbkV4cG9ydHNBdHRlbXB0cyIsInVpUm91dGVzIiwic2VuZEZpbGUiLCJhY2NlcHRSYW5nZXMiLCJ2ZXJzaW9uQ2hhbmdlUm91dGVzIiwiYWRtaW5Ub2tlbiIsInRva2VuIiwiYWN0aXZlU2VydmVycyIsIk1hcCIsImV4cHJlc3MiLCJzdGFydFNlcnZlciIsInNlcnZlck9wdGlvbnMiLCJ1cGxvYWRMaW1pdEJ5dGVzIiwic3RvcmFnZSIsIm11bHRlciIsIm1lbW9yeVN0b3JhZ2UiLCJ1cGxvYWQiLCJsaW1pdHMiLCJmaWVsZFNpemUiLCJkaXNhYmxlIiwiY29ycyIsIm1ldGhvZHMiLCJzZXQiLCJ1cmxlbmNvZGVkIiwiZXh0ZW5kZWQiLCJub25lIiwic3RhdGljIiwiaHR0cFNlcnZlciIsImNyZWF0ZVNlcnZlciIsIl9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwibGlzdGVuIiwiY2VydCIsInJlYWRGaWxlIiwiaHR0cHNTZXJ2ZXIiLCJjbG9zZVNlcnZlcnMiLCJkZWxldGUiLCJnZXRTZXJ2ZXJzIiwiZ2V0RXhwcmVzcyIsImdldEFwcCIsImVuYWJsZVJhdGVMaW1pdGluZyIsIm1pZGRsZXdhcmVzIiwic2h1dGRvd25DbGVhblVwIiwiZXhpdENvZGUiLCJleGl0IiwiaW5pdEV4cG9ydCIsImluaXRPcHRpb25zIiwiX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzIiwiY29kZSJdLCJtYXBwaW5ncyI6InduQkEyQk8sTUFBTUEsWUFBWUMsSUFBYUEsY0FBQyxJQUFJQyxJQUFJLE9BQVEsb0JBQUFDLFNBQUFDLFFBQUEsT0FBQUMsY0FBQUMsWUFBQUMsS0FBQUMsd0JBQUEsV0FBQUEsdUJBQUFDLFFBQUFDLGVBQUFGLHVCQUFBRyxLQUFBLElBQUFULElBQUEsWUFBQUMsU0FBQVMsU0FBQUwsT0ErQmhELFNBQVNNLFNBQVNDLEdBRXZCLEdBQWUsT0FBWEEsR0FBcUMsaUJBQVhBLEVBQzVCLE9BQU9BLEVBSVQsTUFBTUMsRUFBYUMsTUFBTUMsUUFBUUgsR0FBVSxHQUFLLEdBR2hELElBQUssTUFBTUksS0FBT0osRUFDWkssT0FBT0MsVUFBVUMsZUFBZUMsS0FBS1IsRUFBUUksS0FDL0NILEVBQVdHLEdBQU9MLFNBQVNDLEVBQU9JLEtBS3RDLE9BQU9ILENBQ1QsQ0EyRE8sU0FBU1EsVUFBVUMsR0FDeEIsSUFFRSxNQUFNQyxFQUFjLEdBQUdELEVBQU9FLGNBQWNDLFFBQVEsUUFBUyxXQVE3RCxNQUxvQixVQUFoQkYsR0FDRkEsRUFBWUMsY0FJUCxDQUFDLFFBQVMsYUFBYyxXQUFZLGNBQWNFLFNBQ3ZESCxHQUVFQSxFQUNBLE9BQ1IsQ0FBSSxNQUVBLE1BQU8sT0FDUixDQUNILENBWU8sU0FBU0ksV0FBV0MsRUFBTUMsR0FPL0IsTUFBTyxHQUxVQyxnQkFBZ0JELEdBQVcsU0FDekNFLE1BQU0sS0FDTkMsV0FHbUJKLEdBQ3hC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 diff --git a/dist/index.esm.js b/dist/index.esm.js index 4d431f23..4d06b6f0 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -1,2 +1,2 @@ -import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import{readFile}from"fs/promises";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?e:join(__dirname,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t.message,{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=e}function enableFileLogging(e,t,o){logging.toFile=o,o&&(logging.dest=e,logging.file=t)}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initGlobalOptions(defaultConfig);function getOptions(e=!0){return e?globalOptions:deepCopy(globalOptions)}function setOptions(e={},t=[],o=!1){let r={},n={};t.length&&(r=_loadConfigFile(t),n=_pairArgumentValue(nestedProps,t));const i=getOptions(o);return _updateOptions(defaultConfig,i,r,e,n),i}function mergeOptions(e,t){if(isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?mergeOptions(e[o],r):void 0!==r?r:e[o];return e}function mapToNewOptions(e){const t={};if("[object Object]"===Object.prototype.toString.call(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initGlobalOptions(e){const t={};for(const[o,r]of Object.entries(e))t[o]=Object.prototype.hasOwnProperty.call(r,"value")?r.value:_initGlobalOptions(r);return t}function _updateOptions(e,t,o,r,n){Object.keys(e).forEach((i=>{const s=e[i],a=o&&o[i],l=r&&r[i],c=n&&n[i];if(void 0===s.value)_updateOptions(s,t[i],a,l,c);else{null!=a&&(t[i]=a);const e=envs[s.envLink];s.envLink in envs&&null!=e&&(t[i]=e),null!=l&&(t[i]=l),null!=c&&(t[i]=c)}}))}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _loadConfigFile(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,""))),o=t>-1&&e[t+1];if(o)try{return JSON.parse(readFileSync(getAbsolutePath(o)))}catch(e){logWithStack(2,e,`[config] Unable to load the configuration from the ${o} file.`)}return{}}function _pairArgumentValue(e,t){const o={};for(let r=0;r{if(i.length-1===s){const i=t[++r];i||log(2,`[config] Missing value for the CLI '--${n}' argument. Using the default value.`),e[o]=i||null}else void 0===e[o]&&(e[o]={});return e[o]}),o)}return o}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n));if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=getOptions();t.highcharts.version=e,await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e){const{getOptions:t,merge:o,setOptions:r,wrap:n}=Highcharts;Highcharts.setOptionsObj=o(!1,{},t()),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=o(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,o){e.apply(this,[t,o])}));const i={chart:{animation:!1,height:e.export.height,width:e.export.width},exporting:{enabled:!1}},s=new Function(`return ${e.export.instr}`)(),a=new Function(`return ${e.export.themeOptions}`)(),l=new Function(`return ${e.export.globalOptions}`)(),c=o(!1,a,s,i),p=e.customLogic.callback?new Function(`return ${e.customLogic.callback}`)():null;e.customLogic.customCode&&new Function("options",e.customLogic.customCode)(s),l&&r(l),Highcharts[e.export.constr]("container",c,p);const u=t();for(const e in u)"function"!=typeof u[e]&&delete u[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:join(__dirname,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t){const o=[];try{const r=t.export;let n=!1;if(r.svg){if(log(4,"[export] Treating as SVG input."),"svg"===r.type)return r.svg;n=!0,await _setAsSvg(e,r.svg)}else log(4,"[export] Treating as JSON config."),await _setAsOptions(e,t);o.push(...await addPageResources(e,t.customLogic));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(r.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||r.height)),c=Math.abs(Math.ceil(i.chartWidth||r.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(r.scale)}),r.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,r.type,{width:c,height:l,x:s,y:a},r.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,r.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${r.type}.`,400)}return await clearPageResources(e,o),p}catch(t){return await clearPageResources(e,o),t}}async function _setAsSvg(e,t){await e.setContent(svgTemplate(t),{waitUntil:"domcontentloaded"})}async function _setAsOptions(e,t){await e.evaluate(createChart,t)}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e=getOptions().pool,t=[]){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,getOptions().pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,e._requestId?`[benchmark] Request [${e._requestId}] - `:"[benchmark] ",`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+"Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.").setError(i):new ExportError("[pool] "+(e._requestId?`Request [${e._requestId}] - `:"")+`Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,e._requestId?`[benchmark] Request [${e._requestId}] - `:"[benchmark] ",`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport(e,(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({...e,export:{...e.export,infile:o[0],outfile:o[1]}},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{log(4,"[chart] Starting the exporting process.");const o=mergeOptions(getOptions(!1),e),r=o.export;if(null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t=getOptions().server.rateLimiting){try{if(t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,max:r.max,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>!1!==r.skipKey&&!1!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.max} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}class HttpError extends ExportError{constructor(e,t){super(e,t)}setStatus(e){return this.statusCode=e,this}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new HttpError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4().replace(/-/g,"");if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new HttpError("[validation] The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.",400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new HttpError("[validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new HttpError("[validation] 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);return e.validatedOptions={_requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${fixType(t.type)}`,type:fixType(t.type,t.outfile),constr:fixConstr(t.constr),b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n._requestId;log(4,`[export] Got an incoming HTTP request with ID ${i}.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new HttpError("[export] Unexpected return of the export result from the chart generation. Please check your request data.",400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"))),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new HttpError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new HttpError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new HttpError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new HttpError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e=getOptions().server){try{if(!e.enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const t=1024*e.uploadLimit*1024,o=multer.memoryStorage(),r=multer({storage:o,limits:{fieldSize:t}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:t})),app.use(express.urlencoded({extended:!0,limit:t})),app.use(r.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),healthRoutes(app),exportRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){rateLimitingMiddleware(app,e)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=mergeOptions(getOptions(!1),e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp(0)})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={server:server,startServer:startServer,getOptions:getOptions,setOptions:setOptions,mergeOptions:mergeOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,checkAndUpdateCache:checkAndUpdateCache,initPool:initPool,killPool:killPool,log:log,logWithStack:logWithStack,setLogLevel:setLogLevel,enableConsoleLogging:enableConsoleLogging,enableFileLogging:enableFileLogging,shutdownCleanUp:shutdownCleanUp};export{index as default,initExport}; +import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import{readFile}from"fs/promises";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?e:join(__dirname,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env);class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 globalOptions=_initGlobalOptions(defaultConfig),instanceOptions=deepCopy(globalOptions);function getOptions(e=!0){return e?instanceOptions:globalOptions}function setGlobalOptions(e={},t=[]){let o={},r={};return t&&Array.isArray(t)&&t.length&&(o=_loadConfigFile(t,globalOptions.customLogic),r=_pairArgumentValue(nestedProps,t)),_updateGlobalOptions(defaultConfig,globalOptions,o,r,e),globalOptions}function updateOptions(e,t=!1){return t&&(Object.keys(instanceOptions).forEach((e=>{delete instanceOptions[e]})),mergeOptions(instanceOptions,deepCopy(globalOptions))),mergeOptions(instanceOptions,e),instanceOptions}function mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initGlobalOptions(e){const t={};for(const[o,r]of Object.entries(e))if(Object.prototype.hasOwnProperty.call(r,"value")){const e=envs[r.envLink];t[o]=null!=e?e:r.value}else t[o]=_initGlobalOptions(r);return t}function _updateGlobalOptions(e,t,o,r,n){Object.keys(e).forEach((i=>{const s=e[i],a=o&&o[i],l=r&&r[i],c=n&&n[i];if(void 0===s.value)_updateGlobalOptions(s,t[i],a,l,c);else{null!=a&&(t[i]=a);const e=envs[s.envLink];s.envLink in envs&&null!=e&&(t[i]=e),null!=l&&(t[i]=l),null!=c&&(t[i]=c)}}))}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _loadConfigFile(e,t){const o=e.findIndex((e=>"loadConfig"===e.replace(/-/g,""))),r=o>-1&&e[o+1];if(r&&t.allowFileResources)try{return isAllowedConfig(readFileSync(getAbsolutePath(r),"utf8"),!1,t.allowCodeExecution)}catch(e){logWithStack(2,e,`[config] Unable to load the configuration from the ${r} file.`)}return{}}function _pairArgumentValue(e,t){const o={};for(let r=0;r{if(i.length-1===s){const i=t[++r];i||log(2,`[config] Missing value for the CLI '--${n}' argument. Using the default value.`),e[o]=i||null}else void 0===e[o]&&(e[o]={});return e[o]}),o)}return o}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}const cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=getOptions();t.highcharts.version=e,await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:join(__dirname,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.",400);const o=mergeOptions(deepCopy(getOptions()),{export:e.export,customLogic:e.customLogic}),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e={}){const t=updateOptions(e,!0);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,setGlobalOptions:setGlobalOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; //# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index 6ba656dd..a9644ea5 100644 --- a/dist/index.esm.js.map +++ b/dist/index.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/errors/HttpError.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is 0.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is null.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? path : join(__dirname, path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message. Accepts a variable amount of arguments. Arguments after\r\n * the `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 * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the rest are strings to build a message with.\r\n *\r\n * @returns {void} Ends the function execution when attempting to log\r\n * information at a higher level than what is allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object.\r\n * @param {string} customMessage - An optional custom message to be logged\r\n * along with the error.\r\n *\r\n * @returns {void} Ends the function execution when attempting to log\r\n * information at a higher level than what is allowed.\r\n */\r\nexport function 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 the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - Object containing `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (level >= 0 && level <= logging.levelsDesc.length) {\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update options for the console logging\r\n logging.toConsole = toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path for the log file.\r\n * @param {string} file - The log file name.\r\n * @param {boolean} toFile - The flag for setting the logging to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update options for the file logging\r\n logging.toFile = toFile;\r\n\r\n // Set the `dest` and `file` only if the file logging is enabled\r\n if (toFile) {\r\n logging.dest = dest;\r\n logging.file = file;\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, isObject, deepCopy, getAbsolutePath } from './utils.js';\r\nimport { defaultConfig, nestedProps, absoluteProps } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initGlobalOptions(defaultConfig);\r\n\r\n/**\r\n * Gets the reference to the global options of the server instance object\r\n * or its copy.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getReference=true] - Optional parameter to decide whether\r\n * to return the reference to the global options of the server instance object\r\n * or return a copy of it. The default value is true.\r\n *\r\n * @returns {Object} The reference to the global options of the server instance\r\n * object or its copy.\r\n */\r\nexport function getOptions(getReference = true) {\r\n return getReference ? globalOptions : deepCopy(globalOptions);\r\n}\r\n\r\n/**\r\n * Sets the global options of the export server instance, keeping the principle\r\n * of the options load priority from all available sources. It accepts optional\r\n * `customOptions` object and `cliArgs` array with arguments from the CLI. These\r\n * options will be validated and applied if provided.\r\n *\r\n * The priority order of setting values is:\r\n *\r\n * 1. Options from the `lib/schemas/config.js` file (default values).\r\n * 2. Options from a custom JSON file (loaded by the `loadConfig` option).\r\n * 3. Options from the environment variables (the `.env` file).\r\n * 4. Options from the first parameter (by default an empty object).\r\n * 5. Options from the CLI.\r\n *\r\n * @function setOptions\r\n *\r\n * @param {Object} [customOptions={}] - Optional custom options for additional\r\n * configuration. The default value is an empty object.\r\n * @param {Array.} [cliArgs=[]] - Optional command line arguments\r\n * for additional configuration. The default value is an empty array.\r\n * @param {boolean} [modifyGlobal=false] - Optional parameter to decide\r\n * whether to update and return the reference to the global options\r\n * of the server instance object or return a copy of it. The default value\r\n * is false.\r\n *\r\n * @returns {Object} The updated general options object, reflecting the merged\r\n * configuration from all available sources.\r\n */\r\nexport function setOptions(\r\n customOptions = {},\r\n cliArgs = [],\r\n modifyGlobal = false\r\n) {\r\n // Object for options loaded via the `loadConfig` option\r\n let configOptions = {};\r\n\r\n // Object for options from the CLI\r\n let cliOptions = {};\r\n\r\n // Only for the CLI usage\r\n if (cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `loadConfig`\r\n configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Get options from the CLI\r\n cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n }\r\n\r\n // Get the reference to the global options object or a copy of the object\r\n const generalOptions = getOptions(modifyGlobal);\r\n\r\n // Update values of the general options with values from each source possible\r\n _updateOptions(\r\n defaultConfig,\r\n generalOptions,\r\n configOptions,\r\n customOptions,\r\n cliOptions\r\n );\r\n\r\n // Return options\r\n return generalOptions;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function mergeOptions(originalOptions, newOptions) {\r\n // Check if the `newOptions` is a correct object\r\n if (isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key];\r\n }\r\n }\r\n\r\n // Return the result options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (Object.prototype.toString.call(oldOptions) === '[object Object]') {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is false.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is false.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is false.\r\n */\r\nexport function printVersion(noLogo = false) {\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(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg')).toString().bold\r\n .yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns global options object based on provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * @function _initGlobalOptions\r\n *\r\n * @param {Object} config - The configuration object to be used for initializing\r\n * options.\r\n *\r\n * @returns {Object} Initialized options object.\r\n */\r\nfunction _initGlobalOptions(config) {\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n options[name] = Object.prototype.hasOwnProperty.call(item, 'value')\r\n ? item.value\r\n : _initGlobalOptions(item);\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Updates options object with values from various sources, following a specific\r\n * prioritization order. The function checks for values in the following order\r\n * of precedence: the `loadConfig` configuration options, environment variables,\r\n * custom options, and CLI options.\r\n *\r\n * @function _updateOptions\r\n *\r\n * @param {Object} config - The configuration object, which includes the initial\r\n * settings and metadata for each option. This object is used to determine\r\n * the structure and default values for the options.\r\n * @param {Object} options - The options object that will be updated with values\r\n * from other sources.\r\n * @param {Object} configOpt - The configuration options object, loaded with\r\n * the `loadConfig` option, which may provide values to override defaults.\r\n * @param {Object} customOpt - The custom options object, typically containing\r\n * additional and user-defined values, which may override configuration options.\r\n * @param {Object} cliOpt - The CLI options object, which may include values\r\n * provided through command-line arguments and has the highest precedence among\r\n * options.\r\n */\r\nfunction _updateOptions(config, options, configOpt, customOpt, cliOpt) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the config entry of a specific option\r\n const entry = config[key];\r\n\r\n // Gather values for the options from every possible source, if exists\r\n const configVal = configOpt && configOpt[key];\r\n const customVal = customOpt && customOpt[key];\r\n const cliVal = cliOpt && cliOpt[key];\r\n\r\n // If the value not found, need to go deeper\r\n if (typeof entry.value === 'undefined') {\r\n _updateOptions(entry, options[key], configVal, customVal, cliVal);\r\n } else {\r\n // If a value from custom JSON options exists, it take precedence\r\n if (configVal !== undefined && configVal !== null) {\r\n options[key] = configVal;\r\n }\r\n\r\n // If a value from environment variables exists, it take precedence\r\n const envVal = envs[entry.envLink];\r\n if (entry.envLink in envs && envVal !== undefined && envVal !== null) {\r\n options[key] = envVal;\r\n }\r\n\r\n // If a value from user options exists, it take precedence\r\n if (customVal !== undefined && customVal !== null) {\r\n options[key] = customVal;\r\n }\r\n\r\n // If a value from CLI options exists, it take precedence\r\n if (cliVal !== undefined && cliVal !== null) {\r\n options[key] = cliVal;\r\n }\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Check if the `loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `loadConfig` is present and has a correct value\r\n if (configFileName) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return JSON.parse(readFileSync(getAbsolutePath(configFileName)));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[config] Unable to load the configuration from the ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options.\r\n * It may include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n setOptions,\r\n mergeOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - Object containing `highcharts` options.\r\n * @param {Object} serverProxyOptions - Object containing `server.proxy`\r\n * options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } = highchartsOptions;\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 !== highchartsOptions.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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Get the reference to the global options to update to the new version\r\n const options = getOptions();\r\n\r\n // Set to the new version\r\n options.highcharts.version = newVersion;\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is false.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - Object containing `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - Object containing `server.proxy`\r\n * options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - Object containing `highcharts` options.\r\n * @param {Object} serverProxyOptions - Object containing `server.proxy`\r\n * options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n */\r\nexport async function createChart(options) {\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\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: options.export.height,\r\n width: options.export.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${options.export.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${options.export.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(\r\n `return ${options.export.globalOptions}`\r\n )();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = options.customLogic.callback\r\n ? new Function(`return ${options.customLogic.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (options.customLogic.customCode) {\r\n new Function('options', options.customLogic.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[options.export.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is false.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The object containing `customLogic`\r\n * options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n 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(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, options) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n let isSVG = false;\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await _setAsSvg(page, exportOptions.svg);\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await _setAsOptions(page, options);\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.push(\r\n ...(await addPageResources(page, options.customLogic))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Sets the specified page's content with provided export input within\r\n * the window context using the `page.setContent` function.\r\n *\r\n * @async\r\n * @function _setAsSvg\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {Promise} A Promise that resolves after the content is set.\r\n */\r\nasync function _setAsSvg(page, svg) {\r\n await page.setContent(svgTemplate(svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n}\r\n\r\n/**\r\n * Sets the options with specified export input and sizes as configuration into\r\n * the `createChart` function within the window context using\r\n * the `page.evaluate` function.\r\n *\r\n * @async\r\n * @function _setAsOptions\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves after the configuration\r\n * is set.\r\n */\r\nasync function _setAsOptions(page, options) {\r\n await page.evaluate(createChart, options);\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { getOptions } from './config.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} [poolOptions=getOptions().pool] - Object containing `pool`\r\n * options. The default value is the global pool options of the export server\r\n * instance.\r\n * @param {Array.} [puppeteerArgs=[]] - Additional arguments\r\n * for Puppeteer launch. The default value is an empty array.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(\r\n poolOptions = getOptions().pool,\r\n puppeteerArgs = []\r\n) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\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 * @async\r\n * @function killPool\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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (getOptions().pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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._requestId\r\n ? `[benchmark] Request [${options._requestId}] - `\r\n : '[benchmark] ',\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[pool] ' +\r\n (options._requestId ? `Request [${options._requestId}] - ` : '') +\r\n `Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Perform an export on a puppeteer level\r\n const exportCounter = measureTime();\r\n const result = await puppeteerExport(workerHandle.page, options);\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 '[pool] ' +\r\n (options._requestId ? `Request [${options._requestId}] - ` : '') +\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 '[pool] ' +\r\n (options._requestId ? `Request [${options._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._requestId\r\n ? `[benchmark] Request [${options._requestId}] - `\r\n : '[benchmark] ',\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - Object containing `pool` options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { getOptions, mergeOptions, isAllowedConfig } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { killPool, postWork, getPoolStats } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the image in the provided outfile.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which may be a partial\r\n * or complete set of options. It must contain at least one of the following\r\n * properties: `infile`, `instr`, `options`, or `svg` to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(options, async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n\r\n // Kill pool and close browser after finishing single export\r\n await killPool();\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\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;...\". Results are saved\r\n * in provided outfiles.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which may be a partial\r\n * or complete set of options. It must contain the `batch` option to generate\r\n * valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `customOptions` parameter is an object that\r\n * may be partial or complete set of options. The `endCallback` is called when\r\n * the export is completed, with the `error` object as the first argument\r\n * and the `data` object as the second, which contains the Base64 representation\r\n * of the chart in the `result` property and the full set of export options\r\n * in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} customOptions - The `customOptions` object, which may\r\n * be a partial or complete set of options. If the provided options are partial,\r\n * missing values will be merged with the default general options, retrieved\r\n * using the `getOptions` function.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing work or upon error occurance of the exporting process. The first\r\n * argument is `error` object and the `data` object is the second, that contains\r\n * the Base64 representation of the chart in the `result` property and the full\r\n * set of export options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(customOptions, endCallback) {\r\n try {\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Merge the custom options into default ones\r\n const options = mergeOptions(getOptions(false), customOptions);\r\n\r\n // Get the export options\r\n const exportOptions = options.export;\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The `options` object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The object containing `export` options.\r\n *\r\n * @returns {Object} An object containing the calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The object containing `customLogic`\r\n * options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is null.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The object containing `export` options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n startExport,\r\n singleExport,\r\n batchExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} [rateLimitingOptions=getOptions().server.rateLimiting] -\r\n * Object containing `rateLimiting` options. The default value is the global\r\n * rate limiting options of the export server instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(\r\n app,\r\n rateLimitingOptions = getOptions().server.rateLimiting\r\n) {\r\n try {\r\n // Check if the rate limiting is enabled\r\n if (rateLimitingOptions.enable) {\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: rateLimitingOptions.maxRequests || 30,\r\n window: rateLimitingOptions.window || 1,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || false,\r\n skipToken: rateLimitingOptions.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 } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 ExportError from './ExportError.js';\r\n\r\n/**\r\n * A custom HTTP error class that extends the `ExportError`. Used to handle\r\n * errors with HTTP status codes.\r\n */\r\nclass HttpError extends ExportError {\r\n /**\r\n * Creates an instance of the `HttpError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super(message, statusCode);\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {HttpError} The updated instance of the `HttpError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\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-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport {\r\n fixConstr,\r\n fixType,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound\r\n} from '../../utils.js';\r\n\r\nimport HttpError from '../../errors/HttpError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {HttpError} Throws an `HttpError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new HttpError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {HttpError} Throws an `HttpError` if the body is not correct.\r\n * @throws {HttpError} Throws an `HttpError` if the chart data from the body\r\n * is not correct.\r\n * @throws {HttpError} Throws an `HttpError` in case of the private range url\r\n * error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid().replace(/-/g, '');\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new HttpError(\r\n \"[validation] 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new HttpError(\r\n \"[validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new HttpError(\r\n \"[validation] 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 // Get options from the body and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `_requestId` property in the validated options\r\n _requestId: requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${fixType(body.type)}`,\r\n type: fixType(body.type, body.outfile),\r\n constr: fixConstr(body.constr),\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } 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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions._requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Got an incoming HTTP request with ID ${requestId}.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new HttpError(\r\n '[export] Unexpected return of the export result from the chart generation. Please check your request data.',\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(readFileSync(join(__dirname, 'package.json')));\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { updateHighchartsVersion, getHighchartsVersion } 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 `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n // Get the token directly from envs\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 '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new HttpError(\r\n '[version] Invalid or missing token: Set the token in the hc-auth header.',\r\n 401\r\n );\r\n }\r\n\r\n // Compare versions\r\n const newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new HttpError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new HttpError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFile } from 'fs/promises';\r\nimport { join } 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 { getOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts HTTP or/and HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains all server related properties (see\r\n * the `server` section in the `lib/schemas/config.js` file for a reference).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} [serverOptions=getOptions().server] - Object containing\r\n * `server` options. The default value is the global server options\r\n * of the export server instance.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when the server should not be enabled or when no valid Express app\r\n * is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions = getOptions().server) {\r\n try {\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 readFile(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = await readFile(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n healthRoutes(app);\r\n exportRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - Object containing `rateLimiting`\r\n * options.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n rateLimitingMiddleware(app, rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Cleans up function to trigger before ending process for the graceful\r\n * shutdown.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} exitCode - An exit code for the `process.exit()` function.\r\n */\r\nexport async function shutdownCleanUp(exitCode) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport {\r\n getOptions,\r\n setOptions,\r\n mergeOptions,\r\n mapToNewOptions\r\n} from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\nimport server, { startServer } from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage. This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} customOptions - The `customOptions` object, which may\r\n * be a partial or complete set of options. If the provided options are partial,\r\n * missing values will be merged with the default general options, retrieved\r\n * using the `getOptions` function.\r\n */\r\nexport async function initExport(customOptions) {\r\n // Get the global options object copy and extend it with the incoming options\r\n const options = mergeOptions(getOptions(false), customOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] 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, `[process] 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, `[process] 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, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n server,\r\n startServer,\r\n\r\n // Options\r\n getOptions,\r\n setOptions,\r\n mergeOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Cache\r\n checkAndUpdateCache,\r\n\r\n // Pool\r\n initPool,\r\n killPool,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n\r\n // Utils\r\n shutdownCleanUp\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","join","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","existsSync","mkdirSync","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initGlobalOptions","getOptions","getReference","setOptions","customOptions","cliArgs","modifyGlobal","configOptions","cliOptions","_loadConfigFile","_pairArgumentValue","generalOptions","_updateOptions","mergeOptions","originalOptions","newOptions","entries","mapToNewOptions","oldOptions","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","configOpt","customOpt","cliOpt","configVal","customVal","cliVal","envVal","stringifyFunctions","stringify","replaceAll","Error","configIndex","findIndex","arg","configFileName","i","option","async","fetch","requestOptions","Promise","resolve","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","setupHighcharts","Highcharts","animObject","duration","createChart","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","customLogicOptions","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","exportOptions","isSVG","_setAsSvg","_setAsOptions","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","_requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","msg","rateOptions","limiter","rateLimit","windowMs","delayMs","handler","format","send","default","skip","query","access_token","HttpError","setStatus","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","requestId","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","limit","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","readFile","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","_attachProcessExitListeners","code"],"mappings":"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B,GAGP,CCrOO,MAAMiB,cAAgB,CAC3BC,UAAW,CACT7B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFqD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbnG,KAAM,OACNoG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP5D,MAAO,SACPqD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbnG,KAAM,SAGVuG,OAAQ,CACN7D,MAAO,8BACPqD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbnG,KAAM,SAGVwG,WAAY,CACV9D,OAAO,EACPqD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbnG,KAAM,WAGVyG,UAAW,CACT/D,MAAO,SACPqD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbnG,KAAM,SAGV0G,YAAa,CACXhE,MAAO,CAAC,aAAc,kBAAmB,iBACzCqD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbnG,KAAM,cACN2G,aAAc,0DAGlBC,cAAe,CACblE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,kBACA,SACA,cACA,mBACA,eACA,kBACA,cACA,eAEA,cACA,WACA,eACA,WACA,SACA,OACA,WACA,YACA,SACA,qBACA,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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/errors/ExportError.js","../lib/config.js","../lib/fetch.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? path : join(__dirname, path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initGlobalOptions(defaultConfig);\r\n\r\n// An object for the instance options, created each time the `initExport` occurs\r\nconst instanceOptions = deepCopy(globalOptions);\r\n\r\n/**\r\n * Retrieves a reference to the options object. Depending on the `getInstance`\r\n * parameter, it returns either the global options or the instance-specific\r\n * options object.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getInstance=true] - Optional parameter that decides whether\r\n * to return the instance-specific options (when `true`) or the global options\r\n * (when `false`). The default value is `true`.\r\n *\r\n * @returns {Object} A reference to either the global options\r\n * or the instance-specific options, based on the `getInstance` parameter.\r\n */\r\nexport function getOptions(getInstance = true) {\r\n return getInstance ? instanceOptions : globalOptions;\r\n}\r\n\r\n/**\r\n * Sets the global options of the export server, keeping the principle\r\n * of the options load priority from all available sources. It accepts optional\r\n * `customOptions` object and `cliArgs` array with arguments from the CLI. These\r\n * options will be validated and applied if provided.\r\n *\r\n * The priority order of setting values is:\r\n *\r\n * 1. Options from the `lib/schemas/config.js` file (default values).\r\n * 2. Options from a custom JSON file (loaded by the `loadConfig` option).\r\n * 3. Options from the environment variables (the `.env` file).\r\n * 4. Options from the command line interface (CLI).\r\n * 5. Options from the first parameter (the `customOptions` is by default\r\n * an empty object).\r\n *\r\n * @function setGlobalOptions\r\n *\r\n * @param {Object} [customOptions={}] - Optional custom options for additional\r\n * configuration. The default value is an empty object.\r\n * @param {Array.} [cliArgs=[]] - Optional command line arguments\r\n * for additional configuration. The default value is an empty array.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from all available sources.\r\n */\r\nexport function setGlobalOptions(customOptions = {}, cliArgs = []) {\r\n // Object for options loaded via the `loadConfig` option\r\n let configOptions = {};\r\n\r\n // Object for options from the CLI\r\n let cliOptions = {};\r\n\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `loadConfig`\r\n configOptions = _loadConfigFile(cliArgs, globalOptions.customLogic);\r\n\r\n // Get options from the CLI\r\n cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n }\r\n\r\n // Update values of the global options with values from each source possible\r\n _updateGlobalOptions(\r\n defaultConfig,\r\n globalOptions,\r\n configOptions,\r\n cliOptions,\r\n customOptions\r\n );\r\n\r\n // Return updated global options\r\n return globalOptions;\r\n}\r\n\r\n/**\r\n * Updates the instance options with additional options. It optionally allows\r\n * to reinitialize the instance options with the values of a current global\r\n * options object.\r\n *\r\n * @param {Object} updateOptions - The update options to merge into the instance\r\n * options.\r\n * @param {boolean} [newInstance=false] - A flag to indicate whether to init\r\n * options for a new instance. If `true`, the existing instance options will\r\n * be cleared and reinitialized based on the global options.\r\n *\r\n * @returns {Object} - The updated instance options.\r\n */\r\nexport function updateOptions(updateOptions, newInstance = false) {\r\n // Check if options need to be created for a new instance\r\n if (newInstance) {\r\n // Get rid of the old instance options\r\n Object.keys(instanceOptions).forEach((key) => {\r\n delete instanceOptions[key];\r\n });\r\n\r\n // Init the new instance options based on the global options\r\n mergeOptions(instanceOptions, deepCopy(globalOptions));\r\n }\r\n\r\n // Merge additional options to the instance options\r\n mergeOptions(instanceOptions, updateOptions);\r\n\r\n // Return the reference to the instance options object\r\n return instanceOptions;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns global options object based on provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * @function _initGlobalOptions\r\n *\r\n * @param {Object} config - The configuration object to be used for initializing\r\n * options.\r\n *\r\n * @returns {Object} Initialized options object.\r\n */\r\nfunction _initGlobalOptions(config) {\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // If a value from environment variables exists, it takes precedence\r\n const envVal = envs[item.envLink];\r\n if (envVal !== undefined && envVal !== null) {\r\n options[name] = envVal;\r\n } else {\r\n options[name] = item.value;\r\n }\r\n } else {\r\n options[name] = _initGlobalOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Updates global options object with values from various sources, following\r\n * a specific prioritization order. The function checks for values in the order\r\n * of precedence: the `loadConfig` configuration options, environment variables,\r\n * custom options, and CLI options.\r\n *\r\n * @function _updateGlobalOptions\r\n *\r\n * @param {Object} config - The configuration object, which includes the initial\r\n * settings and metadata for each option. This object is used to determine\r\n * the structure and default values for the options.\r\n * @param {Object} options - The global options object that will be updated\r\n * with values from other sources.\r\n * @param {Object} configOpt - The configuration options object, loaded with\r\n * the `loadConfig` option, which may provide values to override defaults.\r\n * @param {Object} cliOpt - The CLI options object, which may include values\r\n * provided through command-line arguments and may override configuration\r\n * options.\r\n * @param {Object} customOpt - The custom options object, typically containing\r\n * additional and user-defined values, which has the highest precedence among\r\n * options.\r\n */\r\nfunction _updateGlobalOptions(config, options, configOpt, cliOpt, customOpt) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the config entry of a specific option\r\n const entry = config[key];\r\n\r\n // Gather values for the options from every possible source, if exists\r\n const configVal = configOpt && configOpt[key];\r\n const cliVal = cliOpt && cliOpt[key];\r\n const customVal = customOpt && customOpt[key];\r\n\r\n // If the value not found, need to go deeper\r\n if (typeof entry.value === 'undefined') {\r\n _updateGlobalOptions(entry, options[key], configVal, cliVal, customVal);\r\n } else {\r\n // If a value from custom JSON options exists, it takes precedence\r\n if (configVal !== undefined && configVal !== null) {\r\n options[key] = configVal;\r\n }\r\n\r\n // If a value from environment variables exists, it takes precedence\r\n const envVal = envs[entry.envLink];\r\n if (entry.envLink in envs && envVal !== undefined && envVal !== null) {\r\n options[key] = envVal;\r\n }\r\n\r\n // If a value from CLI options exists, it takes precedence\r\n if (cliVal !== undefined && cliVal !== null) {\r\n options[key] = cliVal;\r\n }\r\n\r\n // If a value from user options exists, it takes precedence\r\n if (customVal !== undefined && customVal !== null) {\r\n options[key] = customVal;\r\n }\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `loadConfig` option and the corresponding file path.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs, customLogicOptions) {\r\n // Check if the `loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `loadConfig` is present and has a correct value\r\n if (configFileName && customLogicOptions.allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n customLogicOptions.allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n setGlobalOptions,\r\n updateOptions,\r\n mergeOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Get the reference to the global options to update to the new version\r\n const options = getOptions();\r\n\r\n // Set to the new version\r\n options.highcharts.version = newVersion;\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n 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(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { getOptions, isAllowedConfig, mergeOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n deepCopy,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `exportingOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} exportingOptions - The `exportingOptions` object, which\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(exportingOptions, endCallback) {\r\n try {\r\n // Check if provided options is an object\r\n if (!isObject(exportingOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = mergeOptions(deepCopy(getOptions()), {\r\n export: exportingOptions.export,\r\n customLogic: exportingOptions.customLogic\r\n });\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled\r\n if (rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFile } from 'fs/promises';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 readFile(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = await readFile(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport {\r\n getOptions,\r\n updateOptions,\r\n setGlobalOptions,\r\n mapToNewOptions\r\n} from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} [initOptions={}] - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration. The default value\r\n * is an empty object.\r\n */\r\nexport async function initExport(initOptions = {}) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions, true);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n setGlobalOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","join","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","ExportError","Error","constructor","statusCode","super","this","setStatus","setError","name","_initGlobalOptions","instanceOptions","getOptions","getInstance","setGlobalOptions","customOptions","cliArgs","configOptions","cliOptions","_loadConfigFile","_pairArgumentValue","_updateGlobalOptions","updateOptions","newInstance","mergeOptions","originalOptions","newOptions","entries","mapToNewOptions","oldOptions","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","envVal","configOpt","cliOpt","customOpt","configVal","cliVal","customVal","stringifyFunctions","stringify","replaceAll","customLogicOptions","configIndex","findIndex","arg","configFileName","i","option","async","fetch","requestOptions","Promise","resolve","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","exportingOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","readFile","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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CKA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMkB,cAAgB,CAC3BC,UAAW,CACT9B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFsD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbpG,KAAM,OACNqG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP7D,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbpG,KAAM,SAGVwG,OAAQ,CACN9D,MAAO,8BACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbpG,KAAM,SAGVyG,WAAY,CACV/D,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbpG,KAAM,WAGV0G,UAAW,CACThE,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbpG,KAAM,SAGV2G,YAAa,CACXjE,MAAO,CAAC,aAAc,kBAAmB,iBACzCsD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbpG,KAAM,cACN4G,aAAc,0DAGlBC,cAAe,CACbnE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,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"} \ No newline at end of file From b2a974334560db2298759b3469ae7e06f053eaf4 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Wed, 22 Jan 2025 17:24:36 +0100 Subject: [PATCH 078/102] Simplified and optimized options processing logic. --- bin/cli.js | 6 +- lib/cache.js | 13 +- lib/chart.js | 14 +- lib/config.js | 285 +++++++++---------------- lib/index.js | 18 +- lib/server/middlewares/rateLimiting.js | 4 +- lib/server/server.js | 2 +- 7 files changed, 126 insertions(+), 216 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index bd2f243c..cabdf6b0 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -26,7 +26,7 @@ import { printLicense, printUsage, printVersion, - setGlobalOptions + setCliOptions } from '../lib/config.js'; import { initExport } from '../lib/index.js'; import { log, logWithStack } from '../lib/logger.js'; @@ -81,8 +81,8 @@ async function start() { return; } - // Set the options, keeping the priority order of setting values - const options = setGlobalOptions({}, args); + // Set the options from CLI, keeping the priority order of setting values + const options = setCliOptions(args); // If all options are correctly parsed if (options) { diff --git a/lib/cache.js b/lib/cache.js index 4622469f..e1987ccd 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -26,7 +26,7 @@ import { join } from 'path'; import { HttpsProxyAgent } from 'https-proxy-agent'; -import { getOptions } from './config.js'; +import { getOptions, updateOptions } from './config.js'; import { fetch } from './fetch.js'; import { log } from './logger.js'; import { __dirname, getAbsolutePath } from './utils.js'; @@ -182,11 +182,12 @@ export function getHighchartsVersion() { * @param {string} newVersion - The new Highcharts version to be applied. */ export async function updateHighchartsVersion(newVersion) { - // Get the reference to the global options to update to the new version - const options = getOptions(); - - // Set to the new version - options.highcharts.version = newVersion; + // Update to the new version + const options = updateOptions({ + highcharts: { + version: newVersion + } + }); // Check if cache needs to be updated await checkAndUpdateCache(options.highcharts, options.server.proxy); diff --git a/lib/chart.js b/lib/chart.js index 69d7d6d3..75332c48 100644 --- a/lib/chart.js +++ b/lib/chart.js @@ -21,12 +21,11 @@ See LICENSE file in root for details. import { readFileSync, writeFileSync } from 'fs'; -import { getOptions, isAllowedConfig, mergeOptions } from './config.js'; +import { isAllowedConfig, updateOptions } from './config.js'; import { log, logWithStack } from './logger.js'; import { getPoolStats, killPool, postWork } from './pool.js'; import { sanitize } from './sanitize.js'; import { - deepCopy, fixConstr, fixOutfile, fixType, @@ -260,10 +259,13 @@ export async function startExport(exportingOptions, endCallback) { } // Merge additional options to the copy of the instance options - const options = mergeOptions(deepCopy(getOptions()), { - export: exportingOptions.export, - customLogic: exportingOptions.customLogic - }); + const options = updateOptions( + { + export: exportingOptions.export, + customLogic: exportingOptions.customLogic + }, + true + ); // Get the `export` options const exportOptions = options.export; diff --git a/lib/config.js b/lib/config.js index ba1a3c9e..9be870d5 100644 --- a/lib/config.js +++ b/lib/config.js @@ -29,145 +29,79 @@ import { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js'; import { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js'; -import ExportError from './errors/ExportError.js'; - // Sets the global options with initial values from the default config -const globalOptions = _initGlobalOptions(defaultConfig); - -// An object for the instance options, created each time the `initExport` occurs -const instanceOptions = deepCopy(globalOptions); +const globalOptions = _initOptions(defaultConfig); /** - * Retrieves a reference to the options object. Depending on the `getInstance` - * parameter, it returns either the global options or the instance-specific - * options object. + * Retrieves a copy of the global options object. * * @function getOptions * - * @param {boolean} [getInstance=true] - Optional parameter that decides whether - * to return the instance-specific options (when `true`) or the global options - * (when `false`). The default value is `true`. - * - * @returns {Object} A reference to either the global options - * or the instance-specific options, based on the `getInstance` parameter. + * @returns {Object} A reference to the global options object. */ -export function getOptions(getInstance = true) { - return getInstance ? instanceOptions : globalOptions; +export function getOptions() { + return deepCopy(globalOptions); } /** - * Sets the global options of the export server, keeping the principle - * of the options load priority from all available sources. It accepts optional - * `customOptions` object and `cliArgs` array with arguments from the CLI. These - * options will be validated and applied if provided. + * Updates the global options with the provided options. * - * The priority order of setting values is: + * @function updateOptions * - * 1. Options from the `lib/schemas/config.js` file (default values). - * 2. Options from a custom JSON file (loaded by the `loadConfig` option). - * 3. Options from the environment variables (the `.env` file). - * 4. Options from the command line interface (CLI). - * 5. Options from the first parameter (the `customOptions` is by default - * an empty object). + * @param {Object} newOptions - An object containing the new options to be + * merged into the global options. + * @param {boolean} [getCopy=false] - Determines whether to merge the new + * options into a copy of the global options object (`true`) or directly into + * the global options object (`false`). The default value is `false`. * - * @function setGlobalOptions - * - * @param {Object} [customOptions={}] - Optional custom options for additional - * configuration. The default value is an empty object. - * @param {Array.} [cliArgs=[]] - Optional command line arguments - * for additional configuration. The default value is an empty array. - * - * @returns {Object} The updated global options object, reflecting the merged - * configuration from all available sources. + * @returns {Object} The updated options object, either the modified global + * options or a modified copy, based on the value of `getCopy`. */ -export function setGlobalOptions(customOptions = {}, cliArgs = []) { - // Object for options loaded via the `loadConfig` option - let configOptions = {}; - - // Object for options from the CLI - let cliOptions = {}; - - // Only for the CLI usage - if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) { - // Get options from the custom JSON loaded via the `loadConfig` - configOptions = _loadConfigFile(cliArgs, globalOptions.customLogic); - - // Get options from the CLI - cliOptions = _pairArgumentValue(nestedProps, cliArgs); - } - - // Update values of the global options with values from each source possible - _updateGlobalOptions( - defaultConfig, - globalOptions, - configOptions, - cliOptions, - customOptions +export function updateOptions(newOptions, getCopy = false) { + // Merge new options to the global options or its copy and return the result + return _mergeOptions( + getCopy ? deepCopy(globalOptions) : globalOptions, + newOptions ); - - // Return updated global options - return globalOptions; } /** - * Updates the instance options with additional options. It optionally allows - * to reinitialize the instance options with the values of a current global - * options object. + * Updates the global options with values provided through the CLI, keeping + * the principle of options load priority. This function accepts a `cliArgs` + * array containing arguments from the CLI, which will be validated and applied + * if provided. * - * @param {Object} updateOptions - The update options to merge into the instance - * options. - * @param {boolean} [newInstance=false] - A flag to indicate whether to init - * options for a new instance. If `true`, the existing instance options will - * be cleared and reinitialized based on the global options. + * The priority order for setting values is: * - * @returns {Object} - The updated instance options. - */ -export function updateOptions(updateOptions, newInstance = false) { - // Check if options need to be created for a new instance - if (newInstance) { - // Get rid of the old instance options - Object.keys(instanceOptions).forEach((key) => { - delete instanceOptions[key]; - }); - - // Init the new instance options based on the global options - mergeOptions(instanceOptions, deepCopy(globalOptions)); - } - - // Merge additional options to the instance options - mergeOptions(instanceOptions, updateOptions); - - // Return the reference to the instance options object - return instanceOptions; -} - -/** - * Merges two sets of configuration options, considering absolute properties. + * 1. Values from a custom JSON file (loaded by the `--loadConfig` option). + * 2. Values from the command line interface (CLI). * - * @function mergeOptions + * @function setCliOptions * - * @param {Object} originalOptions - Original configuration options. - * @param {Object} newOptions - New configuration options to be merged. + * @param {Array.} cliArgs - An array of command line arguments used + * for additional configuration. * - * @returns {Object} Merged configuration options. + * @returns {Object} The updated global options object, reflecting the merged + * configuration from sources provided through the CLI. */ -export function mergeOptions(originalOptions, newOptions) { - // Check if the `originalOptions` and `newOptions` are correct objects - if (isObject(originalOptions) && isObject(newOptions)) { - for (const [key, value] of Object.entries(newOptions)) { - originalOptions[key] = - isObject(value) && - !absoluteProps.includes(key) && - originalOptions[key] !== undefined - ? mergeOptions(originalOptions[key], value) - : value !== undefined - ? value - : originalOptions[key] || null; - } +export function setCliOptions(cliArgs) { + // Only for the CLI usage + if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) { + // Get options from the custom JSON loaded via the `--loadConfig` + const configOptions = _loadConfigFile(cliArgs, globalOptions.customLogic); + + // Update global options with the values from the `configOptions` + updateOptions(configOptions); + + // Get options from the CLI + const cliOptions = _pairArgumentValue(nestedProps, cliArgs); + + // Update global options with the values from the `cliOptions` + updateOptions(cliOptions); } - // Return the original (modified or not) options - return originalOptions; + // Return global options + return globalOptions; } /** @@ -357,31 +291,42 @@ export function printVersion(noLogo = false) { } /** - * Initializes and returns global options object based on provided + * Initializes and returns the global options object based on the provided * configuration, setting values from nested properties recursively. * - * @function _initGlobalOptions + * The priority order for setting values is: * - * @param {Object} config - The configuration object to be used for initializing - * options. + * 1. Values from the `./lib/schemas/config.js` file (defaults). + * 2. Values from environment variables (specified in the `.env` file). + * + * @function _initOptions * - * @returns {Object} Initialized options object. + * @param {Object} config - The configuration object used for initializing + * the global options. It should include nested properties with a `value` + * and an `envLink` for linking to environment variables. + * + * @returns {Object} The initialized global options object, populated with + * values based on the provided configuration and the established priority + * order. */ -function _initGlobalOptions(config) { +function _initOptions(config) { + // Init the object for options const options = {}; // Start initializing the `options` object recursively for (const [name, item] of Object.entries(config)) { if (Object.prototype.hasOwnProperty.call(item, 'value')) { - // If a value from environment variables exists, it takes precedence - const envVal = envs[item.envLink]; - if (envVal !== undefined && envVal !== null) { - options[name] = envVal; + // Set the correct value based on the established priority order + if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) { + // The environment variables value + options[name] = envs[item.envLink]; } else { + // The value from the config file options[name] = item.value; } } else { - options[name] = _initGlobalOptions(item); + // Create a section in the options + options[name] = _initOptions(item); } } @@ -390,63 +335,32 @@ function _initGlobalOptions(config) { } /** - * Updates global options object with values from various sources, following - * a specific prioritization order. The function checks for values in the order - * of precedence: the `loadConfig` configuration options, environment variables, - * custom options, and CLI options. - * - * @function _updateGlobalOptions - * - * @param {Object} config - The configuration object, which includes the initial - * settings and metadata for each option. This object is used to determine - * the structure and default values for the options. - * @param {Object} options - The global options object that will be updated - * with values from other sources. - * @param {Object} configOpt - The configuration options object, loaded with - * the `loadConfig` option, which may provide values to override defaults. - * @param {Object} cliOpt - The CLI options object, which may include values - * provided through command-line arguments and may override configuration - * options. - * @param {Object} customOpt - The custom options object, typically containing - * additional and user-defined values, which has the highest precedence among - * options. + * Merges two sets of configuration options, considering absolute properties. + * + * @function _mergeOptions + * + * @param {Object} originalOptions - Original configuration options. + * @param {Object} newOptions - New configuration options to be merged. + * + * @returns {Object} Merged configuration options. */ -function _updateGlobalOptions(config, options, configOpt, cliOpt, customOpt) { - Object.keys(config).forEach((key) => { - // Get the config entry of a specific option - const entry = config[key]; - - // Gather values for the options from every possible source, if exists - const configVal = configOpt && configOpt[key]; - const cliVal = cliOpt && cliOpt[key]; - const customVal = customOpt && customOpt[key]; - - // If the value not found, need to go deeper - if (typeof entry.value === 'undefined') { - _updateGlobalOptions(entry, options[key], configVal, cliVal, customVal); - } else { - // If a value from custom JSON options exists, it takes precedence - if (configVal !== undefined && configVal !== null) { - options[key] = configVal; - } - - // If a value from environment variables exists, it takes precedence - const envVal = envs[entry.envLink]; - if (entry.envLink in envs && envVal !== undefined && envVal !== null) { - options[key] = envVal; - } - - // If a value from CLI options exists, it takes precedence - if (cliVal !== undefined && cliVal !== null) { - options[key] = cliVal; - } - - // If a value from user options exists, it takes precedence - if (customVal !== undefined && customVal !== null) { - options[key] = customVal; - } +export function _mergeOptions(originalOptions, newOptions) { + // Check if the `originalOptions` and `newOptions` are correct objects + if (isObject(originalOptions) && isObject(newOptions)) { + for (const [key, value] of Object.entries(newOptions)) { + originalOptions[key] = + isObject(value) && + !absoluteProps.includes(key) && + originalOptions[key] !== undefined + ? _mergeOptions(originalOptions[key], value) + : value !== undefined + ? value + : originalOptions[key] || null; } - }); + } + + // Return the original (modified or not) options + return originalOptions; } /** @@ -510,12 +424,12 @@ export function _optionsStringify(options, allowFunctions, stringifyFunctions) { /** * Loads additional configuration from a specified file provided via - * the `loadConfig` option in the command-line arguments. + * the `--loadConfig` option in the command-line arguments. * * @function _loadConfigFile * * @param {Array.} cliArgs - Command-line arguments to search - * for the `loadConfig` option and the corresponding file path. + * for the `--loadConfig` option and the corresponding file path. * @param {Object} customLogicOptions - The configuration object containing * `customLogic` options. * @@ -524,15 +438,15 @@ export function _optionsStringify(options, allowFunctions, stringifyFunctions) { * occurs. */ function _loadConfigFile(cliArgs, customLogicOptions) { - // Check if the `loadConfig` option was used + // Check if the `--loadConfig` option was used const configIndex = cliArgs.findIndex( (arg) => arg.replace(/-/g, '') === 'loadConfig' ); - // Get the `loadConfig` option value + // Get the `--loadConfig` option value const configFileName = configIndex > -1 && cliArgs[configIndex + 1]; - // Check if the `loadConfig` is present and has a correct value + // Check if the `--loadConfig` is present and has a correct value if (configFileName && customLogicOptions.allowFileResources) { try { // Load an optional custom JSON config file @@ -650,9 +564,8 @@ function _cycleCategories(options) { export default { getOptions, - setGlobalOptions, updateOptions, - mergeOptions, + setCliOptions, mapToNewOptions, isAllowedConfig, printLicense, diff --git a/lib/index.js b/lib/index.js index 5af42ea5..bf4cffca 100644 --- a/lib/index.js +++ b/lib/index.js @@ -28,12 +28,7 @@ import { startExport, setAllowCodeExecution } from './chart.js'; -import { - getOptions, - updateOptions, - setGlobalOptions, - mapToNewOptions -} from './config.js'; +import { getOptions, updateOptions, mapToNewOptions } from './config.js'; import { log, logWithStack, @@ -58,14 +53,13 @@ import server from './server/server.js'; * @async * @function initExport * - * @param {Object} [initOptions={}] - The `initOptions` object, which may + * @param {Object} initOptions - The `initOptions` object, which may * be a partial or complete set of options. If the options are partial, missing - * values will default to the current global configuration. The default value - * is an empty object. + * values will default to the current global configuration. */ -export async function initExport(initOptions = {}) { +export async function initExport(initOptions) { // Init and update the instance options object - const options = updateOptions(initOptions, true); + const options = updateOptions(initOptions); // Set the `allowCodeExecution` per export module scope setAllowCodeExecution(options.customLogic.allowCodeExecution); @@ -131,7 +125,7 @@ export default { // Options getOptions, - setGlobalOptions, + updateOptions, mapToNewOptions, // Exporting diff --git a/lib/server/middlewares/rateLimiting.js b/lib/server/middlewares/rateLimiting.js index 9eedaaae..d64278f2 100644 --- a/lib/server/middlewares/rateLimiting.js +++ b/lib/server/middlewares/rateLimiting.js @@ -36,8 +36,8 @@ import ExportError from '../../errors/ExportError.js'; */ export default function rateLimitingMiddleware(app, rateLimitingOptions) { try { - // Check if the rate limiting is enabled - if (rateLimitingOptions.enable) { + // Check if the rate limiting is enabled and the app exists + if (app && rateLimitingOptions.enable) { const message = 'Too many requests, you have been rate limited. Please try again later.'; diff --git a/lib/server/server.js b/lib/server/server.js index ba744174..49e4f6a9 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -54,7 +54,7 @@ const app = express(); /** * Starts an HTTP and/or HTTPS server based on the provided configuration. * The `serverOptions` object contains server-related properties (refer - * to the `server` section in the `lib/schemas/config.js` file for details). + * to the `server` section in the `./lib/schemas/config.js` file for details). * * @async * @function startServer From 19846ed36f3526a549e29f64d0c4f39cd2189d8c Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Wed, 22 Jan 2025 17:25:13 +0100 Subject: [PATCH 079/102] Samples, scenarios and tests corrections. --- samples/http/requestInfile.json | 18 +++--- samples/module/optionsPhantom.js | 11 ++-- samples/module/optionsPuppeteer.js | 21 +++---- samples/module/promises.js | 39 ++++++------ samples/module/svg.js | 13 ++-- ...NotAllowCodeExecutionAndFileResources.json | 9 --- .../allowCodeExecutionAndFileResources.json | 2 +- tests/http/scenarios/allowCodeExecution.json | 2 +- .../scenarios/doNotAllowFileResources.json | 2 +- ...NotAllowCodeExecutionAndFileResources.json | 50 ---------------- .../optionsStringifiedWrong.json | 6 -- tests/node/scenarios/allowCodeExecution.json | 2 +- tests/node/scenarios/allowFileResources.json | 2 +- .../scenarios/allowFileResourcesFalse.json | 2 +- tests/node/scenarios/constrChart.json | 4 +- tests/node/scenarios/constrGanttChart.json | 4 +- tests/node/scenarios/constrMapChart.json | 4 +- tests/node/scenarios/constrStockChart.json | 4 +- tests/node/scenarios/optionsJson.json | 1 - tests/node/scenarios/optionsStringified.json | 1 - .../scenarios/sizesAndScaleFromCliPost.json | 8 +-- tests/node/scenarios/svgBasic.json | 4 +- tests/node/scenarios/svgBasicWithScale.json | 4 +- .../scenarios/svgBasicWithScaleToPdf.json | 6 +- tests/node/scenarios/svgForeignObject.json | 4 +- tests/node/scenarios/typeJpeg.json | 4 +- tests/node/scenarios/typePdf.json | 4 +- tests/node/scenarios/typePng.json | 4 +- tests/node/scenarios/typeSvg.json | 4 +- tests/other/privateRangeUrl.js | 59 ------------------- 30 files changed, 82 insertions(+), 216 deletions(-) delete mode 100644 tests/cli/errorScenarios/doNotAllowCodeExecutionAndFileResources.json delete mode 100644 tests/node/errorScenarios/doNotAllowCodeExecutionAndFileResources.json delete mode 100644 tests/node/errorScenarios/optionsStringifiedWrong.json delete mode 100644 tests/other/privateRangeUrl.js diff --git a/samples/http/requestInfile.json b/samples/http/requestInfile.json index f141f20b..286722c4 100644 --- a/samples/http/requestInfile.json +++ b/samples/http/requestInfile.json @@ -32,17 +32,12 @@ ] }, "type": "png", - "scale": 2, - "width": 800, - "height": 800, - "callback": "function callback(chart) {chart.renderer.label('This label is added in the stringified callback.
Highcharts version ' + Highcharts.version,75,75).attr({fill: '#90ed7d', padding: 10, r: 10, zIndex: 10}).css({color: 'black', width: '100px'}).add();}", - "resources": { - "js": "Highcharts.charts[0].update({title: {text: 'Resources title'}});", - "css": ".highcharts-color-0 {fill: #7cb5ec; stroke: #7cb5ec;} .highcharts-axis.highcharts-color-0 .highcharts-axis-line {stroke: #7cb5ec;} .highcharts-axis.highcharts-color-0 text {fill: #7cb5ec;}.highcharts-color-1 {fill: #90ed7d; stroke: #90ed7d;} .highcharts-axis.highcharts-color-1 .highcharts-axis-line {stroke: #90ed7d;} .highcharts-axis.highcharts-color-1 text {fill: #90ed7d;}.highcharts-yaxis .highcharts-axis-line {stroke-width: 2px;}" - }, "constr": "chart", "b64": false, "noDownload": false, + "height": 800, + "width": 800, + "scale": 2, "globalOptions": { "chart": { "borderWidth": 2, @@ -95,5 +90,10 @@ } } }, - "customCode": "function () {Highcharts.setOptions({chart: {borderWidth: 2, plotBackgroundColor: 'rgba(255, 255, 255, .9)', plotShadow: true, plotBorderWidth: 1, events: {render: function() {this.renderer.image('https://www.highcharts.com/samples/graphics/sun.png', 250, 120, 20, 20).add();}}}});}" + "customCode": "function () {Highcharts.setOptions({chart: {borderWidth: 2, plotBackgroundColor: 'rgba(255, 255, 255, .9)', plotShadow: true, plotBorderWidth: 1, events: {render: function() {this.renderer.image('https://www.highcharts.com/samples/graphics/sun.png', 250, 120, 20, 20).add();}}}});}", + "callback": "function callback(chart) {chart.renderer.label('This label is added in the stringified callback.
Highcharts version ' + Highcharts.version,75,75).attr({fill: '#90ed7d', padding: 10, r: 10, zIndex: 10}).css({color: 'black', width: '100px'}).add();}", + "resources": { + "js": "Highcharts.charts[0].update({title: {text: 'Resources title'}});", + "css": ".highcharts-color-0 {fill: #7cb5ec; stroke: #7cb5ec;} .highcharts-axis.highcharts-color-0 .highcharts-axis-line {stroke: #7cb5ec;} .highcharts-axis.highcharts-color-0 text {fill: #7cb5ec;}.highcharts-color-1 {fill: #90ed7d; stroke: #90ed7d;} .highcharts-axis.highcharts-color-1 .highcharts-axis-line {stroke: #90ed7d;} .highcharts-axis.highcharts-color-1 text {fill: #90ed7d;}.highcharts-yaxis .highcharts-axis-line {stroke-width: 2px;}" + } } diff --git a/samples/module/optionsPhantom.js b/samples/module/optionsPhantom.js index 83e6fe1a..ea4e6413 100644 --- a/samples/module/optionsPhantom.js +++ b/samples/module/optionsPhantom.js @@ -50,11 +50,11 @@ const oldOptions = { } ] }, + outfile: './samples/module/optionsPhantom.png', type: 'png', constr: 'chart', - outfile: './samples/module/optionsPhantom.png', - scale: 1, width: 1000, + scale: 1, globalOptions: './samples/resources/optionsGlobal.json', allowFileResources: true, callback: './samples/resources/callback.js', @@ -73,14 +73,11 @@ const oldOptions = { // Map to fit the new options structure (Puppeteer) const newOptions = exporter.mapToNewOptions(oldOptions); - // Set the new options - const options = exporter.setGlobalOptions(newOptions); - // Init a pool for one export - await initExport(options); + await initExport(newOptions); // Perform an export - await exporter.singleExport(options); + await exporter.singleExport(newOptions); } catch (error) { // Log the error with stack exporter.logWithStack(1, error); diff --git a/samples/module/optionsPuppeteer.js b/samples/module/optionsPuppeteer.js index 9fc74799..f3c37b87 100644 --- a/samples/module/optionsPuppeteer.js +++ b/samples/module/optionsPuppeteer.js @@ -17,12 +17,6 @@ import exporter, { initExport } from '../../lib/index.js'; // New options structure (Puppeteer) const newOptions = { export: { - type: 'jpeg', - constr: 'chart', - outfile: './samples/module/optionsPuppeteer.jpeg', - height: 800, - width: 1200, - scale: 1, options: { chart: { type: 'column' @@ -70,6 +64,12 @@ const newOptions = { } ] }, + outfile: './samples/module/optionsPuppeteer.jpeg', + type: 'jpeg', + constr: 'chart', + height: 800, + width: 1200, + scale: 1, globalOptions: { chart: { borderWidth: 2, @@ -120,8 +120,8 @@ const newOptions = { customLogic: { allowCodeExecution: true, allowFileResources: true, - callback: './samples/resources/callback.js', customCode: './samples/resources/customCode.js', + callback: './samples/resources/callback.js', resources: { js: "Highcharts.charts[0].update({xAxis: {title: {text: 'Resources axis title'}}});", css: '.highcharts-yaxis .highcharts-axis-line { stroke-width: 2px; } .highcharts-color-0 { fill: #f7a35c; stroke: #f7a35c; }' @@ -137,14 +137,11 @@ const newOptions = { (async () => { try { - // Set the new options - const options = exporter.setGlobalOptions(newOptions); - // Init a pool for one export - await initExport(options); + await initExport(newOptions); // Perform an export - await exporter.singleExport(options); + await exporter.singleExport(newOptions); } catch (error) { // Log the error with stack exporter.logWithStack(1, error); diff --git a/samples/module/promises.js b/samples/module/promises.js index 26196a6b..01bdbcff 100644 --- a/samples/module/promises.js +++ b/samples/module/promises.js @@ -16,32 +16,33 @@ import { writeFileSync } from 'fs'; import exporter, { initExport } from '../../lib/index.js'; -const exportCharts = async (charts, exportOptions = {}) => { - // Set the new options - const options = exporter.setGlobalOptions(exportOptions); - +const exportCharts = async (charts, initOptions) => { // Init the pool - await initExport(options); + await initExport(initOptions); const promises = []; const chartResults = []; // Start exporting charts - charts.forEach((chart) => { + charts.forEach((options) => { promises.push( new Promise((resolve, reject) => { - const settings = { ...options }; - settings.export.options = chart; - - exporter.startExport(settings, (error, data) => { - if (error) { - return reject(error); + exporter.startExport( + { + export: { + options + } + }, + (error, data) => { + if (error) { + return reject(error); + } + + // Add the data to the chartResults + chartResults.push(data.result); + resolve(); } - - // Add the data to the chartResults - chartResults.push(data.result); - resolve(); - }); + ); }) ); }); @@ -101,8 +102,8 @@ exportCharts( Buffer.from(chart, 'base64') ); }); - exporter.log(4, 'All done!'); + exporter.log(4, '[promises] All done!'); }) .catch((error) => { - exporter.logWithStack(1, error, 'Something went wrong!'); + exporter.logWithStack(1, error, '[promises] Something went wrong!'); }); diff --git a/samples/module/svg.js b/samples/module/svg.js index 62f59162..f5e3caf8 100644 --- a/samples/module/svg.js +++ b/samples/module/svg.js @@ -17,10 +17,10 @@ import exporter, { initExport } from '../../lib/index.js'; // SVG options const svgOptions = { export: { - type: 'png', + svg: 'Highcharts.com', outfile: './samples/module/svg.png', - scale: 2, - svg: 'Highcharts.com' + type: 'png', + scale: 2 }, pool: { maxWorkers: 1 @@ -32,14 +32,11 @@ const svgOptions = { (async () => { try { - // Set the new options - const options = exporter.setGlobalOptions(svgOptions); - // Init a pool for one export - await initExport(options); + await initExport(svgOptions); // Perform an export - await exporter.singleExport(options); + await exporter.singleExport(svgOptions); } catch (error) { // Log the error with stack exporter.logWithStack(1, error); diff --git a/tests/cli/errorScenarios/doNotAllowCodeExecutionAndFileResources.json b/tests/cli/errorScenarios/doNotAllowCodeExecutionAndFileResources.json deleted file mode 100644 index 3133799e..00000000 --- a/tests/cli/errorScenarios/doNotAllowCodeExecutionAndFileResources.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "instr": "{\"title\":{\"text\":\"Do not allow code execution from strings\"},\"xAxis\":{\"categories\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\"]},\"series\":[{\"type\":\"column\",\"data\":[5,6,7,8]},{\"type\":\"line\",\"data\":[1,2,3,4]}]}", - "outfile": "allowCodeExecutionStringified.png", - "allowCodeExecution": false, - "allowFileResources": false, - "callback": "function callback(chart){chart.renderer.label('This label is added in the callback.
Highcharts version '+Highcharts.version,75,75).attr({id:'renderer-callback-label',fill:'#90ed7d',padding:10,r:10,zIndex:10}).css({color:'black',width:'100px'}).add();}", - "customCode": "./samples/resources/customCode.js", - "resources": "./samples/resources/resources.js" -} diff --git a/tests/cli/scenarios/allowCodeExecutionAndFileResources.json b/tests/cli/scenarios/allowCodeExecutionAndFileResources.json index 0c10c420..fb369141 100644 --- a/tests/cli/scenarios/allowCodeExecutionAndFileResources.json +++ b/tests/cli/scenarios/allowCodeExecutionAndFileResources.json @@ -2,7 +2,7 @@ "instr": "{\"title\":{\"text\":\"Allow code execution and file resources\"},\"xAxis\":{\"categories\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\"]},\"series\":[{\"type\":\"column\",\"data\":[5,6,7,8]},{\"type\":\"line\",\"data\":[1,2,3,4]}]}", "allowCodeExecution": true, "allowFileResources": true, - "callback": "./samples/resources/callback.js", "customCode": "Highcharts.setOptions({chart:{events:{render:function (){this.renderer.image('https://www.highcharts.com/samples/graphics/sun.png',75,50,20,20).add();}}}});", + "callback": "./samples/resources/callback.js", "resources": "{\"js\":\"Highcharts.charts[0].update({xAxis:{title:{text:'Title from the resources object, js section'}}});\",\"css\":\".highcharts-yaxis .highcharts-axis-line{stroke-width:2px;stroke:#FF0000;}\",\"files\":[\"./samples/resources/resourcesFile1.js\",\"./samples/resources/resourcesFile2.js\"]}" } diff --git a/tests/http/scenarios/allowCodeExecution.json b/tests/http/scenarios/allowCodeExecution.json index 95bbcc0b..4519b245 100644 --- a/tests/http/scenarios/allowCodeExecution.json +++ b/tests/http/scenarios/allowCodeExecution.json @@ -17,7 +17,7 @@ } ] }, - "callback": "function callback(chart){chart.renderer.label('This label is added in the stringified callback.
Highcharts version '+Highcharts.version,75,75).attr({id:'renderer-callback-label',fill:'#90ed7d',padding:10,r:10,zIndex:10}).css({color:'black',width:'100px'}).add();}", "customCode": "Highcharts.setOptions({chart:{events:{render:function (){this.renderer.image('https://www.highcharts.com/samples/graphics/sun.png',75,50,20,20).add();}}}});", + "callback": "function callback(chart){chart.renderer.label('This label is added in the stringified callback.
Highcharts version '+Highcharts.version,75,75).attr({id:'renderer-callback-label',fill:'#90ed7d',padding:10,r:10,zIndex:10}).css({color:'black',width:'100px'}).add();}", "resources": "{\"js\":\"Highcharts.charts[0].update({xAxis:{title:{text:'Title from the resources stringified object, js section'}}});\",\"css\":\".highcharts-yaxis .highcharts-axis-line{stroke-width:2px;stroke:#FF0000;}\"}" } diff --git a/tests/http/scenarios/doNotAllowFileResources.json b/tests/http/scenarios/doNotAllowFileResources.json index 4adc4624..5cfd69fe 100644 --- a/tests/http/scenarios/doNotAllowFileResources.json +++ b/tests/http/scenarios/doNotAllowFileResources.json @@ -17,7 +17,7 @@ } ] }, - "callback": "./samples/resources/callback.js", "customCode": "./samples/resources/customCode.js", + "callback": "./samples/resources/callback.js", "resources": "./samples/resources/resources.js" } diff --git a/tests/node/errorScenarios/doNotAllowCodeExecutionAndFileResources.json b/tests/node/errorScenarios/doNotAllowCodeExecutionAndFileResources.json deleted file mode 100644 index eff5177e..00000000 --- a/tests/node/errorScenarios/doNotAllowCodeExecutionAndFileResources.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "export": { - "options": { - "chart": { - "type": "column" - }, - "title": { - "text": "Do not allow code execution" - }, - "yAxis": [ - { - "title": { - "text": "Primary axis" - } - }, - { - "opposite": true, - "title": { - "text": "Secondary axis" - } - } - ], - "plotOptions": { - "column": { - "borderRadius": 5 - } - }, - "series": [ - { - "data": [1, 3, 2, 4] - }, - { - "data": [324, 124, 547, 221], - "yAxis": 1 - } - ] - } - }, - "customLogic": { - "allowCodeExecution": false, - "allowFileResources": false, - "callback": "function callback(chart) {chart.renderer.label('This label is added in the callback.
Highcharts version '+Highcharts.version,75,75).attr({id:'renderer-callback-label',fill:'#90ed7d',padding:10,r:10,zIndex:10}).css({color:'black',width:'100px'}).add();}", - "customCode": "Highcharts.setOptions({chart:{events:{render:function (){this.renderer.image('https://www.highcharts.com/samples/graphics/sun.png',100,50,20,20).add();}}}});", - "resources": { - "js": "Highcharts.charts[0].update({xAxis:{title:{text:'Title from the resources file, js section'}}});", - "css": ".highcharts-yaxis .highcharts-axis-line {stroke-width:2px;stroke:#FF0000;}", - "files": "./samples/resources/resourcesFile1.js,./samples/resources/resourcesFile2.js" - } - } -} diff --git a/tests/node/errorScenarios/optionsStringifiedWrong.json b/tests/node/errorScenarios/optionsStringifiedWrong.json deleted file mode 100644 index 12e4bbb5..00000000 --- a/tests/node/errorScenarios/optionsStringifiedWrong.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "export": { - "constr": "chart", - "options": "xAxis: {categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']}, series: [{type: 'line', data: [1, 3, 2, 4]}, {type: 'line', data: [5, 3, 4, 2]}]}" - } -} diff --git a/tests/node/scenarios/allowCodeExecution.json b/tests/node/scenarios/allowCodeExecution.json index 9357eff4..5f2b289d 100644 --- a/tests/node/scenarios/allowCodeExecution.json +++ b/tests/node/scenarios/allowCodeExecution.json @@ -38,8 +38,8 @@ }, "customLogic": { "allowCodeExecution": true, - "callback": "function callback(chart){chart.renderer.label('This label is added in the stringified callback.
Highcharts version '+Highcharts.version,75,75).attr({id:'renderer-callback-label',fill:'#90ed7d',padding:10,r:10,zIndex:10}).css({color:'black',width:'100px'}).add();}", "customCode": "Highcharts.setOptions({chart:{events:{render:function (){this.renderer.image('https://www.highcharts.com/samples/graphics/sun.png',75,50,20,20).add();}}}});", + "callback": "function callback(chart){chart.renderer.label('This label is added in the stringified callback.
Highcharts version '+Highcharts.version,75,75).attr({id:'renderer-callback-label',fill:'#90ed7d',padding:10,r:10,zIndex:10}).css({color:'black',width:'100px'}).add();}", "resources": { "js": "Highcharts.charts[0].update({xAxis:{title:{text:'Title from the resources object, js section'}}});", "css": ".highcharts-yaxis .highcharts-axis-line{stroke-width:2px;stroke:#FF0000}" diff --git a/tests/node/scenarios/allowFileResources.json b/tests/node/scenarios/allowFileResources.json index 21ab61e7..e42210e8 100644 --- a/tests/node/scenarios/allowFileResources.json +++ b/tests/node/scenarios/allowFileResources.json @@ -39,8 +39,8 @@ "customLogic": { "allowCodeExecution": true, "allowFileResources": true, - "callback": "./samples/resources/callback.js", "customCode": "./samples/resources/customCode.js", + "callback": "./samples/resources/callback.js", "resources": "./samples/resources/resources.json" } } diff --git a/tests/node/scenarios/allowFileResourcesFalse.json b/tests/node/scenarios/allowFileResourcesFalse.json index 377b5405..2563ed0e 100644 --- a/tests/node/scenarios/allowFileResourcesFalse.json +++ b/tests/node/scenarios/allowFileResourcesFalse.json @@ -39,8 +39,8 @@ "customLogic": { "allowCodeExecution": true, "allowFileResources": false, - "callback": "./samples/resources/callback.js", "customCode": "./samples/resources/customCode.js", + "callback": "./samples/resources/callback.js", "resources": "./samples/resources/resources.json" } } diff --git a/tests/node/scenarios/constrChart.json b/tests/node/scenarios/constrChart.json index 2a02bf27..451dd139 100644 --- a/tests/node/scenarios/constrChart.json +++ b/tests/node/scenarios/constrChart.json @@ -1,6 +1,5 @@ { "export": { - "constr": "chart", "options": { "title": { "text": "Chart" @@ -48,6 +47,7 @@ ] } ] - } + }, + "constr": "chart" } } diff --git a/tests/node/scenarios/constrGanttChart.json b/tests/node/scenarios/constrGanttChart.json index de93f1c8..c4345eea 100644 --- a/tests/node/scenarios/constrGanttChart.json +++ b/tests/node/scenarios/constrGanttChart.json @@ -1,6 +1,5 @@ { "export": { - "constr": "ganttChart", "options": { "title": { "text": "Gantt Chart" @@ -41,6 +40,7 @@ ] } ] - } + }, + "constr": "ganttChart" } } diff --git a/tests/node/scenarios/constrMapChart.json b/tests/node/scenarios/constrMapChart.json index de72b6f1..99001edc 100644 --- a/tests/node/scenarios/constrMapChart.json +++ b/tests/node/scenarios/constrMapChart.json @@ -1,6 +1,5 @@ { "export": { - "constr": "mapChart", "options": { "chart": { "map": { @@ -8249,6 +8248,7 @@ ] } ] - } + }, + "constr": "mapChart" } } diff --git a/tests/node/scenarios/constrStockChart.json b/tests/node/scenarios/constrStockChart.json index 86663a58..98426e84 100644 --- a/tests/node/scenarios/constrStockChart.json +++ b/tests/node/scenarios/constrStockChart.json @@ -1,6 +1,5 @@ { "export": { - "constr": "stockChart", "options": { "rangeSelector": { "selected": 1 @@ -520,6 +519,7 @@ ] } ] - } + }, + "constr": "stockChart" } } diff --git a/tests/node/scenarios/optionsJson.json b/tests/node/scenarios/optionsJson.json index c9d52404..78c0a55e 100644 --- a/tests/node/scenarios/optionsJson.json +++ b/tests/node/scenarios/optionsJson.json @@ -1,6 +1,5 @@ { "export": { - "constr": "chart", "options": { "title": { "text": "Basic options" diff --git a/tests/node/scenarios/optionsStringified.json b/tests/node/scenarios/optionsStringified.json index d04738aa..1c5f9567 100644 --- a/tests/node/scenarios/optionsStringified.json +++ b/tests/node/scenarios/optionsStringified.json @@ -1,6 +1,5 @@ { "export": { - "constr": "chart", "options": "{\"title\":{\"text\":\"Stringified options\"},\"xAxis\":{\"categories\":[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"]},\"series\":[{\"type\":\"column\",\"data\":[1,3,2,4]},{\"type\":\"column\",\"data\":[5,3,4,2]}]}" } } diff --git a/tests/node/scenarios/sizesAndScaleFromCliPost.json b/tests/node/scenarios/sizesAndScaleFromCliPost.json index 36724f31..a307b83e 100644 --- a/tests/node/scenarios/sizesAndScaleFromCliPost.json +++ b/tests/node/scenarios/sizesAndScaleFromCliPost.json @@ -1,8 +1,5 @@ { "export": { - "height": 800, - "width": 1200, - "scale": 2, "options": { "chart": { "type": "column" @@ -37,6 +34,9 @@ "yAxis": 1 } ] - } + }, + "height": 800, + "width": 1200, + "scale": 2 } } diff --git a/tests/node/scenarios/svgBasic.json b/tests/node/scenarios/svgBasic.json index e267dba7..c190c479 100644 --- a/tests/node/scenarios/svgBasic.json +++ b/tests/node/scenarios/svgBasic.json @@ -1,7 +1,7 @@ { "export": { + "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com", "outfile": "svgBasic.png", - "scale": 2, - "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com" + "scale": 2 } } diff --git a/tests/node/scenarios/svgBasicWithScale.json b/tests/node/scenarios/svgBasicWithScale.json index b4f82271..a8cb83c9 100644 --- a/tests/node/scenarios/svgBasicWithScale.json +++ b/tests/node/scenarios/svgBasicWithScale.json @@ -1,7 +1,7 @@ { "export": { + "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com", "outfile": "svgBasicWithScale.png", - "scale": 3, - "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com" + "scale": 3 } } diff --git a/tests/node/scenarios/svgBasicWithScaleToPdf.json b/tests/node/scenarios/svgBasicWithScaleToPdf.json index 77bdba94..c88f7676 100644 --- a/tests/node/scenarios/svgBasicWithScaleToPdf.json +++ b/tests/node/scenarios/svgBasicWithScaleToPdf.json @@ -1,8 +1,8 @@ { "export": { - "type": "pdf", + "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com", "outfile": "svgBasicWithScaleToPdf.pdf", - "scale": 2, - "svg": "Created with Highcharts 10.2.1ValuesBasic SVGSeries 1Series 2JanFebMarApr0246810Highcharts.com" + "type": "pdf", + "scale": 2 } } diff --git a/tests/node/scenarios/svgForeignObject.json b/tests/node/scenarios/svgForeignObject.json index e7f3ace7..9d5e3fa0 100644 --- a/tests/node/scenarios/svgForeignObject.json +++ b/tests/node/scenarios/svgForeignObject.json @@ -1,7 +1,7 @@ { "export": { + "svg": "Created with Highcharts 10.2.1ValuesSVG with a foreign objectSeries 1Series 2JanFebMarAprMayJunJulAugSepOctNovDec-1001020304050Highcharts.comThis subtitle is HTML", "outfile": "svgForeignObject.png", - "scale": 2, - "svg": "Created with Highcharts 10.2.1ValuesSVG with a foreign objectSeries 1Series 2JanFebMarAprMayJunJulAugSepOctNovDec-1001020304050Highcharts.comThis subtitle is HTML" + "scale": 2 } } diff --git a/tests/node/scenarios/typeJpeg.json b/tests/node/scenarios/typeJpeg.json index 4a2c2bee..fac68bdd 100644 --- a/tests/node/scenarios/typeJpeg.json +++ b/tests/node/scenarios/typeJpeg.json @@ -1,6 +1,5 @@ { "export": { - "type": "jpeg", "options": { "title": { "text": "Chart type set to JPEG" @@ -18,6 +17,7 @@ "data": [1, 2, 3, 4] } ] - } + }, + "type": "jpeg" } } diff --git a/tests/node/scenarios/typePdf.json b/tests/node/scenarios/typePdf.json index f34fd2e6..61bc2f77 100644 --- a/tests/node/scenarios/typePdf.json +++ b/tests/node/scenarios/typePdf.json @@ -1,6 +1,5 @@ { "export": { - "type": "pdf", "options": { "title": { "text": "Chart type set to PDF" @@ -18,6 +17,7 @@ "data": [1, 2, 3, 4] } ] - } + }, + "type": "pdf" } } diff --git a/tests/node/scenarios/typePng.json b/tests/node/scenarios/typePng.json index c7ab4ec4..0196640a 100644 --- a/tests/node/scenarios/typePng.json +++ b/tests/node/scenarios/typePng.json @@ -1,6 +1,5 @@ { "export": { - "type": "png", "options": { "title": { "text": "Chart type set to PNG" @@ -18,6 +17,7 @@ "data": [1, 2, 3, 4] } ] - } + }, + "type": "png" } } diff --git a/tests/node/scenarios/typeSvg.json b/tests/node/scenarios/typeSvg.json index 1dd39d3b..6a902f2b 100644 --- a/tests/node/scenarios/typeSvg.json +++ b/tests/node/scenarios/typeSvg.json @@ -1,6 +1,5 @@ { "export": { - "type": "svg", "options": { "title": { "text": "Chart type set to SVG" @@ -18,6 +17,7 @@ "data": [1, 2, 3, 4] } ] - } + }, + "type": "svg" } } diff --git a/tests/other/privateRangeUrl.js b/tests/other/privateRangeUrl.js deleted file mode 100644 index 9566e76b..00000000 --- a/tests/other/privateRangeUrl.js +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - -Highcharts Export Server - -Copyright (c) 2016-2025, Highsoft - -Licenced under the MIT licence. - -Additionally a valid Highcharts license is required for use. - -See LICENSE file in root for details. - -*******************************************************************************/ - -import 'colors'; - -import { isPrivateRangeUrlFound } from '../../lib/utils.js'; - -// Test message -console.log( - 'The isPrivateRangeUrlFound utility test'.yellow, - `\nIt checks multiple IPs and finds which are public and private.\n`.green -); - -// IP adresses to test -const ipAddresses = [ - // The localhost - 'localhost', - '127.0.0.1', - // Private range (10.0.0.0/8) - '10.151.223.167', - '10.190.93.233', - // Private range (172.0.0.0/12) - '172.22.34.250', - '172.27.95.8', - // Private range (192.168.0.0/16) - '192.168.218.176', - '192.168.231.157', - // Public range - '53.96.110.150', - '155.212.200.223' -]; - -// Test ips in different configurations, with or without a protocol prefix -['', 'http://', 'https://'].forEach((protocol) => { - if (protocol) { - console.log(`\n${protocol}`.blue.underline); - } - - ipAddresses.forEach((ip) => { - const url = `${protocol}${ip}`; - console.log( - `${url} - ` + - (isPrivateRangeUrlFound(`xlink:href="${url}"`) - ? 'private IP'.red - : 'public IP'.green) - ); - }); -}); From 6c3c10d31a464381c9331a5c8843777585c793f9 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Wed, 22 Jan 2025 17:25:36 +0100 Subject: [PATCH 080/102] Extended README with an updated options handling and processing information. --- README.md | 96 ++++++++++++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 0b8e6e24..fa93afe7 100644 --- a/README.md +++ b/README.md @@ -82,16 +82,38 @@ highcharts-export-server There are four main ways of loading configurations: -- By loading default options from the `lib/schemas/config.js` file. -- By loading options from a custom JSON file. +- By loading default options from the `./lib/schemas/config.js` file. - By providing configurations via environment variables from the `.env` file. +- By loading options from a custom JSON file. - By passing arguments through command line interface (CLI). -...or any combination of the four. In such cases, the options from the later step take precedence (config file -> custom JSON -> envs -> CLI arguments). +...or any combination of the four. In such cases, the options from the later step take precedence (config file -> envs -> custom JSON -> CLI arguments). + +## Options Handling + +A description of how options are handled and processed when using a specific export method. + +### Server And CLI Usage + +When starting a server or performing single or batch exports via the CLI, the server's global options are initialized from the `defaultConfig` object located in `./lib/schemas/config.js`, along with values from the `.env` file. These options can be extended with additional values provided through CLI arguments, which are internally set using the `setCliOptions()` function. + +### Node.js Module Usage + +For `Node.js Module` usage, the process differs slightly because API functions are called manually. By default, global options are initialized from the `defaultConfig` object and the `.env` file at startup, similar to the server and CLI scenarios. However, to include additional options, you need to explicitly call the `updateOptions()` function. If the `updateOptions()` function is not invoked, the system will rely on the default values previously set in the global options object. + +### Options Management + +All API functions accept specific sets of options that, if provided, extend the global options. When such options are not provided, the default global option values are used. Additionally, the `updateOptions()` function allows you to extend global options in advance, eliminating the need to provide options to each function individually. However, `singleExport()`, `batchExport()`, and `startExport()` must still be provided with export-related options. + +This flexibility is particularly useful for deciding whether global options should remain static during subsequent exports or be updated dynamically. In either case, new options from various sources are merged into the configuration, and the resulting options are applied in API functions. + +### Export Functions + +The `singleExport()`, `batchExport()`, and `startExport()` functions must be provided with at least partial options that include one of the following options from the `export` section: `infile`, `instr`, `svg`, or `batch`. Any missing values in the provided options object will automatically default to those specified in the global options object. Unlike other API functions, options provided to the export functions will not be merged into the global options object, as these options represent a specific export process. To make the export options global, you can use the `updateOptions()` function before initiating the export. ## Default JSON Config -The JSON below represents the default configuration stored in the `lib/schemas/config.js` file. If no `.env` file is found (more details on the file and environment variables below), these options will be used. The configuration is not recommended to be modified directly, as it can typically be managed through other sources. +The JSON below represents the default configuration stored in the `./lib/schemas/config.js` file. If no `.env` file is found (more details on the file and environment variables below), these options will be used. The configuration is not recommended to be modified directly, as it can typically be managed through other sources. The format, along with its default values, is as follows (using the recommended ordering of core and module scripts below). @@ -284,13 +306,9 @@ _Available default JSON config:_ } ``` -## Custom JSON Config - -To load an additional JSON configuration file, use the `--loadConfig ` option. This JSON file can either be manually created or generated through a prompt triggered by the `--createConfig ` option. The `` value does not need a _.json_ extension, but the file's content must be valid JSON when using the `--loadConfig` option. - ## Environment Variables -These variables are set in your environment and take precedence over options from the `lib/schemas/config.js` file. They can be set in the `.env` file (refer to the `.env.sample` file). If you prefer setting these variables through the `package.json`, use `export` command on Linux/Mac OS X and `set` command on Windows. +These variables are set in your environment and take precedence over options from the `./lib/schemas/config.js` file. They can be set in the `.env` file (refer to the `.env.sample` file). If you prefer setting these variables through the `package.json`, use `export` command on Linux/Mac OS X and `set` command on Windows. _Available environment variables:_ @@ -328,8 +346,8 @@ _Available environment variables:_ - `EXPORT_DEFAULT_HEIGHT`: The default fallback height for exported charts if not set explicitly (defaults to `400`). - `EXPORT_DEFAULT_WIDTH`: The default fallback width for exported charts if not set explicitly (defaults to `600`). - `EXPORT_DEFAULT_SCALE`: The default fallback scale for exported charts if not set explicitly. Ranges between **0.1** and **5.0** (defaults to `1`). -- `EXPORT_GLOBAL_OPTIONS`: Either a stringified JSON or a filename containing global options to be passed into the `Highcharts.setOptions` (defaults to ``). -- `EXPORT_THEME_OPTIONS`: Either a stringified JSON or a filename containing theme options to be passed into the `Highcharts.setOptions` (defaults to ``). +- `EXPORT_GLOBAL_OPTIONS`: Either a stringified JSON or a filename containing global options to be passed into the `Highcharts.setOptions()` (defaults to ``). +- `EXPORT_THEME_OPTIONS`: Either a stringified JSON or a filename containing theme options to be passed into the `Highcharts.setOptions()` (defaults to ``). - `EXPORT_RASTERIZATION_TIMEOUT`: The specified duration, in milliseconds, to wait for rendering a webpage (defaults to `1500`). ### Custom Logic Config @@ -417,6 +435,10 @@ _Available environment variables:_ - `DEBUG_SLOW_MO`: Slows down Puppeteer operations by the specified number of milliseconds (defaults to `0`). - `DEBUG_DEBUGGING_PORT`: Specifies the debugging port (defaults to `9222`). +## Custom JSON Config + +To load an additional JSON configuration file, use the `--loadConfig ` option. This JSON file can either be manually created or generated through a prompt triggered by the `--createConfig ` option. The `` value does not need a _.json_ extension, but the file's content must be valid JSON when using the `--loadConfig` option. + ## Command Line Arguments To supply command line arguments, add them as flags when running the application: @@ -459,8 +481,8 @@ _Available CLI arguments:_ - `--defaultHeight`: The default fallback height for exported charts if not set explicitly (defaults to `400`). - `--defaultWidth`: The default fallback width for exported charts if not set explicitly (defaults to `600`). - `--defaultScale`: The default fallback scale for exported charts if not set explicitly. Ranges between **0.1** and **5.0** (defaults to `1`). -- `--globalOptions`: Either a stringified JSON or a filename containing global options to be passed into the `Highcharts.setOptions` (defaults to `null`). -- `--themeOptions`: Either a stringified JSON or a filename containing theme options to be passed into the `Highcharts.setOptions` (defaults to `null`). +- `--globalOptions`: Either a stringified JSON or a filename containing global options to be passed into the `Highcharts.setOptions()` (defaults to `null`). +- `--themeOptions`: Either a stringified JSON or a filename containing theme options to be passed into the `Highcharts.setOptions()` (defaults to `null`). - `--rasterizationTimeout`: The specified duration, in milliseconds, to wait for rendering a webpage (defaults to `1500`). ### Custom Logic Config @@ -586,8 +608,8 @@ _Available request arguments:_ - `height`: The height of the exported chart. - `width`: The width of the exported chart. - `scale`: The scale factor of the exported chart. Use it to improve resolution in PNG and JPEG, for example setting scale to 2 on a 600px chart will result in a 1200px output. -- `globalOptions`: Either a JSON or a stringified JSON with global options to be passed into `Highcharts.setOptions`. -- `themeOptions`: Either a JSON or a stringified JSON with theme options to be passed into `Highcharts.setOptions`. +- `globalOptions`: Either a JSON or a stringified JSON with global options to be passed into `Highcharts.setOptions()`. +- `themeOptions`: Either a JSON or a stringified JSON with theme options to be passed into `Highcharts.setOptions()`. - `resources`: Additional resources in the form of a JSON or a stringified JSON. It may contain `files` (array of JS filenames), `js` (stringified JS), and `css` (stringified CSS) sections. - `callback`: Stringified JavaScript function to execute in the Highcharts constructor. - `customCode`: Custom code to be executed before the chart initialization. This can be a function, code wrapped within a function, or a filename with the _.js_ extension. Both `allowFileResources` and `allowCodeExecution` must be set to **true** for the option to be considered. @@ -666,9 +688,6 @@ const customOptions = { // Logic must be triggered in an asynchronous function (async () => { - // Set options with user configuration - const options = exporter.setGlobalOptions(customOptions); - // Must initialize exporting before being able to export charts await exporter.initExport(options); @@ -683,22 +702,7 @@ const customOptions = { })(); ``` -In order for everything to work as it is supposed to, the `setGlobalOptions` function should be called before running the `initExport` and any export-related function (`startExport`, `singleExport`, or `batchExport`) to correctly initialize all option values. - -## Options Handling - -When starting a server or performing single or batch exports via the CLI, server's global options are initialized from the `defaultConfig` object located in `./lib/schemas/config.js`. These options are then extended with values from all other sources mentioned in the [Configuration](#configuration) section. - -For `Node.js Module` usage, the process differs slightly. Some API functions must be called manually. By default, global options are initialized solely from the `defaultConfig` object at the start, similar to the server and CLI scenarios. However, to include options from other sources (as outlined in the [Configuration](#configuration) section), you need to explicitly call the `setGlobalOptions()` function. If `setGlobalOptions()` is not used, the system will rely on the default values from the defaultConfig object. - -The `setGlobalOptions()` function allows you to either extend or copy global options. - -- `Extend`: Adds new options to the global configuration while keeping the original options intact. -- `Copy`: Merges new options into a separate set, leaving global options unaffected. - -This flexibility is useful for deciding whether global options should remain unmodified during subsequent exports or be updated dynamically. In either case, new options from other sources are merged into the configuration, and the final options are returned for use in API functions. - -Functions such as `initExport()`, `singleExport()`, `batchExport()`, and `startExport()` accept partial options. Any missing values in these options will be automatically filled in using the default values from the defaultConfig object. +In order for exporting to work as intended, the `initExport()` function must be called before running any export-related functions (`singleExport()`, `batchExport()`, or `startExport()`). This initializes all required mechanisms, such as script fetching, cache setting, resource pooling, and browser startup. ## CommonJS Support @@ -708,7 +712,7 @@ This package supports both CommonJS and ES modules. **highcharts-export-server module** -- `async function startServer(serverOptions)`: Starts an HTTP and/or HTTPS server based on the provided configuration. The `serverOptions` object contains server-related properties (refer to the `server` section in the `lib/schemas/config.js` file for details). +- `async function startServer(serverOptions)`: Starts an HTTP and/or HTTPS server based on the provided configuration. The `serverOptions` object contains server-related properties (refer to the `server` section in the `./lib/schemas/config.js` file for details). - `@param {Object} serverOptions` - The configuration object containing `server` options. This object may include a partial or complete set of the `server` options. If the options are partial, missing values will default to the current global configuration. @@ -749,26 +753,16 @@ This package supports both CommonJS and ES modules. - `@param {string} path` - The path to which the middleware(s) should be applied. - `@param {...Function} middlewares` - The middleware function(s) to be applied. -- `function getOptions(getInstance = true)`: Retrieves a reference to the options object. Depending on the `getInstance` parameter, it returns either the global options or the instance-specific options object. - - - `@param {boolean} [getInstance=true]` - Optional parameter that decides whether to return the instance-specific options (when `true`) or the global options (when `false`). The default value is `true`. - - - `@returns {Object}` A reference to either the global options or the instance-specific options, based on the `getInstance` parameter. - -- `function setGlobalOptions(customOptions = {}, cliArgs = [])`: Sets the global options of the export server, keeping the principle of the options load priority from all available sources. It accepts optional `customOptions` object and `cliArgs` array with arguments from the CLI. These options will be validated and applied if provided. +- `function getOptions()`: Retrieves a copy of the global options object. - The priority order of setting values is: + - `@returns {Object}` A reference to the global options object. - 1. Options from the `lib/schemas/config.js` file (default values). - 2. Options from a custom JSON file (loaded by the `loadConfig` option). - 3. Options from the environment variables (the `.env` file). - 4. Options from the command line interface (CLI). - 5. Options from the first parameter (the `customOptions` is by default an empty object). +- `function updateOptions(newOptions, getCopy = false)`: Updates the global options with the provided options. - - `@param {Object} [customOptions={}]` - Optional custom options for additional configuration. The default value is an empty object. - - `@param {Array.} [cliArgs=[]]` - Optional command line arguments for additional configuration. The default value is an empty array. + - `@param {Object} newOptions` - An object containing the new options to be merged into the global options. + - `@param {boolean} [getCopy=false]` - Determines whether to merge the new options into a copy of the global options object (`true`) or directly into the global options object (`false`). The default value is `false`. - - `@returns {Object}` The updated global options object, reflecting the merged configuration from all available sources. + - `@returns {Object}` The updated options object, either the modified global options or a modified copy, based on the value of `getCopy`. - `function mapToNewOptions(oldOptions)`: Maps old-structured configuration options (PhantomJS) to a new format (Puppeteer). This function converts flat, old-structured options into a new, nested configuration format based on a predefined mapping (`nestedProps`). The new format is used for Puppeteer, while the old format was used for PhantomJS. From c7c8a830cc8c18be7dead7a6241b1fe7eabe602b Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Wed, 22 Jan 2025 17:25:56 +0100 Subject: [PATCH 081/102] Built scripts. --- dist/index.cjs | 4 ++-- dist/index.esm.js | 2 +- dist/index.esm.js.map | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/index.cjs b/dist/index.cjs index 6dc0dbd7..4aa6cbd8 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),promises=require("fs/promises"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?e:path.join(__dirname$1,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env);class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 globalOptions=_initGlobalOptions(defaultConfig),instanceOptions=deepCopy(globalOptions);function getOptions(e=!0){return e?instanceOptions:globalOptions}function setGlobalOptions(e={},t=[]){let o={},r={};return t&&Array.isArray(t)&&t.length&&(o=_loadConfigFile(t,globalOptions.customLogic),r=_pairArgumentValue(nestedProps,t)),_updateGlobalOptions(defaultConfig,globalOptions,o,r,e),globalOptions}function updateOptions(e,t=!1){return t&&(Object.keys(instanceOptions).forEach((e=>{delete instanceOptions[e]})),mergeOptions(instanceOptions,deepCopy(globalOptions))),mergeOptions(instanceOptions,e),instanceOptions}function mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initGlobalOptions(e){const t={};for(const[o,r]of Object.entries(e))if(Object.prototype.hasOwnProperty.call(r,"value")){const e=envs[r.envLink];t[o]=null!=e?e:r.value}else t[o]=_initGlobalOptions(r);return t}function _updateGlobalOptions(e,t,o,r,n){Object.keys(e).forEach((i=>{const s=e[i],a=o&&o[i],l=r&&r[i],c=n&&n[i];if(void 0===s.value)_updateGlobalOptions(s,t[i],a,l,c);else{null!=a&&(t[i]=a);const e=envs[s.envLink];s.envLink in envs&&null!=e&&(t[i]=e),null!=l&&(t[i]=l),null!=c&&(t[i]=c)}}))}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _loadConfigFile(e,t){const o=e.findIndex((e=>"loadConfig"===e.replace(/-/g,""))),r=o>-1&&e[o+1];if(r&&t.allowFileResources)try{return isAllowedConfig(fs.readFileSync(getAbsolutePath(r),"utf8"),!1,t.allowCodeExecution)}catch(e){logWithStack(2,e,`[config] Unable to load the configuration from the ${r} file.`)}return{}}function _pairArgumentValue(e,t){const o={};for(let r=0;r{if(i.length-1===s){const i=t[++r];i||log(2,`[config] Missing value for the CLI '--${n}' argument. Using the default value.`),e[o]=i||null}else void 0===e[o]&&(e[o]={});return e[o]}),o)}return o}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}const cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=getOptions();t.highcharts.version=e,await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:path.join(__dirname$1,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.",400);const o=mergeOptions(deepCopy(getOptions()),{export:e.export,customLogic:e.customLogic}),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e={}){const t=updateOptions(e,!0);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,setGlobalOptions:setGlobalOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvZXJyb3JzL0V4cG9ydEVycm9yLmpzIiwiLi4vbGliL2NvbmZpZy5qcyIsIi4uL2xpYi9mZXRjaC5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIGpvaW4gfSBmcm9tICdwYXRoJztcclxuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCc7XHJcblxyXG5jb25zdCBNQVhfQkFDS09GRl9BVFRFTVBUUyA9IDY7XHJcblxyXG4vLyBUaGUgZGlyZWN0b3J5IHBhdGhcclxuZXhwb3J0IGNvbnN0IF9fZGlybmFtZSA9IGZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLi4vLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbmQgc3RhbmRhcmRpemVzIHRleHQgYnkgcmVwbGFjaW5nIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2VcclxuICogY2hhcmFjdGVycyB3aXRoIGEgc2luZ2xlIHNwYWNlIGFuZCB0cmltbWluZyBhbnkgbGVhZGluZyBvciB0cmFpbGluZ1xyXG4gKiB3aGl0ZXNwYWNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJUZXh0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IC0gVGhlIGlucHV0IHRleHQgdG8gYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtSZWdFeHB9IFtydWxlPS9cXHNcXHMrL2ddIC0gVGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBydWxlIHRvIG1hdGNoXHJcbiAqIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgdGhlICcvXFxzXFxzKy9nJyBSZWdFeHAuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcmVwbGFjZXI9JyAnXSAtIFRoZSBzdHJpbmcgdXNlZCB0byByZXBsYWNlIG11bHRpcGxlXHJcbiAqIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgdGhlICcgJyBzdHJpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjbGVhcmVkIGFuZCBzdGFuZGFyZGl6ZWQgdGV4dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhclRleHQodGV4dCwgcnVsZSA9IC9cXHNcXHMrL2csIHJlcGxhY2VyID0gJyAnKSB7XHJcbiAgcmV0dXJuIHRleHQucmVwbGFjZUFsbChydWxlLCByZXBsYWNlcikudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIGRlZXAgY29weSBvZiB0aGUgZ2l2ZW4gb2JqZWN0IG9yIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZGVlcENvcHlcclxuICpcclxuICogQHBhcmFtIHsoT2JqZWN0fEFycmF5KX0gb2JqQXJyIC0gVGhlIG9iamVjdCBvciBhcnJheSB0byBiZSBkZWVwbHkgY29waWVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxBcnJheSl9IFRoZSBkZWVwIGNvcHkgb2YgdGhlIHByb3ZpZGVkIG9iamVjdCBvciBhcnJheS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBkZWVwQ29weShvYmpBcnIpIHtcclxuICAvLyBJZiB0aGUgYG9iakFycmAgaXMgbnVsbCBvciBub3Qgb2YgdGhlIGBvYmplY3RgIHR5cGUsIHJldHVybiBpdFxyXG4gIGlmIChvYmpBcnIgPT09IG51bGwgfHwgdHlwZW9mIG9iakFyciAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmpBcnI7XHJcbiAgfVxyXG5cclxuICAvLyBQcmVwYXJlIGVpdGhlciBhIG5ldyBhcnJheSBvciBhIG5ldyBvYmplY3RcclxuICBjb25zdCBvYmpBcnJDb3B5ID0gQXJyYXkuaXNBcnJheShvYmpBcnIpID8gW10gOiB7fTtcclxuXHJcbiAgLy8gUmVjdXJzaXZlbHkgY29weSBlYWNoIHByb3BlcnR5XHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqQXJyKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iakFyciwga2V5KSkge1xyXG4gICAgICBvYmpBcnJDb3B5W2tleV0gPSBkZWVwQ29weShvYmpBcnJba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNvcGllZCBvYmplY3RcclxuICByZXR1cm4gb2JqQXJyQ29weTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEltcGxlbWVudHMgYW4gZXhwb25lbnRpYWwgYmFja29mZiBzdHJhdGVneSBmb3IgcmV0cnlpbmcgYSBmdW5jdGlvbiB1bnRpbFxyXG4gKiBhIGNlcnRhaW4gbnVtYmVyIG9mIGF0dGVtcHRzIGFyZSByZWFjaGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGV4cEJhY2tvZmZcclxuICpcclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gLSBUaGUgZnVuY3Rpb24gdG8gYmUgcmV0cmllZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IFthdHRlbXB0PTBdIC0gVGhlIGN1cnJlbnQgYXR0ZW1wdCBudW1iZXIuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGAwYC5cclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0XHJcbiAqIG9mIHRoZSBmdW5jdGlvbiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwQmFja29mZihmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlcGVhdCwgdGhyb3cgYW4gZXJyb3JcclxuICAgIGlmICgrK2F0dGVtcHQgPj0gTUFYX0JBQ0tPRkZfQVRURU1QVFMpIHtcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxyXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc3BvbnNlKSA9PiBzZXRUaW1lb3V0KHJlc3BvbnNlLCBkZWxheUluTXMpKTtcclxuXHJcbiAgICAvLy8gVE8gRE86IENvcnJlY3RcclxuICAgIC8vIC8vIEluZm9ybWF0aW9uIGFib3V0IHRoZSByZXNvdXJjZSB0aW1lb3V0XHJcbiAgICAvLyBsb2coXHJcbiAgICAvLyAgIDMsXHJcbiAgICAvLyAgIGBbdXRpbHNdIFdhaXRlZCAke2RlbGF5SW5Nc31tcyB1bnRpbCBuZXh0IGNhbGwgZm9yIHRoZSByZXNvdXJjZSBvZiBJRDogJHthcmdzWzBdfS5gXHJcbiAgICAvLyApO1xyXG5cclxuICAgIC8vIFRyeSBhZ2FpblxyXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkanVzdHMgdGhlIGNvbnN0cnVjdG9yIG5hbWUgYnkgdHJhbnNmb3JtaW5nIGFuZCBub3JtYWxpemluZyBpdCBiYXNlZFxyXG4gKiBvbiBjb21tb24gY2hhcnQgdHlwZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhDb25zdHJcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnN0ciAtIFRoZSBvcmlnaW5hbCBjb25zdHJ1Y3RvciBuYW1lIHRvIGJlIGZpeGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGNvbnN0cnVjdG9yIG5hbWUsIG9yICdjaGFydCcgaWYgdGhlIGlucHV0XHJcbiAqIGlzIG5vdCByZWNvZ25pemVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeENvbnN0cihjb25zdHIpIHtcclxuICB0cnkge1xyXG4gICAgLy8gRml4IHRoZSBjb25zdHJ1Y3RvciBieSBsb3dlcmluZyBjYXNpbmdcclxuICAgIGNvbnN0IGZpeGVkQ29uc3RyID0gYCR7Y29uc3RyLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnY2hhcnQnLCAnJyl9Q2hhcnRgO1xyXG5cclxuICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgcmVzdWx0IGlzIGp1c3QgJ0NoYXJ0J1xyXG4gICAgaWYgKGZpeGVkQ29uc3RyID09PSAnQ2hhcnQnKSB7XHJcbiAgICAgIGZpeGVkQ29uc3RyLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IsIG90aGVyd2lzZSBkZWZhdWx0IHRvICdjaGFydCdcclxuICAgIHJldHVybiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddLmluY2x1ZGVzKFxyXG4gICAgICBmaXhlZENvbnN0clxyXG4gICAgKVxyXG4gICAgICA/IGZpeGVkQ29uc3RyXHJcbiAgICAgIDogJ2NoYXJ0JztcclxuICB9IGNhdGNoIHtcclxuICAgIC8vIERlZmF1bHQgdG8gJ2NoYXJ0JyBpbiBjYXNlIG9mIGFueSBlcnJvclxyXG4gICAgcmV0dXJuICdjaGFydCc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIG91dGZpbGUgYmFzZWQgb24gcHJvdmlkZWQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeE91dGZpbGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBvdXRmaWxlIC0gVGhlIGZpbGUgcGF0aCBvciBuYW1lLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIG91dGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4T3V0ZmlsZSh0eXBlLCBvdXRmaWxlKSB7XHJcbiAgLy8gR2V0IHRoZSBmaWxlIG5hbWUgZnJvbSB0aGUgYG91dGZpbGVgIG9wdGlvblxyXG4gIGNvbnN0IGZpbGVOYW1lID0gZ2V0QWJzb2x1dGVQYXRoKG91dGZpbGUgfHwgJ2NoYXJ0JylcclxuICAgIC5zcGxpdCgnLicpXHJcbiAgICAuc2hpZnQoKTtcclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCBvdXRmaWxlXHJcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke3R5cGV9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhUeXBlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW291dGZpbGU9bnVsbF0gLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGBudWxsYC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNvcnJlY3RlZCBleHBvcnQgdHlwZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhUeXBlKHR5cGUsIG91dGZpbGUgPSBudWxsKSB7XHJcbiAgLy8gTUlNRSB0eXBlc1xyXG4gIGNvbnN0IG1pbWVUeXBlcyA9IHtcclxuICAgICdpbWFnZS9wbmcnOiAncG5nJyxcclxuICAgICdpbWFnZS9qcGVnJzogJ2pwZWcnLFxyXG4gICAgJ2FwcGxpY2F0aW9uL3BkZic6ICdwZGYnLFxyXG4gICAgJ2ltYWdlL3N2Zyt4bWwnOiAnc3ZnJ1xyXG4gIH07XHJcblxyXG4gIC8vIEdldCBmb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IE9iamVjdC52YWx1ZXMobWltZVR5cGVzKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdHlwZSBhbmQgb3V0ZmlsZSdzIGV4dGVuc2lvbnMgYXJlIHRoZSBzYW1lXHJcbiAgaWYgKG91dGZpbGUpIHtcclxuICAgIGNvbnN0IG91dFR5cGUgPSBvdXRmaWxlLnNwbGl0KCcuJykucG9wKCk7XHJcblxyXG4gICAgLy8gU3VwcG9ydCB0aGUgSlBHIHR5cGVcclxuICAgIGlmIChvdXRUeXBlID09PSAnanBnJykge1xyXG4gICAgICB0eXBlID0gJ2pwZWcnO1xyXG4gICAgfSBlbHNlIGlmIChmb3JtYXRzLmluY2x1ZGVzKG91dFR5cGUpICYmIHR5cGUgIT09IG91dFR5cGUpIHtcclxuICAgICAgdHlwZSA9IG91dFR5cGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IHR5cGVcclxuICByZXR1cm4gbWltZVR5cGVzW3R5cGVdIHx8IGZvcm1hdHMuZmluZCgodCkgPT4gdCA9PT0gdHlwZSkgfHwgJ3BuZyc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIHBhdGggaXMgcmVsYXRpdmUgb3IgYWJzb2x1dGUgYW5kIHJldHVybnMgdGhlIGNvcnJlY3RlZCxcclxuICogYWJzb2x1dGUgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzQWJzb2x1dGVQYXRoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gYmUgY2hlY2tlZCBvbi5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGguXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWJzb2x1dGVQYXRoKHBhdGgpIHtcclxuICByZXR1cm4gaXNBYnNvbHV0ZShwYXRoKSA/IHBhdGggOiBqb2luKF9fZGlybmFtZSwgcGF0aCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBpbnB1dCBkYXRhIHRvIGEgQmFzZTY0IHN0cmluZyBiYXNlZCBvbiB0aGUgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRCYXNlNjRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IC0gVGhlIGlucHV0IHRvIGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmb3JtYXQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQmFzZTY0IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QmFzZTY0KGlucHV0LCB0eXBlKSB7XHJcbiAgLy8gRm9yIHBkZiBhbmQgc3ZnIHR5cGVzIHRoZSBpbnB1dCBtdXN0IGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmcm9tIGEgYnVmZmVyXHJcbiAgaWYgKHR5cGUgPT09ICdwZGYnIHx8IHR5cGUgPT0gJ3N2ZycpIHtcclxuICAgIHJldHVybiBCdWZmZXIuZnJvbShpbnB1dCwgJ3V0ZjgnKS50b1N0cmluZygnYmFzZTY0Jyk7XHJcbiAgfVxyXG5cclxuICAvLyBGb3IgcG5nIGFuZCBqcGVnIGlucHV0IGlzIGFscmVhZHkgYSBCYXNlNjQgc3RyaW5nXHJcbiAgcmV0dXJuIGlucHV0O1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyBzdHJpbmdpZmllZCBkYXRlIHdpdGhvdXQgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGUoKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgR01UIHRleHQgaW5mb3JtYXRpb25cclxuICByZXR1cm4gbmV3IERhdGUoKS50b1N0cmluZygpLnNwbGl0KCcoJylbMF0udHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgc3RvcmVkIHRpbWUgdmFsdWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVRpbWVcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXROZXdEYXRlVGltZSgpIHtcclxuICByZXR1cm4gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIGl0ZW0gaXMgYW4gb2JqZWN0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNPYmplY3RcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIGl0ZW0gdG8gYmUgY2hlY2tlZC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIGl0ZW0gaXMgYW4gb2JqZWN0LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3QoaXRlbSkge1xyXG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaXRlbSkgPT09ICdbb2JqZWN0IE9iamVjdF0nO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdEVtcHR5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGVtcHR5LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3RFbXB0eShpdGVtKSB7XHJcbiAgcmV0dXJuIChcclxuICAgIHR5cGVvZiBpdGVtID09PSAnb2JqZWN0JyAmJlxyXG4gICAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICAgIGl0ZW0gIT09IG51bGwgJiZcclxuICAgIE9iamVjdC5rZXlzKGl0ZW0pLmxlbmd0aCA9PT0gMFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCBpbiB0aGUgZ2l2ZW4gc3RyaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaXRlbSAtIFRoZSBzdHJpbmcgdG8gYmUgY2hlY2tlZCBmb3IgYSBwcml2YXRlIElQIHJhbmdlIFVSTC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoaXRlbSkge1xyXG4gIGNvbnN0IHJlZ2V4UGF0dGVybnMgPSBbXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/bG9jYWxob3N0XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTI3XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xNzJcXC4oMVs2LTldfDJbMC05XXwzWzAtMV0pXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTkyXFwuMTY4XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi9cclxuICBdO1xyXG5cclxuICByZXR1cm4gcmVnZXhQYXR0ZXJucy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3QoaXRlbSkpO1xyXG59XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIGVsYXBzZWQgdGltZSB1c2luZyB0aGUgTm9kZS5qcyBgcHJvY2Vzcy5ocnRpbWUoKWAgbWV0aG9kLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbWVhc3VyZVRpbWVcclxuICpcclxuICogQHJldHVybnMge0Z1bmN0aW9ufSBBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZWxhcHNlZCB0aW1lIGluIG1pbGxpc2Vjb25kcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBtZWFzdXJlVGltZSgpIHtcclxuICBjb25zdCBzdGFydCA9IHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO1xyXG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xyXG59XHJcblxyXG4vKipcclxuICogUm91bmRzIGEgbnVtYmVyIHRvIHRoZSBzcGVjaWZpZWQgcHJlY2lzaW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcm91bmROdW1iZXJcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IHZhbHVlIC0gVGhlIG51bWJlciB0byBiZSByb3VuZGVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcHJlY2lzaW9uIC0gVGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyB0byByb3VuZCB0by5cclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gVGhlIHJvdW5kZWQgbnVtYmVyLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHJvdW5kTnVtYmVyKHZhbHVlLCBwcmVjaXNpb24gPSAxKSB7XHJcbiAgY29uc3QgbXVsdGlwbGllciA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24gfHwgMCk7XHJcbiAgcmV0dXJuIE1hdGgucm91bmQoK3ZhbHVlICogbXVsdGlwbGllcikgLyBtdWx0aXBsaWVyO1xyXG59XHJcblxyXG4vKipcclxuICogQ29udmVydHMgYSB2YWx1ZSB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB0b0Jvb2xlYW5cclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUaGUgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQgdmFsdWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdG9Cb29sZWFuKGl0ZW0pIHtcclxuICByZXR1cm4gWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBXcmFwcyBjdXN0b20gY29kZSB0byBleGVjdXRlIGl0IHNhZmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIHdyYXBBcm91bmRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tIGNvZGUgdG8gYmUgd3JhcHBlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBGbGFnIHRvIGFsbG93IGxvYWRpbmcgY29kZSBmcm9tIGEgZmlsZS5cclxuICogQHBhcmFtIHtib29sZWFufSBbaXNDYWxsYmFjaz1mYWxzZV0gLSBGbGFnIHRoYXQgaW5kaWNhdGVzIHRoZSByZXR1cm5lZCBjb2RlXHJcbiAqIG11c3QgYmUgaW4gYSBjYWxsYmFjayBmb3JtYXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoc3RyaW5nfG51bGwpfSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBudWxsIGlmIHdyYXBwaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHdyYXBBcm91bmQoY3VzdG9tQ29kZSwgYWxsb3dGaWxlUmVzb3VyY2VzLCBpc0NhbGxiYWNrID0gZmFsc2UpIHtcclxuICBpZiAoY3VzdG9tQ29kZSAmJiB0eXBlb2YgY3VzdG9tQ29kZSA9PT0gJ3N0cmluZycpIHtcclxuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcclxuXHJcbiAgICBpZiAoY3VzdG9tQ29kZS5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgICAgLy8gTG9hZCBhIGZpbGUgaWYgdGhlIGZpbGUgcmVzb3VyY2VzIGFyZSBhbGxvd2VkXHJcbiAgICAgIHJldHVybiBhbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgICA/IHdyYXBBcm91bmQoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY3VzdG9tQ29kZSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgICAgaXNDYWxsYmFja1xyXG4gICAgICAgICAgKVxyXG4gICAgICAgIDogbnVsbDtcclxuICAgIH0gZWxzZSBpZiAoXHJcbiAgICAgICFpc0NhbGxiYWNrICYmXHJcbiAgICAgIChjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24gKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFRyZWF0IGEgZnVuY3Rpb24gYXMgYSBzZWxmLWludm9raW5nIGV4cHJlc3Npb25cclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gT3IgcmV0dXJuIGFzIGEgc3RyaW5naWZpZWQgY29kZVxyXG4gICAgcmV0dXJuIGN1c3RvbUNvZGUucmVwbGFjZSgvOyQvLCAnJyk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgX19kaXJuYW1lLFxyXG4gIGNsZWFyVGV4dCxcclxuICBkZWVwQ29weSxcclxuICBleHBCYWNrb2ZmLFxyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBnZXROZXdEYXRlLFxyXG4gIGdldE5ld0RhdGVUaW1lLFxyXG4gIGlzT2JqZWN0LFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBtZWFzdXJlVGltZSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQSBtb2R1bGUgZm9yIG1hbmFnaW5nIGxvZ2dpbmcgZnVuY3Rpb25hbGl0eSB3aXRoIGN1c3RvbWl6YWJsZVxyXG4gKiBsb2cgbGV2ZWxzLCBjb25zb2xlIGFuZCBmaWxlIGxvZ2dpbmcgb3B0aW9ucywgYW5kIGVycm9yIGhhbmRsaW5nIHN1cHBvcnQuXHJcbiAqIFRoZSBtb2R1bGUgYWxzbyBlbnN1cmVzIHRoYXQgZmlsZS1iYXNlZCBsb2dzIGFyZSBzdG9yZWQgaW4gYSBzdHJ1Y3R1cmVkXHJcbiAqIGRpcmVjdG9yeSwgY3JlYXRpbmcgdGhlIG5lY2Vzc2FyeSBwYXRocyBhdXRvbWF0aWNhbGx5IGlmIHRoZXkgZG8gbm90IGV4aXN0LlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWJzb2x1dGVQYXRoLCBnZXROZXdEYXRlIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG4vLyBUaGUgYXZhaWxhYmxlIGNvbG9yc1xyXG5jb25zdCBjb2xvcnMgPSBbJ3JlZCcsICd5ZWxsb3cnLCAnYmx1ZScsICdncmF5JywgJ2dyZWVuJ107XHJcblxyXG4vLyBUaGUgZGVmYXVsdCBsb2dnaW5nIGNvbmZpZ1xyXG5jb25zdCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBGdWxsIHBhdGggdG8gdGhlIGxvZyBmaWxlXHJcbiAgcGF0aFRvTG9nOiAnJyxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdXHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhIG1lc3NhZ2Ugd2l0aCBhIHNwZWNpZmllZCBsb2cgbGV2ZWwuIEFjY2VwdHMgYSB2YXJpYWJsZSBudW1iZXJcclxuICogb2YgYXJndW1lbnRzLiBUaGUgYXJndW1lbnRzIGFmdGVyIHRoZSBgbGV2ZWxgIGFyZSBwYXNzZWQgdG8gYGNvbnNvbGUubG9nYFxyXG4gKiBhbmQvb3IgdXNlZCB0byBjb25zdHJ1Y3QgYW5kIGFwcGVuZCBtZXNzYWdlcyB0byBhIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Li4udW5rbm93bn0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZ1xyXG4gKiBsZXZlbCBhbmQgdGhlIHJlbWFpbmluZyBhcmUgc3RyaW5ncyB1c2VkIHRvIGJ1aWxkIHRoZSBsb2cgbWVzc2FnZS5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZyguLi5hcmdzKSB7XHJcbiAgY29uc3QgW25ld0xldmVsLCAuLi50ZXh0c10gPSBhcmdzO1xyXG5cclxuICAvLyBDdXJyZW50IGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgbGV2ZWxzRGVzYywgbGV2ZWwgfSA9IGxvZ2dpbmc7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZSBvciBpcyBpdCBhIGJlbmNobWFyayBsb2dcclxuICBpZiAoXHJcbiAgICBuZXdMZXZlbCAhPT0gNSAmJlxyXG4gICAgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aClcclxuICApIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtnZXROZXdEYXRlKCl9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgYW4gZXJyb3IgbWVzc2FnZSBhbG9uZyB3aXRoIGl0cyBzdGFjayB0cmFjZS4gT3B0aW9uYWxseSwgYSBjdXN0b20gbWVzc2FnZVxyXG4gKiBjYW4gYmUgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dXaXRoU3RhY2tcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IG5ld0xldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHN0YWNrIHRyYWNlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGluY2x1ZGVkXHJcbiAqIGluIHRoZSBsb2cgYWxvbmdzaWRlIHRoZSBlcnJvci5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZ1dpdGhTdGFjayhuZXdMZXZlbCwgZXJyb3IsIGN1c3RvbU1lc3NhZ2UpIHtcclxuICAvLyBHZXQgdGhlIG1haW4gbWVzc2FnZVxyXG4gIGNvbnN0IG1haW5NZXNzYWdlID0gY3VzdG9tTWVzc2FnZSB8fCAoZXJyb3IgJiYgZXJyb3IubWVzc2FnZSkgfHwgJyc7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlXHJcbiAgaWYgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aCkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIEFkZCB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9IGVycm9yICYmIGVycm9yLnN0YWNrO1xyXG5cclxuICAvLyBDb21iaW5lIGN1c3RvbSBtZXNzYWdlIG9yIGVycm9yIG1lc3NhZ2Ugd2l0aCBlcnJvciBzdGFjayBtZXNzYWdlLCBpZiBleGlzdHNcclxuICBjb25zdCB0ZXh0cyA9IFttYWluTWVzc2FnZV07XHJcbiAgaWYgKHN0YWNrTWVzc2FnZSkge1xyXG4gICAgdGV4dHMucHVzaCgnXFxuJywgc3RhY2tNZXNzYWdlKTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgdGV4dHMuc2hpZnQoKVtjb2xvcnNbbmV3TGV2ZWwgLSAxXV0sXHJcbiAgICAgICAgLi4udGV4dHNcclxuICAgICAgXSlcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgbG9nZ2luZyBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaW5pdExvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGxvZ2dpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGxvZ2dpbmdgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaW5pdExvZ2dpbmcobG9nZ2luZ09wdGlvbnMpIHtcclxuICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBgbG9nZ2luZ09wdGlvbnNgIG9iamVjdFxyXG4gIGNvbnN0IHsgbGV2ZWwsIGRlc3QsIGZpbGUsIHRvQ29uc29sZSwgdG9GaWxlIH0gPSBsb2dnaW5nT3B0aW9ucztcclxuXHJcbiAgLy8gUmVzZXQgZmxhZ3MgdG8gdGhlIGRlZmF1bHQgdmFsdWVzXHJcbiAgbG9nZ2luZy5wYXRoQ3JlYXRlZCA9IGZhbHNlO1xyXG4gIGxvZ2dpbmcucGF0aFRvTG9nID0gJyc7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nZ2luZyBsZXZlbFxyXG4gIHNldExvZ0xldmVsKGxldmVsKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb25zb2xlIGxvZ2dpbmdcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpO1xyXG5cclxuICAvLyBTZXQgdGhlIGZpbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKGAwYCA9IG5vIGxvZ2dpbmcsXHJcbiAqIGAxYCA9IGVycm9yLCBgMmAgPSB3YXJuaW5nLCBgM2AgPSBub3RpY2UsIGA0YCA9IHZlcmJvc2UsIG9yIGA1YCA9IGJlbmNobWFyaykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRMb2dMZXZlbFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgbG9nIGxldmVsIHRvIGJlIHNldC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRMb2dMZXZlbChsZXZlbCkge1xyXG4gIGlmIChcclxuICAgIE51bWJlci5pc0ludGVnZXIobGV2ZWwpICYmXHJcbiAgICBsZXZlbCA+PSAwICYmXHJcbiAgICBsZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoXHJcbiAgKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYGxldmVsYCBvcHRpb25cclxuICAgIGxvZ2dpbmcubGV2ZWwgPSBsZXZlbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGNvbnNvbGUgbG9nZ2luZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUNvbnNvbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9Db25zb2xlIC0gVGhlIGZsYWcgZm9yIHNldHRpbmcgdGhlIGxvZ2dpbmcgdG8gdGhlIGNvbnNvbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmcodG9Db25zb2xlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0NvbnNvbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9Db25zb2xlID0gISF0b0NvbnNvbGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlIG5hbWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVGaWxlTG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZGVzdCAtIFRoZSBkZXN0aW5hdGlvbiBwYXRoIHdoZXJlIHRoZSBsb2cgZmlsZSBzaG91bGRcclxuICogYmUgc2F2ZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlIC0gVGhlIG5hbWUgb2YgdGhlIGxvZyBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvRmlsZSAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9nZ2luZyBzaG91bGRcclxuICogYmUgZGlyZWN0ZWQgdG8gYSBmaWxlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgdG9GaWxlYCBvcHRpb25cclxuICBsb2dnaW5nLnRvRmlsZSA9ICEhdG9GaWxlO1xyXG5cclxuICAvLyBTZXQgdGhlIGBkZXN0YCBhbmQgYGZpbGVgIG9wdGlvbnMgb25seSBpZiB0aGUgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWRcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIGxvZ2dpbmcuZGVzdCA9IGRlc3QgfHwgJyc7XHJcbiAgICBsb2dnaW5nLmZpbGUgPSBmaWxlIHx8ICcnO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgdGhlIHByb3ZpZGVkIHRleHRzIHRvIGEgZmlsZSwgaWYgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWQuIEl0IGNyZWF0ZXNcclxuICogdGhlIG5lY2Vzc2FyeSBkaXJlY3Rvcnkgc3RydWN0dXJlIGlmIG5vdCBhbHJlYWR5IGNyZWF0ZWQgYW5kIGFwcGVuZHNcclxuICogdGhlIGNvbnRlbnQsIGluY2x1ZGluZyBhbiBvcHRpb25hbCBwcmVmaXgsIHRvIHRoZSBzcGVjaWZpZWQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9nVG9GaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHRleHRzIC0gQW4gYXJyYXkgb2YgdGV4dHMgdG8gYmUgbG9nZ2VkLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4IC0gQW4gb3B0aW9uYWwgcHJlZml4IHRvIGJlIGFkZGVkIHRvIGVhY2ggbG9nIGVudHJ5LlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KSB7XHJcbiAgaWYgKCFsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAvLyBDcmVhdGUgaWYgZG9lcyBub3QgZXhpc3RcclxuICAgICFleGlzdHNTeW5jKGdldEFic29sdXRlUGF0aChsb2dnaW5nLmRlc3QpKSAmJlxyXG4gICAgICBta2RpclN5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpO1xyXG5cclxuICAgIC8vIENyZWF0ZSB0aGUgZnVsbCBwYXRoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyA9IGdldEFic29sdXRlUGF0aChqb2luKGxvZ2dpbmcuZGVzdCwgbG9nZ2luZy5maWxlKSk7XHJcblxyXG4gICAgLy8gV2Ugbm93IGFzc3VtZSB0aGUgcGF0aCBpcyBhdmFpbGFibGUsIGUuZy4gaXQncyB0aGUgcmVzcG9uc2liaWxpdHlcclxuICAgIC8vIG9mIHRoZSB1c2VyIHRvIGNyZWF0ZSB0aGUgcGF0aCB3aXRoIHRoZSBjb3JyZWN0IGFjY2VzcyByaWdodHMuXHJcbiAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICB9XHJcblxyXG4gIC8vIEFkZCB0aGUgY29udGVudCB0byBhIGZpbGVcclxuICBhcHBlbmRGaWxlKFxyXG4gICAgbG9nZ2luZy5wYXRoVG9Mb2csXHJcbiAgICBbcHJlZml4XS5jb25jYXQodGV4dHMpLmpvaW4oJyAnKSArICdcXG4nLFxyXG4gICAgKGVycm9yKSA9PiB7XHJcbiAgICAgIGlmIChlcnJvciAmJiBsb2dnaW5nLnRvRmlsZSAmJiBsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmcsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmdcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvbmZpZ3VyYXRpb24gbWFuYWdlbWVudCBtb2R1bGUgZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuXHJcbiAqIFByb3ZpZGVzIGRlZmF1bHQgY29uZmlndXJhdGlvbnMgdGhhdCBzdXBwb3J0IGVudmlyb25tZW50IHZhcmlhYmxlcywgQ0xJXHJcbiAqIGFyZ3VtZW50cywgYW5kIGludGVyYWN0aXZlIHByb21wdHMgZm9yIGN1c3RvbWl6YXRpb24gb2Ygb3B0aW9ucyBhbmQgZmVhdHVyZXMuXHJcbiAqIEFkZGl0aW9uYWxseSwgaXQgbWFwcyBsZWdhY3kgb3B0aW9ucyB0byBtb2Rlcm4gc3RydWN0dXJlcywgZ2VuZXJhdGVzIG5lc3RlZFxyXG4gKiBhcmd1bWVudCBtYXBwaW5ncywgYW5kIGRpc3BsYXlzIENMSSB1c2FnZSBpbmZvcm1hdGlvbi5cclxuICovXHJcblxyXG4vKipcclxuICogVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYWxsIGF2YWlsYWJsZSBvcHRpb25zLCBvcmdhbml6ZWRcclxuICogYnkgc2VjdGlvbnMuXHJcbiAqXHJcbiAqIFRoaXMgb2JqZWN0IGluY2x1ZGVzOlxyXG4gKiAtIERlZmF1bHQgdmFsdWVzIGZvciBlYWNoIG9wdGlvblxyXG4gKiAtIERhdGEgdHlwZXMgZm9yIHZhbGlkYXRpb25cclxuICogLSBOYW1lcyBvZiBjb3JyZXNwb25kaW5nIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiAtIERlc2NyaXB0aW9ucyBvZiBlYWNoIHByb3BlcnR5XHJcbiAqIC0gSW5mb3JtYXRpb24gdXNlZCBmb3IgcHJvbXB0cyBpbiBpbnRlcmFjdGl2ZSBjb25maWd1cmF0aW9uXHJcbiAqIC0gW09wdGlvbmFsXSBDb3JyZXNwb25kaW5nIENMSSBhcmd1bWVudCBuYW1lcyBmb3IgQ0xJIHVzYWdlXHJcbiAqIC0gW09wdGlvbmFsXSBMZWdhY3kgbmFtZXMgZnJvbSB0aGUgcHJldmlvdXMgUGhhbnRvbUpTLWJhc2VkIHNlcnZlclxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZmF1bHRDb25maWcgPSB7XHJcbiAgcHVwcGV0ZWVyOiB7XHJcbiAgICBhcmdzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJy0tYWxsb3ctcnVubmluZy1pbnNlY3VyZS1jb250ZW50JyxcclxuICAgICAgICAnLS1hc2gtbm8tbnVkZ2VzJyxcclxuICAgICAgICAnLS1hdXRvcGxheS1wb2xpY3k9dXNlci1nZXN0dXJlLXJlcXVpcmVkJyxcclxuICAgICAgICAnLS1ibG9jay1uZXctd2ViLWNvbnRlbnRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWFjY2VsZXJhdGVkLTJkLWNhbnZhcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLW5ldHdvcmtpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1icmVha3BhZCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jaGVja2VyLWltYWdpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC1leHRlbnNpb25zLXdpdGgtYmFja2dyb3VuZC1wYWdlcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtdXBkYXRlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRlZmF1bHQtYXBwcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZXYtc2htLXVzYWdlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRvbWFpbi1yZWxpYWJpbGl0eScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1leHRlbnNpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWZlYXR1cmVzPUNhbGN1bGF0ZU5hdGl2ZVdpbk9jY2x1c2lvbixJbnRlcmVzdEZlZWRDb250ZW50U3VnZ2VzdGlvbnMsV2ViT1RQJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1sb2dnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXBvcHVwLWJsb2NraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1yZW5kZXJlci1iYWNrZ3JvdW5kaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlYXJjaC1lbmdpbmUtY2hvaWNlLXNjcmVlbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNpdGUtaXNvbGF0aW9uLXRyaWFscycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zcGVlY2gtYXBpJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXN5bmMnLFxyXG4gICAgICAgICctLWVuYWJsZS11bnNhZmUtd2ViZ3B1JyxcclxuICAgICAgICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcclxuICAgICAgICAnLS1oaWRlLXNjcm9sbGJhcnMnLFxyXG4gICAgICAgICctLW1ldHJpY3MtcmVjb3JkaW5nLW9ubHknLFxyXG4gICAgICAgICctLW11dGUtYXVkaW8nLFxyXG4gICAgICAgICctLW5vLWRlZmF1bHQtYnJvd3Nlci1jaGVjaycsXHJcbiAgICAgICAgJy0tbm8tZmlyc3QtcnVuJyxcclxuICAgICAgICAnLS1uby1waW5ncycsXHJcbiAgICAgICAgJy0tbm8tc2FuZGJveCcsXHJcbiAgICAgICAgJy0tbm8tc3RhcnR1cC13aW5kb3cnLFxyXG4gICAgICAgICctLW5vLXp5Z290ZScsXHJcbiAgICAgICAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxyXG4gICAgICAgICctLXByb2Nlc3MtcGVyLXRhYicsXHJcbiAgICAgICAgJy0tdXNlLW1vY2sta2V5Y2hhaW4nXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdQVVBQRVRFRVJfQVJHUycsXHJcbiAgICAgIGNsaU5hbWU6ICdwdXBwZXRlZXJBcmdzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBcnJheSBvZiBQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgICBzZXBhcmF0b3I6ICc7J1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBoaWdoY2hhcnRzOiB7XHJcbiAgICB2ZXJzaW9uOiB7XHJcbiAgICAgIHZhbHVlOiAnbGF0ZXN0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgdmVyc2lvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNkblVybDoge1xyXG4gICAgICB2YWx1ZTogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmb3JjZUZldGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmxhZyB0byByZWZldGNoIHNjcmlwdHMgYWZ0ZXIgZWFjaCBzZXJ2ZXIgcmVydW4nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhY2hlUGF0aDoge1xyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DQUNIRV9QQVRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEaXJlY3RvcnkgcGF0aCBmb3IgY2FjaGVkIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2hpZ2hjaGFydHMnLCAnaGlnaGNoYXJ0cy1tb3JlJywgJ2hpZ2hjaGFydHMtM2QnXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbW9kdWxlU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdzdG9jaycsXHJcbiAgICAgICAgJ21hcCcsXHJcbiAgICAgICAgJ2dhbnR0JyxcclxuICAgICAgICAnZXhwb3J0aW5nJyxcclxuICAgICAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgICAgICdhY2Nlc3NpYmlsaXR5JyxcclxuICAgICAgICAvLyAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgICAgICdib29zdCcsXHJcbiAgICAgICAgJ2RhdGEnLFxyXG4gICAgICAgICdkYXRhLXRvb2xzJyxcclxuICAgICAgICAnZHJhZ2dhYmxlLXBvaW50cycsXHJcbiAgICAgICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAgICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICAgICAnaGVhdG1hcCcsXHJcbiAgICAgICAgJ3RpbGVtYXAnLFxyXG4gICAgICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAgICAgJ3RpbWVsaW5lJyxcclxuICAgICAgICAndHJlZW1hcCcsXHJcbiAgICAgICAgJ3RyZWVncmFwaCcsXHJcbiAgICAgICAgJ2l0ZW0tc2VyaWVzJyxcclxuICAgICAgICAnZHJpbGxkb3duJyxcclxuICAgICAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAgICAgJ2J1bGxldCcsXHJcbiAgICAgICAgJ2Z1bm5lbCcsXHJcbiAgICAgICAgJ2Z1bm5lbDNkJyxcclxuICAgICAgICAnZ2VvaGVhdG1hcCcsXHJcbiAgICAgICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAgICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAgICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzZXJpZXMtb24tcG9pbnQnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgLy8gJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaScsXHJcbiAgICAgICAgJ2Zsb3dtYXAnLFxyXG4gICAgICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAgICAgJ25hdmlnYXRvcicsXHJcbiAgICAgICAgJ3RleHRwYXRoJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBtb2R1bGUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogWydpbmRpY2F0b3JzLWFsbCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBpbmRpY2F0b3Igc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQuanMvMi4zMC4xL21vbWVudC5taW4uanMnLFxyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQtdGltZXpvbmUvMC41LjQ1L21vbWVudC10aW1lem9uZS13aXRoLWRhdGEubWluLmpzJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DVVNUT01fU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBjdXN0b20gc2NyaXB0cyBvciBkZXBlbmRlbmNpZXMgdG8gZmV0Y2gnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGV4cG9ydDoge1xyXG4gICAgaW5maWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0lORklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnB1dCBmaWxlbmFtZSB3aXRoIHR5cGUsIGZvcm1hdHRlZCBjb3JyZWN0bHkgYXMgSlNPTiBvciBTVkcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbnN0cjoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5TVFInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3ZlcnJpZGVzIHRoZSBgaW5maWxlYCB3aXRoIEpTT04sIHN0cmluZ2lmaWVkIEpTT04sIG9yIFNWRyBpbnB1dCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09QVElPTlMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FsaWFzIGZvciB0aGUgYGluc3RyYCBvcHRpb24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzdmc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU1ZHJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTVkcgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCB0byByZW5kZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CQVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdCYXRjaCBqb2Igc3RyaW5nIHdpdGggaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09VVEZJTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3V0cHV0IGZpbGVuYW1lIHdpdGggdHlwZS4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcgYW5kIGlnbm9yZXMgYHR5cGVgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIGV4cG9ydCBmb3JtYXQuIENhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBwbmcnLFxyXG4gICAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0NPTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDaGFydCBjb25zdHJ1Y3Rvci4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgICBoaW50OiAnRGVmYXVsdDogY2hhcnQnLFxyXG4gICAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGI2NDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CNjQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gdGhlIGNoYXJ0IHNob3VsZCBiZSByZWNlaXZlZCBpbiBCYXNlNjQgZm9ybWF0IGluc3RlYWQgb2YgYmluYXJ5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBub0Rvd25sb2FkOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX05PX0RPV05MT0FEJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZXRoZXIgb3Igbm90IHRvIGluY2x1ZGUgb3IgZXhjbHVkZSBhdHRhY2htZW50IGhlYWRlcnMgaW4gdGhlIHJlc3BvbnNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSEVJR0hUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1NDQUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0RlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgbWluOiAwLjEsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9HTE9CQUxfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggZ2xvYmFsIG9wdGlvbnMgZm9yIEhpZ2hjaGFydHMuc2V0T3B0aW9ucycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVEhFTUVfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHdlYnBhZ2UgcmVuZGVyaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgZHVyaW5nIGV4cG9ydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FsbG93cyBvciBkaXNhbGxvd3MgaW5qZWN0aW9uIG9mIGZpbGVzeXN0ZW0gcmVzb3VyY2VzIChkaXNhYmxlZCBpbiBzZXJ2ZXIgbW9kZSknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DQUxMQkFDSycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIENhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgLmpzIGZpbGVuYW1lJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZXMgYXMgSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgZmlsZW5hbWUsIGNvbnRhaW5pbmcgZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSB3aXRoIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdQcm9tcHQtYmFzZWQgb3B0aW9uIHNldHRpbmcsIHNhdmVkIHRvIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVTZXJ2ZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0cyB0aGUgc2VydmVyIHdoZW4gdHJ1ZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaG9zdDoge1xyXG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIb3N0bmFtZSBvZiB0aGUgc2VydmVyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUG9ydCBudW1iZXIgZm9yIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHVwbG9hZExpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiAzLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9VUExPQURfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVxdWVzdCBib2R5IHNpemUgaW4gTUInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAnc2VydmVyQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Rpc3BsYXlzIG9yIG5vdCBhY3Rpb24gZHVyYXRpb25zIGluIG1pbGxpc2Vjb25kcyBkdXJpbmcgc2VydmVyIHJlcXVlc3RzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwcm94eToge1xyXG4gICAgICBob3N0OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eUhvc3QnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnSG9zdCBvZiB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB3aW5kb3c6IHtcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RpbWUgd2luZG93IGluIG1pbnV0ZXMgZm9yIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnRGVsYXkgZHVyYXRpb24gYmV0d2VlbiBzdWNjZXNzaXZlIHJlcXVlc3RzIGJlZm9yZSByZWFjaGluZyB0aGUgbGltaXQnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcEtleToge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0tleSB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwVG9rZW5gJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBUb2tlbjoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVG9rZW4gdG8gYnlwYXNzIHRoZSByYXRlIGxpbWl0ZXIsIHVzZWQgd2l0aCBgc2tpcEtleWAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdGb3JjZXMgdGhlIHNlcnZlciB0byB1c2UgSFRUUFMgb25seSB3aGVuIHRydWUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IGZvciB0aGUgU1NMIHNlcnZlcicsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xDZXJ0UGF0aCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWluaW11bSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICB3b3JrTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDQwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTnVtYmVyIG9mIHRhc2tzIGEgd29ya2VyIGNhbiBoYW5kbGUgYmVmb3JlIHJlc3RhcnRpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpZGxlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMzAwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGlkbGUgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAyMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGJlZm9yZSByZXRyeWluZyByZXNvdXJjZSBjcmVhdGlvbiBvbiBmYWlsdXJlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgdG8gY2hlY2sgYW5kIGRlc3Ryb3kgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1Nob3dzIHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdMb2dnaW5nIHZlcmJvc2l0eSBsZXZlbCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICByb3VuZDogMCxcclxuICAgICAgICBtaW46IDAsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnTG9nIGZpbGUgbmFtZS4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCB0byBiZSBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZXN0OiB7XHJcbiAgICAgIHZhbHVlOiAnbG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFJlcXVpcmVzIGBsb2dUb0ZpbGVgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBjb25zb2xlIGxvZ2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvRmlsZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICB1aToge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVVpJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGVuZHBvaW50IHJvdXRlIGZvciB0aGUgVUknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBOb2RlLmpzIGVudmlyb25tZW50IHR5cGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0Rpc3BsYXkgb3Igc2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoYXJkUmVzZXRQYWdlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfSEFSRF9SRVNFVF9QQUdFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaGV0aGVyIG9yIG5vdCB0byByZXNldCB0aGUgcGFnZSBjb250ZW50IGVudGlyZWx5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBicm93c2VyU2hlbGxNb2RlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHNldCB0aGUgYnJvd3NlciB0byBydW4gaW4gc2hlbGwgbW9kZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBkZWJ1Zzoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZURlYnVnJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSB1bmRlcmx5aW5nIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlYWRsZXNzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBoZWFkbGVzcyBtb2RlIGR1cmluZyBkZWJ1Z2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRldnRvb2xzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgRGV2VG9vbHMgaW4gaGVhZGZ1bCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGxpc3RlbmluZyB0byBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGR1bXBpbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RVTVBJTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdSZWRpcmVjdHMgb3Igbm90IGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gcHJvY2Vzcy5zdGRvdXQgYW5kIHByb2Nlc3Muc3RkZXJyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzbG93TW86IHtcclxuICAgICAgdmFsdWU6IDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVsYXlzIFB1cHBldGVlciBvcGVyYXRpb25zIGJ5IHRoZSBzcGVjaWZpZWQgbWlsbGlzZWNvbmRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IHVzZWQgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLy8gUHJvcGVydGllcyBuZXN0aW5nIGxldmVsIG9mIGFsbCBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRQcm9wcyA9IF9jcmVhdGVOZXN0ZWRQcm9wcyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIFByb3BlcnRpZXMgbmFtZXMgdGhhdCBzaG91bGQgbm90IGJlIHJlY3Vyc2l2ZWx5IG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IF9jcmVhdGVBYnNvbHV0ZVByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdlbmVyYXRlcyBhIG1hcHBpbmcgb2YgbmVzdGVkIGFyZ3VtZW50IGNoYWlucyBmcm9tIGEgbmVzdGVkXHJcbiAqIGNvbmZpZyBvYmplY3QuIFRoaXMgZnVuY3Rpb24gdHJhdmVyc2VzIGEgbmVzdGVkIG9iamVjdCBhbmQgY3JlYXRlcyBhIG1hcHBpbmdcclxuICogd2hlcmUgZWFjaCBrZXkgaXMgYW4gYXJndW1lbnQgbmFtZSAoZWl0aGVyIGZyb20gYGNsaU5hbWVgLCBgbGVnYWN5TmFtZWAsXHJcbiAqIG9yIHRoZSBvcmlnaW5hbCBrZXkpIGFuZCBlYWNoIHZhbHVlIGlzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY2hhaW5cclxuICogb2YgbmVzdGVkIHByb3BlcnRpZXMgbGVhZGluZyB0byB0aGF0IGFyZ3VtZW50LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbbmVzdGVkUHJvcHM9e31dIC0gVGhlIGFjY3VtdWxhdG9yIG9iamVjdCBmb3Igc3RvcmluZ1xyXG4gKiB0aGUgcmVzdWx0aW5nIGFyZ3VtZW50cyBjaGFpbnMuICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcHJvcENoYWluPScnXSAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzLFxyXG4gKiB1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbi4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gb2JqZWN0IG1hcHBpbmcgYXJndW1lbnQgbmFtZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZ1xyXG4gKiBuZXN0ZWQgcHJvcGVydHkgY2hhaW5zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzKGNvbmZpZywgbmVzdGVkUHJvcHMgPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgX2NyZWF0ZU5lc3RlZFByb3BzKGVudHJ5LCBuZXN0ZWRQcm9wcywgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBuZXN0ZWRQcm9wc1tlbnRyeS5jbGlOYW1lIHx8IGtleV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgaWYgKGVudHJ5LmxlZ2FjeU5hbWUgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2tleX1gLnN1YnN0cmluZygxKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIG9iamVjdCB3aXRoIG5lc3RlZCBhcmd1bWVudCBjaGFpbnNcclxuICByZXR1cm4gbmVzdGVkUHJvcHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSBnYXRoZXJzIHRoZSBuYW1lcyBvZiBwcm9wZXJ0aWVzIGZyb20gYSBjb25maWd1cmF0aW9uIG9iamVjdCB0aGF0XHJcbiAqIGNhbiBiZSB0cmVhdGVkIGFzIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZXNlIHByb3BlcnRpZXMgaGF2ZSB2YWx1ZXMgdGhhdFxyXG4gKiBhcmUgb2JqZWN0cyBhbmQgZG8gbm90IGNvbnRhaW4gZnVydGhlciBuZXN0ZWQgZGVwdGggd2hlbiBtZXJnaW5nIGFuIG9iamVjdFxyXG4gKiBjb250YWluaW5nIHRoZXNlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbYWJzb2x1dGVQcm9wcz1bXV0gLSBBbiBhcnJheSB0byBjb2xsZWN0IHRoZSBuYW1lc1xyXG4gKiBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxzdHJpbmc+fSBBbiBhcnJheSBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZVxyXG4gKiBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZUFic29sdXRlUHJvcHMoY29uZmlnLCBhYnNvbHV0ZVByb3BzID0gW10pIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudHlwZXMgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzXHJcbiAgICAgIF9jcmVhdGVBYnNvbHV0ZVByb3BzKGVudHJ5LCBhYnNvbHV0ZVByb3BzKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIHRoZSBvcHRpb24gY2FuIGJlIGFuIG9iamVjdCwgc2F2ZSBpdHMgdHlwZSBpbiB0aGUgYXJyYXlcclxuICAgICAgaWYgKGVudHJ5LnR5cGVzLmluY2x1ZGVzKCdPYmplY3QnKSkge1xyXG4gICAgICAgIGFic29sdXRlUHJvcHMucHVzaChrZXkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgYXJyYXkgd2l0aCB0aGUgbmFtZXMgb2YgYWJzb2x1dGUgcHJvcGVydGllc1xyXG4gIHJldHVybiBhYnNvbHV0ZVByb3BzO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRQcm9wcyxcclxuICBhYnNvbHV0ZVByb3BzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZFxyXG4gKiB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBgZW52c2AuIFdlIHNob3VsZCBub3QgdXNlIHRoZSBgcHJvY2Vzcy5lbnZgXHJcbiAqIGRpcmVjdGx5IGluIHRoZSBhcHBsaWNhdGlvbiBhcyB0aGVzZSB3b3VsZCBub3QgYmUgcGFyc2VkIHByb3Blcmx5LlxyXG4gKlxyXG4gKiBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSBwYXJzZWQgYW5kIHZhbGlkYXRlZCBvbmx5IG9uY2Ugd2hlblxyXG4gKiB0aGUgYXBwbGljYXRpb24gc3RhcnRzLiBXZSBzaG91bGQgd3JpdGUgYSBjdXN0b20gdmFsaWRhdG9yIG9yIGEgdHJhbnNmb3JtZXJcclxuICogZm9yIGVhY2ggb2YgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gcHVwcGV0ZWVyXHJcbiAgUFVQUEVURUVSX0FSR1M6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIGhpZ2hjaGFydHNcclxuICBISUdIQ0hBUlRTX1ZFUlNJT046IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PiAvXihsYXRlc3R8XFxkKyhcXC5cXGQrKXswLDJ9KSQvLnRlc3QodmFsdWUpIHx8IHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vJykgfHxcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcclxuICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgSElHSENIQVJUU19DRE5fVVJMLiBJdCBzaG91bGQgc3RhcnQgd2l0aCBodHRwOi8vIG9yIGh0dHBzOi8vLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTOiB2LmFycmF5KGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jb3JlU2NyaXB0cy52YWx1ZSksXHJcbiAgSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5tb2R1bGVTY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9JTkZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0lOU1RSOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9TVkc6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0JBVENIOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PVVRGSUxFOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9UWVBFOiB2LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLFxyXG4gIEVYUE9SVF9DT05TVFI6IHYuZW51bShbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddKSxcclxuICBFWFBPUlRfQjY0OiB2LmJvb2xlYW4oKSxcclxuICBFWFBPUlRfTk9fRE9XTkxPQUQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0dMT0JBTF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9USEVNRV9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19DVVNUT01fQ09ERTogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ0FMTEJBQ0s6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX1JFU09VUkNFUzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfTE9BRF9DT05GSUc6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUc6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlclxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1VQTE9BRF9MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgTE9HR0lOR19UT19GSUxFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gdWlcclxuICBVSV9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PREVfRU5WOiB2LmVudW0oWydkZXZlbG9wbWVudCcsICdwcm9kdWN0aW9uJywgJ3Rlc3QnXSksXHJcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0hBUkRfUkVTRVRfUEFHRTogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBlcnJvciBjbGFzcyBmb3IgaGFuZGxpbmcgZXhwb3J0LXJlbGF0ZWQgZXJyb3JzLiBFeHRlbmRzIHRoZSBuYXRpdmVcclxuICogYEVycm9yYCBjbGFzcyB0byBpbmNsdWRlIGFkZGl0aW9uYWwgcHJvcGVydGllcyBsaWtlIHN0YXR1cyBjb2RlIGFuZCBzdGFja1xyXG4gKiB0cmFjZSBkZXRhaWxzLlxyXG4gKi9cclxuY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gVGhlIGVycm9yIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkLlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gT3B0aW9uYWwgSFRUUCBzdGF0dXMgY29kZSBhc3NvY2lhdGVkXHJcbiAgICogd2l0aCB0aGUgZXJyb3IgKGUuZy4sIDQwMCwgNTAwKS5cclxuICAgKi9cclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXNDb2RlKSB7XHJcbiAgICBzdXBlcigpO1xyXG5cclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcblxyXG4gICAgaWYgKHN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgb3IgdXBkYXRlcyB0aGUgSFRUUCBzdGF0dXMgY29kZSBmb3IgdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBUaGUgSFRUUCBzdGF0dXMgY29kZSB0byBhc3NpZ24gdG8gdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgYWRkaXRpb25hbCBlcnJvciBkZXRhaWxzIGJhc2VkIG9uIGFuIGV4aXN0aW5nIGVycm9yIG9iamVjdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gQW4gZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgZGV0YWlscyB0byBwb3B1bGF0ZVxyXG4gICAqIHRoZSBgRXhwb3J0RXJyb3JgIGluc3RhbmNlLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG5cclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IE1hbmFnZXMgY29uZmlndXJhdGlvbiBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBieSBsb2FkaW5nXHJcbiAqIGFuZCBtZXJnaW5nIG9wdGlvbnMgZnJvbSBtdWx0aXBsZSBzb3VyY2VzLCBzdWNoIGFzIGRlZmF1bHQgc2V0dGluZ3MsXHJcbiAqIGVudmlyb25tZW50IHZhcmlhYmxlcywgdXNlci1wcm92aWRlZCBvcHRpb25zLCBhbmQgY29tbWFuZC1saW5lIGFyZ3VtZW50cy5cclxuICogRW5zdXJlcyB0aGUgZ2xvYmFsIG9wdGlvbnMgYXJlIHVwLXRvLWRhdGUgd2l0aCB0aGUgaGlnaGVzdCBwcmlvcml0eSB2YWx1ZXMuXHJcbiAqIFByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgYWNjZXNzaW5nIGFuZCB1cGRhdGluZyBjb25maWd1cmF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuL2VudnMuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGRlZXBDb3B5LCBnZXRBYnNvbHV0ZVBhdGgsIGlzT2JqZWN0IH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgeyBhYnNvbHV0ZVByb3BzLCBuZXN0ZWRQcm9wcywgZGVmYXVsdENvbmZpZyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0R2xvYmFsT3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIEFuIG9iamVjdCBmb3IgdGhlIGluc3RhbmNlIG9wdGlvbnMsIGNyZWF0ZWQgZWFjaCB0aW1lIHRoZSBgaW5pdEV4cG9ydGAgb2NjdXJzXHJcbmNvbnN0IGluc3RhbmNlT3B0aW9ucyA9IGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpO1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhIHJlZmVyZW5jZSB0byB0aGUgb3B0aW9ucyBvYmplY3QuIERlcGVuZGluZyBvbiB0aGUgYGdldEluc3RhbmNlYFxyXG4gKiBwYXJhbWV0ZXIsIGl0IHJldHVybnMgZWl0aGVyIHRoZSBnbG9iYWwgb3B0aW9ucyBvciB0aGUgaW5zdGFuY2Utc3BlY2lmaWNcclxuICogb3B0aW9ucyBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldEluc3RhbmNlPXRydWVdIC0gT3B0aW9uYWwgcGFyYW1ldGVyIHRoYXQgZGVjaWRlcyB3aGV0aGVyXHJcbiAqIHRvIHJldHVybiB0aGUgaW5zdGFuY2Utc3BlY2lmaWMgb3B0aW9ucyAod2hlbiBgdHJ1ZWApIG9yIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gKiAod2hlbiBgZmFsc2VgKS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYHRydWVgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBIHJlZmVyZW5jZSB0byBlaXRoZXIgdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9yIHRoZSBpbnN0YW5jZS1zcGVjaWZpYyBvcHRpb25zLCBiYXNlZCBvbiB0aGUgYGdldEluc3RhbmNlYCBwYXJhbWV0ZXIuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0T3B0aW9ucyhnZXRJbnN0YW5jZSA9IHRydWUpIHtcclxuICByZXR1cm4gZ2V0SW5zdGFuY2UgPyBpbnN0YW5jZU9wdGlvbnMgOiBnbG9iYWxPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgZ2xvYmFsIG9wdGlvbnMgb2YgdGhlIGV4cG9ydCBzZXJ2ZXIsIGtlZXBpbmcgdGhlIHByaW5jaXBsZVxyXG4gKiBvZiB0aGUgb3B0aW9ucyBsb2FkIHByaW9yaXR5IGZyb20gYWxsIGF2YWlsYWJsZSBzb3VyY2VzLiBJdCBhY2NlcHRzIG9wdGlvbmFsXHJcbiAqIGBjdXN0b21PcHRpb25zYCBvYmplY3QgYW5kIGBjbGlBcmdzYCBhcnJheSB3aXRoIGFyZ3VtZW50cyBmcm9tIHRoZSBDTEkuIFRoZXNlXHJcbiAqIG9wdGlvbnMgd2lsbCBiZSB2YWxpZGF0ZWQgYW5kIGFwcGxpZWQgaWYgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIFRoZSBwcmlvcml0eSBvcmRlciBvZiBzZXR0aW5nIHZhbHVlcyBpczpcclxuICpcclxuICogMS4gT3B0aW9ucyBmcm9tIHRoZSBgbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIChkZWZhdWx0IHZhbHVlcykuXHJcbiAqIDIuIE9wdGlvbnMgZnJvbSBhIGN1c3RvbSBKU09OIGZpbGUgKGxvYWRlZCBieSB0aGUgYGxvYWRDb25maWdgIG9wdGlvbikuXHJcbiAqIDMuIE9wdGlvbnMgZnJvbSB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzICh0aGUgYC5lbnZgIGZpbGUpLlxyXG4gKiA0LiBPcHRpb25zIGZyb20gdGhlIGNvbW1hbmQgbGluZSBpbnRlcmZhY2UgKENMSSkuXHJcbiAqIDUuIE9wdGlvbnMgZnJvbSB0aGUgZmlyc3QgcGFyYW1ldGVyICh0aGUgYGN1c3RvbU9wdGlvbnNgIGlzIGJ5IGRlZmF1bHRcclxuICogYW4gZW1wdHkgb2JqZWN0KS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldEdsb2JhbE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IFtjdXN0b21PcHRpb25zPXt9XSAtIE9wdGlvbmFsIGN1c3RvbSBvcHRpb25zIGZvciBhZGRpdGlvbmFsXHJcbiAqIGNvbmZpZ3VyYXRpb24uIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gW2NsaUFyZ3M9W11dIC0gT3B0aW9uYWwgY29tbWFuZCBsaW5lIGFyZ3VtZW50c1xyXG4gKiBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIHVwZGF0ZWQgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCByZWZsZWN0aW5nIHRoZSBtZXJnZWRcclxuICogY29uZmlndXJhdGlvbiBmcm9tIGFsbCBhdmFpbGFibGUgc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRHbG9iYWxPcHRpb25zKGN1c3RvbU9wdGlvbnMgPSB7fSwgY2xpQXJncyA9IFtdKSB7XHJcbiAgLy8gT2JqZWN0IGZvciBvcHRpb25zIGxvYWRlZCB2aWEgdGhlIGBsb2FkQ29uZmlnYCBvcHRpb25cclxuICBsZXQgY29uZmlnT3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBPYmplY3QgZm9yIG9wdGlvbnMgZnJvbSB0aGUgQ0xJXHJcbiAgbGV0IGNsaU9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzICYmIEFycmF5LmlzQXJyYXkoY2xpQXJncykgJiYgY2xpQXJncy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGxvYWRlZCB2aWEgdGhlIGBsb2FkQ29uZmlnYFxyXG4gICAgY29uZmlnT3B0aW9ucyA9IF9sb2FkQ29uZmlnRmlsZShjbGlBcmdzLCBnbG9iYWxPcHRpb25zLmN1c3RvbUxvZ2ljKTtcclxuXHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBDTElcclxuICAgIGNsaU9wdGlvbnMgPSBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpO1xyXG4gIH1cclxuXHJcbiAgLy8gVXBkYXRlIHZhbHVlcyBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCB2YWx1ZXMgZnJvbSBlYWNoIHNvdXJjZSBwb3NzaWJsZVxyXG4gIF91cGRhdGVHbG9iYWxPcHRpb25zKFxyXG4gICAgZGVmYXVsdENvbmZpZyxcclxuICAgIGdsb2JhbE9wdGlvbnMsXHJcbiAgICBjb25maWdPcHRpb25zLFxyXG4gICAgY2xpT3B0aW9ucyxcclxuICAgIGN1c3RvbU9wdGlvbnNcclxuICApO1xyXG5cclxuICAvLyBSZXR1cm4gdXBkYXRlZCBnbG9iYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnbG9iYWxPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgaW5zdGFuY2Ugb3B0aW9ucyB3aXRoIGFkZGl0aW9uYWwgb3B0aW9ucy4gSXQgb3B0aW9uYWxseSBhbGxvd3NcclxuICogdG8gcmVpbml0aWFsaXplIHRoZSBpbnN0YW5jZSBvcHRpb25zIHdpdGggdGhlIHZhbHVlcyBvZiBhIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gdXBkYXRlT3B0aW9ucyAtIFRoZSB1cGRhdGUgb3B0aW9ucyB0byBtZXJnZSBpbnRvIHRoZSBpbnN0YW5jZVxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtuZXdJbnN0YW5jZT1mYWxzZV0gLSBBIGZsYWcgdG8gaW5kaWNhdGUgd2hldGhlciB0byBpbml0XHJcbiAqIG9wdGlvbnMgZm9yIGEgbmV3IGluc3RhbmNlLiBJZiBgdHJ1ZWAsIHRoZSBleGlzdGluZyBpbnN0YW5jZSBvcHRpb25zIHdpbGxcclxuICogYmUgY2xlYXJlZCBhbmQgcmVpbml0aWFsaXplZCBiYXNlZCBvbiB0aGUgZ2xvYmFsIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IC0gVGhlIHVwZGF0ZWQgaW5zdGFuY2Ugb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVPcHRpb25zKHVwZGF0ZU9wdGlvbnMsIG5ld0luc3RhbmNlID0gZmFsc2UpIHtcclxuICAvLyBDaGVjayBpZiBvcHRpb25zIG5lZWQgdG8gYmUgY3JlYXRlZCBmb3IgYSBuZXcgaW5zdGFuY2VcclxuICBpZiAobmV3SW5zdGFuY2UpIHtcclxuICAgIC8vIEdldCByaWQgb2YgdGhlIG9sZCBpbnN0YW5jZSBvcHRpb25zXHJcbiAgICBPYmplY3Qua2V5cyhpbnN0YW5jZU9wdGlvbnMpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgICBkZWxldGUgaW5zdGFuY2VPcHRpb25zW2tleV07XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBJbml0IHRoZSBuZXcgaW5zdGFuY2Ugb3B0aW9ucyBiYXNlZCBvbiB0aGUgZ2xvYmFsIG9wdGlvbnNcclxuICAgIG1lcmdlT3B0aW9ucyhpbnN0YW5jZU9wdGlvbnMsIGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpKTtcclxuICB9XHJcblxyXG4gIC8vIE1lcmdlIGFkZGl0aW9uYWwgb3B0aW9ucyB0byB0aGUgaW5zdGFuY2Ugb3B0aW9uc1xyXG4gIG1lcmdlT3B0aW9ucyhpbnN0YW5jZU9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMpO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIHJlZmVyZW5jZSB0byB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICByZXR1cm4gaW5zdGFuY2VPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogTWVyZ2VzIHR3byBzZXRzIG9mIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgY29uc2lkZXJpbmcgYWJzb2x1dGUgcHJvcGVydGllcy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1lcmdlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3JpZ2luYWxPcHRpb25zIC0gT3JpZ2luYWwgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIE5ldyBjb25maWd1cmF0aW9uIG9wdGlvbnMgdG8gYmUgbWVyZ2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBNZXJnZWQgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlT3B0aW9ucyhvcmlnaW5hbE9wdGlvbnMsIG5ld09wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgYG9yaWdpbmFsT3B0aW9uc2AgYW5kIGBuZXdPcHRpb25zYCBhcmUgY29ycmVjdCBvYmplY3RzXHJcbiAgaWYgKGlzT2JqZWN0KG9yaWdpbmFsT3B0aW9ucykgJiYgaXNPYmplY3QobmV3T3B0aW9ucykpIHtcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICAgIG9yaWdpbmFsT3B0aW9uc1trZXldID1cclxuICAgICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXHJcbiAgICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgPyBtZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zW2tleV0sIHZhbHVlKVxyXG4gICAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgICAgOiBvcmlnaW5hbE9wdGlvbnNba2V5XSB8fCBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBvcmlnaW5hbCAobW9kaWZpZWQgb3Igbm90KSBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG9sZE9wdGlvbnMpKSB7XHJcbiAgICAvLyBJdGVyYXRlIG92ZXIgZWFjaCBrZXktdmFsdWUgcGFpciBpbiB0aGUgb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgdGhlcmUgaXMgYSBuZXN0ZWQgbWFwcGluZywgc3BsaXQgaXQgaW50byBhIHByb3BlcnRpZXMgY2hhaW5cclxuICAgICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNba2V5XVxyXG4gICAgICAgID8gbmVzdGVkUHJvcHNba2V5XS5zcGxpdCgnLicpXHJcbiAgICAgICAgOiBbXTtcclxuXHJcbiAgICAgIC8vIElmIGl0IGlzIHRoZSBsYXN0IHByb3BlcnR5IGluIHRoZSBjaGFpbiwgYXNzaWduIHRoZSB2YWx1ZSwgb3RoZXJ3aXNlLFxyXG4gICAgICAvLyBjcmVhdGUgb3IgcmV1c2UgdGhlIG5lc3RlZCBvYmplY3RcclxuICAgICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cclxuICAgICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgICAgbmV3T3B0aW9uc1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgICdbY29uZmlnXSBObyBjb3JyZWN0IG9iamVjdCB3aXRoIG9wdGlvbnMgd2FzIHByb3ZpZGVkLiBSZXR1cm5pbmcgYW4gZW1wdHkgYXJyYXkuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgbmV3LCBzdHJ1Y3R1cmVkIG9wdGlvbnMgb2JqZWN0XHJcbiAgcmV0dXJuIG5ld09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBWYWxpZGF0ZXMsIHBhcnNlcywgYW5kIGNoZWNrcyBpZiB0aGUgcHJvdmlkZWQgY29uZmlnIGlzIGFsbG93ZWQgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0FsbG93ZWRDb25maWdcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBjb25maWcgLSBUaGUgY29uZmlnIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkIGFzIGEgc2V0XHJcbiAqIG9mIG9wdGlvbnMuIE11c3QgYmUgZWl0aGVyIGFuIG9iamVjdCBvciBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBbdG9TdHJpbmc9ZmFsc2VdIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCB2ZXJzaW9uXHJcbiAqIG9mIHRoZSBwYXJzZWQgY29uZmlnLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFthbGxvd0Z1bmN0aW9ucz1mYWxzZV0gLSBXaGV0aGVyIHRvIGFsbG93IGZ1bmN0aW9uc1xyXG4gKiBpbiB0aGUgcGFyc2VkIGNvbmZpZy4gSWYgdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWQuIE90aGVyd2lzZSwgd2hlblxyXG4gKiBhIGZ1bmN0aW9uIGlzIGZvdW5kLCBudWxsIGlzIHJldHVybmVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFZlcnNpb24obm9Mb2dvID0gZmFsc2UpIHtcclxuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGAuZW52YCBvciBmcm9tIGBwYWNrYWdlLmpzb25gXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuICApLnZlcnNpb247XHJcblxyXG4gIC8vIFByaW50IHRleHQgb25seVxyXG4gIGlmIChub0xvZ28pIHtcclxuICAgIGNvbnNvbGUubG9nKGBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259YCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFByaW50IHRoZSBsb2dvXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAnbXNnJywgJ3N0YXJ0dXAubXNnJyksICd1dGY4JykudG9TdHJpbmcoKVxyXG4gICAgICAgIC5ib2xkLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyBnbG9iYWwgb3B0aW9ucyBvYmplY3QgYmFzZWQgb24gcHJvdmlkZWRcclxuICogY29uZmlndXJhdGlvbiwgc2V0dGluZyB2YWx1ZXMgZnJvbSBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9pbml0R2xvYmFsT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvIGJlIHVzZWQgZm9yIGluaXRpYWxpemluZ1xyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBJbml0aWFsaXplZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmZ1bmN0aW9uIF9pbml0R2xvYmFsT3B0aW9ucyhjb25maWcpIHtcclxuICBjb25zdCBvcHRpb25zID0ge307XHJcblxyXG4gIC8vIFN0YXJ0IGluaXRpYWxpemluZyB0aGUgYG9wdGlvbnNgIG9iamVjdCByZWN1cnNpdmVseVxyXG4gIGZvciAoY29uc3QgW25hbWUsIGl0ZW1dIG9mIE9iamVjdC5lbnRyaWVzKGNvbmZpZykpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoaXRlbSwgJ3ZhbHVlJykpIHtcclxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcyBleGlzdHMsIGl0IHRha2VzIHByZWNlZGVuY2VcclxuICAgICAgY29uc3QgZW52VmFsID0gZW52c1tpdGVtLmVudkxpbmtdO1xyXG4gICAgICBpZiAoZW52VmFsICE9PSB1bmRlZmluZWQgJiYgZW52VmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1tuYW1lXSA9IGVudlZhbDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gaXRlbS52YWx1ZTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgb3B0aW9uc1tuYW1lXSA9IF9pbml0R2xvYmFsT3B0aW9ucyhpdGVtKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY3JlYXRlZCBgb3B0aW9uc2Agb2JqZWN0XHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIGdsb2JhbCBvcHRpb25zIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIHZhcmlvdXMgc291cmNlcywgZm9sbG93aW5nXHJcbiAqIGEgc3BlY2lmaWMgcHJpb3JpdGl6YXRpb24gb3JkZXIuIFRoZSBmdW5jdGlvbiBjaGVja3MgZm9yIHZhbHVlcyBpbiB0aGUgb3JkZXJcclxuICogb2YgcHJlY2VkZW5jZTogdGhlIGBsb2FkQ29uZmlnYCBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGVudmlyb25tZW50IHZhcmlhYmxlcyxcclxuICogY3VzdG9tIG9wdGlvbnMsIGFuZCBDTEkgb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIF91cGRhdGVHbG9iYWxPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QsIHdoaWNoIGluY2x1ZGVzIHRoZSBpbml0aWFsXHJcbiAqIHNldHRpbmdzIGFuZCBtZXRhZGF0YSBmb3IgZWFjaCBvcHRpb24uIFRoaXMgb2JqZWN0IGlzIHVzZWQgdG8gZGV0ZXJtaW5lXHJcbiAqIHRoZSBzdHJ1Y3R1cmUgYW5kIGRlZmF1bHQgdmFsdWVzIGZvciB0aGUgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IHRoYXQgd2lsbCBiZSB1cGRhdGVkXHJcbiAqIHdpdGggdmFsdWVzIGZyb20gb3RoZXIgc291cmNlcy5cclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZ09wdCAtIFRoZSBjb25maWd1cmF0aW9uIG9wdGlvbnMgb2JqZWN0LCBsb2FkZWQgd2l0aFxyXG4gKiB0aGUgYGxvYWRDb25maWdgIG9wdGlvbiwgd2hpY2ggbWF5IHByb3ZpZGUgdmFsdWVzIHRvIG92ZXJyaWRlIGRlZmF1bHRzLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2xpT3B0IC0gVGhlIENMSSBvcHRpb25zIG9iamVjdCwgd2hpY2ggbWF5IGluY2x1ZGUgdmFsdWVzXHJcbiAqIHByb3ZpZGVkIHRocm91Z2ggY29tbWFuZC1saW5lIGFyZ3VtZW50cyBhbmQgbWF5IG92ZXJyaWRlIGNvbmZpZ3VyYXRpb25cclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbU9wdCAtIFRoZSBjdXN0b20gb3B0aW9ucyBvYmplY3QsIHR5cGljYWxseSBjb250YWluaW5nXHJcbiAqIGFkZGl0aW9uYWwgYW5kIHVzZXItZGVmaW5lZCB2YWx1ZXMsIHdoaWNoIGhhcyB0aGUgaGlnaGVzdCBwcmVjZWRlbmNlIGFtb25nXHJcbiAqIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfdXBkYXRlR2xvYmFsT3B0aW9ucyhjb25maWcsIG9wdGlvbnMsIGNvbmZpZ09wdCwgY2xpT3B0LCBjdXN0b21PcHQpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBjb25maWcgZW50cnkgb2YgYSBzcGVjaWZpYyBvcHRpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gR2F0aGVyIHZhbHVlcyBmb3IgdGhlIG9wdGlvbnMgZnJvbSBldmVyeSBwb3NzaWJsZSBzb3VyY2UsIGlmIGV4aXN0c1xyXG4gICAgY29uc3QgY29uZmlnVmFsID0gY29uZmlnT3B0ICYmIGNvbmZpZ09wdFtrZXldO1xyXG4gICAgY29uc3QgY2xpVmFsID0gY2xpT3B0ICYmIGNsaU9wdFtrZXldO1xyXG4gICAgY29uc3QgY3VzdG9tVmFsID0gY3VzdG9tT3B0ICYmIGN1c3RvbU9wdFtrZXldO1xyXG5cclxuICAgIC8vIElmIHRoZSB2YWx1ZSBub3QgZm91bmQsIG5lZWQgdG8gZ28gZGVlcGVyXHJcbiAgICBpZiAodHlwZW9mIGVudHJ5LnZhbHVlID09PSAndW5kZWZpbmVkJykge1xyXG4gICAgICBfdXBkYXRlR2xvYmFsT3B0aW9ucyhlbnRyeSwgb3B0aW9uc1trZXldLCBjb25maWdWYWwsIGNsaVZhbCwgY3VzdG9tVmFsKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIGEgdmFsdWUgZnJvbSBjdXN0b20gSlNPTiBvcHRpb25zIGV4aXN0cywgaXQgdGFrZXMgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY29uZmlnVmFsICE9PSB1bmRlZmluZWQgJiYgY29uZmlnVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY29uZmlnVmFsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzIGV4aXN0cywgaXQgdGFrZXMgcHJlY2VkZW5jZVxyXG4gICAgICBjb25zdCBlbnZWYWwgPSBlbnZzW2VudHJ5LmVudkxpbmtdO1xyXG4gICAgICBpZiAoZW50cnkuZW52TGluayBpbiBlbnZzICYmIGVudlZhbCAhPT0gdW5kZWZpbmVkICYmIGVudlZhbCAhPT0gbnVsbCkge1xyXG4gICAgICAgIG9wdGlvbnNba2V5XSA9IGVudlZhbDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIENMSSBvcHRpb25zIGV4aXN0cywgaXQgdGFrZXMgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY2xpVmFsICE9PSB1bmRlZmluZWQgJiYgY2xpVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY2xpVmFsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gdXNlciBvcHRpb25zIGV4aXN0cywgaXQgdGFrZXMgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoY3VzdG9tVmFsICE9PSB1bmRlZmluZWQgJiYgY3VzdG9tVmFsICE9PSBudWxsKSB7XHJcbiAgICAgICAgb3B0aW9uc1trZXldID0gY3VzdG9tVmFsO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmdcclxuICogd2l0aCB0aGUgb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy4gSW4gb3JkZXIgZm9yIGEgZnVuY3Rpb25cclxuICogdG8gYmUgcHJlc2VydmVkLCBpdCBuZWVkcyB0byBmb2xsb3cgdGhlIGZvcm1hdCBgZnVuY3Rpb24gKC4uLikgey4uLn1gLlxyXG4gKiBTdWNoIGEgZnVuY3Rpb24gY2FuIGFsc28gYmUgc3RyaW5naWZpZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWRcclxuICogaW4gdGhlIG91dHB1dC4gT3RoZXJ3aXNlIGFuIGVycm9yIGlzIHRocm93bi5cclxuICogQHBhcmFtIHtib29sZWFufSBzdHJpbmdpZnlGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBzYXZlZFxyXG4gKiBhcyBzdHJpbmdzLiBUaGUgYGFsbG93RnVuY3Rpb25zYCBtdXN0IGJlIHNldCB0byB0cnVlIGFzIHdlbGwgZm9yIHRoaXMgdG8gdGFrZVxyXG4gKiBhbiBlZmZlY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgd2hlbiBmdW5jdGlvbnMgYXJlIG5vdCBhbGxvd2VkIGJ1dCBhcmVcclxuICogZm91bmQgaW4gcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX29wdGlvbnNTdHJpbmdpZnkob3B0aW9ucywgYWxsb3dGdW5jdGlvbnMsIHN0cmluZ2lmeUZ1bmN0aW9ucykge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAoXywgdmFsdWUpID0+IHtcclxuICAgIC8vIFRyaW0gc3RyaW5nIHZhbHVlc1xyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgdmFsdWUgaXMgYSBmdW5jdGlvbiBvciBzdHJpbmdpZmllZCBmdW5jdGlvblxyXG4gICAgaWYgKFxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgfHxcclxuICAgICAgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpICYmXHJcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBJZiBhbGxvd0Z1bmN0aW9ucyBpcyBzZXQgdG8gdHJ1ZSwgcHJlc2VydmUgZnVuY3Rpb25zXHJcbiAgICAgIGlmIChhbGxvd0Z1bmN0aW9ucykge1xyXG4gICAgICAgIC8vIEJhc2VkIG9uIHRoZSBgc3RyaW5naWZ5RnVuY3Rpb25zYCBvcHRpb25zLCBzZXQgZnVuY3Rpb24gdmFsdWVzXHJcbiAgICAgICAgcmV0dXJuIHN0cmluZ2lmeUZ1bmN0aW9uc1xyXG4gICAgICAgICAgPyAvLyBBcyBzdHJpbmdpZmllZCBmdW5jdGlvbnNcclxuICAgICAgICAgICAgYFwiRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOXCJgXHJcbiAgICAgICAgICA6IC8vIEFzIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOYDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaHJvdyBhbiBlcnJvciBvdGhlcndpc2VcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgc2ltcGx5IHJldHVybiB0aGUgdmFsdWVcclxuICAgIHJldHVybiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIHN0cmluZ2lmeUZ1bmN0aW9ucyA/IC9cXFxcXCJFWFBfRlVOfEVYUF9GVU5cXFxcXCIvZyA6IC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHByb3ZpZGVkIHZpYVxyXG4gKiB0aGUgYGxvYWRDb25maWdgIG9wdGlvbiBpbiB0aGUgY29tbWFuZC1saW5lIGFyZ3VtZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2FkQ29uZmlnRmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjbGlBcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyB0byBzZWFyY2hcclxuICogZm9yIHRoZSBgbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGBsb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGBsb2FkQ29uZmlnYCBvcHRpb24gdmFsdWVcclxuICBjb25zdCBjb25maWdGaWxlTmFtZSA9IGNvbmZpZ0luZGV4ID4gLTEgJiYgY2xpQXJnc1tjb25maWdJbmRleCArIDFdO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgYGxvYWRDb25maWdgIGlzIHByZXNlbnQgYW5kIGhhcyBhIGNvcnJlY3QgdmFsdWVcclxuICBpZiAoY29uZmlnRmlsZU5hbWUgJiYgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICByZXR1cm4gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGVycm9yLFxyXG4gICAgICAgIGBbY29uZmlnXSBVbmFibGUgdG8gbG9hZCB0aGUgY29uZmlndXJhdGlvbiBmcm9tIHRoZSAke2NvbmZpZ0ZpbGVOYW1lfSBmaWxlLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIE5vIGFkZGl0aW9uYWwgb3B0aW9ucyB0byByZXR1cm5cclxuICByZXR1cm4ge307XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQYXJzZXMgY29tbWFuZC1saW5lIGFyZ3VtZW50cyBhbmQgcGFpcnMgZWFjaCBhcmd1bWVudCB3aXRoIGl0cyBjb3JyZXNwb25kaW5nXHJcbiAqIG9wdGlvbiBpbiB0aGUgY29uZmlndXJhdGlvbi4gVGhlIHZhbHVlcyBhcmUgc3RydWN0dXJlZCBpbnRvIGEgbmVzdGVkIG9wdGlvbnNcclxuICogb2JqZWN0LCBiYXNlZCBvbiBwcmVkZWZpbmVkIG1hcHBpbmdzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3BhaXJBcmd1bWVudFZhbHVlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG5lc3RlZFByb3BzIC0gQW4gYXJyYXkgb2YgbmVzdGluZyBsZXZlbCBmb3IgYWxsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBBbiBhcnJheSBvZiBjb21tYW5kLWxpbmUgYXJndW1lbnRzXHJcbiAqIGNvbnRhaW5pbmcgb3B0aW9ucyBhbmQgdGhlaXIgYXNzb2NpYXRlZCB2YWx1ZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFuIHVwZGF0ZWQgb3B0aW9ucyBvYmplY3Qgd2hlcmUgZWFjaCBvcHRpb24gZnJvbVxyXG4gKiB0aGUgY29tbWFuZC1saW5lIGlzIHBhaXJlZCB3aXRoIGl0cyB2YWx1ZSwgc3RydWN0dXJlZCBpbnRvIG5lc3RlZCBvYmplY3RzXHJcbiAqIGFzIGRlZmluZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpIHtcclxuICAvLyBBbiBlbXB0eSBvYmplY3QgdG8gY29sbGVjdCBhbmQgc3RydWN0dXJpemUgZGF0YSBmcm9tIHRoZSBhcmdzXHJcbiAgY29uc3QgY2xpT3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDeWNsZSB0aHJvdWdoIGFsbCBDTEkgYXJncyBhbmQgZmlsdGVyIHRoZW1cclxuICBmb3IgKGxldCBpID0gMDsgaSA8IGNsaUFyZ3MubGVuZ3RoOyBpKyspIHtcclxuICAgIGNvbnN0IG9wdGlvbiA9IGNsaUFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW29wdGlvbl1cclxuICAgICAgPyBuZXN0ZWRQcm9wc1tvcHRpb25dLnNwbGl0KCcuJylcclxuICAgICAgOiBbXTtcclxuXHJcbiAgICAvLyBDcmVhdGUgb3B0aW9ucyBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSBDTEkgZm9yIGxhdGVyIHBhcnNpbmcgYW5kIG1lcmdpbmdcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcclxuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xyXG4gICAgICAgIGNvbnN0IHZhbHVlID0gY2xpQXJnc1srK2ldO1xyXG4gICAgICAgIGlmICghdmFsdWUpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSBDTEkgJy0tJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgb2JqW3Byb3BdID0gdmFsdWUgfHwgbnVsbDtcclxuICAgICAgfSBlbHNlIGlmIChvYmpbcHJvcF0gPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG9ialtwcm9wXSA9IHt9O1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBvYmpbcHJvcF07XHJcbiAgICB9LCBjbGlPcHRpb25zKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBwYXJzZWQgQ0xJIG9wdGlvbnNcclxuICByZXR1cm4gY2xpT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IHRyYXZlcnNlcyB0aGUgb3B0aW9ucyBvYmplY3QgdG8gcHJpbnQgdGhlIHVzYWdlIGluZm9ybWF0aW9uXHJcbiAqIGZvciBlYWNoIG9wdGlvbiBjYXRlZ29yeSBhbmQgaW5kaXZpZHVhbCBvcHRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3ljbGVDYXRlZ29yaWVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgQ0xJIG9wdGlvbnMuIEl0IG1heVxyXG4gKiBpbmNsdWRlIG5lc3RlZCBjYXRlZ29yaWVzIGFuZCBpbmRpdmlkdWFsIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3ljbGVDYXRlZ29yaWVzKG9wdGlvbnMpIHtcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKG9wdGlvbnMpKSB7XHJcbiAgICAvLyBJZiB0aGUgY3VycmVudCBlbnRyeSBpcyBhIGNhdGVnb3J5IGFuZCBub3QgYSBsZWFmIG9wdGlvbiwgcmVjdXJzZSBpbnRvIGl0XHJcbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvcHRpb24sICd2YWx1ZScpKSB7XHJcbiAgICAgIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9uKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIFByZXBhcmUgZGVzY3JpcHRpb25cclxuICAgICAgY29uc3QgZGVzY05hbWUgPSBgIC0tJHtvcHRpb24uY2xpTmFtZSB8fCBuYW1lfWA7XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIHZhbHVlXHJcbiAgICAgIGxldCBvcHRpb25WYWx1ZSA9IG9wdGlvbi52YWx1ZTtcclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhcnJheSBvZiBzdHJpbmdzXHJcbiAgICAgIGlmIChvcHRpb25WYWx1ZSAhPT0gbnVsbCAmJiBvcHRpb24udHlwZXMuaW5jbHVkZXMoJ3N0cmluZ1tdJykpIHtcclxuICAgICAgICBvcHRpb25WYWx1ZSA9XHJcbiAgICAgICAgICAnWycgKyBvcHRpb25WYWx1ZS5tYXAoKGl0ZW0pID0+IGAnJHtpdGVtfSdgKS5qb2luKCcsICcpICsgJ10nO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBQcmVwYXJlIHZhbHVlIGZvciBvcHRpb24gdGhhdCBpcyBub3QgbnVsbCBhbmQgaXMgYSBzdHJpbmdcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nJykpIHtcclxuICAgICAgICBvcHRpb25WYWx1ZSA9IGAnJHtvcHRpb25WYWx1ZX0nYDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRGlzcGxheSBjb3JyZWN0bHkgYWxpZ25lZCBtZXNzYWdlc1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICBkZXNjTmFtZS5ncmVlbixcclxuICAgICAgICBgJHsoJzwnICsgb3B0aW9uLnR5cGVzLmpvaW4oJ3wnKSArICc+JykueWVsbG93fWAsXHJcbiAgICAgICAgYCR7U3RyaW5nKG9wdGlvblZhbHVlKS5ib2xkfWAuYmx1ZSxcclxuICAgICAgICBgLSAke29wdGlvbi5kZXNjcmlwdGlvbn0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgc2V0R2xvYmFsT3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIG1lcmdlT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcbiAgaXNBbGxvd2VkQ29uZmlnLFxyXG4gIHByaW50TGljZW5zZSxcclxuICBwcmludFVzYWdlLFxyXG4gIHByaW50VmVyc2lvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgSFRUUCB1dGlsaXR5IG1vZHVsZSBmb3IgZmV0Y2hpbmcgYW5kIHBvc3RpbmcgZGF0YS4gU3VwcG9ydHMgYm90aFxyXG4gKiBIVFRQIGFuZCBIVFRQUyBwcm90b2NvbHMsIHByb3ZpZGluZyBtZXRob2RzIHRvIG1ha2UgR0VUIGFuZCBQT1NUIHJlcXVlc3RzXHJcbiAqIHdpdGggY3VzdG9taXphYmxlIG9wdGlvbnMuIEluY2x1ZGVzIHByb3RvY29sIGRldGVybWluYXRpb24gYmFzZWQgb24gVVJMXHJcbiAqIGFuZCBhdWdtZW50cyByZXNwb25zZSBvYmplY3RzIHdpdGggYSAndGV4dCcgcHJvcGVydHkgZm9yIGVhc2llciBkYXRhIGFjY2Vzcy5cclxuICovXHJcblxyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGRhdGEgZnJvbSB0aGUgc3BlY2lmaWVkIFVSTCB1c2luZyBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBmZXRjaFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBmZXRjaCBkYXRhIGZyb20uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmV0Y2godXJsLCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpXHJcbiAgICAgIC5nZXQodXJsLCByZXF1ZXN0T3B0aW9ucywgKHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2VEYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFyZXNwb25zZURhdGEpIHtcclxuICAgICAgICAgICAgcmVqZWN0KCdOb3RoaW5nIHdhcyBmZXRjaGVkIGZyb20gdGhlIFVSTC4nKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogU2VuZHMgYSBQT1NUIHJlcXVlc3QgdG8gdGhlIHNwZWNpZmllZCBVUkwgd2l0aCB0aGUgcHJvdmlkZWQgSlNPTiBib2R5IHVzaW5nXHJcbiAqIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHBvc3RcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gc2VuZCB0aGUgUE9TVCByZXF1ZXN0IHRvLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2JvZHk9e31dIC0gVGhlIEpTT04gYm9keSB0byBpbmNsdWRlIGluIHRoZSBQT1NUIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtyZXF1ZXN0T3B0aW9ucz17fV0gLSBPcHRpb25zIGZvciB0aGUgSFRUUC9IVFRQUyByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBIVFRQL0hUVFBTIHJlc3BvbnNlXHJcbiAqIG9iamVjdCB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0KHVybCwgYm9keSA9IHt9LCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIGNvbnN0IGRhdGEgPSBKU09OLnN0cmluZ2lmeShib2R5KTtcclxuXHJcbiAgICAvLyBTZXQgZGVmYXVsdCBoZWFkZXJzIGFuZCBtZXJnZSB3aXRoIHJlcXVlc3RPcHRpb25zXHJcbiAgICBjb25zdCBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihcclxuICAgICAge1xyXG4gICAgICAgIG1ldGhvZDogJ1BPU1QnLFxyXG4gICAgICAgIGhlYWRlcnM6IHtcclxuICAgICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXHJcbiAgICAgICAgICAnQ29udGVudC1MZW5ndGgnOiBkYXRhLmxlbmd0aFxyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcmVxdWVzdE9wdGlvbnNcclxuICAgICk7XHJcblxyXG4gICAgY29uc3QgcmVxdWVzdCA9IF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpXHJcbiAgICAgIC5yZXF1ZXN0KHVybCwgb3B0aW9ucywgKHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2VEYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgLy8gV3JpdGUgdGhlIHJlcXVlc3QgYm9keSBhbmQgZW5kIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LndyaXRlKGRhdGEpO1xyXG4gICAgcmVxdWVzdC5lbmQoKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgdGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBVUkwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZGV0ZXJtaW5lIHRoZSBwcm90b2NvbC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIChodHRwIG9yIGh0dHBzKS5cclxuICovXHJcbmZ1bmN0aW9uIF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpIHtcclxuICByZXR1cm4gdXJsLnN0YXJ0c1dpdGgoJ2h0dHBzJykgPyBodHRwcyA6IGh0dHA7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBmZXRjaCxcclxuICBwb3N0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgY2FjaGUgbWFuYWdlciBpcyByZXNwb25zaWJsZSBmb3IgaGFuZGxpbmcgYW5kIG1hbmFnaW5nXHJcbiAqIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYWxvbmcgd2l0aCBpdHMgZGVwZW5kZW5jaWVzLiBJdCBlbnN1cmVzIHRoYXQgdGhlc2VcclxuICogcmVzb3VyY2VzIGFyZSBzdG9yZWQgYW5kIHJldHJpZXZlZCBlZmZpY2llbnRseSB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZVxyXG4gKiBhbmQgcmVkdWNlIHJlZHVuZGFudCBuZXR3b3JrIHJlcXVlc3RzLiBUaGUgY2FjaGUgaXMgc3RvcmVkIGluIHRoZSBgLmNhY2hlYFxyXG4gKiBkaXJlY3RvcnkgYnkgZGVmYXVsdCwgd2hpY2ggc2VydmVzIGFzIGEgZGVkaWNhdGVkIGZvbGRlciBmb3Iga2VlcGluZyBjYWNoZWRcclxuICogZmlsZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IEh0dHBzUHJveHlBZ2VudCB9IGZyb20gJ2h0dHBzLXByb3h5LWFnZW50JztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGZldGNoIH0gZnJvbSAnLi9mZXRjaC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnNcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNhY2hlIHBhdGhcclxuICAgIGNvbnN0IGNhY2hlUGF0aCA9IGdldENhY2hlUGF0aCgpO1xyXG5cclxuICAgIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgICBjb25zdCBtYW5pZmVzdFBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKTtcclxuICAgIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ3NvdXJjZXMuanMnKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcclxuXHJcbiAgICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIHRoZSBgbWFuaWZlc3QuanNvbmAgZG9lcyBub3QgZXhpc3RcclxuICAgIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICAgIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGhpZ2hjaGFydHNPcHRpb25zLmZvcmNlRmV0Y2gpIHtcclxuICAgICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAgIC8vIFJlYWQgdGhlIG1hbmlmZXN0IEpTT05cclxuICAgICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpLCAndXRmOCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG1vZHVsZXMgaXMgYW4gYXJyYXksIGlmIHNvLCB3ZSByZXdyaXRlIGl0IHRvIGEgbWFwIHRvIG1ha2VcclxuICAgICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIGFjdHVhbCBudW1iZXIgb2Ygc2NyaXB0cyB0byBiZSBmZXRjaGVkXHJcbiAgICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9XHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnM7XHJcbiAgICAgIGNvbnN0IG51bWJlck9mTW9kdWxlcyA9XHJcbiAgICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcclxuICAgICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24pIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAgIE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzXHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgX3VwZGF0ZUNhY2hlKFxyXG4gICAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgICBzb3VyY2VQYXRoXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xyXG5cclxuICAgICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEZpbmFsbHksIHNhdmUgdGhlIG5ldyBtYW5pZmVzdCwgd2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjdXJyZW50IGNvbmZpZ1xyXG4gICAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgICBhd2FpdCBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY29uZmlndXJlIGNhY2hlIGFuZCBjcmVhdGUgb3IgdXBkYXRlIHRoZSBjb25maWcgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIEdldCB0aGUgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyB0byB1cGRhdGUgdG8gdGhlIG5ldyB2ZXJzaW9uXHJcbiAgY29uc3Qgb3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRvIHRoZSBuZXcgdmVyc2lvblxyXG4gIG9wdGlvbnMuaGlnaGNoYXJ0cy52ZXJzaW9uID0gbmV3VmVyc2lvbjtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgsICd1dGY4Jyk7IC8vICM1NjJcclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBgZmV0Y2hlZE1vZHVsZXNgIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxyXG4gKiB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxyXG4gKiBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuIFRoZSBkZWZhdWx0XHJcbiAqIHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbVxyXG4gKiB3aXRoIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxyXG4gIHNjcmlwdCxcclxuICByZXF1ZXN0T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlcyxcclxuICBzaG91bGRUaHJvd0Vycm9yID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgLmpzIGZyb20gdGhlIGN1c3RvbSBzdHJpbmdzXHJcbiAgaWYgKHNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgIHNjcmlwdCA9IHNjcmlwdC5zdWJzdHJpbmcoMCwgc2NyaXB0Lmxlbmd0aCAtIDMpO1xyXG4gIH1cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gIH1cclxuXHJcbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYCxcclxuICAgICAgNDA0XHJcbiAgICApLnNldEVycm9yKHJlc3BvbnNlKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uLmBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3RcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbZmV0Y2hlZE1vZHVsZXM9e31dIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzT3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMgPSB7fSkge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzXHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihnZXRDYWNoZVBhdGgoKSwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgYHNjcmlwdHNgIGFuZCBgY3VzdG9tU2NyaXB0c2AgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNvcmVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBjb3JlIHNjcmlwdHMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG1vZHVsZVNjcmlwdHMgLSBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGN1c3RvbVNjcmlwdHMgLSBDdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsXHJcbiAqIFVSTHMpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmZXRjaGVkIHNjcmlwdHNcclxuICogY29udGVudCBqb2luZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogc2V0dGluZyBhbiBIVFRQIEFnZW50IGZvciBwcm94eS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHMoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pIHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gc2VydmVyUHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gc2VydmVyUHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiBleGlzdHMsIGFkZCBwcm94eSBhZ2VudCB0byByZXF1ZXN0IG9wdGlvbnNcclxuICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHByb3h5QWdlbnRcclxuICAgID8ge1xyXG4gICAgICAgIGFnZW50OiBwcm94eUFnZW50LFxyXG4gICAgICAgIHRpbWVvdXQ6IHNlcnZlclByb3h5T3B0aW9ucy50aW1lb3V0XHJcbiAgICAgIH1cclxuICAgIDoge307XHJcblxyXG4gIGNvbnN0IGFsbEZldGNoUHJvbWlzZXMgPSBbXHJcbiAgICAuLi5jb3JlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcylcclxuICAgICksXHJcbiAgICAuLi5jdXN0b21TY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucylcclxuICAgIClcclxuICBdO1xyXG5cclxuICBjb25zdCBmZXRjaGVkU2NyaXB0cyA9IGF3YWl0IFByb21pc2UuYWxsKGFsbEZldGNoUHJvbWlzZXMpO1xyXG4gIHJldHVybiBmZXRjaGVkU2NyaXB0cy5qb2luKCc7XFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgcmVwcmVzZW50aW5nXHJcbiAqIHRoZSBmZXRjaGVkIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF91cGRhdGVDYWNoZShoaWdoY2hhcnRzT3B0aW9ucywgc2VydmVyUHJveHlPcHRpb25zLCBzb3VyY2VQYXRoKSB7XHJcbiAgLy8gR2V0IEhpZ2hjaGFydHMgdmVyc2lvbiBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGhjVmVyc2lvbiA9XHJcbiAgICBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uID09PSAnbGF0ZXN0J1xyXG4gICAgICA/IG51bGxcclxuICAgICAgOiBgJHtoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9ufWA7XHJcblxyXG4gIC8vIEdldCB0aGUgQ0ROIHVybCBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGNkblVybCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVybCB8fCBjYWNoZS5jZG5Vcmw7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5zb3VyY2VzID0gYXdhaXQgX2ZldGNoU2NyaXB0cyhcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoYykgPT5cclxuICAgICAgICAgIGhjVmVyc2lvbiA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259LyR7Y31gIDogYCR7Y2RuVXJsfS8ke2N9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vbWFwcy8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tYXBzL21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgOiBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICksXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGkpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb25cclxuICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L3N0b2NrLyR7aGNWZXJzaW9ufS9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9zdG9jay9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldEhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIGV4dHJhY3RWZXJzaW9uLFxyXG4gIGV4dHJhY3RNb2R1bGVOYW1lLFxyXG4gIGdldENhY2hlLFxyXG4gIGdldENhY2hlUGF0aFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWV0aG9kcyBmb3IgaW5pdGlhbGl6aW5nIEhpZ2hjaGFydHMgd2l0aCBjdXN0b21pemVkXHJcbiAqIGFuaW1hdGlvbiBzZXR0aW5ncyBhbmQgdHJpZ2dlcmluZyB0aGUgY3JlYXRpb24gb2YgSGlnaGNoYXJ0cyBjaGFydHMgd2l0aFxyXG4gKiBleHBvcnQtc3BlY2lmaWMgY29uZmlndXJhdGlvbnMgaW4gdGhlIHBhZ2UgY29udGV4dC4gU3VwcG9ydHMgZHluYW1pYyBvcHRpb25cclxuICogbWVyZ2luZywgY3VzdG9tIGxvZ2ljIGluamVjdGlvbiwgYW5kIGNvbnRyb2wgb3ZlciByZW5kZXJpbmcgYmVoYXZpb3JzLiBVc2VkXHJcbiAqIGJ5IHRoZSBQdXBwZXRlZXIgcGFnZS5cclxuICovXHJcblxyXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xyXG5cclxuLyoqXHJcbiAqIFNldHRpbmcgdGhlIGBIaWdoY2hhcnRzLmFuaW1PYmplY3RgIGZ1bmN0aW9uLiBDYWxsZWQgd2hlbiBpbml0aW5nIHRoZSBwYWdlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xyXG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcclxuICAgIHJldHVybiB7IGR1cmF0aW9uOiAwIH07XHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBIaWdoY2hhcnRzIGNoYXJ0IG9uIGEgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVDaGFydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQ2hhcnQoZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgc2V0T3B0aW9ucywgbWVyZ2UsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHRoZSBgdXNlck9wdGlvbnNgIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB0aGUgYHVzZXJPcHRpb25zYCB3aGVuIGBmb3JFeHBvcnRgIGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBTb21lIG1hbmRhdG9yeSBhZGRpdGlvbmFsIGBjaGFydGAgYW5kIGBleHBvcnRpbmdgIG9wdGlvbnNcclxuICBjb25zdCBhZGRpdGlvbmFsT3B0aW9ucyA9IHtcclxuICAgIGNoYXJ0OiB7XHJcbiAgICAgIC8vIEJ5IGRlZmF1bHQgYW5pbWF0aW9uIGlzIGRpc2FibGVkXHJcbiAgICAgIGFuaW1hdGlvbjogZmFsc2UsXHJcbiAgICAgIC8vIEdldCB0aGUgcmlnaHQgc2l6ZSB2YWx1ZXNcclxuICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcclxuICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmluc3RyfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYHRoZW1lT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgdGhlbWVPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9uc31gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb25cclxuICBjb25zdCBnbG9iYWxPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gTWVyZ2UgdGhlIGZvbGxvd2luZyBvcHRpb25zIG9iamVjdHMgdG8gY3JlYXRlIGZpbmFsIG9wdGlvbnNcclxuICBjb25zdCBmaW5hbE9wdGlvbnMgPSBtZXJnZShcclxuICAgIGZhbHNlLFxyXG4gICAgdGhlbWVPcHRpb25zLFxyXG4gICAgdXNlck9wdGlvbnMsXHJcbiAgICAvLyBQbGFjZWQgaXQgaGVyZSBpbnN0ZWFkIGluIHRoZSBpbml0IGJlY2F1c2Ugb2YgdGhlIHNpemUgaXNzdWVzXHJcbiAgICBhZGRpdGlvbmFsT3B0aW9uc1xyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjYWxsYmFja2Agb3B0aW9uXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke2N1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja31gKSgpXHJcbiAgICA6IG51bGw7XHJcblxyXG4gIC8vIFRyaWdnZXIgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbignb3B0aW9ucycsIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbZXhwb3J0T3B0aW9ucy5jb25zdHJdKCdjb250YWluZXInLCBmaW5hbE9wdGlvbnMsIGZpbmFsQ2FsbGJhY2spO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBgc2V0T3B0aW9uc2Agd2FzIHVzZWQgaW4gdGhlIGBjdXN0b21Db2RlYClcclxuICBmb3IgKGNvbnN0IHByb3AgaW4gZGVmYXVsdE9wdGlvbnMpIHtcclxuICAgIGlmICh0eXBlb2YgZGVmYXVsdE9wdGlvbnNbcHJvcF0gIT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgZGVsZXRlIGRlZmF1bHRPcHRpb25zW3Byb3BdO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBkZWZhdWx0IG9wdGlvbnMgYmFja1xyXG4gIHNldE9wdGlvbnMoSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqKTtcclxuXHJcbiAgLy8gRW1wdHkgdGhlIGN1c3RvbSBnbG9iYWwgb3B0aW9ucyBvYmplY3RcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSB7fTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNldHVwSGlnaGNoYXJ0cyxcclxuICBjcmVhdGVDaGFydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZSwgY3JlYXRpbmcgYW5kIGNsZWFyaW5nIHBhZ2VzLCBpbmplY3RpbmcgY3VzdG9tIHJlc291cmNlcyxcclxuICogYW5kIHNldHRpbmcgdXAgSGlnaGNoYXJ0cyBmb3Igc2VydmVyLXNpZGUgcmVuZGVyaW5nLiBUaGUgbW9kdWxlIGVuc3VyZXNcclxuICogdGhhdCByZXNvdXJjZXMgYXJlIGNvcnJlY3RseSBtYW5hZ2VkIGFuZCBjYW4gaGFuZGxlIGZhaWx1cmVzIGR1cmluZ1xyXG4gKiBvcGVyYXRpb25zIGxpa2UgbGF1bmNoaW5nIHRoZSBicm93c2VyIG9yIGNyZWF0aW5nIG5ldyBwYWdlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciBwYWdlc1xyXG5jb25zdCB0ZW1wbGF0ZSA9IHJlYWRGaWxlU3luYyhcclxuICBqb2luKF9fZGlybmFtZSwgJ3RlbXBsYXRlcycsICd0ZW1wbGF0ZS5odG1sJyksXHJcbiAgJ3V0ZjgnXHJcbik7XHJcblxyXG4vLyBUbyBzYXZlIHRoZSBicm93c2VyXHJcbmxldCBicm93c2VyID0gbnVsbDtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QnJvd3NlclxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBubyB2YWxpZCBicm93c2VyXHJcbiAqIGhhcyBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QnJvd3NlcigpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nLCA1MDApO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXJcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgY3JlYXRlZCBQdXBwZXRlZXJcclxuICogYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG1heCByZXRyaWVzIHRvIG9wZW5cclxuICogYSBicm93c2VyIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyXHJcbiAqIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2AgYW5kIGBvdGhlcmAgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWdPcHRpb25zIH0gPSBkZWJ1ZztcclxuXHJcbiAgLy8gTGF1bmNoIG9wdGlvbnMgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgY29uc3QgbGF1bmNoT3B0aW9ucyA9IHtcclxuICAgIGhlYWRsZXNzOiBvdGhlci5icm93c2VyU2hlbGxNb2RlID8gJ3NoZWxsJyA6IHRydWUsXHJcbiAgICB1c2VyRGF0YURpcjogJ3RtcCcsXHJcbiAgICBhcmdzOiBwdXBwZXRlZXJBcmdzIHx8IFtdLFxyXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHSFVQOiBmYWxzZSxcclxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXHJcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXHJcbiAgICAuLi4oZW5hYmxlZERlYnVnICYmIGRlYnVnT3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICAvLyBBIGNvdW50ZXIgZm9yIHRoZSBicm93c2VyJ3MgbGF1bmNoIHJldHJpZXNcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFticm93c2VyXSBBdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeSAkeysrdHJ5Q291bnR9KS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gTGF1bmNoIHRoZSBicm93c2VyXHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgJ1ticm93c2VyXSBGYWlsZWQgdG8gbGF1bmNoIGEgYnJvd3NlciBpbnN0YW5jZS4nXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0cnkgdG8gbGF1bmNoIGJyb3dzZXIgdW50aWwgcmVhY2hpbmcgbWF4IGF0dGVtcHRzXHJcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcclxuICAgICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIFJldHJ5IHRvIG9wZW4gYSBicm93c2VyICgke3RyeUNvdW50fSBvdXQgb2YgMjUpLmApO1xyXG5cclxuICAgICAgICAgIC8vIFdhaXQgZm9yIGEgNCBzZWNvbmRzIGJlZm9yZSB0cnlpbmcgYWdhaW5cclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG5cclxuICAgICAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGxhdW5jaE9wdGlvbnMuaGVhZGxlc3MgPT09ICdzaGVsbCcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIHNoZWxsIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERlYnVnIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChlbmFibGVkRGVidWcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIGRlYnVnIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2Jyb3dzZXJdIE1heGltdW0gcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlciBpbnN0YW5jZSByZWFjaGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nLCA1MDApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xvc2VCcm93c2VyXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2VCcm93c2VyKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIgJiYgYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgYnJvd3NlciA9IG51bGw7XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgcGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHRoZSBgX3NldFBhZ2VDb250ZW50KClgLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBuZXdQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIGBpZGAsXHJcbiAqIGB3b3JrQ291bnRgLCBhbmQgYHBhZ2VgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjb25uZWN0ZWQgb3IgaWYgYSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG5ld1BhZ2UocG9vbFJlc291cmNlKSB7XHJcbiAgLy8gRXJyb3IgaW4gY2FzZSBvZiBubyBjb25uZWN0ZWQgYnJvd3NlclxyXG4gIGlmICghYnJvd3NlciB8fCAhYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW2Jyb3dzZXJdIEJyb3dzZXIgaXMgbm90IHlldCBjb25uZWN0ZWQuYCwgNTAwKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBwb29sUmVzb3VyY2UucGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBEaXNhYmxlIGNhY2hlXHJcbiAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXHJcbiAgX3NldFBhZ2VFdmVudHMocG9vbFJlc291cmNlLnBhZ2UpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgcGFnZSBpcyBjb3JyZWN0bHkgY3JlYXRlZFxyXG4gIGlmICghcG9vbFJlc291cmNlLnBhZ2UgfHwgcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgdGhlIGNvbnRlbnQgb2YgYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG1vZGUuIExvZ3NcclxuICogdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIG9mIGEgcGFnZSdzIGNvbnRlbnQgZmFpbHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIHBhZ2UgYW5kIGlkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtoYXJkUmVzZXQ9ZmFsc2VdIC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gYGFib3V0OmJsYW5rYCBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgd2hlbiBwYWdlXHJcbiAqIGlzIGNvcnJlY3RseSBjbGVhcmVkIGFuZCBmYWxzZSB3aGVuIGl0IGlzIG5vdC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2UocG9vbFJlc291cmNlLCBoYXJkUmVzZXQgPSBmYWxzZSkge1xyXG4gIHRyeSB7XHJcbiAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGBhYm91dDpibGFua2BcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHtcclxuICAgICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBfc2V0UGFnZUNvbnRlbnQocG9vbFJlc291cmNlLnBhZ2UpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gQ29udGVudCBvZiB0aGUgcGFnZSBjb3VsZCBub3QgYmUgY2xlYXJlZC5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICBwb29sUmVzb3VyY2Uud29ya0NvdW50ID0gZ2V0T3B0aW9ucygpLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICB9XHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyBjdXN0b20gSlMgYW5kIENTUyByZXNvdXJjZXMgdG8gYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYWRkUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9ICFmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRydWUgOiBmYWxzZTtcclxuXHJcbiAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICBpbmplY3RlZEpzLnB1c2goXHJcbiAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgID8ge1xyXG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChmaWxlKSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZvciAoY29uc3QganNSZXNvdXJjZSBvZiBpbmplY3RlZEpzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhqc1Jlc291cmNlKSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIFRoZSBKUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpbmplY3RlZEpzLmxlbmd0aCA9IDA7XHJcblxyXG4gICAgLy8gTG9hZCBDU1NcclxuICAgIGNvbnN0IGluamVjdGVkQ3NzID0gW107XHJcbiAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgcGF0aDogam9pbihfX2Rpcm5hbWUsIGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoY3NzUmVzb3VyY2UpKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICAgYFticm93c2VyXSBUaGUgQ1NTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaW5qZWN0ZWRDc3MubGVuZ3RoID0gMDtcclxuICAgIH1cclxuICB9XHJcbiAgcmV0dXJuIGluamVjdGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIG91dCBhbGwgc3RhdGUgc2V0IG9uIHRoZSBwYWdlIHdpdGggYGFkZFNjcmlwdFRhZ2AgYW5kIGBhZGRTdHlsZVRhZ2AuXHJcbiAqIFJlbW92ZXMgaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS5cclxuICogQWRkaXRpb25hbGx5LCBpdCBkZXN0cm95cyBwcmV2aW91c2x5IGV4aXN0aW5nIGNoYXJ0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtBcnJheS48T2JqZWN0Pn0gaW5qZWN0ZWRSZXNvdXJjZXMgLSBBcnJheSBvZiBpbmplY3RlZCByZXNvdXJjZXNcclxuICogdG8gYmUgY2xlYXJlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICB0cnkge1xyXG4gICAgZm9yIChjb25zdCByZXNvdXJjZSBvZiBpbmplY3RlZFJlc291cmNlcykge1xyXG4gICAgICBhd2FpdCByZXNvdXJjZS5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzIGFmdGVyIGV4cG9ydCBpcyBkb25lIGFuZCByZXNldCBhbGwgQ1NTIGFuZCBzY3JpcHQgdGFnc1xyXG4gICAgYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCB3aGVuIGRvaW5nIFNWRyBleHBvcnRzXHJcbiAgICAgIGlmICh0eXBlb2YgSGlnaGNoYXJ0cyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcclxuXHJcbiAgICAgICAgLy8gQ2hlY2sgaW4gYW55IGFscmVhZHkgZXhpc3RpbmcgY2hhcnRzXHJcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XHJcbiAgICAgICAgICAvLyBEZXN0cm95IG9sZCBjaGFydHNcclxuICAgICAgICAgIGZvciAoY29uc3Qgb2xkQ2hhcnQgb2Ygb2xkQ2hhcnRzKSB7XHJcbiAgICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcclxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICAgIEhpZ2hjaGFydHMuY2hhcnRzLnNoaWZ0KCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLnNjcmlwdHNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLCAuLi5zdHlsZXNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc3R5bGUnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5saW5rc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdsaW5rJyk7XHJcblxyXG4gICAgICAvLyBSZW1vdmUgdGFnc1xyXG4gICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgW1xyXG4gICAgICAgIC4uLnNjcmlwdHNUb1JlbW92ZSxcclxuICAgICAgICAuLi5zdHlsZXNUb1JlbW92ZSxcclxuICAgICAgICAuLi5saW5rc1RvUmVtb3ZlXHJcbiAgICAgIF0pIHtcclxuICAgICAgICBlbGVtZW50LnJlbW92ZSgpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciBwYWdlJ3MgcmVzb3VyY2VzLmApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvbnRlbnQgZm9yIGEgUHVwcGV0ZWVyIFBhZ2UgdXNpbmcgYSBwcmVkZWZpbmVkIHRlbXBsYXRlXHJcbiAqIGFuZCBhZGRpdGlvbmFsIHNjcmlwdHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VDb250ZW50XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgY29udGVudFxyXG4gKiBpcyBiZWluZyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnQocGFnZSkge1xyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBwYWdlIGNvbnRlbnRcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gIC8vIEFkZCBhbGwgcmVnaXN0ZXJlZCBIaWdjaGFydHMgc2NyaXB0cywgcXVpdGUgZGVtYW5kaW5nXHJcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBqb2luKGdldENhY2hlUGF0aCgpLCAnc291cmNlcy5qcycpIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGluaXRpYWwgYGFuaW1PYmplY3RgIGZvciBIaWdoY2hhcnRzXHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IGV2ZW50cyAobGlrZSBgcGFnZWVycm9yYCBhbmQgYGNvbnNvbGVgKSBmb3IgYSBQdXBwZXRlZXIgUGFnZSBpbiBvcmRlclxyXG4gKiB0byBjYXRjaCBhbmQgZGlzcGxheSBlcnJvcnMgYW5kIGNvbnNvbGUgbG9ncyBmcm9tIHRoZSB3aW5kb3cgY29udGV4dC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgbGlzdGVuZXJzXHJcbiAqIGFyZSBiZWluZyBzZXQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc2V0UGFnZUV2ZW50cyhwYWdlKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgcGFnZWVycm9yYCBsaXN0ZW5lclxyXG4gIHBhZ2Uub24oJ3BhZ2VlcnJvcicsIGFzeW5jICgpID0+IHtcclxuICAgIC8vIEl0IHdvdWxkIHNlZW0gbGlrZSB0aGlzIG1heSBmaXJlIGF0IHRoZSBzYW1lIHRpbWUgb3Igc2hvcnRseSBiZWZvcmVcclxuICAgIC8vIGEgcGFnZSBpcyBjbG9zZWQuXHJcbiAgICBpZiAocGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgY29uc29sZWAgbGlzdGVuZXIsIGlmIG5lZWRlZFxyXG4gIGlmIChkZWJ1Zy5lbmFibGUgJiYgZGVidWcubGlzdGVuVG9Db25zb2xlKSB7XHJcbiAgICBwYWdlLm9uKCdjb25zb2xlJywgKG1lc3NhZ2UpID0+IHtcclxuICAgICAgY29uc29sZS5sb2coYFtkZWJ1Z10gJHttZXNzYWdlLnRleHQoKX1gKTtcclxuICAgIH0pO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldEJyb3dzZXIsXHJcbiAgY3JlYXRlQnJvd3NlcixcclxuICBjbG9zZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSxcclxuICBjbGVhclBhZ2UsXHJcbiAgYWRkUGFnZVJlc291cmNlcyxcclxuICBjbGVhclBhZ2VSZXNvdXJjZXNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogVGhlIENTUyB0byBiZSB1c2VkIG9uIHRoZSBleHBvcnRlZCBwYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQ1NTIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoKSA9PiBgXHJcblxyXG5odG1sLCBib2R5IHtcclxuICBtYXJnaW46IDA7XHJcbiAgcGFkZGluZzogMDtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG59XHJcblxyXG4jdGFibGUtZGl2LCAjc2xpZGVycywgI2RhdGF0YWJsZSwgI2NvbnRyb2xzLCAubGQtcm93IHtcclxuICBkaXNwbGF5OiBub25lO1xyXG4gIGhlaWdodDogMDtcclxufVxyXG5cclxuI2NoYXJ0LWNvbnRhaW5lciB7XHJcbiAgYm94LXNpemluZzogYm9yZGVyLWJveDtcclxuICBtYXJnaW46IDA7XHJcbiAgb3ZlcmZsb3c6IGF1dG87XHJcbiAgZm9udC1zaXplOiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyID4gZmlndXJlLCBkaXYge1xyXG4gIG1hcmdpbi10b3A6IDAgIWltcG9ydGFudDtcclxuICBtYXJnaW4tYm90dG9tOiAwICFpbXBvcnRhbnQ7XHJcbn1cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNzc1RlbXBsYXRlIGZyb20gJy4vY3NzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBUaGUgU1ZHIHRlbXBsYXRlIHRvIHVzZSB3aGVuIGxvYWRpbmcgU1ZHIGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIGlucHV0IGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBTVkcgdGVtcGxhdGUuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoc3ZnKSA9PiBgXHJcbjwhRE9DVFlQRSBodG1sPlxyXG48aHRtbCBsYW5nPSdlbi1VUyc+XHJcbiAgPGhlYWQ+XHJcbiAgICA8bWV0YSBodHRwLWVxdWl2PVwiQ29udGVudC1UeXBlXCIgY29udGVudD1cInRleHQvaHRtbDsgY2hhcnNldD11dGYtOFwiPlxyXG4gICAgPHRpdGxlPkhpZ2hjaGFydHMgRXhwb3J0PC90aXRsZT5cclxuICA8L2hlYWQ+XHJcbiAgPHN0eWxlPlxyXG4gICAgJHtjc3NUZW1wbGF0ZSgpfVxyXG4gIDwvc3R5bGU+XHJcbiAgPGJvZHk+XHJcbiAgICA8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+XHJcbiAgICAgICR7c3ZnfVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgaGFuZGxlcyBjaGFydCBleHBvcnQgZnVuY3Rpb25hbGl0eSB1c2luZyBQdXBwZXRlZXIuXHJcbiAqIEl0IHN1cHBvcnRzIGV4cG9ydGluZyBjaGFydHMgYXMgU1ZHLCBQTkcsIEpQRUcsIGFuZCBQREYgZm9ybWF0cy4gVGhlIG1vZHVsZVxyXG4gKiBtYW5hZ2VzIHBhZ2UgcmVzb3VyY2VzLCBzZXRzIHVwIHRoZSBleHBvcnQgZW52aXJvbm1lbnQsIGFuZCBwcm9jZXNzZXMgY2hhcnRcclxuICogY29uZmlndXJhdGlvbnMgb3IgU1ZHIGlucHV0cyBmb3IgcmVuZGVyaW5nLiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2VcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFkZFBhZ2VSZXNvdXJjZXMsIGNsZWFyUGFnZVJlc291cmNlcyB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IGNyZWF0ZUNoYXJ0IH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IHN2Z1RlbXBsYXRlIGZyb20gJy4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8KHN0cmluZ3xCdWZmZXJ8RXhwb3J0RXJyb3IpPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXNcclxuICogdG8gdGhlIGV4cG9ydGVkIGRhdGEgb3IgcmVqZWN0aW5nIHdpdGggYW4gYEV4cG9ydEVycm9yYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGV4cG9ydCB0byBhbiB1bnN1cHBvcnRlZFxyXG4gKiBvdXRwdXQgZm9ybWF0IG9jY3Vycy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnQocGFnZSwgZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5IChhZGRpdGlvbmFsIEpTIGFuZCBDU1MpXHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxldCBpc1NWRyA9IGZhbHNlO1xyXG5cclxuICAgIC8vIERlY2lkZSBvbiB0aGUgZXhwb3J0IG1ldGhvZFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnKSB7XHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgU1ZHIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gSWYgdGhlIGB0eXBlYCBpcyBhbHNvIFNWRywgcmV0dXJuIHRoZSBpbnB1dFxyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAgIHJldHVybiBleHBvcnRPcHRpb25zLnN2ZztcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gTWFyayBhcyBTVkcgZXhwb3J0IGZvciB0aGUgbGF0ZXIgc2l6ZSBjb3JyZWN0aW9uc1xyXG4gICAgICBpc1NWRyA9IHRydWU7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2Uuc2V0Q29udGVudChzdmdUZW1wbGF0ZShleHBvcnRPcHRpb25zLnN2ZyksIHtcclxuICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xyXG4gICAgICB9KTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgSlNPTiBjb25maWcuJyk7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGV4cG9ydFxyXG4gICAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKGNyZWF0ZUNoYXJ0LCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXBzIHRyYWNrIG9mIGFsbCByZXNvdXJjZXMgYWRkZWQgb24gdGhlIHBhZ2Ugd2l0aCBhZGRYWFhUYWcuIGV0Y1xyXG4gICAgLy8gSXQncyBWSVRBTCB0aGF0IGFsbCBhZGRlZCByZXNvdXJjZXMgZW5kcyB1cCBoZXJlIHNvIHdlIGNhbiBjbGVhciB0aGluZ3NcclxuICAgIC8vIG91dCB3aGVuIGRvaW5nIGEgbmV3IGV4cG9ydCBpbiB0aGUgc2FtZSBwYWdlIVxyXG4gICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgLi4uKGF3YWl0IGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZWFsIGNoYXJ0IHNpemUgYW5kIHNldCB0aGUgem9vbSBhY2NvcmRpbmdseVxyXG4gICAgY29uc3Qgc2l6ZSA9IGlzU1ZHXHJcbiAgICAgID8gYXdhaXQgcGFnZS5ldmFsdWF0ZSgoc2NhbGUpID0+IHtcclxuICAgICAgICAgIGNvbnN0IHN2Z0VsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFxyXG4gICAgICAgICAgICAnI2NoYXJ0LWNvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZSdcclxuICAgICAgICAgICk7XHJcblxyXG4gICAgICAgICAgLy8gR2V0IHRoZSB2YWx1ZXMgY29ycmVjdGx5IHNjYWxlZFxyXG4gICAgICAgICAgY29uc3QgY2hhcnRIZWlnaHQgPSBzdmdFbGVtZW50LmhlaWdodC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcbiAgICAgICAgICBjb25zdCBjaGFydFdpZHRoID0gc3ZnRWxlbWVudC53aWR0aC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gSW4gY2FzZSBvZiBTVkcgdGhlIHpvb20gbXVzdCBiZSBzZXQgZGlyZWN0bHkgZm9yIGJvZHkgYXMgc2NhbGVcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUubWFyZ2luID0gJzBweCc7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSwgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKSlcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG5cclxuICAgICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXNcclxuICAgICAgICAgIC8vIG9mIGV4cG9ydHMuIFJlc2V0IHRoZSB6b29tIGZvciBvdGhlciBleHBvcnRzIHRoYW4gdG8gU1ZHc1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY2xpcCByZWdpb24gZm9yIHRoZSBwYWdlXHJcbiAgICBjb25zdCB7IHgsIHkgfSA9IGF3YWl0IF9nZXRDbGlwUmVnaW9uKHBhZ2UpO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgaGVpZ2h0YCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0SGVpZ2h0ID0gTWF0aC5hYnMoXHJcbiAgICAgIE1hdGguY2VpbChzaXplLmNoYXJ0SGVpZ2h0IHx8IGV4cG9ydE9wdGlvbnMuaGVpZ2h0KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgZmluYWwgYHdpZHRoYCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0V2lkdGggPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRXaWR0aCB8fCBleHBvcnRPcHRpb25zLndpZHRoKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGZpbmFsIHZpZXdwb3J0IG5vdyB0aGF0IHdlIGhhdmUgdGhlIHJlYWwgaGVpZ2h0XHJcbiAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcclxuICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgIGRldmljZVNjYWxlRmFjdG9yOiBpc1NWRyA/IDEgOiBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpXHJcbiAgICB9KTtcclxuXHJcbiAgICBsZXQgcmVzdWx0O1xyXG4gICAgLy8gUmFzdGVyaXphdGlvbiBwcm9jZXNzXHJcbiAgICBzd2l0Y2ggKGV4cG9ydE9wdGlvbnMudHlwZSkge1xyXG4gICAgICBjYXNlICdzdmcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVTVkcocGFnZSk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BuZyc6XHJcbiAgICAgIGNhc2UgJ2pwZWcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVJbWFnZShcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnR5cGUsXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgICB4LFxyXG4gICAgICAgICAgICB5XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BkZic6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVBERihcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgZGVmYXVsdDpcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdDogJHtleHBvcnRPcHRpb25zLnR5cGV9LmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENsZWFyIHByZXZpb3VzbHkgaW5qZWN0ZWQgSlMgYW5kIENTUyByZXNvdXJjZXNcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50XHJcbiAqIHdpdGggdGhlICdjaGFydC1jb250YWluZXInIGlkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgeGAsIGB5YCwgYHdpZHRoYCwgYW5kIGBoZWlnaHRgIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZ2V0Q2xpcFJlZ2lvbihwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoJyNjaGFydC1jb250YWluZXInLCAoZWxlbWVudCkgPT4ge1xyXG4gICAgY29uc3QgeyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH0gPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgeCxcclxuICAgICAgeSxcclxuICAgICAgd2lkdGgsXHJcbiAgICAgIGhlaWdodDogTWF0aC50cnVuYyhoZWlnaHQgPiAxID8gaGVpZ2h0IDogNTAwKVxyXG4gICAgfTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gU1ZHIGJ5IGV2YWx1YXRpbmcgdGhlIGBvdXRlckhUTUxgIG9mIHRoZSBmaXJzdCAnc3ZnJyBlbGVtZW50XHJcbiAqIGluc2lkZSBhbiBlbGVtZW50IHdpdGggdGhlIGlkICdjb250YWluZXInLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVTVkdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBTVkcgc3RyaW5nLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVNWRyhwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoXHJcbiAgICAnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsXHJcbiAgICAoZWxlbWVudCkgPT4gZWxlbWVudC5vdXRlckhUTUxcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBzY3JlZW5zaG90YCBmdW5jdGlvbmFsaXR5IHdpdGhcclxuICogc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZUltYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEltYWdlIHR5cGUuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjbGlwIC0gQ2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGltYWdlIGJ1ZmZlclxyXG4gKiBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgIGZvciB0aW1lb3V0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZUltYWdlKHBhZ2UsIHR5cGUsIGNsaXAsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgcmV0dXJuIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xyXG4gICAgICB0eXBlLFxyXG4gICAgICBjbGlwLFxyXG4gICAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICAgIGZ1bGxQYWdlOiBmYWxzZSxcclxuICAgICAgb3B0aW1pemVGb3JTcGVlZDogdHJ1ZSxcclxuICAgICAgY2FwdHVyZUJleW9uZFZpZXdwb3J0OiB0cnVlLFxyXG4gICAgICAuLi4odHlwZSAhPT0gJ3BuZycgPyB7IHF1YWxpdHk6IDgwIH0gOiB7fSksXHJcbiAgICAgIC8vIEFsd2F5cyByZW5kZXIgb24gYSB0cmFuc3BhcmVudCBwYWdlIGlmIHRoZSBleHBlY3RlZCB0eXBlIGZvcm1hdCBpcyBQTkdcclxuICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZycgLy8gIzQ0NywgIzQ2M1xyXG4gICAgfSksXHJcbiAgICBuZXcgUHJvbWlzZSgoX3Jlc29sdmUsIHJlamVjdCkgPT5cclxuICAgICAgc2V0VGltZW91dChcclxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEV4cG9ydEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnLCA0MDgpKSxcclxuICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgICAgIClcclxuICAgIClcclxuICBdKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQREYgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBgcGRmYCBmdW5jdGlvbmFsaXR5IHdpdGggc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVBERlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtudW1iZXJ9IGhlaWdodCAtIFBERiBoZWlnaHQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aCAtIFBERiB3aWR0aC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBQREYgYnVmZmVyLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVBERihwYWdlLCBoZWlnaHQsIHdpZHRoLCByYXN0ZXJpemF0aW9uVGltZW91dCkge1xyXG4gIGF3YWl0IHBhZ2UuZW11bGF0ZU1lZGlhVHlwZSgnc2NyZWVuJyk7XHJcbiAgcmV0dXJuIHBhZ2UucGRmKHtcclxuICAgIC8vIFRoaXMgd2lsbCByZW1vdmUgYW4gZXh0cmEgZW1wdHkgcGFnZSBpbiBQREYgZXhwb3J0c1xyXG4gICAgaGVpZ2h0OiBoZWlnaHQgKyAxLFxyXG4gICAgd2lkdGgsXHJcbiAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICB0aW1lb3V0OiByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBwdXBwZXRlZXJFeHBvcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGEgd29ya2VyIHBvb2wgaW1wbGVtZW50YXRpb24gZm9yIG1hbmFnaW5nXHJcbiAqIHRoZSBicm93c2VyIGluc3RhbmNlIGFuZCBwYWdlcywgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIGZvciB1c2Ugd2l0aFxyXG4gKiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBvcHRpbWl6ZXMgcmVzb3VyY2VzIHVzYWdlIGFuZCBwZXJmb3JtYW5jZVxyXG4gKiBieSBtYWludGFpbmluZyBhIHBvb2wgb2Ygd29ya2VycyB0aGF0IGNhbiBoYW5kbGUgY29uY3VycmVudCBleHBvcnQgdGFza3NcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgY3JlYXRlQnJvd3NlciwgY2xvc2VCcm93c2VyLCBuZXdQYWdlLCBjbGVhclBhZ2UgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBwdXBwZXRlZXJFeHBvcnQgfSBmcm9tICcuL2V4cG9ydC5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXROZXdEYXRlVGltZSwgbWVhc3VyZVRpbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgcG9vbCBpbnN0YW5jZVxyXG5sZXQgcG9vbCA9IG51bGw7XHJcblxyXG4vLyBQb29sIHN0YXRpc3RpY3NcclxuY29uc3QgcG9vbFN0YXRzID0ge1xyXG4gIGV4cG9ydHNBdHRlbXB0ZWQ6IDAsXHJcbiAgZXhwb3J0c1BlcmZvcm1lZDogMCxcclxuICBleHBvcnRzRHJvcHBlZDogMCxcclxuICBleHBvcnRzRnJvbVN2ZzogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnM6IDAsXHJcbiAgZXhwb3J0c0Zyb21TdmdBdHRlbXB0czogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0czogMCxcclxuICB0aW1lU3BlbnQ6IDAsXHJcbiAgdGltZVNwZW50QXZlcmFnZTogMFxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNldHRpbmcgdXAgd29ya2VyIHJlc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0UG9vbFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gZW5kaW5nIHRoZSBmdW5jdGlvblxyXG4gKiBleGVjdXRpb24gd2hlbiBhbiBhbHJlYWR5IGluaXRpYWxpemVkIHBvb2wgb2YgcmVzb3VyY2VzIGlzIGZvdW5kLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbFxyXG4gKiBvZiB3b3JrZXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRQb29sKHBvb2xPcHRpb25zLCBwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBwdXBwZXRlZXIgYXJndW1lbnRzXHJcbiAgYXdhaXQgY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBJbml0aWFsaXppbmcgcG9vbCB3aXRoIHdvcmtlcnM6IG1pbiAke3Bvb2xPcHRpb25zLm1pbldvcmtlcnN9LCBtYXggJHtwb29sT3B0aW9ucy5tYXhXb3JrZXJzfS5gXHJcbiAgICApO1xyXG5cclxuICAgIGlmIChwb29sKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgICdbcG9vbF0gQWxyZWFkeSBpbml0aWFsaXplZCwgcGxlYXNlIGtpbGwgaXQgYmVmb3JlIGNyZWF0aW5nIGEgbmV3IG9uZS4nXHJcbiAgICAgICk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBLZWVwIGFuIGV5ZSBvbiBhIGNvcnJlY3QgbWluIGFuZCBtYXggd29ya2VycyBudW1iZXJcclxuICAgIGlmIChwb29sT3B0aW9ucy5taW5Xb3JrZXJzID4gcG9vbE9wdGlvbnMubWF4V29ya2Vycykge1xyXG4gICAgICBwb29sT3B0aW9ucy5taW5Xb3JrZXJzID0gcG9vbE9wdGlvbnMubWF4V29ya2VycztcclxuICAgIH1cclxuXHJcbiAgICAvLyBDcmVhdGUgYSBwb29sIGFsb25nIHdpdGggYSBtaW5pbWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIHBvb2wgPSBuZXcgUG9vbCh7XHJcbiAgICAgIC8vIEdldCB0aGUgYGNyZWF0ZWAsIGB2YWxpZGF0ZWAsIGFuZCBgZGVzdHJveWAgZnVuY3Rpb25zXHJcbiAgICAgIC4uLl9mYWN0b3J5KHBvb2xPcHRpb25zKSxcclxuICAgICAgbWluOiBwb29sT3B0aW9ucy5taW5Xb3JrZXJzLFxyXG4gICAgICBtYXg6IHBvb2xPcHRpb25zLm1heFdvcmtlcnMsXHJcbiAgICAgIGFjcXVpcmVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5hY3F1aXJlVGltZW91dCxcclxuICAgICAgY3JlYXRlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlVGltZW91dCxcclxuICAgICAgZGVzdHJveVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmRlc3Ryb3lUaW1lb3V0LFxyXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuaWRsZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXM6IHBvb2xPcHRpb25zLmNyZWF0ZVJldHJ5SW50ZXJ2YWwsXHJcbiAgICAgIHJlYXBJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMucmVhcGVySW50ZXJ2YWwsXHJcbiAgICAgIHByb3BhZ2F0ZUNyZWF0ZUVycm9yOiBmYWxzZVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGV2ZW50c1xyXG4gICAgcG9vbC5vbigncmVsZWFzZScsIGFzeW5jIChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBDbGVhciBwYWdlXHJcbiAgICAgIGNvbnN0IGNsZWFyU3RhdHVzID0gYXdhaXQgY2xlYXJQYWdlKHJlc291cmNlLCBmYWxzZSk7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gUmVsZWFzaW5nIGEgd29ya2VyLiBDbGVhciBwYWdlIHN0YXR1czogJHtjbGVhclN0YXR1c30uYFxyXG4gICAgICApO1xyXG4gICAgfSk7XHJcblxyXG4gICAgcG9vbC5vbignZGVzdHJveVN1Y2Nlc3MnLCAoX2V2ZW50SWQsIHJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gRGVzdHJveWVkIGEgd29ya2VyIHN1Y2Nlc3NmdWxseS5gXHJcbiAgICAgICk7XHJcbiAgICAgIHJlc291cmNlLnBhZ2UgPSBudWxsO1xyXG4gICAgfSk7XHJcblxyXG4gICAgY29uc3QgaW5pdGlhbFJlc291cmNlcyA9IFtdO1xyXG4gICAgLy8gQ3JlYXRlIGFuIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwb29sT3B0aW9ucy5taW5Xb3JrZXJzOyBpKyspIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcbiAgICAgICAgaW5pdGlhbFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSBhbiBpbml0aWFsIHJlc291cmNlLicpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHJlc291cmNlKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGggPyBgIHdpdGggJHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aH0gaW5pdGlhbCByZXNvdXJjZXMgd2FpdGluZy5gIDogJy4nfWBcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1twb29sXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBjcmVhdGUgdGhlIHBvb2wgb2Ygd29ya2Vycy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFRlcm1pbmF0ZXMgYWxsIHdvcmtlcnMgaW4gdGhlIHBvb2wsIGRlc3Ryb3lzIHRoZSBwb29sLCBhbmQgY2xvc2VzIHRoZSBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGtpbGxQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIGFsbCB3b3JrZXJzIGFyZVxyXG4gKiB0ZXJtaW5hdGVkLCB0aGUgcG9vbCBpcyBkZXN0cm95ZWQsIGFuZCB0aGUgYnJvd3NlciBpcyBzdWNjZXNzZnVsbHkgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGtpbGxQb29sKCkge1xyXG4gIGxvZygzLCAnW3Bvb2xdIEtpbGxpbmcgcG9vbCB3aXRoIGFsbCB3b3JrZXJzIGFuZCBjbG9zaW5nIGJyb3dzZXIuJyk7XHJcblxyXG4gIC8vIElmIHN0aWxsIGFsaXZlLCBkZXN0cm95IHRoZSBwb29sIG9mIHBhZ2VzIGJlZm9yZSBjbG9zaW5nIGEgYnJvd3NlclxyXG4gIGlmIChwb29sKSB7XHJcbiAgICAvLyBGcmVlIHVwIG5vdCByZWxlYXNlZCB3b3JrZXJzXHJcbiAgICBmb3IgKGNvbnN0IHdvcmtlciBvZiBwb29sLnVzZWQpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlci5yZXNvdXJjZSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSB0aGUgcG9vbCBpZiBpdCBpcyBzdGlsbCBhdmFpbGFibGVcclxuICAgIGlmICghcG9vbC5kZXN0cm95ZWQpIHtcclxuICAgICAgYXdhaXQgcG9vbC5kZXN0cm95KCk7XHJcbiAgICAgIGxvZyg0LCAnW3Bvb2xdIERlc3Ryb3llZCB0aGUgcG9vbCBvZiByZXNvdXJjZXMuJyk7XHJcbiAgICB9XHJcbiAgICBwb29sID0gbnVsbDtcclxuICB9XHJcblxyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgYXdhaXQgY2xvc2VCcm93c2VyKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcm9jZXNzZXMgdGhlIGV4cG9ydCB3b3JrIHVzaW5nIGEgd29ya2VyIGZyb20gdGhlIHBvb2wuIEFjcXVpcmVzIGEgd29ya2VyXHJcbiAqIGhhbmRsZSBmcm9tIHRoZSBwb29sLCBwZXJmb3JtcyB0aGUgZXhwb3J0IHVzaW5nIHB1cHBldGVlciwgYW5kIHJlbGVhc2VzXHJcbiAqIHRoZSB3b3JrZXIgaGFuZGxlIGJhY2sgdG8gdGhlIHBvb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFdvcmtcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGV4cG9ydCByZXN1bHRcclxuICogYW5kIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0V29yayhvcHRpb25zKSB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIHN0YXJ0aW5nIHRvIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gQW4gZXhwb3J0IGF0dGVtcHQgY291bnRlZFxyXG4gICAgKytwb29sU3RhdHMuZXhwb3J0c0F0dGVtcHRlZDtcclxuXHJcbiAgICAvLyBEaXNwbGF5IHRoZSBwb29sIGluZm9ybWF0aW9uIGlmIG5lZWRlZFxyXG4gICAgaWYgKG9wdGlvbnMucG9vbC5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgZ2V0UG9vbEluZm8oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpbiBjYXNlIG9mIGxhY2tpbmcgdGhlIHBvb2wgaW5zdGFuY2VcclxuICAgIGlmICghcG9vbCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1twb29sXSBXb3JrIHJlY2VpdmVkLCBidXQgcG9vbCBoYXMgbm90IGJlZW4gc3RhcnRlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRoZSBhY3F1aXJlIGNvdW50ZXJcclxuICAgIGNvbnN0IGFjcXVpcmVDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBUcnkgdG8gYWNxdWlyZSB0aGUgd29ya2VyIGFsb25nIHdpdGggdGhlIGlkLCB3b3JrcyBjb3VudCBhbmQgcGFnZVxyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuXHJcbiAgICAgIC8vIEFjcXVpcmUgYSBwb29sIHJlc291cmNlXHJcbiAgICAgIHdvcmtlckhhbmRsZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcblxyXG4gICAgICAvLyBDaGVjayB0aGUgcGFnZSBhY3F1aXJlIHRpbWVcclxuICAgICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDUsXHJcbiAgICAgICAgICBgW2JlbmNobWFya10gJHtvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJyd9YCxcclxuICAgICAgICAgIGBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlIHRvb2sgJHthY3F1aXJlQ291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgfUVycm9yIGVuY291bnRlcmVkIHdoZW4gYWNxdWlyaW5nIGFuIGF2YWlsYWJsZSBlbnRyeTogJHthY3F1aXJlQ291bnRlcigpfW1zLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyZWQgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgIGlmICghd29ya2VySGFuZGxlLnBhZ2UpIHtcclxuICAgICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1twb29sXSBSZXNvbHZlZCB3b3JrZXIgcGFnZSBpcyBpbnZhbGlkOiB0aGUgcG9vbCBzZXR1cCBpcyB3b25reS4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgdGhlIHN0YXJ0IHRpbWVcclxuICAgIGNvbnN0IHdvcmtTdGFydCA9IGdldE5ld0RhdGVUaW1lKCk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICA0LFxyXG4gICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7d29ya2VySGFuZGxlLmlkfV0gLSBTdGFydGluZyB3b3JrIG9uIHRoaXMgcG9vbCBlbnRyeS5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFN0YXJ0IG1lYXN1cmluZyBleHBvcnQgdGltZVxyXG4gICAgY29uc3QgZXhwb3J0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnQgb24gYSBwdXBwZXRlZXIgbGV2ZWxcclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHB1cHBldGVlckV4cG9ydChcclxuICAgICAgd29ya2VySGFuZGxlLnBhZ2UsXHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICApO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIGl0J3MgYW4gZXJyb3JcclxuICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBFcnJvcikge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJZiB0aGVyZSdzIGEgcmFzdGVyaXphdGlvbiB0aW1lb3V0LCB3ZSB3YW50IG5lZWQgdG8gZmx1c2ggdGhlIHBhZ2UuXHJcbiAgICAgIC8vIFRoaXMgaXMgYmVjYXVzZSB0aGUgcGFnZSBtYXkgYmUgaW4gYSBzdGF0ZSB3aGVyZSBpdCdzIHdhaXRpbmcgZm9yXHJcbiAgICAgIC8vIHRoZSBzY3JlZW5zaG90IHRvIGZpbmlzaCBldmVuIHRob3VnaCB0aGUgdGltZW91dCBoYXMgb2NjdXJlZC5cclxuICAgICAgLy8gV2hpY2ggb2YgY291cnNlIGNhdXNlcyBhIGxvdCBvZiBpc3N1ZXMgd2l0aCB0aGUgZXZlbnQgc3lzdGVtLFxyXG4gICAgICAvLyBhbmQgcGFnZSBjb25zaXN0ZW5jeS5cclxuICAgICAgLy9cclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gT25seSBwYWdlLnNjcmVlbnNob3Qgd2lsbCB0aHJvdyB0aGlzLCB0aW1lb3V0cyBmb3IgUERGJ3MgYXJlXHJcbiAgICAgIC8vIGhhbmRsZWQgYnkgdGhlIHBhZ2UucGRmIGZ1bmN0aW9uIGl0c2VsZi5cclxuICAgICAgLy9cclxuICAgICAgLy8gLi4ueWVzLCB0aGlzIGlzIHVnbHkuXHJcbiAgICAgIGlmIChyZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpIHtcclxuICAgICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICAgIHdvcmtlckhhbmRsZS53b3JrQ291bnQgPSBvcHRpb25zLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICAgICAgICB3b3JrZXJIYW5kbGUucGFnZSA9IG51bGw7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChcclxuICAgICAgICByZXN1bHQubmFtZSA9PT0gJ1RpbWVvdXRFcnJvcicgfHxcclxuICAgICAgICByZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCdcclxuICAgICAgKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICAgIH1SYXN0ZXJpemF0aW9uIHRpbWVvdXQ6IHlvdXIgY2hhcnQgbWF5IGJlIHRvbyBjb21wbGV4IG9yIGxhcmdlLCBhbmQgZmFpbGVkIHRvIHJlbmRlciB3aXRoaW4gdGhlIGFsbG90dGVkIHRpbWUuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfUVycm9yIGVuY291bnRlcmVkIGR1cmluZyBleHBvcnQ6ICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApLnNldEVycm9yKHJlc3VsdCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayB0aGUgUHVwcGV0ZWVyIGV4cG9ydCB0aW1lXHJcbiAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA1LFxyXG4gICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgIGBFeHBvcnRpbmcgYSBjaGFydCBzdWNlc3NmdWxseSB0b29rICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXHJcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXHJcbiAgICBjb25zdCB3b3JrRW5kID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuICAgIGNvbnN0IGV4cG9ydFRpbWUgPSB3b3JrRW5kIC0gd29ya1N0YXJ0O1xyXG5cclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnQgKz0gZXhwb3J0VGltZTtcclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnRBdmVyYWdlID1cclxuICAgICAgcG9vbFN0YXRzLnRpbWVTcGVudCAvICsrcG9vbFN0YXRzLmV4cG9ydHNQZXJmb3JtZWQ7XHJcblxyXG4gICAgbG9nKDQsIGBbcG9vbF0gV29yayBjb21wbGV0ZWQgaW4gJHtleHBvcnRUaW1lfW1zLmApO1xyXG5cclxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gdGhlIHJlc3VsdFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzdWx0LFxyXG4gICAgICBvcHRpb25zXHJcbiAgICB9O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzRHJvcHBlZDtcclxuXHJcbiAgICBpZiAod29ya2VySGFuZGxlKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG4gICAgfVxyXG5cclxuICAgIHRocm93IGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZSBpZiBpbml0aWFsaXplZCwgb3IgbnVsbFxyXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sKCkge1xyXG4gIHJldHVybiBwb29sO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgc3RhdGlzdGljIG9mIGEgcG9vbCBpbnN0YWNlIGFib3V0IGV4cG9ydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sU3RhdHNcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGN1cnJlbnQgcG9vbCBzdGF0aXN0aWNzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xTdGF0cygpIHtcclxuICByZXR1cm4gcG9vbFN0YXRzO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXHJcbiAqIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZSByZXF1ZXN0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBQb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTigpIHtcclxuICByZXR1cm4ge1xyXG4gICAgbWluOiBwb29sLm1pbixcclxuICAgIG1heDogcG9vbC5tYXgsXHJcbiAgICB1c2VkOiBwb29sLm51bVVzZWQoKSxcclxuICAgIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXHJcbiAgICBhbGxDcmVhdGVkOiBwb29sLm51bVVzZWQoKSArIHBvb2wubnVtRnJlZSgpLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXM6IHBvb2wubnVtUGVuZGluZ0NyZWF0ZXMoKSxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9uczogcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSxcclxuICAgIHBlbmRpbmdEZXN0cm95czogcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoLFxyXG4gICAgYWJzb2x1dGVBbGw6XHJcbiAgICAgIHBvb2wubnVtVXNlZCgpICtcclxuICAgICAgcG9vbC5udW1GcmVlKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSArXHJcbiAgICAgIHBvb2wucGVuZGluZ0Rlc3Ryb3lzLmxlbmd0aFxyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cclxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXHJcbiAqIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mbygpIHtcclxuICBjb25zdCB7XHJcbiAgICBtaW4sXHJcbiAgICBtYXgsXHJcbiAgICB1c2VkLFxyXG4gICAgYXZhaWxhYmxlLFxyXG4gICAgYWxsQ3JlYXRlZCxcclxuICAgIHBlbmRpbmdBY3F1aXJlcyxcclxuICAgIHBlbmRpbmdDcmVhdGVzLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzLFxyXG4gICAgYWJzb2x1dGVBbGxcclxuICB9ID0gZ2V0UG9vbEluZm9KU09OKCk7XHJcblxyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21pbn0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWF4fS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHVzZWQgcmVzb3VyY2VzOiAke3VzZWR9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgZnJlZSByZXNvdXJjZXM6ICR7YXZhaWxhYmxlfS5gKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCBjcmVhdGVkICh1c2VkIGFuZCBmcmVlKSByZXNvdXJjZXM6ICR7YWxsQ3JlYXRlZH0uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBhY3F1aXJlZDogJHtwZW5kaW5nQWNxdWlyZXN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgY3JlYXRlZDogJHtwZW5kaW5nQ3JlYXRlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSB2YWxpZGF0ZWQ6ICR7cGVuZGluZ1ZhbGlkYXRpb25zfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGRlc3Ryb3llZDogJHtwZW5kaW5nRGVzdHJveXN9LmBcclxuICApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIHJlc291cmNlczogJHthYnNvbHV0ZUFsbH0uYCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGYWN0b3J5IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbiBvYmplY3Qgd2l0aCBgY3JlYXRlYCwgYHZhbGlkYXRlYCxcclxuICogYW5kIGBkZXN0cm95YCBmdW5jdGlvbnMgZm9yIHRoZSBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZhY3RvcnlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgXHJcbiAqIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmFjdG9yeShwb29sT3B0aW9ucykge1xyXG4gIHJldHVybiB7XHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiBjcmVhdGVcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3RcclxuICAgICAqIGNvbnRhaW5pbmcgdGhlIHdvcmtlciBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIGluaXRpYWxcclxuICAgICAqIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGFuIGVycm9yIGR1cmluZ1xyXG4gICAgICogdGhlIGNyZWF0aW9uIG9mIHRoZSBuZXcgcGFnZS5cclxuICAgICAqL1xyXG4gICAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgIC8vIEluaXQgdGhlIHJlc291cmNlIHdpdGggdW5pcXVlIGlkIGFuZCB3b3JrIGNvdW50XHJcbiAgICAgIGNvbnN0IHBvb2xSZXNvdXJjZSA9IHtcclxuICAgICAgICBpZDogdXVpZCgpLFxyXG4gICAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sT3B0aW9ucy53b3JrTGltaXQgLyAyKSlcclxuICAgICAgfTtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gU3RhcnQgbWVhc3VyaW5nIGEgcGFnZSBjcmVhdGlvbiB0aW1lXHJcbiAgICAgICAgY29uc3Qgc3RhcnREYXRlID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICAgICAgLy8gQ3JlYXRlIGEgbmV3IHBhZ2VcclxuICAgICAgICBhd2FpdCBuZXdQYWdlKHBvb2xSZXNvdXJjZSk7XHJcblxyXG4gICAgICAgIC8vIE1lYXN1cmUgdGhlIHRpbWUgb2YgZnVsbCBjcmVhdGlvbiBhbmQgY29uZmlndXJhdGlvbiBvZiBhIHBhZ2VcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIsIHRvb2sgJHtcclxuICAgICAgICAgICAgZ2V0TmV3RGF0ZVRpbWUoKSAtIHN0YXJ0RGF0ZVxyXG4gICAgICAgICAgfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBSZXR1cm4gcmVhZHkgcG9vbCByZXNvdXJjZVxyXG4gICAgICAgIHJldHVybiBwb29sUmVzb3VyY2U7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIEVycm9yIGVuY291bnRlcmVkIHdoZW4gY3JlYXRpbmcgYSBuZXcgcGFnZS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIFZhbGlkYXRlcyBhIHdvcmtlciBwYWdlIGluIHRoZSBleHBvcnQgcG9vbCwgY2hlY2tpbmcgaWYgaXQgaGFzIGV4Y2VlZGVkXHJcbiAgICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiB2YWxpZGF0ZVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgaWYgdGhlIHdvcmtlclxyXG4gICAgICogaXMgdmFsaWQgYW5kIHdpdGhpbiB0aGUgd29yayBsaW1pdDsgb3RoZXJ3aXNlLCB0byBmYWxzZS5cclxuICAgICAqL1xyXG4gICAgdmFsaWRhdGU6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSW4gY2VydGFpbiBjYXNlcyBhY3F1aXJpbmcgdGhyb3dzIGEgVGFyZ2V0Q2xvc2VFcnJvciwgd2hpY2ggbWF5XHJcbiAgICAgIC8vIGJlIGNhdXNlZCBieSB0d28gdGhpbmdzOlxyXG4gICAgICAvLyAtIFRoZSBwYWdlIGlzIGNsb3NlZCBhbmQgYXR0ZW1wdGVkIHRvIGJlIHJldXNlZC5cclxuICAgICAgLy8gLSBMb3N0IGNvbnRhY3Qgd2l0aCB0aGUgYnJvd3Nlci5cclxuICAgICAgLy9cclxuICAgICAgLy8gV2hhdCB3ZSdyZSBzZWVpbmcgaW4gbG9ncyBpcyB0aGF0IHN1Y2Nlc3NpdmUgZXhwb3J0cyB0eXBpY2FsbHlcclxuICAgICAgLy8gc3VjY2VlZHMsIGFuZCB0aGUgc2VydmVyIHJlY292ZXJzLCBpbmRpY2F0aW5nIHRoYXQgaXQncyBsaWtlbHlcclxuICAgICAgLy8gdGhlIGZpcnN0IGNhc2UuIFRoaXMgaXMgYW4gYXR0ZW1wdCBhdCBhbGxpZXZhdGluZyB0aGUgaXNzdWUgYnlcclxuICAgICAgLy8gc2ltcGx5IG5vdCB2YWxpZGF0aW5nIHRoZSB3b3JrZXIgaWYgdGhlIHBhZ2UgaXMgbnVsbCBvciBjbG9zZWQuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIFRoZSBhY3R1YWwgcmVzdWx0IGZyb20gd2hlbiB0aGlzIGhhcHBlbmVkLCB3YXMgdGhhdCBhIHdvcmtlciB3b3VsZFxyXG4gICAgICAvLyBiZSBjb21wbGV0ZWx5IGxvY2tlZCwgc3RvcHBpbmcgaXQgZnJvbSBiZWluZyBhY3F1aXJlZCB1bnRpbFxyXG4gICAgICAvLyBpdHMgd29yayBjb3VudCByZWFjaGVkIHRoZSBsaW1pdC5cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgdmFsaWRcclxuICAgICAgaWYgKCFwb29sUmVzb3VyY2UucGFnZSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAobm8gdmFsaWQgcGFnZSBpcyBmb3VuZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHBhZ2VgIGlzIGNsb3NlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAocGFnZSBpcyBjbG9zZWQgb3IgaW52YWxpZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYG1haW5GcmFtZWAgaXMgZGV0YWNoZWRcclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlLm1haW5GcmFtZSgpLmRldGFjaGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlJ3MgZnJhbWUgaXMgZGV0YWNoZWQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGB3b3JrTGltaXRgIGlzIGV4Y2VlZGVkXHJcbiAgICAgIGlmIChcclxuICAgICAgICBwb29sT3B0aW9ucy53b3JrTGltaXQgJiZcclxuICAgICAgICArK3Bvb2xSZXNvdXJjZS53b3JrQ291bnQgPiBwb29sT3B0aW9ucy53b3JrTGltaXRcclxuICAgICAgKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChleGNlZWRlZCB0aGUgJHtwb29sT3B0aW9ucy53b3JrTGltaXR9IHdvcmtzIHBlciByZXNvdXJjZSBsaW1pdCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBUaGUgYHBvb2xSZXNvdXJjZWAgaXMgdmFsaWRhdGVkXHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIERlc3Ryb3lzIGEgd29ya2VyIGVudHJ5IGluIHRoZSBleHBvcnQgcG9vbCwgY2xvc2luZyBpdHMgYXNzb2NpYXRlZCBwYWdlLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGRlc3Ryb3lcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICAgKiB0aGUgd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAgICovXHJcbiAgICBkZXN0cm95OiBhc3luYyAocG9vbFJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIERlc3Ryb3lpbmcgYSB3b3JrZXIuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFJlbW92ZSBhbGwgYXR0YWNoZWQgZXZlbnQgbGlzdGVuZXJzIGZyb20gdGhlIHJlc291cmNlXHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3BhZ2VlcnJvcicpO1xyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdjb25zb2xlJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2ZyYW1lZGV0YWNoZWQnKTtcclxuXHJcbiAgICAgICAgICAvLyBXZSBuZWVkIHRvIHdhaXQgYXJvdW5kIGZvciB0aGlzXHJcbiAgICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5jbG9zZSgpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFBhZ2UgY291bGQgbm90IGJlIGNsb3NlZCB1cG9uIGRlc3Ryb3lpbmcuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH07XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBpbml0UG9vbCxcclxuICBraWxsUG9vbCxcclxuICBwb3N0V29yayxcclxuICBnZXRQb29sLFxyXG4gIGdldFBvb2xTdGF0cyxcclxuICBnZXRQb29sSW5mbyxcclxuICBnZXRQb29sSW5mb0pTT05cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFVzZWQgdG8gc2FuaXRpemUgdGhlIHN0cmluZ3MgY29taW5nIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVcclxuICogdG8gcHJldmVudCBYU1MgYXR0YWNrcyAod2l0aCB0aGUgRE9NUHVyaWZ5IGxpYnJhcnkpLlxyXG4gKi9cclxuXHJcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcclxuaW1wb3J0IHsgSlNET00gfSBmcm9tICdqc2RvbSc7XHJcblxyXG4vKipcclxuICogU2FuaXRpemVzIGEgZ2l2ZW4gSFRNTCBzdHJpbmcgYnkgcmVtb3ZpbmcgPHNjcmlwdD4gdGFncy4gVGhpcyBmdW5jdGlvbiB1c2VzXHJcbiAqIGEgcmVndWxhciBleHByZXNzaW9uIHRvIGZpbmQgYW5kIHJlbW92ZSBhbGwgb2NjdXJyZW5jZXMgb2YgPHNjcmlwdD48L3NjcmlwdD5cclxuICogdGFncyBhbmQgYW55IGNvbnRlbnQgd2l0aGluIHRoZW0uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzYW5pdGl6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgSFRNTCBzdHJpbmcgdG8gYmUgc2FuaXRpemVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2FuaXRpemVkIEhUTUwgc3RyaW5nLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplKGlucHV0KSB7XHJcbiAgLy8gR2V0IHRoZSB2aXJ0dWFsIERPTVxyXG4gIGNvbnN0IHdpbmRvdyA9IG5ldyBKU0RPTSgnJykud2luZG93O1xyXG5cclxuICAvLyBDcmVhdGUgYSBwdXJpZnlpbmcgaW5zdGFuY2VcclxuICBjb25zdCBwdXJpZnkgPSBET01QdXJpZnkod2luZG93KTtcclxuXHJcbiAgLy8gUmV0dXJuIHNhbml0aXplZCBpbnB1dFxyXG4gIHJldHVybiBwdXJpZnkuc2FuaXRpemUoaW5wdXQsIHsgQUREX1RBR1M6IFsnZm9yZWlnbk9iamVjdCddIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2FuaXRpemVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyB0byBwcmVwYXJlIGZvciB0aGUgZXhwb3J0aW5nIGNoYXJ0c1xyXG4gKiBpbnRvIHZhcmlvdXMgaW1hZ2Ugb3V0cHV0IGZvcm1hdHMgc3VjaCBhcyBKUEVHLCBQTkcsIFBERiwgYW5kIFNWR3MuXHJcbiAqIEl0IHN1cHBvcnRzIHNpbmdsZSBhbmQgYmF0Y2ggZXhwb3J0IG9wZXJhdGlvbnMgYW5kIGFsbG93cyBjdXN0b21pemF0aW9uXHJcbiAqIHRocm91Z2ggb3B0aW9ucyBwYXNzZWQgZnJvbSBjb25maWd1cmF0aW9ucyBvciBBUElzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIGlzQWxsb3dlZENvbmZpZywgbWVyZ2VPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGRlZXBDb3B5LFxyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBpc09iamVjdCxcclxuICByb3VuZE51bWJlcixcclxuICB3cmFwQXJvdW5kXHJcbn0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGdsb2JhbCBmbGFnIGZvciB0aGUgY29kZSBleGVjdXRpb24gcGVybWlzc2lvblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucyBhbmQgc2F2ZXNcclxuICogdGhlIHJlc3VsdGluZyBpbWFnZSB0byB0aGUgcHJvdmlkZWQgb3V0cHV0IGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIFRoZSBvYmplY3QgbXVzdCBjb250YWluIGF0IGxlYXN0IG9uZVxyXG4gKiBvZiB0aGUgZm9sbG93aW5nIGBleHBvcnRgIHByb3BlcnRpZXM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgXHJcbiAqIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpbmdsZUV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0KSB7XHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQoXHJcbiAgICAgIHsgZXhwb3J0OiBvcHRpb25zLmV4cG9ydCwgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWMgfSxcclxuICAgICAgYXN5bmMgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSBvbmUgb2YgdGhlIGZvbGxvd2luZyBvcHRpb25zOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIGluZm9ybWF0aW9uXHJcbiAqIHByb3ZpZGVkIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZ1xyXG4gKiBmb3JtYXQ6IFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzXHJcbiAqIGFyZSBzYXZlZCB0byB0aGUgc3BlY2lmaWVkIG91dHB1dCBmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIGZyb21cclxuICogdGhlIGBleHBvcnRgIHNlY3Rpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3NlcyBhcmUgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJhdGNoRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQgJiYgb3B0aW9ucy5leHBvcnQuYmF0Y2gpIHtcclxuICAgIC8vIEFuIGFycmF5IGZvciBjb2xsZWN0aW5nIGJhdGNoIGV4cG9ydHNcclxuICAgIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIGBiYXRjaGAgYXJndW1lbnRzXHJcbiAgICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykgfHwgW10pIHtcclxuICAgICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgZXhwb3J0aW5nT3B0aW9uc2AgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0XHJcbiAqIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW5cclxuICogYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIElmIHBhcnRpYWxcclxuICogb3B0aW9ucyBhcmUgcHJvdmlkZWQsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBgZW5kQ2FsbGJhY2tgIGZ1bmN0aW9uIGlzIGludm9rZWQgdXBvbiB0aGUgY29tcGxldGlvbiBvZiB0aGUgZXhwb3J0LFxyXG4gKiBlaXRoZXIgc3VjY2Vzc2Z1bGx5IG9yIHdpdGggYW4gZXJyb3IuIFRoZSBgZXJyb3JgIG9iamVjdCBpcyBwcm92aWRlZFxyXG4gKiBhcyB0aGUgZmlyc3QgYXJndW1lbnQsIGFuZCB0aGUgYGRhdGFgIG9iamVjdCBpcyB0aGUgc2Vjb25kLCBjb250YWluaW5nXHJcbiAqIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YCBwcm9wZXJ0eVxyXG4gKiBhbmQgdGhlIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGluIHRoZSBgb3B0aW9uc2AgcHJvcGVydHkuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydGluZ09wdGlvbnMgLSBUaGUgYGV4cG9ydGluZ09wdGlvbnNgIG9iamVjdCwgd2hpY2hcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgdGhlIHByb3ZpZGVkXHJcbiAqIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHRoZSBleHBvcnQgcHJvY2VzcyBvciB1cG9uIGVuY291bnRlcmluZyBhbiBlcnJvci4gVGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGlzIHRoZSBgZXJyb3JgIG9iamVjdCwgYW5kIHRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIGBkYXRhYCBvYmplY3QsXHJcbiAqIHdoaWNoIGluY2x1ZGVzIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YFxyXG4gKiBwcm9wZXJ0eSBhbmQgdGhlIGZ1bGwgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGV4cG9ydGluZ09wdGlvbnMsIGVuZENhbGxiYWNrKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHByb3ZpZGVkIG9wdGlvbnMgaXMgYW4gb2JqZWN0XHJcbiAgICBpZiAoIWlzT2JqZWN0KGV4cG9ydGluZ09wdGlvbnMpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NoYXJ0XSBJbmNvcnJlY3QgdmFsdWUgb2YgdGhlIHByb3ZpZGVkIGBleHBvcnRpbmdPcHRpb25zYC4gTmVlZHMgdG8gYmUgYW4gb2JqZWN0LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTWVyZ2UgYWRkaXRpb25hbCBvcHRpb25zIHRvIHRoZSBjb3B5IG9mIHRoZSBpbnN0YW5jZSBvcHRpb25zXHJcbiAgICBjb25zdCBvcHRpb25zID0gbWVyZ2VPcHRpb25zKGRlZXBDb3B5KGdldE9wdGlvbnMoKSksIHtcclxuICAgICAgZXhwb3J0OiBleHBvcnRpbmdPcHRpb25zLmV4cG9ydCxcclxuICAgICAgY3VzdG9tTG9naWM6IGV4cG9ydGluZ09wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICByZXR1cm4gcG9zdFdvcmsob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBmb3IgY2hhcnQgZXhwb3J0cyBiYXNlZFxyXG4gKiBvbiB0aGUgcHJvdmlkZWQgZXhwb3J0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICogMS4gVGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBgc2NhbGVgIGZyb20gdGhlIGBleHBvcnRPcHRpb25zYC5cclxuICogMi4gT3B0aW9ucyBmcm9tIHRoZSBjaGFydCBjb25maWd1cmF0aW9uIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBnbG9iYWwgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgdGhlbWUgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCBzZWN0aW9ucykuXHJcbiAqIDUuIEZhbGxiYWNrIGRlZmF1bHQgdmFsdWVzIChgaGVpZ2h0ID0gNDAwYCwgYHdpZHRoID0gNjAwYCwgYHNjYWxlID0gMWApLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZpbmRDaGFydFNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb2RlIGV4ZWN1dGlvblxyXG4gKiBpcyBub3QgYWxsb3dlZCBidXQgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYXJlIHN0aWxsIHByb3ZpZGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgLy8gSW4gY2FzZSBvZiBhbGxvd2luZyBjb2RlIGV4ZWN1dGlvblxyXG4gIGlmIChhbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIC8vIFByb2Nlc3MgdGhlIGByZXNvdXJjZXNgIG9wdGlvblxyXG4gICAgaWYgKHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID09PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBDdXN0b20gc3RyaW5naWZpZWQgcmVzb3VyY2VzXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gTG9hZCB0aGUgZGVmYXVsdCBvbmVcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoJ3Jlc291cmNlcy5qc29uJyksICd1dGY4JyksXHJcbiAgICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IGByZXNvdXJjZXMuanNvbmAgZmlsZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGN1c3RvbSBjb2RlIGFuZCB3cmFwIGFyb3VuZCBpdCBpbiBhIHNlbGYgaW52b2tpbmcgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1tjaGFydF0gVGhlIGBjdXN0b21Db2RlYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gVHJ5IHRvIGxvYWQgY2FsbGJhY2sgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2ssXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY2FsbGJhY2tgIGNhbm5vdCBiZSBsb2FkZWQuJyk7XHJcblxyXG4gICAgICAvLyBJbiBjYXNlIG9mIGFuIGVycm9yLCBzZXQgdGhlIG9wdGlvbiB3aXRoIG51bGxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGN1c3RvbUNvZGVgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGN1c3RvbUNvZGVgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGNhbGxiYWNrYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgY2FsbGJhY2tgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHJlc291cmNlc2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gSWYgdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIGZsYWcgaXMgc2V0IHRvIGZhbHNlLCB3ZSBzaG91bGQgcmVmdXNlXHJcbiAgICAvLyB0aGUgdXNhZ2Ugb2YgdGhlIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgLCBhbmQgYGN1c3RvbUNvZGVgIG9wdGlvbnMuXHJcbiAgICAvLyBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbCByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LlxyXG4gICAgaWYgKFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFJlc2V0IGFsbCBjdXN0b20gY29kZSBvcHRpb25zXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcblxyXG4gICAgICAvLyBTZW5kIGEgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydCB0aGVzZSBzZXR0aW5nc1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnLCBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gLFxyXG4gICAgICAgIDQwM1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZnJvbSB0aGUgYHJlc291cmNlc2Agb3B0aW9uIGZvciBleHBvcnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFtyZXNvdXJjZXM9bnVsbF0gLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuXHJcbiAqIENhbiBiZSBlaXRoZXIgYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiwgYSBwYXRoIHRvIGEgSlNPTiBmaWxlLFxyXG4gKiBvciBudWxsLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgbnVsbGAuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2FkaW5nXHJcbiAqIHJlc291cmNlcyBmcm9tIGZpbGVzIGlzIGFsbG93ZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGhhbmRsZWQgcmVzb3VyY2VzIG9yIG51bGwgaWYgbm8gdmFsaWQgcmVzb3VyY2VzXHJcbiAqIGFyZSBmb3VuZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgcmVzb3VyY2VzID0gbnVsbCxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIExpc3Qgb2YgYWxsb3dlZCBzZWN0aW9ucyBpbiB0aGUgcmVzb3VyY2VzIEpTT05cclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKHJlc291cmNlcyksICd1dGY4JyksXHJcbiAgICAgICAgZmFsc2UsXHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxyXG4gICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhyZXNvdXJjZXMsIGZhbHNlLCBhbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIGZpbGVzIHNlY3Rpb25cclxuICAgIGlmIChoYW5kbGVkUmVzb3VyY2VzICYmICFhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaWx0ZXIgZnJvbSB1bm5lY2Vzc2FyeSBwcm9wZXJ0aWVzXHJcbiAgZm9yIChjb25zdCBwcm9wTmFtZSBpbiBoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXNbcHJvcE5hbWVdO1xyXG4gICAgfSBlbHNlIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiBhbGxvd2VkIHByb3BlcnRpZXMgaXMgcHJlc2VudFxyXG4gIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgbG9hZGluZyBhbmQgdmFsaWRhdGlvbiBvZiB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYFxyXG4gKiBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIElmIHRoZSBvcHRpb24gaXMgYSBzdHJpbmcgYW5kIHJlZmVyZW5jZXMgYSBKU09OIGZpbGVcclxuICogKHdoZW4gdGhlIGBhbGxvd0ZpbGVSZXNvdXJjZXNgIGlzIHRydWUpLCBpdCByZWFkcyBhbmQgcGFyc2VzIHRoZSBmaWxlLlxyXG4gKiBPdGhlcndpc2UsIGl0IGF0dGVtcHRzIHRvIHBhcnNlIHRoZSBzdHJpbmcgb3Igb2JqZWN0IGFzIEpTT04uIElmIGFueSBlcnJvcnNcclxuICogb2NjdXIgZHVyaW5nIHRoaXMgcHJvY2VzcywgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC4gSWYgdGhlcmUgaXMgYW4gZXJyb3JcclxuICogbG9hZGluZyBvciBwYXJzaW5nIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3IgYHRoZW1lT3B0aW9uc2AsIHRoZSBlcnJvciBpcyBsb2dnZWRcclxuICogYW5kIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBlbmFibGluZyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzcGVjaWZpZWQgRXhwcmVzcyBhcHAuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXRcclxuICogdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCByYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWRcclxuICAgIGlmIChyYXRlTGltaXRpbmdPcHRpb25zLmVuYWJsZSkge1xyXG4gICAgICBjb25zdCBtZXNzYWdlID1cclxuICAgICAgICAnVG9vIG1hbnkgcmVxdWVzdHMsIHlvdSBoYXZlIGJlZW4gcmF0ZSBsaW1pdGVkLiBQbGVhc2UgdHJ5IGFnYWluIGxhdGVyLic7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICAgIGNvbnN0IHJhdGVPcHRpb25zID0ge1xyXG4gICAgICAgIHdpbmRvdzogcmF0ZUxpbWl0aW5nT3B0aW9ucy53aW5kb3cgfHwgMSxcclxuICAgICAgICBtYXhSZXF1ZXN0czogcmF0ZUxpbWl0aW5nT3B0aW9ucy5tYXhSZXF1ZXN0cyB8fCAzMCxcclxuICAgICAgICBkZWxheTogcmF0ZUxpbWl0aW5nT3B0aW9ucy5kZWxheSB8fCAwLFxyXG4gICAgICAgIHRydXN0UHJveHk6IHJhdGVMaW1pdGluZ09wdGlvbnMudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgICAgICBza2lwS2V5OiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBLZXkgfHwgbnVsbCxcclxuICAgICAgICBza2lwVG9rZW46IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcFRva2VuIHx8IG51bGxcclxuICAgICAgfTtcclxuXHJcbiAgICAgIC8vIFNldCBpZiBiZWhpbmQgYSBwcm94eVxyXG4gICAgICBpZiAocmF0ZU9wdGlvbnMudHJ1c3RQcm94eSkge1xyXG4gICAgICAgIGFwcC5lbmFibGUoJ3RydXN0IHByb3h5Jyk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENyZWF0ZSBhIGxpbWl0ZXJcclxuICAgICAgY29uc3QgbGltaXRlciA9IHJhdGVMaW1pdCh7XHJcbiAgICAgICAgLy8gVGltZSBmcmFtZSBmb3Igd2hpY2ggcmVxdWVzdHMgYXJlIGNoZWNrZWQgYW5kIHJlbWVtYmVyZWRcclxuICAgICAgICB3aW5kb3dNczogcmF0ZU9wdGlvbnMud2luZG93ICogNjAgKiAxMDAwLFxyXG4gICAgICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciBgd2luZG93TXNgXHJcbiAgICAgICAgbGltaXQ6IHJhdGVPcHRpb25zLm1heFJlcXVlc3RzLFxyXG4gICAgICAgIC8vIERpc2FibGUgZGVsYXlpbmcsIGZ1bGwgc3BlZWQgdW50aWwgdGhlIG1heCBsaW1pdCBpcyByZWFjaGVkXHJcbiAgICAgICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICAgICAgaGFuZGxlcjogKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5mb3JtYXQoe1xyXG4gICAgICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZCh7IG1lc3NhZ2UgfSk7XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIGRlZmF1bHQ6ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKG1lc3NhZ2UpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICB9LFxyXG4gICAgICAgIHNraXA6IChyZXF1ZXN0KSA9PiB7XHJcbiAgICAgICAgICAvLyBBbGxvdyBieXBhc3NpbmcgdGhlIGxpbWl0ZXIgaWYgYSB2YWxpZCBrZXkvdG9rZW4gaGFzIGJlZW4gc2VudFxyXG4gICAgICAgICAgaWYgKFxyXG4gICAgICAgICAgICByYXRlT3B0aW9ucy5za2lwS2V5ICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBUb2tlbiAhPT0gbnVsbCAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmtleSA9PT0gcmF0ZU9wdGlvbnMuc2tpcEtleSAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmFjY2Vzc190b2tlbiA9PT0gcmF0ZU9wdGlvbnMuc2tpcFRva2VuXHJcbiAgICAgICAgICApIHtcclxuICAgICAgICAgICAgbG9nKDQsICdbcmF0ZSBsaW1pdGluZ10gU2tpcHBpbmcgcmF0ZSBsaW1pdGVyLicpO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgLy8gVXNlIGEgbGltaXRlciBhcyBhIG1pZGRsZXdhcmVcclxuICAgICAgYXBwLnVzZShsaW1pdGVyKTtcclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcmF0ZSBsaW1pdGluZ10gRW5hYmxlZCByYXRlIGxpbWl0aW5nIHdpdGggJHtyYXRlT3B0aW9ucy5tYXhSZXF1ZXN0c30gcmVxdWVzdHMgcGVyICR7cmF0ZU9wdGlvbnMud2luZG93fSBtaW51dGUgZm9yIGVhY2ggSVAsIHRydXN0aW5nIHByb3h5OiAke3JhdGVPcHRpb25zLnRydXN0UHJveHl9LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3JhdGUgbGltaXRpbmddIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHNldCB0aGUgcmF0ZSBsaW1pdGluZyBvcHRpb25zLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtaWRkbGV3YXJlIGZ1bmN0aW9ucyBmb3IgdmFsaWRhdGluZyBpbmNvbWluZyBIVFRQIHJlcXVlc3RzXHJcbiAqIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uIFRoaXMgbW9kdWxlIGVuc3VyZXMgdGhhdCByZXF1ZXN0cyBjb250YWluXHJcbiAqIGFwcHJvcHJpYXRlIGNvbnRlbnQgdHlwZXMgYW5kIHZhbGlkIHJlcXVlc3QgYm9kaWVzLCBpbmNsdWRpbmcgcHJvcGVyIEpTT05cclxuICogc3RydWN0dXJlcyBhbmQgY2hhcnQgZGF0YSBmb3IgZXhwb3J0cy4gSXQgY2hlY2tzIGZvciBwb3RlbnRpYWwgaXNzdWVzIHN1Y2hcclxuICogYXMgbWlzc2luZyBvciBtYWxmb3JtZWQgZGF0YSwgcHJpdmF0ZSByYW5nZSBVUkxzIGluIFNWRyBwYXlsb2FkcywgYW5kIGFsbG93c1xyXG4gKiBmb3IgZmxleGlibGUgb3B0aW9ucyB2YWxpZGF0aW9uLiBUaGUgbWlkZGxld2FyZSBsb2dzIGRldGFpbGVkIGluZm9ybWF0aW9uXHJcbiAqIGFuZCBoYW5kbGVzIGVycm9ycyByZWxhdGVkIHRvIGluY29ycmVjdCBwYXlsb2FkcywgY2hhcnQgZGF0YSwgYW5kIHByaXZhdGUgVVJMXHJcbiAqIHVzYWdlLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7IGdldEFsbG93Q29kZUV4ZWN1dGlvbiB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaXNPYmplY3RFbXB0eSwgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIGNvbnRlbnQtdHlwZSBoZWFkZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9IFRoZSBjYWxsIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY29udGVudC10eXBlXHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gY29udGVudFR5cGVNaWRkbGV3YXJlKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgY29udGVudCB0eXBlIGhlYWRlclxyXG4gICAgY29uc3QgY29udGVudFR5cGUgPSByZXF1ZXN0LmhlYWRlcnNbJ2NvbnRlbnQtdHlwZSddIHx8ICcnO1xyXG5cclxuICAgIC8vIEFsbG93IG9ubHkgSlNPTiwgVVJMLWVuY29kZWQgYW5kIGZvcm0gZGF0YSB3aXRob3V0IGZpbGVzIHR5cGVzIG9mIGRhdGFcclxuICAgIGlmIChcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdhcHBsaWNhdGlvbi9qc29uJykgJiZcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ211bHRpcGFydC9mb3JtLWRhdGEnKVxyXG4gICAgKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3ZhbGlkYXRpb25dIENvbnRlbnQtVHlwZSBtdXN0IGJlIGFwcGxpY2F0aW9uL2pzb24sIGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCwgb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YS4nLFxyXG4gICAgICAgIDQxNVxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENhbGwgdGhlIGByZXF1ZXN0Qm9keU1pZGRsZXdhcmVgIG1pZGRsZXdhcmVcclxuICAgIHJldHVybiBuZXh0KCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciB2YWxpZGF0aW5nIHRoZSByZXF1ZXN0J3MgYm9keS5cclxuICpcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RCb2R5TWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBib2R5IGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGNoYXJ0IGRhdGEgZnJvbSB0aGUgYm9keVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGluIGNhc2Ugb2YgdGhlIHByaXZhdGUgcmFuZ2VcclxuICogdXJsIGVycm9yLlxyXG4gKi9cclxuZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgcmVxdWVzdCBib2R5XHJcbiAgICBjb25zdCBib2R5ID0gcmVxdWVzdC5ib2R5O1xyXG5cclxuICAgIC8vIENyZWF0ZSBhIHVuaXF1ZSBJRCBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSB1dWlkKCk7XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgaXMgbm8gY29ycmVjdCBib2R5XHJcbiAgICBpZiAoIWJvZHkgfHwgaXNPYmplY3RFbXB0eShib2R5KSkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIGVtcHR5LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBib2R5IGlzIHJlcXVpcmVkLiBQbGVhc2UgZW5zdXJlIHRoYXQgeW91ciBDb250ZW50LVR5cGUgaGVhZGVyIGlzIGNvcnJlY3QuIEFjY2VwdGVkIHR5cGVzIGFyZSAnYXBwbGljYXRpb24vanNvbicgYW5kICdtdWx0aXBhcnQvZm9ybS1kYXRhJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIG9wdGlvbiBmb3IgdGhlIHNlcnZlclxyXG4gICAgY29uc3QgYWxsb3dDb2RlRXhlY3V0aW9uID0gZ2V0QWxsb3dDb2RlRXhlY3V0aW9uKCk7XHJcblxyXG4gICAgLy8gRmluZCBhIGNvcnJlY3QgY2hhcnQgb3B0aW9uc1xyXG4gICAgY29uc3QgaW5zdHIgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgIC8vIFVzZSBvbmUgb2YgdGhlIGJlbG93XHJcbiAgICAgIGJvZHkuaW5zdHIgfHwgYm9keS5vcHRpb25zIHx8IGJvZHkuaW5maWxlIHx8IGJvZHkuZGF0YSxcclxuICAgICAgLy8gU3RyaW5naWZ5IG9wdGlvbnNcclxuICAgICAgdHJ1ZSxcclxuICAgICAgLy8gQWxsb3cgb3IgZGlzYWxsb3cgZnVuY3Rpb25zXHJcbiAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGNoYXJ0IGRhdGFcclxuICAgIGlmIChpbnN0ciA9PT0gbnVsbCAmJiAhYm9keS5zdmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fCByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcGF5bG9hZCBpcyBtaXNzaW5nIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3IgZXhwb3J0OiAke0pTT04uc3RyaW5naWZ5KGJvZHkpfS5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBbdmFsaWRhdGlvbl0gTm8gY29ycmVjdCBjaGFydCBkYXRhIGZvdW5kLiBFbnN1cmUgdGhhdCB5b3UgYXJlIHVzaW5nIGVpdGhlciBhcHBsaWNhdGlvbi9qc29uIG9yIG11bHRpcGFydC9mb3JtLWRhdGEgaGVhZGVycy4gSWYgc2VuZGluZyBKU09OLCBtYWtlIHN1cmUgdGhlIGNoYXJ0IGRhdGEgaXMgaW4gdGhlICdpbmZpbGUnLCAnb3B0aW9ucycsIG9yICdkYXRhJyBhdHRyaWJ1dGUuIElmIHNlbmRpbmcgU1ZHLCBlbnN1cmUgaXQgaXMgaW4gdGhlICdzdmcnIGF0dHJpYnV0ZS5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRlc3Qgb2YgeGxpbms6aHJlZiBlbGVtZW50cyBmcm9tIHBheWxvYWQncyBTVkcgZmFpbHNcclxuICAgIGlmIChib2R5LnN2ZyAmJiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKGJvZHkuc3ZnKSkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBbdmFsaWRhdGlvbl0gU1ZHIHBvdGVudGlhbGx5IGNvbnRhaW4gYXQgbGVhc3Qgb25lIGZvcmJpZGRlbiBVUkwgaW4gJ3hsaW5rOmhyZWYnIGVsZW1lbnQuIFBsZWFzZSByZXZpZXcgdGhlIFNWRyBjb250ZW50IGFuZCBlbnN1cmUgdGhhdCBhbGwgcmVmZXJlbmNlZCBVUkxzIGNvbXBseSB3aXRoIHNlY3VyaXR5IHBvbGljaWVzLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IG9wdGlvbnMgYW5kIHN0b3JlIHBhcnNlZCBzdHJ1Y3R1cmUgaW4gdGhlIHJlcXVlc3RcclxuICAgIHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucyA9IHtcclxuICAgICAgLy8gU2V0IHRoZSBjcmVhdGVkIElEIGFzIGEgYHJlcXVlc3RJZGAgcHJvcGVydHkgaW4gdGhlIG9wdGlvbnNcclxuICAgICAgcmVxdWVzdElkLFxyXG4gICAgICBleHBvcnQ6IHtcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICBzdmc6IGJvZHkuc3ZnLFxyXG4gICAgICAgIG91dGZpbGU6XHJcbiAgICAgICAgICBib2R5Lm91dGZpbGUgfHxcclxuICAgICAgICAgIGAke3JlcXVlc3QucGFyYW1zLmZpbGVuYW1lIHx8ICdjaGFydCd9LiR7Ym9keS50eXBlIHx8ICdwbmcnfWAsXHJcbiAgICAgICAgdHlwZTogYm9keS50eXBlLFxyXG4gICAgICAgIGNvbnN0cjogYm9keS5jb25zdHIsXHJcbiAgICAgICAgYjY0OiBib2R5LmI2NCxcclxuICAgICAgICBub0Rvd25sb2FkOiBib2R5Lm5vRG93bmxvYWQsXHJcbiAgICAgICAgaGVpZ2h0OiBib2R5LmhlaWdodCxcclxuICAgICAgICB3aWR0aDogYm9keS53aWR0aCxcclxuICAgICAgICBzY2FsZTogYm9keS5zY2FsZSxcclxuICAgICAgICBnbG9iYWxPcHRpb25zOiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICBib2R5Lmdsb2JhbE9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKSxcclxuICAgICAgICB0aGVtZU9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkudGhlbWVPcHRpb25zLFxyXG4gICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgIClcclxuICAgICAgfSxcclxuICAgICAgY3VzdG9tTG9naWM6IHtcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzOiBmYWxzZSxcclxuICAgICAgICBjdXN0b21Db2RlOiBib2R5LmN1c3RvbUNvZGUsXHJcbiAgICAgICAgY2FsbGJhY2s6IGJvZHkuY2FsbGJhY2ssXHJcbiAgICAgICAgcmVzb3VyY2VzOiBpc0FsbG93ZWRDb25maWcoYm9keS5yZXNvdXJjZXMsIHRydWUsIGFsbG93Q29kZUV4ZWN1dGlvbilcclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBuZXh0IG1pZGRsZXdhcmVcclxuICAgIHJldHVybiBuZXh0KCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVzIHRvIHRoZSBwYXNzZWQgZXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmFsaWRhdGlvbk1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGNvbnRlbnQgdHlwZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCBjb250ZW50VHlwZU1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgcmVxdWVzdCBib2R5IHJlcXVlc3QgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgYXBwLnBvc3QoWycvJywgJy86ZmlsZW5hbWUnXSwgcmVxdWVzdEJvZHlNaWRkbGV3YXJlKTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyB0aGUgZXhwb3J0IHJvdXRlcyBhbmQgbG9naWMgZm9yIGhhbmRsaW5nIGNoYXJ0IGV4cG9ydFxyXG4gKiByZXF1ZXN0cyBpbiBhbiBFeHByZXNzIHNlcnZlci4gVGhpcyBtb2R1bGUgcHJvY2Vzc2VzIGluY29taW5nIHJlcXVlc3RzXHJcbiAqIHRvIGV4cG9ydCBjaGFydHMgaW4gdmFyaW91cyBmb3JtYXRzIChlLmcuIEpQRUcsIFBORywgUERGLCBTVkcpLiBJdCBpbnRlZ3JhdGVzXHJcbiAqIHdpdGggSGlnaGNoYXJ0cycgY29yZSBmdW5jdGlvbmFsaXRpZXMgYW5kIHN1cHBvcnRzIGJvdGggaW1tZWRpYXRlIGRvd25sb2FkXHJcbiAqIHJlc3BvbnNlcyBhbmQgQmFzZTY0LWVuY29kZWQgY29udGVudCByZXR1cm5zLiBUaGUgY29kZSBhbHNvIGZlYXR1cmVzXHJcbiAqIGJlbmNobWFya2luZyBmb3IgcGVyZm9ybWFuY2UgbW9uaXRvcmluZy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBzdGFydEV4cG9ydCB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0QmFzZTY0LCBtZWFzdXJlVGltZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xyXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XHJcbiAgcG5nOiAnaW1hZ2UvcG5nJyxcclxuICBqcGVnOiAnaW1hZ2UvanBlZycsXHJcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcclxuICBwZGY6ICdhcHBsaWNhdGlvbi9wZGYnLFxyXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXHJcbn07XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgZXhwb3J0IHJlcXVlc3RzIGZyb20gdGhlIGNsaWVudC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0KHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0IGNvdW50aW5nIHRpbWUgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgcmVxdWVzdENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIEluIGNhc2UgdGhlIGNvbm5lY3Rpb24gaXMgY2xvc2VkLCBmb3JjZSB0byBhYm9ydCBmdXJ0aGVyIGFjdGlvbnNcclxuICAgIGxldCBjb25uZWN0aW9uQWJvcnRlZCA9IGZhbHNlO1xyXG4gICAgcmVxdWVzdC5zb2NrZXQub24oJ2Nsb3NlJywgKGhhZEVycm9ycykgPT4ge1xyXG4gICAgICBpZiAoaGFkRXJyb3JzKSB7XHJcbiAgICAgICAgY29ubmVjdGlvbkFib3J0ZWQgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIG9wdGlvbnMgcHJldmlvdXNseSB2YWxpZGF0ZWQgaW4gdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gICAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGlkXHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSByZXF1ZXN0T3B0aW9ucy5yZXF1ZXN0SWQ7XHJcblxyXG4gICAgLy8gSW5mbyBhYm91dCBhbiBpbmNvbWluZyByZXF1ZXN0IHdpdGggY29ycmVjdCBkYXRhXHJcbiAgICBsb2coNCwgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBHb3QgYW4gaW5jb21pbmcgSFRUUCByZXF1ZXN0LmApO1xyXG5cclxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQocmVxdWVzdE9wdGlvbnMsIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAvLyBSZW1vdmUgdGhlIGNsb3NlIGV2ZW50IGZyb20gdGhlIHNvY2tldFxyXG4gICAgICByZXF1ZXN0LnNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2Nsb3NlJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkLCBkbyBub3RoaW5nXHJcbiAgICAgIGlmIChjb25uZWN0aW9uQWJvcnRlZCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSBjbGllbnQgY2xvc2VkIHRoZSBjb25uZWN0aW9uIGJlZm9yZSB0aGUgY2hhcnQgZmluaXNoZWQgcHJvY2Vzc2luZy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGVycm9yLCBsb2cgaXQgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGRhdGEgaXMgbWlzc2luZywgbG9nIHRoZSBtZXNzYWdlIGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmICghZGF0YSB8fCAhZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBSZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fFxyXG4gICAgICAgICAgICByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCByZXN1bHQgaXMgJHtkYXRhLnJlc3VsdH0uYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVW5leHBlY3RlZCByZXR1cm4gb2YgdGhlIGV4cG9ydCByZXN1bHQgZnJvbSB0aGUgY2hhcnQgZ2VuZXJhdGlvbi4gUGxlYXNlIGNoZWNrIHlvdXIgcmVxdWVzdCBkYXRhLmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBSZXR1cm4gdGhlIHJlc3VsdCBpbiBhbiBhcHByb3ByaWF0ZSBmb3JtYXRcclxuICAgICAgaWYgKGRhdGEucmVzdWx0KSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzIHRvb2sgJHtyZXF1ZXN0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIGB0eXBlYCwgYGI2NGAsIGBub0Rvd25sb2FkYCwgYW5kIGBvdXRmaWxlYCBmcm9tIG9wdGlvbnNcclxuICAgICAgICBjb25zdCB7IHR5cGUsIGI2NCwgbm9Eb3dubG9hZCwgb3V0ZmlsZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgLy8gSWYgb25seSBCYXNlNjQgaXMgcmVxdWlyZWQsIHJldHVybiBpdFxyXG4gICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gU2V0IGNvcnJlY3QgY29udGVudCB0eXBlXHJcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVyKCdDb250ZW50LVR5cGUnLCByZXZlcnNlZE1pbWVbdHlwZV0gfHwgJ2ltYWdlL3BuZycpO1xyXG5cclxuICAgICAgICAvLyBEZWNpZGUgd2hldGhlciB0byBkb3dubG9hZCBvciBub3QgY2hhcnQgZmlsZVxyXG4gICAgICAgIGlmICghbm9Eb3dubG9hZCkge1xyXG4gICAgICAgICAgcmVzcG9uc2UuYXR0YWNobWVudChvdXRmaWxlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIElmIFNWRywgcmV0dXJuIHBsYWluIGNvbnRlbnQsIG90aGVyd2lzZSBhIGI2NCBzdHJpbmcgZnJvbSBhIGJ1ZmZlclxyXG4gICAgICAgIHJldHVybiB0eXBlID09PSAnc3ZnJ1xyXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGRhdGEucmVzdWx0KVxyXG4gICAgICAgICAgOiByZXNwb25zZS5zZW5kKEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JykpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBleHBvcnRgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4cG9ydFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGV4cG9ydFJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3RcclxuICAgKiBlbmRwb2ludC5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLycsIHJlcXVlc3RFeHBvcnQpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvOmZpbGVuYW1lJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgd2l0aFxyXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLzpmaWxlbmFtZScsIHJlcXVlc3RFeHBvcnQpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHNlcnZlciBoZWFsdGggbW9uaXRvcmluZywgaW5jbHVkaW5nXHJcbiAqIHVwdGltZSwgc3VjY2VzcyByYXRlcywgYW5kIG90aGVyIHNlcnZlciBzdGF0aXN0aWNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldFBvb2xTdGF0cywgZ2V0UG9vbEluZm9KU09OIH0gZnJvbSAnLi4vLi4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGFkZFRpbWVyIH0gZnJvbSAnLi4vLi4vdGltZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGdldE5ld0RhdGVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLy8gU2V0IHRoZSBzdGFydCBkYXRlIG9mIHRoZSBzZXJ2ZXJcclxuY29uc3Qgc2VydmVyU3RhcnRUaW1lID0gbmV3IERhdGUoKTtcclxuXHJcbi8vIEdldCB0aGUgYHBhY2thZ2UuanNvbmAgY29udGVudFxyXG5jb25zdCBwYWNrYWdlRmlsZSA9IEpTT04ucGFyc2UoXHJcbiAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuKTtcclxuXHJcbi8vIEFuIGFycmF5IGZvciBzdWNjZXNzIHJhdGUgcmF0aW9zXHJcbmNvbnN0IHN1Y2Nlc3NSYXRlcyA9IFtdO1xyXG5cclxuLy8gUmVjb3JkIGV2ZXJ5IG1pbnV0ZVxyXG5jb25zdCByZWNvcmRJbnRlcnZhbCA9IDYwICogMTAwMDtcclxuXHJcbi8vIDMwIG1pbnV0ZXNcclxuY29uc3Qgd2luZG93U2l6ZSA9IDMwO1xyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgbW92aW5nIGF2ZXJhZ2UgaW5kaWNhdG9yIGJhc2VkIG9uIHRoZSBkYXRhIGZyb20gdGhlIGBzdWNjZXNzUmF0ZXNgXHJcbiAqIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2VcclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gQSBtb3ZpbmcgYXZlcmFnZSBmb3Igc3VjY2VzcyByYXRpbyBvZiB0aGUgc2VydmVyIGV4cG9ydHMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpIHtcclxuICByZXR1cm4gc3VjY2Vzc1JhdGVzLnJlZHVjZSgoYSwgYikgPT4gYSArIGIsIDApIC8gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyB0aGUgaW50ZXJ2YWwgcmVzcG9uc2libGUgZm9yIGNhbGN1bGF0aW5nIGN1cnJlbnQgc3VjY2VzcyByYXRlIHJhdGlvXHJcbiAqIGFuZCBjb2xsZWN0cyByZWNvcmRzIHRvIHRoZSBgc3VjY2Vzc1JhdGVzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtOb2RlSlMuVGltZW91dH0gSWQgb2YgYW4gaW50ZXJ2YWwuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc3RhcnRTdWNjZXNzUmF0ZSgpIHtcclxuICByZXR1cm4gc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgIGNvbnN0IHN1Y2Nlc3NSYXRpbyA9XHJcbiAgICAgIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQgPT09IDBcclxuICAgICAgICA/IDFcclxuICAgICAgICA6IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDA7XHJcblxyXG4gICAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcclxuICAgIGlmIChzdWNjZXNzUmF0ZXMubGVuZ3RoID4gd2luZG93U2l6ZSkge1xyXG4gICAgICBzdWNjZXNzUmF0ZXMuc2hpZnQoKTtcclxuICAgIH1cclxuICB9LCByZWNvcmRJbnRlcnZhbCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgaGVhbHRoYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBoZWFsdGhSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBoZWFsdGhSb3V0ZXMoYXBwKSB7XHJcbiAgLy8gU3RhcnQgcHJvY2Vzc2luZyBzdWNjZXNzIHJhdGUgcmF0aW8gaW50ZXJ2YWwgYW5kIHNhdmUgaXRzIGlkIHRvIHRoZSBhcnJheVxyXG4gIC8vIGZvciB0aGUgZ3JhY2VmdWwgY2xlYXJpbmcgb24gc2h1dGRvd24gd2l0aCBpbmplY3RlZCBgYWRkVGltZXJgIGZ1bnRpb25cclxuICBhZGRUaW1lcihfc3RhcnRTdWNjZXNzUmF0ZSgpKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgR0VUICcvaGVhbHRoJyAtIEEgcm91dGUgZm9yIGdldHRpbmcgdGhlIGJhc2ljIHN0YXRzIG9mIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldCgnL2hlYWx0aCcsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbaGVhbHRoXSBSZXR1cm5pbmcgc2VydmVyIGhlYWx0aC4nKTtcclxuXHJcbiAgICAgIGNvbnN0IHN0YXRzID0gZ2V0UG9vbFN0YXRzKCk7XHJcbiAgICAgIGNvbnN0IHBlcmlvZCA9IHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbiAgICAgIGNvbnN0IG1vdmluZ0F2ZXJhZ2UgPSBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpO1xyXG5cclxuICAgICAgLy8gU2VuZCB0aGUgc2VydmVyJ3Mgc3RhdGlzdGljc1xyXG4gICAgICByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAvLyBTdGF0dXMgYW5kIHRpbWVzXHJcbiAgICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICAgIGJvb3RUaW1lOiBzZXJ2ZXJTdGFydFRpbWUsXHJcbiAgICAgICAgdXB0aW1lOiBgJHtNYXRoLmZsb29yKChnZXROZXdEYXRlVGltZSgpIC0gc2VydmVyU3RhcnRUaW1lLmdldFRpbWUoKSkgLyAxMDAwIC8gNjApfSBtaW51dGVzYCxcclxuXHJcbiAgICAgICAgLy8gVmVyc2lvbnNcclxuICAgICAgICBzZXJ2ZXJWZXJzaW9uOiBwYWNrYWdlRmlsZS52ZXJzaW9uLFxyXG4gICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG5cclxuICAgICAgICAvLyBFeHBvcnRzXHJcbiAgICAgICAgYXZlcmFnZUV4cG9ydFRpbWU6IHN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UsXHJcbiAgICAgICAgYXR0ZW1wdGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCxcclxuICAgICAgICBwZXJmb3JtZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzUGVyZm9ybWVkLFxyXG4gICAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNEcm9wcGVkLFxyXG4gICAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCAvIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQpICogMTAwLFxyXG5cclxuICAgICAgICAvLyBQb29sXHJcbiAgICAgICAgcG9vbDogZ2V0UG9vbEluZm9KU09OKCksXHJcblxyXG4gICAgICAgIC8vIE1vdmluZyBhdmVyYWdlXHJcbiAgICAgICAgcGVyaW9kLFxyXG4gICAgICAgIG1vdmluZ0F2ZXJhZ2UsXHJcbiAgICAgICAgbWVzc2FnZTpcclxuICAgICAgICAgIGlzTmFOKG1vdmluZ0F2ZXJhZ2UpIHx8ICFzdWNjZXNzUmF0ZXMubGVuZ3RoXHJcbiAgICAgICAgICAgID8gJ1RvbyBlYXJseSB0byByZXBvcnQuIE5vIGV4cG9ydHMgbWFkZSB5ZXQuIFBsZWFzZSBjaGVjayBiYWNrIHNvb24uJ1xyXG4gICAgICAgICAgICA6IGBMYXN0ICR7cGVyaW9kfSBtaW51dGVzIGhhZCBhIHN1Y2Nlc3MgcmF0ZSBvZiAke21vdmluZ0F2ZXJhZ2UudG9GaXhlZCgyKX0lLmAsXHJcblxyXG4gICAgICAgIC8vIFNWRyBhbmQgSlNPTiBleHBvcnRzXHJcbiAgICAgICAgc3ZnRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21TdmcsXHJcbiAgICAgICAganNvbkV4cG9ydHM6IHN0YXRzLmV4cG9ydHNGcm9tT3B0aW9ucyxcclxuICAgICAgICBzdmdFeHBvcnRzQXR0ZW1wdHM6IHN0YXRzLmV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMsXHJcbiAgICAgICAganNvbkV4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHNcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmluZyB0aGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyXHJcbiAqIHdoZW4gZW5hYmxlZC5cclxuICovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB1aWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdWlSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB1aVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy8nIC0gQSByb3V0ZSBmb3IgYSBVSSB3aGVuIGVuYWJsZWQgb24gdGhlIGV4cG9ydCBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldChnZXRPcHRpb25zKCkudWkucm91dGUgfHwgJy8nLCAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3VpXSBSZXR1cm5pbmcgVUkgZm9yIHRoZSBleHBvcnQuJyk7XHJcblxyXG4gICAgICByZXNwb25zZS5zZW5kRmlsZShqb2luKF9fZGlybmFtZSwgJ3B1YmxpYycsICdpbmRleC5odG1sJyksIHtcclxuICAgICAgICBhY2NlcHRSYW5nZXM6IGZhbHNlXHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHVwZGF0aW5nIHRoZSBIaWdoY2hhcnRzIHZlcnNpb25cclxuICogb24gdGhlIHNlcnZlciwgd2l0aCBhdXRoZW50aWNhdGlvbiBhbmQgdmFsaWRhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiwgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuLi8uLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB2ZXJzaW9uX2NoYW5nZWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHZlcnNpb25DaGFuZ2VSb3V0ZXMoYXBwKSB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnL3ZlcnNpb25fY2hhbmdlLzpuZXdWZXJzaW9uJyAtIEEgcm91dGUgZm9yIGNoYW5naW5nXHJcbiAgICogdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBvbiB0aGUgc2VydmVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nLCBhc3luYyAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3ZlcnNpb25dIENoYW5naW5nIEhpZ2hjaGFydHMgdmVyc2lvbi4nKTtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZGlyZWN0bHkgZnJvbSBlbnZzXHJcbiAgICAgIGNvbnN0IGFkbWluVG9rZW4gPSBlbnZzLkhJR0hDSEFSVFNfQURNSU5fVE9LRU47XHJcblxyXG4gICAgICAvLyBDaGVjayB0aGUgZXhpc3RlbmNlIG9mIHRoZSB0b2tlblxyXG4gICAgICBpZiAoIWFkbWluVG9rZW4gfHwgIWFkbWluVG9rZW4ubGVuZ3RoKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1t2ZXJzaW9uXSBUaGUgc2VydmVyIGlzIG5vdCBjb25maWd1cmVkIHRvIHBlcmZvcm0gcnVuLXRpbWUgdmVyc2lvbiBjaGFuZ2VzOiBISUdIQ0hBUlRTX0FETUlOX1RPS0VOIGlzIG5vdCBzZXQuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZnJvbSB0aGUgaGMtYXV0aCBoZWFkZXJcclxuICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXHJcbiAgICAgIGlmICghdG9rZW4gfHwgdG9rZW4gIT09IGFkbWluVG9rZW4pIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIEludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdmVyc2lvbnNcclxuICAgICAgbGV0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG4gICAgICBpZiAobmV3VmVyc2lvbikge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAvLyBVcGRhdGUgdmVyc2lvblxyXG4gICAgICAgICAgYXdhaXQgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbik7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgYFt2ZXJzaW9uXSBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgIDQwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbdmVyc2lvbl0gTm8gbmV3IHZlcnNpb24gc3VwcGxpZWQuJywgNDAwKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSB0aGF0IHNldHMgdXAgYW5kIG1hbmFnZXMgSFRUUCBhbmQgSFRUUFMgc2VydmVyc1xyXG4gKiBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci4gSXQgaGFuZGxlcyBzZXJ2ZXIgaW5pdGlhbGl6YXRpb24sXHJcbiAqIGNvbmZpZ3VyYXRpb24sIGVycm9yIGhhbmRsaW5nLCBtaWRkbGV3YXJlIHNldHVwLCByb3V0ZSBkZWZpbml0aW9uLCBhbmQgcmF0ZVxyXG4gKiBsaW1pdGluZy4gVGhlIG1vZHVsZSBleHBvcnRzIGZ1bmN0aW9ucyB0byBzdGFydCwgc3RvcCwgYW5kIG1hbmFnZSBzZXJ2ZXJcclxuICogaW5zdGFuY2VzLCBhcyB3ZWxsIGFzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBkZWZpbmluZyByb3V0ZXMgYW5kIGF0dGFjaGluZ1xyXG4gKiBtaWRkbGV3YXJlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZSB9IGZyb20gJ2ZzL3Byb21pc2VzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xyXG5cclxuaW1wb3J0IHsgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgZXJyb3JNaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvZXJyb3IuanMnO1xyXG5pbXBvcnQgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3JhdGVMaW1pdGluZy5qcyc7XHJcbmltcG9ydCB2YWxpZGF0aW9uTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3ZhbGlkYXRpb24uanMnO1xyXG5cclxuaW1wb3J0IGV4cG9ydFJvdXRlcyBmcm9tICcuL3JvdXRlcy9leHBvcnQuanMnO1xyXG5pbXBvcnQgaGVhbHRoUm91dGVzIGZyb20gJy4vcm91dGVzL2hlYWx0aC5qcyc7XHJcbmltcG9ydCB1aVJvdXRlcyBmcm9tICcuL3JvdXRlcy91aS5qcyc7XHJcbmltcG9ydCB2ZXJzaW9uQ2hhbmdlUm91dGVzIGZyb20gJy4vcm91dGVzL3ZlcnNpb25DaGFuZ2UuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBBcnJheSBvZiBhbiBhY3RpdmUgc2VydmVyc1xyXG5jb25zdCBhY3RpdmVTZXJ2ZXJzID0gbmV3IE1hcCgpO1xyXG5cclxuLy8gQ3JlYXRlIGV4cHJlc3MgYXBwXHJcbmNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gSFRUUCBhbmQvb3IgSFRUUFMgc2VydmVyIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLlxyXG4gKiBUaGUgYHNlcnZlck9wdGlvbnNgIG9iamVjdCBjb250YWlucyBzZXJ2ZXItcmVsYXRlZCBwcm9wZXJ0aWVzIChyZWZlclxyXG4gKiB0byB0aGUgYHNlcnZlcmAgc2VjdGlvbiBpbiB0aGUgYGxpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgZGV0YWlscykuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRTZXJ2ZXJcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlck9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIHRoZSBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBkZWZhdWx0XHJcbiAqIHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGVpdGhlclxyXG4gKiBub3QgZW5hYmxlZCBvciBubyB2YWxpZCBFeHByZXNzIGFwcCBpcyBmb3VuZCwgc2lnbmFsaW5nIHRoZSBlbmQgb2YgdGhlXHJcbiAqIGZ1bmN0aW9uJ3MgZXhlY3V0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIHNlcnZlciBjYW5ub3RcclxuICogYmUgY29uZmlndXJlZCBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFNlcnZlcihzZXJ2ZXJPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgc2VydmVyOiBzZXJ2ZXJPcHRpb25zXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBVc2UgdmFsaWRhdGVkIG9wdGlvbnNcclxuICAgIHNlcnZlck9wdGlvbnMgPSBvcHRpb25zLnNlcnZlcjtcclxuXHJcbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXHJcbiAgICBpZiAoIXNlcnZlck9wdGlvbnMuZW5hYmxlIHx8ICFhcHApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbc2VydmVyXSBTZXJ2ZXIgY2Fubm90IGJlIHN0YXJ0ZWQgKG5vdCBlbmFibGVkIG9yIG5vIGNvcnJlY3QgRXhwcmVzcyBhcHAgZm91bmQpLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVG9vIGJpZyBsaW1pdHMgbGVhZCB0byB0aW1lb3V0cyBpbiB0aGUgZXhwb3J0IHByb2Nlc3Mgd2hlblxyXG4gICAgLy8gdGhlIHJhc3Rlcml6YXRpb24gdGltZW91dCBpcyBzZXQgdG9vIGxvd1xyXG4gICAgY29uc3QgdXBsb2FkTGltaXRCeXRlcyA9IHNlcnZlck9wdGlvbnMudXBsb2FkTGltaXQgKiAxMDI0ICogMTAyNDtcclxuXHJcbiAgICAvLyBNZW1vcnkgc3RvcmFnZSBmb3IgbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHN0b3JhZ2UgPSBtdWx0ZXIubWVtb3J5U3RvcmFnZSgpO1xyXG5cclxuICAgIC8vIEVuYWJsZSBwYXJzaW5nIG9mIGZvcm0gZGF0YSAoZmlsZXMpIHdpdGggbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHVwbG9hZCA9IG11bHRlcih7XHJcbiAgICAgIHN0b3JhZ2UsXHJcbiAgICAgIGxpbWl0czoge1xyXG4gICAgICAgIGZpZWxkU2l6ZTogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXHJcbiAgICBhcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XHJcblxyXG4gICAgLy8gRW5hYmxlIENPUlMgc3VwcG9ydFxyXG4gICAgYXBwLnVzZShcclxuICAgICAgY29ycyh7XHJcbiAgICAgICAgbWV0aG9kczogWydQT1NUJywgJ0dFVCcsICdPUFRJT05TJ11cclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0dGluZyBhIGxvdCBvZiBgUmFuZ2VOb3RTYXRpc2ZpYWJsZUVycm9yYCBleGNlcHRpb25zIChldmVuIHRob3VnaCB0aGlzXHJcbiAgICAvLyBpcyBhIGRlcHJlY2F0ZWQgb3B0aW9ucywgbGV0J3MgdHJ5IHRvIHNldCBpdCB0byBmYWxzZSlcclxuICAgIGFwcC51c2UoKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgIHJlc3BvbnNlLnNldCgnQWNjZXB0LVJhbmdlcycsICdub25lJyk7XHJcbiAgICAgIG5leHQoKTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgSlNPTiBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLmpzb24oe1xyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgVVJMLWVuY29kZWQgZm9ybSBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLnVybGVuY29kZWQoe1xyXG4gICAgICAgIGV4dGVuZGVkOiB0cnVlLFxyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xyXG4gICAgYXBwLnVzZSh1cGxvYWQubm9uZSgpKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgc3RhdGljIGZvbGRlcidzIHJvdXRlXHJcbiAgICBhcHAudXNlKGV4cHJlc3Muc3RhdGljKGpvaW4oX19kaXJuYW1lLCAncHVibGljJykpKTtcclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5zc2wuZm9yY2UpIHtcclxuICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFApXHJcbiAgICAgIGNvbnN0IGh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApO1xyXG5cclxuICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIC8vIExpc3RlblxyXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnBvcnQsIHNlcnZlck9wdGlvbnMuaG9zdCwgKCkgPT4ge1xyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMucG9ydCwgaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnBvcnR9LmBcclxuICAgICAgICApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgICBpZiAoc2VydmVyT3B0aW9ucy5zc2wuZW5hYmxlKSB7XHJcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cclxuICAgICAgbGV0IGtleSwgY2VydDtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XHJcbiAgICAgICAga2V5ID0gYXdhaXQgcmVhZEZpbGUoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIua2V5JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBjZXJ0aWZpY2F0ZVxyXG4gICAgICAgIGNlcnQgPSBhd2FpdCByZWFkRmlsZShcclxuICAgICAgICAgIGpvaW4oZ2V0QWJzb2x1dGVQYXRoKHNlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRoKSwgJ3NlcnZlci5jcnQnKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgIGBbc2VydmVyXSBVbmFibGUgdG8gbG9hZCBrZXkvY2VydGlmaWNhdGUgZnJvbSB0aGUgJyR7c2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGh9JyBwYXRoLiBDb3VsZCBub3QgcnVuIHNlY3VyZWQgbGF5ZXIgc2VydmVyLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoa2V5ICYmIGNlcnQpIHtcclxuICAgICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUFMpXHJcbiAgICAgICAgY29uc3QgaHR0cHNTZXJ2ZXIgPSBodHRwcy5jcmVhdGVTZXJ2ZXIoeyBrZXksIGNlcnQgfSwgYXBwKTtcclxuXHJcbiAgICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICAgIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgLy8gTGlzdGVuXHJcbiAgICAgICAgaHR0cHNTZXJ2ZXIubGlzdGVuKHNlcnZlck9wdGlvbnMuc3NsLnBvcnQsIHNlcnZlck9wdGlvbnMuaG9zdCwgKCkgPT4ge1xyXG4gICAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFBTIHNlcnZlclxyXG4gICAgICAgICAgYWN0aXZlU2VydmVycy5zZXQoc2VydmVyT3B0aW9ucy5zc2wucG9ydCwgaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFtzZXJ2ZXJdIFN0YXJ0ZWQgSFRUUFMgc2VydmVyIG9uICR7c2VydmVyT3B0aW9ucy5ob3N0fToke3NlcnZlck9wdGlvbnMuc3NsLnBvcnR9LmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBTZXQgdXAgdGhlIHJhdGUgbGltaXRlclxyXG4gICAgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZShhcHAsIHNlcnZlck9wdGlvbnMucmF0ZUxpbWl0aW5nKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgdGhlIHZhbGlkYXRpb24gaGFuZGxlclxyXG4gICAgdmFsaWRhdGlvbk1pZGRsZXdhcmUoYXBwKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgcm91dGVzXHJcbiAgICBleHBvcnRSb3V0ZXMoYXBwKTtcclxuICAgIGhlYWx0aFJvdXRlcyhhcHApO1xyXG4gICAgdWlSb3V0ZXMoYXBwKTtcclxuICAgIHZlcnNpb25DaGFuZ2VSb3V0ZXMoYXBwKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgdGhlIGNlbnRyYWxpemVkIGVycm9yIGhhbmRsZXJcclxuICAgIGVycm9yTWlkZGxld2FyZShhcHApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbc2VydmVyXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBzdGFydCB0aGUgc2VydmVyLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIGFsbCBzZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGNsb3NlU2VydmVyc1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsb3NlU2VydmVycygpIHtcclxuICAvLyBDaGVjayBpZiB0aGVyZSBhcmUgc2VydmVycyB3b3JraW5nXHJcbiAgaWYgKGFjdGl2ZVNlcnZlcnMuc2l6ZSA+IDApIHtcclxuICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2luZyBhbGwgc2VydmVycy5gKTtcclxuXHJcbiAgICAvLyBDbG9zZSBlYWNoIG9uZSBvZiBzZXJ2ZXJzXHJcbiAgICBmb3IgKGNvbnN0IFtwb3J0LCBzZXJ2ZXJdIG9mIGFjdGl2ZVNlcnZlcnMpIHtcclxuICAgICAgc2VydmVyLmNsb3NlKCgpID0+IHtcclxuICAgICAgICBhY3RpdmVTZXJ2ZXJzLmRlbGV0ZShwb3J0KTtcclxuICAgICAgICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NlZCBzZXJ2ZXIgb24gcG9ydDogJHtwb3J0fS5gKTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogR2V0IGFsbCBzZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFNlcnZlcnNcclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxPYmplY3Q+fSBTZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRTZXJ2ZXJzKCkge1xyXG4gIHJldHVybiBhY3RpdmVTZXJ2ZXJzO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0RXhwcmVzc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7RXhwcmVzc30gVGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0RXhwcmVzcygpIHtcclxuICByZXR1cm4gZXhwcmVzcztcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBcHBcclxuICpcclxuICogQHJldHVybnMge0V4cHJlc3N9IFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBcHAoKSB7XHJcbiAgcmV0dXJuIGFwcDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZSByYXRlIGxpbWl0aW5nIGZvciB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlUmF0ZUxpbWl0aW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSByYXRlTGltaXRpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHJhdGVMaW1pdGluZ2Agb3B0aW9ucy4gVGhpcyBvYmplY3QgbWF5IGluY2x1ZGUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldFxyXG4gKiBvZiB0aGUgYHJhdGVMaW1pdGluZ2Agb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzXHJcbiAqIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVSYXRlTGltaXRpbmcocmF0ZUxpbWl0aW5nT3B0aW9ucykge1xyXG4gIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICBzZXJ2ZXI6IHtcclxuICAgICAgcmF0ZUxpbWl0aW5nOiByYXRlTGltaXRpbmdPcHRpb25zXHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgcmF0ZSBsaW1pdGluZyBvcHRpb25zXHJcbiAgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZShhcHAsIG9wdGlvbnMuc2VydmVyLnJhdGVMaW1pdGluZ09wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogQXBwbHkgbWlkZGxld2FyZShzKSB0byBhIHNwZWNpZmljIHBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1c2VcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHVzZShwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC51c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBHRVQgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAuZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggUE9TVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLnBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoIGVycm9yIGhhbmRsZXJzIHRvIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVyc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhodHRwLlNlcnZlcnxodHRwcy5TZXJ2ZXIpfSBzZXJ2ZXIgLSBUaGUgSFRUUC9IVFRQUyBzZXJ2ZXIgaW5zdGFuY2UuXHJcbiAqL1xyXG5mdW5jdGlvbiBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhzZXJ2ZXIpIHtcclxuICBzZXJ2ZXIub24oJ2NsaWVudEVycm9yJywgKGVycm9yLCBzb2NrZXQpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgMSxcclxuICAgICAgZXJyb3IsXHJcbiAgICAgIGBbc2VydmVyXSBDbGllbnQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX0sIGRlc3Ryb3lpbmcgc29ja2V0LmBcclxuICAgICk7XHJcbiAgICBzb2NrZXQuZGVzdHJveSgpO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTZXJ2ZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdjb25uZWN0aW9uJywgKHNvY2tldCkgPT4ge1xyXG4gICAgc29ja2V0Lm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTb2NrZXQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc3RhcnRTZXJ2ZXIsXHJcbiAgY2xvc2VTZXJ2ZXJzLFxyXG4gIGdldFNlcnZlcnMsXHJcbiAgZ2V0RXhwcmVzcyxcclxuICBnZXRBcHAsXHJcbiAgZW5hYmxlUmF0ZUxpbWl0aW5nLFxyXG4gIHVzZSxcclxuICBnZXQsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgSGFuZGxlcyBncmFjZWZ1bCBzaHV0ZG93biBvZiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLCBlbnN1cmluZ1xyXG4gKiBwcm9wZXIgY2xlYW51cCBvZiByZXNvdXJjZXMgc3VjaCBhcyBicm93c2VyLCBwYWdlcywgc2VydmVycywgYW5kIHRpbWVycy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBraWxsUG9vbCB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGNsZWFyQWxsVGltZXJzIH0gZnJvbSAnLi90aW1lci5qcyc7XHJcblxyXG5pbXBvcnQgeyBjbG9zZVNlcnZlcnMgfSBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIFBlcmZvcm1zIGNsZWFudXAgb3BlcmF0aW9ucyB0byBlbnN1cmUgYSBncmFjZWZ1bCBzaHV0ZG93biBvZiB0aGUgcHJvY2Vzcy5cclxuICogVGhpcyBpbmNsdWRlcyBjbGVhcmluZyBhbGwgcmVnaXN0ZXJlZCB0aW1lb3V0cy9pbnRlcnZhbHMsIGNsb3NpbmcgYWN0aXZlXHJcbiAqIHNlcnZlcnMsIHRlcm1pbmF0aW5nIHJlc291cmNlcyAocGFnZXMpIG9mIHRoZSBwb29sLCBwb29sIGl0c2VsZiwgYW5kIGNsb3NpbmdcclxuICogdGhlIGJyb3dzZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXBcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IFtleGl0Q29kZT0wXSAtIFRoZSBleGl0IGNvZGUgdG8gdXNlIHdpdGggYHByb2Nlc3MuZXhpdCgpYC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYDBgLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNodXRkb3duQ2xlYW5VcChleGl0Q29kZSA9IDApIHtcclxuICAvLyBBd2FpdCBmcmVlaW5nIGFsbCByZXNvdXJjZXNcclxuICBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoW1xyXG4gICAgLy8gQ2xlYXIgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzXHJcbiAgICBjbGVhckFsbFRpbWVycygpLFxyXG5cclxuICAgIC8vIEdldCBhdmFpbGFibGUgc2VydmVyIGluc3RhbmNlcyAoSFRUUC9IVFRQUykgYW5kIGNsb3NlIHRoZW1cclxuICAgIGNsb3NlU2VydmVycygpLFxyXG5cclxuICAgIC8vIENsb3NlIGFuIGFjdGl2ZSBwb29sIGFsb25nIHdpdGggaXRzIHdvcmtlcnMgYW5kIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgICBraWxsUG9vbCgpXHJcbiAgXSk7XHJcblxyXG4gIC8vIEV4aXQgcHJvY2VzcyB3aXRoIGEgY29ycmVjdCBjb2RlXHJcbiAgcHJvY2Vzcy5leGl0KGV4aXRDb2RlKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNodXRkb3duQ2xlYW5VcFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29yZSBtb2R1bGUgZm9yIGluaXRpYWxpemluZyBhbmQgbWFuYWdpbmcgdGhlIEhpZ2hjaGFydHMgRXhwb3J0XHJcbiAqIFNlcnZlci4gUHJvdmlkZXMgZnVuY3Rpb25hbGl0aWVzIGZvciBjb25maWd1cmluZyBleHBvcnRzLCBzZXR0aW5nIHVwIHNlcnZlclxyXG4gKiBvcGVyYXRpb25zLCBsb2dnaW5nLCBzY3JpcHRzIGNhY2hpbmcsIHJlc291cmNlIHBvb2xpbmcsIGFuZCBncmFjZWZ1bCBwcm9jZXNzXHJcbiAqIGNsZWFudXAuXHJcbiAqL1xyXG5cclxuaW1wb3J0ICdjb2xvcnMnO1xyXG5cclxuaW1wb3J0IHsgY2hlY2tBbmRVcGRhdGVDYWNoZSB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQge1xyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBiYXRjaEV4cG9ydCxcclxuICBzdGFydEV4cG9ydCxcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxufSBmcm9tICcuL2NoYXJ0LmpzJztcclxuaW1wb3J0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHVwZGF0ZU9wdGlvbnMsXHJcbiAgc2V0R2xvYmFsT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnNcclxufSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbFxyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2h1dGRvd25DbGVhblVwIH0gZnJvbSAnLi9yZXNvdXJjZVJlbGVhc2UuanMnO1xyXG5cclxuaW1wb3J0IHNlcnZlciBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiB0aGUgY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHJlc291cmNlIHBvb2wgb2NjdXIgZHVyaW5nIHRoaXNcclxuICogc3RhZ2UuXHJcbiAqXHJcbiAqIFRoaXMgZnVuY3Rpb24gbXVzdCBiZSBjYWxsZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXRcclxuICogdXAgYSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2luaXRPcHRpb25zPXt9XSAtIFRoZSBgaW5pdE9wdGlvbnNgIG9iamVjdCwgd2hpY2ggbWF5XHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmdcclxuICogdmFsdWVzIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRFeHBvcnQoaW5pdE9wdGlvbnMgPSB7fSkge1xyXG4gIC8vIEluaXQgYW5kIHVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyhpbml0T3B0aW9ucywgdHJ1ZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcblxyXG4gIC8vIEluaXQgdGhlIHBvb2xcclxuICBhd2FpdCBpbml0UG9vbChvcHRpb25zLnBvb2wsIG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoZXMgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MsIGVuc3VyaW5nIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlc1xyXG4gKiBhbmQgdGVybWluYXRpb24gb24gZXhpdCBzaWduYWxzLiBIYW5kbGVzICdleGl0JywgJ1NJR0lOVCcsICdTSUdURVJNJ1xyXG4gKiBhbmQgJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnNcclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LmApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0lOVCdcclxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHVEVSTSdcclxuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0hVUCdcclxuICBwcm9jZXNzLm9uKCdTSUdIVVAnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAndW5jYXVnaHRFeGNlcHRpb24nXHJcbiAgcHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCBhc3luYyAoZXJyb3IsIG5hbWUpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIC4uLnNlcnZlcixcclxuXHJcbiAgLy8gT3B0aW9uc1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgc2V0R2xvYmFsT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcblxyXG4gIC8vIEV4cG9ydGluZ1xyXG4gIGluaXRFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG5cclxuICAvLyBSZWxlYXNlXHJcbiAga2lsbFBvb2wsXHJcbiAgc2h1dGRvd25DbGVhblVwLFxyXG5cclxuICAvLyBMb2dzXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbDogZnVuY3Rpb24gKGxldmVsKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICBsZXZlbFxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgc2V0TG9nTGV2ZWwob3B0aW9ucy5sb2dnaW5nLmxldmVsKTtcclxuICB9LFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nOiBmdW5jdGlvbiAodG9Db25zb2xlKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICB0b0NvbnNvbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUNvbnNvbGVMb2dnaW5nKG9wdGlvbnMubG9nZ2luZy50b0NvbnNvbGUpO1xyXG4gIH0sXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmc6IGZ1bmN0aW9uIChkZXN0LCBmaWxlLCB0b0ZpbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGRlc3QsXHJcbiAgICAgICAgZmlsZSxcclxuICAgICAgICB0b0ZpbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUZpbGVMb2dnaW5nKFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZGVzdCxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLmZpbGUsXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy50b0ZpbGVcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG4iXSwibmFtZXMiOlsiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJiYXNlVVJJIiwiZGVlcENvcHkiLCJvYmpBcnIiLCJvYmpBcnJDb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwiT2JqZWN0IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiZml4Q29uc3RyIiwiY29uc3RyIiwiZml4ZWRDb25zdHIiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJpbmNsdWRlcyIsImZpeE91dGZpbGUiLCJ0eXBlIiwib3V0ZmlsZSIsImdldEFic29sdXRlUGF0aCIsInNwbGl0Iiwic2hpZnQiLCJmaXhUeXBlIiwibWltZVR5cGVzIiwiZm9ybWF0cyIsInZhbHVlcyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsInBhdGgiLCJpc0Fic29sdXRlIiwiam9pbiIsImdldEJhc2U2NCIsImlucHV0IiwiQnVmZmVyIiwiZnJvbSIsInRvU3RyaW5nIiwiZ2V0TmV3RGF0ZSIsIkRhdGUiLCJ0cmltIiwiZ2V0TmV3RGF0ZVRpbWUiLCJnZXRUaW1lIiwiaXNPYmplY3QiLCJpdGVtIiwiaXNPYmplY3RFbXB0eSIsImtleXMiLCJsZW5ndGgiLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwic29tZSIsInBhdHRlcm4iLCJ0ZXN0IiwibWVhc3VyZVRpbWUiLCJzdGFydCIsInByb2Nlc3MiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJyb3VuZE51bWJlciIsInZhbHVlIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsIk1hdGgiLCJwb3ciLCJyb3VuZCIsIndyYXBBcm91bmQiLCJjdXN0b21Db2RlIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiaXNDYWxsYmFjayIsImVuZHNXaXRoIiwicmVhZEZpbGVTeW5jIiwic3RhcnRzV2l0aCIsImNvbG9ycyIsImxvZ2dpbmciLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsInBhdGhUb0xvZyIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibG9nIiwiYXJncyIsIm5ld0xldmVsIiwidGV4dHMiLCJsZXZlbCIsInByZWZpeCIsIl9sb2dUb0ZpbGUiLCJjb25zb2xlIiwiYXBwbHkiLCJ1bmRlZmluZWQiLCJjb25jYXQiLCJsb2dXaXRoU3RhY2siLCJlcnJvciIsImN1c3RvbU1lc3NhZ2UiLCJtYWluTWVzc2FnZSIsIm1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInB1c2giLCJpbml0TG9nZ2luZyIsImxvZ2dpbmdPcHRpb25zIiwiZGVzdCIsImZpbGUiLCJzZXRMb2dMZXZlbCIsImVuYWJsZUNvbnNvbGVMb2dnaW5nIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJpc0ludGVnZXIiLCJleGlzdHNTeW5jIiwibWtkaXJTeW5jIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiRXhwb3J0RXJyb3IiLCJFcnJvciIsImNvbnN0cnVjdG9yIiwic3RhdHVzQ29kZSIsInN1cGVyIiwidGhpcyIsInNldFN0YXR1cyIsInNldEVycm9yIiwibmFtZSIsIl9pbml0R2xvYmFsT3B0aW9ucyIsImluc3RhbmNlT3B0aW9ucyIsImdldE9wdGlvbnMiLCJnZXRJbnN0YW5jZSIsInNldEdsb2JhbE9wdGlvbnMiLCJjdXN0b21PcHRpb25zIiwiY2xpQXJncyIsImNvbmZpZ09wdGlvbnMiLCJjbGlPcHRpb25zIiwiX2xvYWRDb25maWdGaWxlIiwiX3BhaXJBcmd1bWVudFZhbHVlIiwiX3VwZGF0ZUdsb2JhbE9wdGlvbnMiLCJ1cGRhdGVPcHRpb25zIiwibmV3SW5zdGFuY2UiLCJtZXJnZU9wdGlvbnMiLCJvcmlnaW5hbE9wdGlvbnMiLCJuZXdPcHRpb25zIiwiZW50cmllcyIsIm1hcFRvTmV3T3B0aW9ucyIsIm9sZE9wdGlvbnMiLCJwcm9wZXJ0aWVzQ2hhaW4iLCJyZWR1Y2UiLCJvYmoiLCJwcm9wIiwiaW5kZXgiLCJpc0FsbG93ZWRDb25maWciLCJhbGxvd0Z1bmN0aW9ucyIsIm9iamVjdENvbmZpZyIsImV2YWwiLCJKU09OIiwic3RyaW5naWZpZWRPcHRpb25zIiwiX29wdGlvbnNTdHJpbmdpZnkiLCJwYXJzZWRPcHRpb25zIiwiXyIsImVudlZhbCIsImNvbmZpZ09wdCIsImNsaU9wdCIsImN1c3RvbU9wdCIsImNvbmZpZ1ZhbCIsImNsaVZhbCIsImN1c3RvbVZhbCIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJjdXN0b21Mb2dpY09wdGlvbnMiLCJjb25maWdJbmRleCIsImZpbmRJbmRleCIsImFyZyIsImNvbmZpZ0ZpbGVOYW1lIiwiaSIsIm9wdGlvbiIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwiX2dldFByb3RvY29sTW9kdWxlIiwiZ2V0IiwicmVzcG9uc2UiLCJyZXNwb25zZURhdGEiLCJvbiIsImNodW5rIiwidGV4dCIsImh0dHBzIiwiaHR0cCIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsInNldHVwSGlnaGNoYXJ0cyIsIkhpZ2hjaGFydHMiLCJhbmltT2JqZWN0IiwiZHVyYXRpb24iLCJjcmVhdGVDaGFydCIsImV4cG9ydE9wdGlvbnMiLCJzZXRPcHRpb25zIiwibWVyZ2UiLCJ3cmFwIiwic2V0T3B0aW9uc09iaiIsImlzUmVuZGVyQ29tcGxldGUiLCJDaGFydCIsInByb2NlZWQiLCJ1c2VyT3B0aW9ucyIsImNiIiwiZXhwb3J0aW5nIiwiZW5hYmxlZCIsInBsb3RPcHRpb25zIiwic2VyaWVzIiwibGFiZWwiLCJ0b29sdGlwIiwiYW5pbWF0aW9uIiwib25IaWdoY2hhcnRzUmVuZGVyIiwiYWRkRXZlbnQiLCJTZXJpZXMiLCJjaGFydCIsImFkZGl0aW9uYWxPcHRpb25zIiwiRnVuY3Rpb24iLCJmaW5hbE9wdGlvbnMiLCJmaW5hbENhbGxiYWNrIiwiZGVmYXVsdE9wdGlvbnMiLCJ0ZW1wbGF0ZSIsImJyb3dzZXIiLCJjcmVhdGVCcm93c2VyIiwicHVwcGV0ZWVyQXJncyIsImVuYWJsZWREZWJ1ZyIsImRlYnVnT3B0aW9ucyIsImxhdW5jaE9wdGlvbnMiLCJ1c2VyRGF0YURpciIsImhhbmRsZVNJR0lOVCIsImhhbmRsZVNJR1RFUk0iLCJoYW5kbGVTSUdIVVAiLCJ3YWl0Rm9ySW5pdGlhbFBhZ2UiLCJkZWZhdWx0Vmlld3BvcnQiLCJ0cnlDb3VudCIsIm9wZW4iLCJsYXVuY2giLCJzZXRUaW1lb3V0IiwiY2xvc2VCcm93c2VyIiwiY29ubmVjdGVkIiwiY2xvc2UiLCJuZXdQYWdlIiwicG9vbFJlc291cmNlIiwicGFnZSIsInNldENhY2hlRW5hYmxlZCIsIl9zZXRQYWdlQ29udGVudCIsIl9zZXRQYWdlRXZlbnRzIiwiaXNDbG9zZWQiLCJjbGVhclBhZ2UiLCJoYXJkUmVzZXQiLCJnb3RvIiwid2FpdFVudGlsIiwiZXZhbHVhdGUiLCJib2R5IiwiaW5uZXJIVE1MIiwiaWQiLCJ3b3JrQ291bnQiLCJhZGRQYWdlUmVzb3VyY2VzIiwiaW5qZWN0ZWRSZXNvdXJjZXMiLCJpbmplY3RlZEpzIiwianMiLCJjb250ZW50IiwiZmlsZXMiLCJpc0xvY2FsIiwianNSZXNvdXJjZSIsImFkZFNjcmlwdFRhZyIsImluamVjdGVkQ3NzIiwiY3NzIiwiY3NzSW1wb3J0cyIsIm1hdGNoIiwiY3NzSW1wb3J0UGF0aCIsImNzc1Jlc291cmNlIiwiYWRkU3R5bGVUYWciLCJjbGVhclBhZ2VSZXNvdXJjZXMiLCJyZXNvdXJjZSIsImRpc3Bvc2UiLCJvbGRDaGFydHMiLCJjaGFydHMiLCJvbGRDaGFydCIsImRlc3Ryb3kiLCJzY3JpcHRzVG9SZW1vdmUiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsInN0eWxlc1RvUmVtb3ZlIiwibGlua3NUb1JlbW92ZSIsImVsZW1lbnQiLCJyZW1vdmUiLCJzZXRDb250ZW50IiwiY3NzVGVtcGxhdGUiLCJzdmdUZW1wbGF0ZSIsInB1cHBldGVlckV4cG9ydCIsImlzU1ZHIiwic2l6ZSIsInN2Z0VsZW1lbnQiLCJxdWVyeVNlbGVjdG9yIiwiY2hhcnRIZWlnaHQiLCJiYXNlVmFsIiwiY2hhcnRXaWR0aCIsInN0eWxlIiwiem9vbSIsIm1hcmdpbiIsIngiLCJ5IiwiX2dldENsaXBSZWdpb24iLCJ2aWV3cG9ydEhlaWdodCIsImFicyIsImNlaWwiLCJ2aWV3cG9ydFdpZHRoIiwicmVzdWx0Iiwic2V0Vmlld3BvcnQiLCJkZXZpY2VTY2FsZUZhY3RvciIsIl9jcmVhdGVTVkciLCJfY3JlYXRlSW1hZ2UiLCJfY3JlYXRlUERGIiwiJGV2YWwiLCJnZXRCb3VuZGluZ0NsaWVudFJlY3QiLCJ0cnVuYyIsIm91dGVySFRNTCIsImNsaXAiLCJyYWNlIiwic2NyZWVuc2hvdCIsImVuY29kaW5nIiwiZnVsbFBhZ2UiLCJvcHRpbWl6ZUZvclNwZWVkIiwiY2FwdHVyZUJleW9uZFZpZXdwb3J0IiwicXVhbGl0eSIsIm9taXRCYWNrZ3JvdW5kIiwiX3Jlc29sdmUiLCJlbXVsYXRlTWVkaWFUeXBlIiwicGRmIiwicG9vbFN0YXRzIiwiZXhwb3J0c0F0dGVtcHRlZCIsImV4cG9ydHNQZXJmb3JtZWQiLCJleHBvcnRzRHJvcHBlZCIsImV4cG9ydHNGcm9tU3ZnIiwiZXhwb3J0c0Zyb21PcHRpb25zIiwiZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyIsImV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzIiwidGltZVNwZW50IiwidGltZVNwZW50QXZlcmFnZSIsImluaXRQb29sIiwicG9vbE9wdGlvbnMiLCJQb29sIiwiX2ZhY3RvcnkiLCJhY3F1aXJlVGltZW91dE1pbGxpcyIsImNyZWF0ZVRpbWVvdXRNaWxsaXMiLCJkZXN0cm95VGltZW91dE1pbGxpcyIsImlkbGVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpcyIsInJlYXBJbnRlcnZhbE1pbGxpcyIsInByb3BhZ2F0ZUNyZWF0ZUVycm9yIiwiY2xlYXJTdGF0dXMiLCJfZXZlbnRJZCIsImluaXRpYWxSZXNvdXJjZXMiLCJhY3F1aXJlIiwicHJvbWlzZSIsInJlbGVhc2UiLCJraWxsUG9vbCIsIndvcmtlciIsInVzZWQiLCJkZXN0cm95ZWQiLCJwb3N0V29yayIsIndvcmtlckhhbmRsZSIsImdldFBvb2xJbmZvIiwiYWNxdWlyZUNvdW50ZXIiLCJyZXF1ZXN0SWQiLCJ3b3JrU3RhcnQiLCJleHBvcnRDb3VudGVyIiwiZXhwb3J0VGltZSIsImdldFBvb2xTdGF0cyIsImdldFBvb2xJbmZvSlNPTiIsIm51bVVzZWQiLCJhdmFpbGFibGUiLCJudW1GcmVlIiwiYWxsQ3JlYXRlZCIsInBlbmRpbmdBY3F1aXJlcyIsIm51bVBlbmRpbmdBY3F1aXJlcyIsInBlbmRpbmdDcmVhdGVzIiwibnVtUGVuZGluZ0NyZWF0ZXMiLCJwZW5kaW5nVmFsaWRhdGlvbnMiLCJudW1QZW5kaW5nVmFsaWRhdGlvbnMiLCJwZW5kaW5nRGVzdHJveXMiLCJhYnNvbHV0ZUFsbCIsImNyZWF0ZSIsInV1aWQiLCJyYW5kb20iLCJzdGFydERhdGUiLCJ2YWxpZGF0ZSIsIm1haW5GcmFtZSIsImRldGFjaGVkIiwicmVtb3ZlQWxsTGlzdGVuZXJzIiwic2FuaXRpemUiLCJKU0RPTSIsIkRPTVB1cmlmeSIsIkFERF9UQUdTIiwic2luZ2xlRXhwb3J0Iiwic3RhcnRFeHBvcnQiLCJkYXRhIiwiYmF0Y2hFeHBvcnQiLCJiYXRjaEZ1bmN0aW9ucyIsInBhaXIiLCJiYXRjaFJlc3VsdHMiLCJhbGxTZXR0bGVkIiwicmVhc29uIiwiZXhwb3J0aW5nT3B0aW9ucyIsImVuZENhbGxiYWNrIiwiZmlsZUNvbnRlbnQiLCJfZXhwb3J0RnJvbVN2ZyIsIl9leHBvcnRGcm9tT3B0aW9ucyIsImdldEFsbG93Q29kZUV4ZWN1dGlvbiIsInNldEFsbG93Q29kZUV4ZWN1dGlvbiIsImlucHV0VG9FeHBvcnQiLCJfcHJlcGFyZUV4cG9ydCIsIl9oYW5kbGVDdXN0b21Mb2dpYyIsIl9oYW5kbGVHbG9iYWxBbmRUaGVtZSIsIl9maW5kQ2hhcnRTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidGltZXJJZHMiLCJhZGRUaW1lciIsImNsZWFyQWxsVGltZXJzIiwiY2xlYXJJbnRlcnZhbCIsImNsZWFyVGltZW91dCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcXVlc3QiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RhdHVzIiwianNvbiIsImVycm9yTWlkZGxld2FyZSIsImFwcCIsInVzZSIsInJhdGVMaW1pdGluZ01pZGRsZXdhcmUiLCJyYXRlTGltaXRpbmdPcHRpb25zIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwicmF0ZUxpbWl0Iiwid2luZG93TXMiLCJsaW1pdCIsImRlbGF5TXMiLCJoYW5kbGVyIiwiZm9ybWF0Iiwic2VuZCIsImRlZmF1bHQiLCJza2lwIiwicXVlcnkiLCJhY2Nlc3NfdG9rZW4iLCJjb250ZW50VHlwZU1pZGRsZXdhcmUiLCJjb250ZW50VHlwZSIsImhlYWRlcnMiLCJyZXF1ZXN0Qm9keU1pZGRsZXdhcmUiLCJjb25uZWN0aW9uIiwicmVtb3RlQWRkcmVzcyIsInZhbGlkYXRlZE9wdGlvbnMiLCJwYXJhbXMiLCJmaWxlbmFtZSIsInZhbGlkYXRpb25NaWRkbGV3YXJlIiwicG9zdCIsInJldmVyc2VkTWltZSIsInBuZyIsImpwZWciLCJnaWYiLCJyZXF1ZXN0RXhwb3J0IiwicmVxdWVzdENvdW50ZXIiLCJjb25uZWN0aW9uQWJvcnRlZCIsInNvY2tldCIsImhhZEVycm9ycyIsImhlYWRlciIsImF0dGFjaG1lbnQiLCJleHBvcnRSb3V0ZXMiLCJzZXJ2ZXJTdGFydFRpbWUiLCJwYWNrYWdlRmlsZSIsInN1Y2Nlc3NSYXRlcyIsInJlY29yZEludGVydmFsIiwid2luZG93U2l6ZSIsIl9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlIiwiYSIsImIiLCJfc3RhcnRTdWNjZXNzUmF0ZSIsInNldEludGVydmFsIiwic3RhdHMiLCJzdWNjZXNzUmF0aW8iLCJoZWFsdGhSb3V0ZXMiLCJwZXJpb2QiLCJtb3ZpbmdBdmVyYWdlIiwiYm9vdFRpbWUiLCJ1cHRpbWUiLCJmbG9vciIsInNlcnZlclZlcnNpb24iLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VFeHBvcnRUaW1lIiwiYXR0ZW1wdGVkRXhwb3J0cyIsInBlcmZvcm1lZEV4cG9ydHMiLCJmYWlsZWRFeHBvcnRzIiwic3VjZXNzUmF0aW8iLCJ0b0ZpeGVkIiwic3ZnRXhwb3J0cyIsImpzb25FeHBvcnRzIiwic3ZnRXhwb3J0c0F0dGVtcHRzIiwianNvbkV4cG9ydHNBdHRlbXB0cyIsInVpUm91dGVzIiwic2VuZEZpbGUiLCJhY2NlcHRSYW5nZXMiLCJ2ZXJzaW9uQ2hhbmdlUm91dGVzIiwiYWRtaW5Ub2tlbiIsInRva2VuIiwiYWN0aXZlU2VydmVycyIsIk1hcCIsImV4cHJlc3MiLCJzdGFydFNlcnZlciIsInNlcnZlck9wdGlvbnMiLCJ1cGxvYWRMaW1pdEJ5dGVzIiwic3RvcmFnZSIsIm11bHRlciIsIm1lbW9yeVN0b3JhZ2UiLCJ1cGxvYWQiLCJsaW1pdHMiLCJmaWVsZFNpemUiLCJkaXNhYmxlIiwiY29ycyIsIm1ldGhvZHMiLCJzZXQiLCJ1cmxlbmNvZGVkIiwiZXh0ZW5kZWQiLCJub25lIiwic3RhdGljIiwiaHR0cFNlcnZlciIsImNyZWF0ZVNlcnZlciIsIl9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwibGlzdGVuIiwiY2VydCIsInJlYWRGaWxlIiwiaHR0cHNTZXJ2ZXIiLCJjbG9zZVNlcnZlcnMiLCJkZWxldGUiLCJnZXRTZXJ2ZXJzIiwiZ2V0RXhwcmVzcyIsImdldEFwcCIsImVuYWJsZVJhdGVMaW1pdGluZyIsIm1pZGRsZXdhcmVzIiwic2h1dGRvd25DbGVhblVwIiwiZXhpdENvZGUiLCJleGl0IiwiaW5pdEV4cG9ydCIsImluaXRPcHRpb25zIiwiX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzIiwiY29kZSJdLCJtYXBwaW5ncyI6InduQkEyQk8sTUFBTUEsWUFBWUMsSUFBYUEsY0FBQyxJQUFJQyxJQUFJLE9BQVEsb0JBQUFDLFNBQUFDLFFBQUEsT0FBQUMsY0FBQUMsWUFBQUMsS0FBQUMsd0JBQUEsV0FBQUEsdUJBQUFDLFFBQUFDLGVBQUFGLHVCQUFBRyxLQUFBLElBQUFULElBQUEsWUFBQUMsU0FBQVMsU0FBQUwsT0ErQmhELFNBQVNNLFNBQVNDLEdBRXZCLEdBQWUsT0FBWEEsR0FBcUMsaUJBQVhBLEVBQzVCLE9BQU9BLEVBSVQsTUFBTUMsRUFBYUMsTUFBTUMsUUFBUUgsR0FBVSxHQUFLLEdBR2hELElBQUssTUFBTUksS0FBT0osRUFDWkssT0FBT0MsVUFBVUMsZUFBZUMsS0FBS1IsRUFBUUksS0FDL0NILEVBQVdHLEdBQU9MLFNBQVNDLEVBQU9JLEtBS3RDLE9BQU9ILENBQ1QsQ0EyRE8sU0FBU1EsVUFBVUMsR0FDeEIsSUFFRSxNQUFNQyxFQUFjLEdBQUdELEVBQU9FLGNBQWNDLFFBQVEsUUFBUyxXQVE3RCxNQUxvQixVQUFoQkYsR0FDRkEsRUFBWUMsY0FJUCxDQUFDLFFBQVMsYUFBYyxXQUFZLGNBQWNFLFNBQ3ZESCxHQUVFQSxFQUNBLE9BQ1IsQ0FBSSxNQUVBLE1BQU8sT0FDUixDQUNILENBWU8sU0FBU0ksV0FBV0MsRUFBTUMsR0FPL0IsTUFBTyxHQUxVQyxnQkFBZ0JELEdBQVcsU0FDekNFLE1BQU0sS0FDTkMsV0FHbUJKLEdBQ3hCLENBYU8sU0FBU0ssUUFBUUwsRUFBTUMsRUFBVSxNQUV0QyxNQUFNSyxFQUFZLENBQ2hCLFlBQWEsTUFDYixhQUFjLE9BQ2Qsa0JBQW1CLE1BQ25CLGdCQUFpQixPQUliQyxFQUFVbEIsT0FBT21CLE9BQU9GLEdBRzlCLEdBQUlMLEVBQVMsQ0FDWCxNQUFNUSxFQUFVUixFQUFRRSxNQUFNLEtBQUtPLE1BR25CLFFBQVpELEVBQ0ZULEVBQU8sT0FDRU8sRUFBUVQsU0FBU1csSUFBWVQsSUFBU1MsSUFDL0NULEVBQU9TLEVBRVYsQ0FHRCxPQUFPSCxFQUFVTixJQUFTTyxFQUFRSSxNQUFNQyxHQUFNQSxJQUFNWixLQUFTLEtBQy9ELENBWU8sU0FBU0UsZ0JBQWdCVyxHQUM5QixPQUFPQyxLQUFBQSxXQUFXRCxHQUFRQSxFQUFPRSxLQUFBQSxLQUFLN0MsWUFBVzJDLEVBQ25ELENBWU8sU0FBU0csVUFBVUMsRUFBT2pCLEdBRS9CLE1BQWEsUUFBVEEsR0FBMEIsT0FBUkEsRUFDYmtCLE9BQU9DLEtBQUtGLEVBQU8sUUFBUUcsU0FBUyxVQUl0Q0gsQ0FDVCxDQU9PLFNBQVNJLGFBRWQsT0FBTyxJQUFJQyxNQUFPRixXQUFXakIsTUFBTSxLQUFLLEdBQUdvQixNQUM3QyxDQU9PLFNBQVNDLGlCQUNkLE9BQU8sSUFBSUYsTUFBT0csU0FDcEIsQ0FXTyxTQUFTQyxTQUFTQyxHQUN2QixNQUFnRCxvQkFBekN0QyxPQUFPQyxVQUFVOEIsU0FBUzVCLEtBQUttQyxFQUN4QyxDQVdPLFNBQVNDLGNBQWNELEdBQzVCLE1BQ2tCLGlCQUFUQSxJQUNOekMsTUFBTUMsUUFBUXdDLElBQ04sT0FBVEEsR0FDNkIsSUFBN0J0QyxPQUFPd0MsS0FBS0YsR0FBTUcsTUFFdEIsQ0FXTyxTQUFTQyx1QkFBdUJKLEdBU3JDLE1BUnNCLENBQ3BCLG1EQUNBLHVFQUNBLHdFQUNBLHVGQUNBLHFFQUdtQkssTUFBTUMsR0FBWUEsRUFBUUMsS0FBS1AsSUFDdEQsQ0FTTyxTQUFTUSxjQUNkLE1BQU1DLEVBQVFDLFFBQVFDLE9BQU9DLFNBQzdCLE1BQU8sSUFBTUMsT0FBT0gsUUFBUUMsT0FBT0MsU0FBV0gsR0FBUyxHQUN6RCxDQVlPLFNBQVNLLFlBQVlDLEVBQU9DLEVBQVksR0FDN0MsTUFBTUMsRUFBYUMsS0FBS0MsSUFBSSxHQUFJSCxHQUFhLEdBQzdDLE9BQU9FLEtBQUtFLE9BQU9MLEVBQVFFLEdBQWNBLENBQzNDLENBNkJPLFNBQVNJLFdBQVdDLEVBQVlDLEVBQW9CQyxHQUFhLEdBQ3RFLEdBQUlGLEdBQW9DLGlCQUFmQSxFQUd2QixPQUZBQSxFQUFhQSxFQUFXMUIsUUFFVDZCLFNBQVMsT0FFZkYsRUFDSEYsV0FDRUssR0FBQUEsYUFBYW5ELGdCQUFnQitDLEdBQWEsUUFDMUNDLEVBQ0FDLEdBRUYsTUFFSEEsSUFDQUYsRUFBV0ssV0FBVyxlQUNyQkwsRUFBV0ssV0FBVyxnQkFDdEJMLEVBQVdLLFdBQVcsU0FDdEJMLEVBQVdLLFdBQVcsVUFHakIsSUFBSUwsT0FJTkEsRUFBV3BELFFBQVEsS0FBTSxHQUVwQyxDQ3ZYQSxNQUFNMEQsT0FBUyxDQUFDLE1BQU8sU0FBVSxPQUFRLE9BQVEsU0FHM0NDLFFBQVUsQ0FFZEMsV0FBVyxFQUNYQyxRQUFRLEVBQ1JDLGFBQWEsRUFFYkMsVUFBVyxHQUVYQyxXQUFZLENBQ1YsQ0FDRUMsTUFBTyxRQUNQQyxNQUFPUixPQUFPLElBRWhCLENBQ0VPLE1BQU8sVUFDUEMsTUFBT1IsT0FBTyxJQUVoQixDQUNFTyxNQUFPLFNBQ1BDLE1BQU9SLE9BQU8sSUFFaEIsQ0FDRU8sTUFBTyxVQUNQQyxNQUFPUixPQUFPLElBRWhCLENBQ0VPLE1BQU8sWUFDUEMsTUFBT1IsT0FBTyxNQWtCYixTQUFTUyxPQUFPQyxHQUNyQixNQUFPQyxLQUFhQyxHQUFTRixHQUd2QkosV0FBRUEsRUFBVU8sTUFBRUEsR0FBVVosUUFHOUIsR0FDZSxJQUFiVSxJQUNjLElBQWJBLEdBQWtCQSxFQUFXRSxHQUFTQSxFQUFRUCxFQUFXL0IsUUFFMUQsT0FJRixNQUFNdUMsRUFBUyxHQUFHaEQsaUJBQWlCd0MsRUFBV0ssRUFBVyxHQUFHSixXQUd4RE4sUUFBUUUsUUFDVlksV0FBV0gsRUFBT0UsR0FJaEJiLFFBQVFDLFdBQ1ZjLFFBQVFQLElBQUlRLFdBQ1ZDLEVBQ0EsQ0FBQ0osRUFBT2pELFdBQVdvQyxRQUFRSyxXQUFXSyxFQUFXLEdBQUdILFFBQVFXLE9BQU9QLEdBR3pFLENBZ0JPLFNBQVNRLGFBQWFULEVBQVVVLEVBQU9DLEdBRTVDLE1BQU1DLEVBQWNELEdBQWtCRCxHQUFTQSxFQUFNRyxTQUFZLElBRzNEWCxNQUFFQSxFQUFLUCxXQUFFQSxHQUFlTCxRQUc5QixHQUFpQixJQUFiVSxHQUFrQkEsRUFBV0UsR0FBU0EsRUFBUVAsRUFBVy9CLE9BQzNELE9BSUYsTUFBTXVDLEVBQVMsR0FBR2hELGlCQUFpQndDLEVBQVdLLEVBQVcsR0FBR0osV0FHdERrQixFQUFlSixHQUFTQSxFQUFNSyxNQUc5QmQsRUFBUSxDQUFDVyxHQUNYRSxHQUNGYixFQUFNZSxLQUFLLEtBQU1GLEdBSWZ4QixRQUFRRSxRQUNWWSxXQUFXSCxFQUFPRSxHQUloQmIsUUFBUUMsV0FDVmMsUUFBUVAsSUFBSVEsV0FDVkMsRUFDQSxDQUFDSixFQUFPakQsV0FBV29DLFFBQVFLLFdBQVdLLEVBQVcsR0FBR0gsUUFBUVcsT0FBTyxDQUNqRVAsRUFBTS9ELFFBQVFtRCxPQUFPVyxFQUFXLE9BQzdCQyxJQUlYLENBVU8sU0FBU2dCLFlBQVlDLEdBRTFCLE1BQU1oQixNQUFFQSxFQUFLaUIsS0FBRUEsRUFBSUMsS0FBRUEsRUFBSTdCLFVBQUVBLEVBQVNDLE9BQUVBLEdBQVcwQixFQUdqRDVCLFFBQVFHLGFBQWMsRUFDdEJILFFBQVFJLFVBQVksR0FHcEIyQixZQUFZbkIsR0FHWm9CLHFCQUFxQi9CLEdBR3JCZ0Msa0JBQWtCSixFQUFNQyxFQUFNNUIsRUFDaEMsQ0FVTyxTQUFTNkIsWUFBWW5CLEdBRXhCNUIsT0FBT2tELFVBQVV0QixJQUNqQkEsR0FBUyxHQUNUQSxHQUFTWixRQUFRSyxXQUFXL0IsU0FHNUIwQixRQUFRWSxNQUFRQSxFQUVwQixDQVNPLFNBQVNvQixxQkFBcUIvQixHQUVuQ0QsUUFBUUMsWUFBY0EsQ0FDeEIsQ0FhTyxTQUFTZ0Msa0JBQWtCSixFQUFNQyxFQUFNNUIsR0FFNUNGLFFBQVFFLFNBQVdBLEVBR2ZGLFFBQVFFLFNBQ1ZGLFFBQVE2QixLQUFPQSxHQUFRLEdBQ3ZCN0IsUUFBUThCLEtBQU9BLEdBQVEsR0FFM0IsQ0FZQSxTQUFTaEIsV0FBV0gsRUFBT0UsR0FDcEJiLFFBQVFHLGVBRVZnQyxjQUFXekYsZ0JBQWdCc0QsUUFBUTZCLFFBQ2xDTyxHQUFBQSxVQUFVMUYsZ0JBQWdCc0QsUUFBUTZCLE9BR3BDN0IsUUFBUUksVUFBWTFELGdCQUFnQmEsS0FBSUEsS0FBQ3lDLFFBQVE2QixLQUFNN0IsUUFBUThCLE9BSS9EOUIsUUFBUUcsYUFBYyxHQUl4QmtDLEdBQVVBLFdBQ1JyQyxRQUFRSSxVQUNSLENBQUNTLEdBQVFLLE9BQU9QLEdBQU9wRCxLQUFLLEtBQU8sTUFDbEM2RCxJQUNLQSxHQUFTcEIsUUFBUUUsUUFBVUYsUUFBUUcsY0FDckNILFFBQVFFLFFBQVMsRUFDakJGLFFBQVFHLGFBQWMsRUFDdEJnQixhQUFhLEVBQUdDLEVBQU8seUNBQ3hCLEdBR1AsQ0NqUE8sTUFBTWtCLGNBQWdCLENBQzNCQyxVQUFXLENBQ1Q5QixLQUFNLENBQ0p2QixNQUFPLENBQ0wsbUNBQ0Esa0JBQ0EsMENBQ0EsMkJBQ0Esa0NBQ0Esa0NBQ0Esd0NBQ0EsMkNBQ0EscUJBQ0EsNEJBQ0EsMkNBQ0EsdURBQ0EsNkJBQ0EseUJBQ0EsMEJBQ0EsK0JBQ0EsdUJBQ0EsdUZBQ0EseUJBQ0Esb0NBQ0Esb0JBQ0EsMEJBQ0EsOENBQ0EsMkJBQ0EsMEJBQ0EsNkJBQ0EsbUNBQ0Esd0NBQ0EsbUNBQ0EsMkJBQ0Esa0NBQ0EsdUJBQ0EsaUJBQ0EseUJBQ0EsOEJBQ0Esb0JBQ0EsMkJBQ0EsZUFDQSw2QkFDQSxpQkFDQSxhQUNBLGVBQ0Esc0JBQ0EsY0FDQSx5QkFDQSxvQkFDQSx1QkFFRnNELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLGlCQUNUQyxRQUFTLGdCQUNUQyxZQUFhLCtCQUNiQyxjQUFlLENBQ2JwRyxLQUFNLE9BQ05xRyxVQUFXLE9BSWpCQyxXQUFZLENBQ1ZDLFFBQVMsQ0FDUDdELE1BQU8sU0FDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHFCQUNURSxZQUFhLHFCQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFNBR1Z3RyxPQUFRLENBQ045RCxNQUFPLDhCQUNQc0QsTUFBTyxDQUFDLFVBQ1JDLFFBQVMscUJBQ1RFLFlBQWEsaUNBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sU0FHVnlHLFdBQVksQ0FDVi9ELE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLHlCQUNURSxZQUFhLGtEQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1YwRyxVQUFXLENBQ1RoRSxNQUFPLFNBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyx3QkFDVEUsWUFBYSwrQ0FDYkMsY0FBZSxDQUNicEcsS0FBTSxTQUdWMkcsWUFBYSxDQUNYakUsTUFBTyxDQUFDLGFBQWMsa0JBQW1CLGlCQUN6Q3NELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLDBCQUNURSxZQUFhLG1DQUNiQyxjQUFlLENBQ2JwRyxLQUFNLGNBQ040RyxhQUFjLDBEQUdsQkMsY0FBZSxDQUNibkUsTUFBTyxDQUNMLFFBQ0EsTUFDQSxRQUNBLFlBQ0EsdUJBQ0EsZ0JBRUEsZUFDQSxRQUNBLE9BQ0EsYUFDQSxtQkFDQSxlQUNBLGNBQ0EsVUFDQSxVQUNBLGNBQ0EsV0FDQSxVQUNBLFlBQ0EsY0FDQSxZQUNBLHNCQUNBLFNBQ0EsU0FDQSxXQUNBLGFBQ0EsWUFDQSxlQUNBLHlCQUNBLFNBQ0EsZUFDQSxZQUNBLGtCQUNBLFNBQ0EsY0FDQSxtQkFDQSxlQUNBLGtCQUNBLGNBQ0EsZUFFQSxjQUNBLFdBQ0EsZUFDQSxXQUNBLFNBQ0EsT0FDQSxXQUNBLFlBQ0EsU0FDQSxxQkFDQSxhQUNBLFdBQ0EsV0FDQSxXQUNBLFdBQ0EsZUFDQSxVQUNBLGtCQUNBLG9CQUNBLGFBQ0EsVUFDQSxjQUNBLFlBQ0EsWUFFRnNELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLDRCQUNURSxZQUFhLHFDQUNiQyxjQUFlLENBQ2JwRyxLQUFNLGNBQ040RyxhQUFjLDBEQUdsQkUsaUJBQWtCLENBQ2hCcEUsTUFBTyxDQUFDLGtCQUNSc0QsTUFBTyxDQUFDLFlBQ1JDLFFBQVMsK0JBQ1RFLFlBQWEsd0NBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sY0FDTjRHLGFBQWMsMERBR2xCRyxjQUFlLENBQ2JyRSxNQUFPLENBQ0wsd0VBQ0Esa0dBRUZzRCxNQUFPLENBQUMsWUFDUkMsUUFBUyw0QkFDVEUsWUFBYSxxREFDYkMsY0FBZSxDQUNicEcsS0FBTSxPQUNOcUcsVUFBVyxPQUlqQlcsT0FBUSxDQUNOQyxPQUFRLENBQ052RSxNQUFPLEtBQ1BzRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQkFDVEUsWUFDRSwrREFDRkMsY0FBZSxDQUNicEcsS0FBTSxTQUdWa0gsTUFBTyxDQUNMeEUsTUFBTyxLQUNQc0QsTUFBTyxDQUFDLFNBQVUsU0FBVSxRQUM1QkMsUUFBUyxlQUNURSxZQUNFLG1FQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFNBR1ZtSCxRQUFTLENBQ1B6RSxNQUFPLEtBQ1BzRCxNQUFPLENBQUMsU0FBVSxTQUFVLFFBQzVCQyxRQUFTLGlCQUNURSxZQUFhLCtCQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFNBR1ZvSCxJQUFLLENBQ0gxRSxNQUFPLEtBQ1BzRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxhQUNURSxZQUFhLG1EQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFNBR1ZxSCxNQUFPLENBQ0wzRSxNQUFPLEtBQ1BzRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxlQUNURSxZQUNFLGdFQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFNBR1ZDLFFBQVMsQ0FDUHlDLE1BQU8sS0FDUHNELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLGlCQUNURSxZQUNFLHFGQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFNBR1ZBLEtBQU0sQ0FDSjBDLE1BQU8sTUFDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGNBQ1RFLFlBQWEsb0RBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sU0FDTnNILEtBQU0sZUFDTkMsUUFBUyxDQUFDLE1BQU8sT0FBUSxNQUFPLFNBR3BDN0gsT0FBUSxDQUNOZ0QsTUFBTyxRQUNQc0QsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsZ0JBQ1RFLFlBQ0UsdUVBQ0ZDLGNBQWUsQ0FDYnBHLEtBQU0sU0FDTnNILEtBQU0saUJBQ05DLFFBQVMsQ0FBQyxRQUFTLGFBQWMsV0FBWSxnQkFHakRDLElBQUssQ0FDSDlFLE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGFBQ1RFLFlBQ0Usb0ZBQ0ZDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVnlILFdBQVksQ0FDVi9FLE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLHFCQUNURSxZQUNFLDBFQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1YwSCxPQUFRLENBQ05oRixNQUFPLEtBQ1BzRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQkFDVEUsWUFBYSx5REFDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWMkgsTUFBTyxDQUNMakYsTUFBTyxLQUNQc0QsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsZUFDVEUsWUFBYSx3REFDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWNEgsTUFBTyxDQUNMbEYsTUFBTyxLQUNQc0QsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsZUFDVEUsWUFDRSxnRkFDRkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWNkgsY0FBZSxDQUNibkYsTUFBTyxJQUNQc0QsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsd0JBQ1RFLFlBQWEsa0RBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVjhILGFBQWMsQ0FDWnBGLE1BQU8sSUFDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHVCQUNURSxZQUFhLGlEQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1YrSCxhQUFjLENBQ1pyRixNQUFPLEVBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyx1QkFDVEUsWUFDRSx5RUFDRkMsY0FBZSxDQUNicEcsS0FBTSxTQUNOZ0ksSUFBSyxHQUNMQyxJQUFLLElBR1RDLGNBQWUsQ0FDYnhGLE1BQU8sS0FDUHNELE1BQU8sQ0FBQyxTQUFVLFNBQVUsUUFDNUJDLFFBQVMsd0JBQ1RFLFlBQ0UsbUZBQ0ZDLGNBQWUsQ0FDYnBHLEtBQU0sU0FHVm1JLGFBQWMsQ0FDWnpGLE1BQU8sS0FDUHNELE1BQU8sQ0FBQyxTQUFVLFNBQVUsUUFDNUJDLFFBQVMsdUJBQ1RFLFlBQ0Usa0ZBQ0ZDLGNBQWUsQ0FDYnBHLEtBQU0sU0FHVm9JLHFCQUFzQixDQUNwQjFGLE1BQU8sS0FDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLCtCQUNURSxZQUFhLDZDQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFlBSVpxSSxZQUFhLENBQ1hDLG1CQUFvQixDQUNsQjVGLE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG9DQUNURSxZQUNFLG1FQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZrRCxtQkFBb0IsQ0FDbEJSLE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG9DQUNURSxZQUNFLGtGQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZpRCxXQUFZLENBQ1ZQLE1BQU8sS0FDUHNELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLDJCQUNURSxZQUNFLHVIQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFNBR1Z1SSxTQUFVLENBQ1I3RixNQUFPLEtBQ1BzRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyx3QkFDVEUsWUFDRSxrRkFDRkMsY0FBZSxDQUNicEcsS0FBTSxTQUdWd0ksVUFBVyxDQUNUOUYsTUFBTyxLQUNQc0QsTUFBTyxDQUFDLFNBQVUsU0FBVSxRQUM1QkMsUUFBUyx5QkFDVEUsWUFDRSxzR0FDRkMsY0FBZSxDQUNicEcsS0FBTSxTQUdWeUksV0FBWSxDQUNWL0YsTUFBTyxLQUNQc0QsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsMkJBQ1R5QyxXQUFZLFdBQ1p2QyxZQUFhLCtDQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFNBR1YySSxhQUFjLENBQ1pqRyxNQUFPLEtBQ1BzRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyw2QkFDVEUsWUFDRSwrREFDRkMsY0FBZSxDQUNicEcsS0FBTSxVQUlaNEksT0FBUSxDQUNOQyxPQUFRLENBQ05uRyxPQUFPLEVBQ1BzRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxnQkFDVEMsUUFBUyxlQUNUQyxZQUFhLDhCQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1Y4SSxLQUFNLENBQ0pwRyxNQUFPLFVBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxjQUNURSxZQUFhLHlCQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFNBR1YrSSxLQUFNLENBQ0pyRyxNQUFPLEtBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxjQUNURSxZQUFhLDZCQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZnSixZQUFhLENBQ1h0RyxNQUFPLEVBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxzQkFDVEUsWUFBYSxrQ0FDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWaUosYUFBYyxDQUNadkcsT0FBTyxFQUNQc0QsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsc0JBQ1RDLFFBQVMscUJBQ1RDLFlBQ0UsMEVBQ0ZDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVmtKLE1BQU8sQ0FDTEosS0FBTSxDQUNKcEcsTUFBTyxLQUNQc0QsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsb0JBQ1RDLFFBQVMsWUFDVEMsWUFBYSwwQ0FDYkMsY0FBZSxDQUNicEcsS0FBTSxTQUdWK0ksS0FBTSxDQUNKckcsTUFBTyxLQUNQc0QsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsb0JBQ1RDLFFBQVMsWUFDVEMsWUFBYSwwQ0FDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWbUosUUFBUyxDQUNQekcsTUFBTyxJQUNQc0QsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RDLFFBQVMsZUFDVEMsWUFDRSw4REFDRkMsY0FBZSxDQUNicEcsS0FBTSxZQUlab0osYUFBYyxDQUNaUCxPQUFRLENBQ05uRyxPQUFPLEVBQ1BzRCxNQUFPLENBQUMsV0FDUkMsUUFBUyw4QkFDVEMsUUFBUyxxQkFDVEMsWUFBYSxrREFDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWcUosWUFBYSxDQUNYM0csTUFBTyxHQUNQc0QsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsb0NBQ1R5QyxXQUFZLFlBQ1p2QyxZQUFhLGdEQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZzSixPQUFRLENBQ041RyxNQUFPLEVBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyw4QkFDVEUsWUFBYSwyQ0FDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWdUosTUFBTyxDQUNMN0csTUFBTyxFQUNQc0QsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsNkJBQ1RFLFlBQ0UsdUVBQ0ZDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVndKLFdBQVksQ0FDVjlHLE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG1DQUNURSxZQUFhLHNEQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1Z5SixRQUFTLENBQ1AvRyxNQUFPLEtBQ1BzRCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQ0FDVEUsWUFBYSx3REFDYkMsY0FBZSxDQUNicEcsS0FBTSxTQUdWMEosVUFBVyxDQUNUaEgsTUFBTyxLQUNQc0QsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsa0NBQ1RFLFlBQWEsd0RBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sVUFJWjJKLElBQUssQ0FDSGQsT0FBUSxDQUNObkcsT0FBTyxFQUNQc0QsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsb0JBQ1RDLFFBQVMsWUFDVEMsWUFBYSxtQ0FDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWNEosTUFBTyxDQUNMbEgsT0FBTyxFQUNQc0QsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsbUJBQ1RDLFFBQVMsV0FDVHdDLFdBQVksVUFDWnZDLFlBQWEsZ0RBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVitJLEtBQU0sQ0FDSnJHLE1BQU8sSUFDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGtCQUNUQyxRQUFTLFVBQ1RDLFlBQWEsMEJBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVjZKLFNBQVUsQ0FDUm5ILE1BQU8sS0FDUHNELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLHVCQUNUQyxRQUFTLGNBQ1R3QyxXQUFZLFVBQ1p2QyxZQUFhLHVDQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBS2Q4SixLQUFNLENBQ0pDLFdBQVksQ0FDVnJILE1BQU8sRUFDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLG1CQUNURSxZQUFhLHNEQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZnSyxXQUFZLENBQ1Z0SCxNQUFPLEVBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxtQkFDVHlDLFdBQVksVUFDWnZDLFlBQWEsMENBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVmlLLFVBQVcsQ0FDVHZILE1BQU8sR0FDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGtCQUNURSxZQUFhLHdEQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZrSyxlQUFnQixDQUNkeEgsTUFBTyxJQUNQc0QsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsbURBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVm1LLGNBQWUsQ0FDYnpILE1BQU8sSUFDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHNCQUNURSxZQUFhLGtEQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZvSyxlQUFnQixDQUNkMUgsTUFBTyxJQUNQc0QsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsb0RBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVnFLLFlBQWEsQ0FDWDNILE1BQU8sSUFDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLG9CQUNURSxZQUFhLHdEQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZzSyxvQkFBcUIsQ0FDbkI1SCxNQUFPLElBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyw2QkFDVEUsWUFDRSx3RUFDRkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWdUssZUFBZ0IsQ0FDZDdILE1BQU8sSUFDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHVCQUNURSxZQUNFLCtEQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZpSixhQUFjLENBQ1p2RyxPQUFPLEVBQ1BzRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxvQkFDVEMsUUFBUyxtQkFDVEMsWUFBYSw2Q0FDYkMsY0FBZSxDQUNicEcsS0FBTSxZQUlad0QsUUFBUyxDQUNQWSxNQUFPLENBQ0wxQixNQUFPLEVBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxnQkFDVEMsUUFBUyxXQUNUQyxZQUFhLDBCQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFNBQ04rQyxNQUFPLEVBQ1BpRixJQUFLLEVBQ0xDLElBQUssSUFHVDNDLEtBQU0sQ0FDSjVDLE1BQU8sK0JBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxlQUNUQyxRQUFTLFVBQ1RDLFlBQ0UsOERBQ0ZDLGNBQWUsQ0FDYnBHLEtBQU0sU0FHVnFGLEtBQU0sQ0FDSjNDLE1BQU8sTUFDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGVBQ1RDLFFBQVMsVUFDVEMsWUFBYSwwREFDYkMsY0FBZSxDQUNicEcsS0FBTSxTQUdWeUQsVUFBVyxDQUNUZixPQUFPLEVBQ1BzRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxxQkFDVEMsUUFBUyxlQUNUQyxZQUFhLHNDQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1YwRCxPQUFRLENBQ05oQixPQUFPLEVBQ1BzRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxrQkFDVEMsUUFBUyxZQUNUQyxZQUFhLHdDQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFlBSVp3SyxHQUFJLENBQ0YzQixPQUFRLENBQ05uRyxPQUFPLEVBQ1BzRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxZQUNUQyxRQUFTLFdBQ1RDLFlBQWEsbURBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVnlLLE1BQU8sQ0FDTC9ILE1BQU8sSUFDUHNELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLFdBQ1RDLFFBQVMsVUFDVEMsWUFBYSxnQ0FDYkMsY0FBZSxDQUNicEcsS0FBTSxVQUlaMEssTUFBTyxDQUNMQyxRQUFTLENBQ1BqSSxNQUFPLGFBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxpQkFDVEUsWUFBYSwrQkFDYkMsY0FBZSxDQUNicEcsS0FBTSxTQUdWNEsscUJBQXNCLENBQ3BCbEksT0FBTyxFQUNQc0QsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsZ0NBQ1RFLFlBQWEsaURBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVjZLLE9BQVEsQ0FDTm5JLE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGdCQUNURSxZQUFhLCtDQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1Y4SyxjQUFlLENBQ2JwSSxPQUFPLEVBQ1BzRCxNQUFPLENBQUMsV0FDUkMsUUFBUyx3QkFDVEUsWUFBYSxvREFDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWK0ssaUJBQWtCLENBQ2hCckksT0FBTyxFQUNQc0QsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsMkJBQ1RFLFlBQWEseURBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sWUFJWmdMLE1BQU8sQ0FDTG5DLE9BQVEsQ0FDTm5HLE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGVBQ1RDLFFBQVMsY0FDVEMsWUFBYSw0REFDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWaUwsU0FBVSxDQUNSdkksT0FBTyxFQUNQc0QsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsaUJBQ1RFLFlBQ0UsNkVBQ0ZDLGNBQWUsQ0FDYnBHLEtBQU0sV0FHVmtMLFNBQVUsQ0FDUnhJLE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGlCQUNURSxZQUFhLCtDQUNiQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZtTCxnQkFBaUIsQ0FDZnpJLE9BQU8sRUFDUHNELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLDBCQUNURSxZQUNFLHFFQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZvTCxPQUFRLENBQ04xSSxPQUFPLEVBQ1BzRCxNQUFPLENBQUMsV0FDUkMsUUFBUyxlQUNURSxZQUNFLGtGQUNGQyxjQUFlLENBQ2JwRyxLQUFNLFdBR1ZxTCxPQUFRLENBQ04zSSxNQUFPLEVBQ1BzRCxNQUFPLENBQUMsVUFDUkMsUUFBUyxnQkFDVEUsWUFBYSw0REFDYkMsY0FBZSxDQUNicEcsS0FBTSxXQUdWc0wsY0FBZSxDQUNiNUksTUFBTyxLQUNQc0QsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsMEJBQ2JDLGNBQWUsQ0FDYnBHLEtBQU0sYUFPRHVMLFlBQWNDLG1CQUFtQjFGLGVBR2pDMkYsY0FBZ0JDLHFCQUFxQjVGLGVBb0JsRCxTQUFTMEYsbUJBQW1CRyxFQUFRSixFQUFjLENBQUEsRUFBSUssRUFBWSxJQXFCaEUsT0FwQkF2TSxPQUFPd0MsS0FBSzhKLEdBQVFFLFNBQVN6TSxJQUUzQixNQUFNME0sRUFBUUgsRUFBT3ZNLFFBR00sSUFBaEIwTSxFQUFNcEosTUFFZjhJLG1CQUFtQk0sRUFBT1AsRUFBYSxHQUFHSyxLQUFheE0sTUFHdkRtTSxFQUFZTyxFQUFNNUYsU0FBVzlHLEdBQU8sR0FBR3dNLEtBQWF4TSxJQUFNMk0sVUFBVSxRQUczQ3RILElBQXJCcUgsRUFBTXBELGFBQ1I2QyxFQUFZTyxFQUFNcEQsWUFBYyxHQUFHa0QsS0FBYXhNLElBQU0yTSxVQUFVLElBRW5FLElBSUlSLENBQ1QsQ0FpQkEsU0FBU0cscUJBQXFCQyxFQUFRRixFQUFnQixJQWtCcEQsT0FqQkFwTSxPQUFPd0MsS0FBSzhKLEdBQVFFLFNBQVN6TSxJQUUzQixNQUFNME0sRUFBUUgsRUFBT3ZNLFFBR00sSUFBaEIwTSxFQUFNOUYsTUFFZjBGLHFCQUFxQkksRUFBT0wsR0FHeEJLLEVBQU05RixNQUFNbEcsU0FBUyxXQUN2QjJMLEVBQWN2RyxLQUFLOUYsRUFFdEIsSUFJSXFNLENBQ1QsQ0NyaENBTyxPQUFPTCxTQUlQLE1BQU1NLEVBQUksQ0FHUkMsTUFBUUMsR0FDTkMsSUFBQ0EsRUFDRUMsU0FDQUMsV0FBVzVKLEdBQ1ZBLEVBQ0d2QyxNQUFNLEtBQ05vTSxLQUFLN0osR0FBVUEsRUFBTW5CLFNBQ3JCaUwsUUFBUTlKLEdBQVV5SixFQUFZck0sU0FBUzRDLE9BRTNDNEosV0FBVzVKLEdBQVdBLEVBQU1aLE9BQVNZLE9BQVErQixJQUlsRGdJLFFBQVMsSUFDUEwsSUFBQ0EsRUFDRU0sS0FBSyxDQUFDLE9BQVEsUUFBUyxLQUN2QkosV0FBVzVKLEdBQXFCLEtBQVZBLEVBQXlCLFNBQVZBLE9BQW1CK0IsSUFJN0RpSSxLQUFPbE0sR0FDTDRMLElBQUNBLEVBQ0VNLEtBQUssSUFBSWxNLEVBQVEsS0FDakI4TCxXQUFXNUosR0FBcUIsS0FBVkEsRUFBZUEsT0FBUStCLElBSWxENEgsT0FBUSxJQUNORCxJQUFDQSxFQUNFQyxTQUNBOUssT0FDQW9MLFFBQ0VqSyxJQUNFLENBQUMsUUFBUyxZQUFhLE9BQVEsT0FBTzVDLFNBQVM0QyxJQUN0QyxLQUFWQSxJQUNEQSxJQUFXLENBQ1ZxQyxRQUFTLG1EQUFtRHJDLFNBRy9ENEosV0FBVzVKLEdBQXFCLEtBQVZBLEVBQWVBLE9BQVErQixJQUlsRG1JLFlBQWEsSUFDWFIsSUFBQ0EsRUFDRUMsU0FDQTlLLE9BQ0FvTCxRQUNFakssR0FDVyxLQUFWQSxJQUFrQm1LLE1BQU1DLFdBQVdwSyxLQUFXb0ssV0FBV3BLLEdBQVMsSUFDbkVBLElBQVcsQ0FDVnFDLFFBQVMscURBQXFEckMsU0FHakU0SixXQUFXNUosR0FBcUIsS0FBVkEsRUFBZW9LLFdBQVdwSyxRQUFTK0IsSUFJOURzSSxlQUFnQixJQUNkWCxJQUFDQSxFQUNFQyxTQUNBOUssT0FDQW9MLFFBQ0VqSyxHQUNXLEtBQVZBLElBQWtCbUssTUFBTUMsV0FBV3BLLEtBQVdvSyxXQUFXcEssSUFBVSxJQUNwRUEsSUFBVyxDQUNWcUMsUUFBUyx5REFBeURyQyxTQUdyRTRKLFdBQVc1SixHQUFxQixLQUFWQSxFQUFlb0ssV0FBV3BLLFFBQVMrQixLQUduRHVJLE9BQVNaLElBQUNBLEVBQUNhLE9BQU8sQ0FFN0JDLGVBQWdCakIsRUFBRUksU0FHbEJjLG1CQUFvQmYsSUFBQ0EsRUFDbEJDLFNBQ0E5SyxPQUNBb0wsUUFDRWpLLEdBQVUsNkJBQTZCUixLQUFLUSxJQUFvQixLQUFWQSxJQUN0REEsSUFBVyxDQUNWcUMsUUFBUyw0RkFBNEZyQyxTQUd4RzRKLFdBQVc1SixHQUFxQixLQUFWQSxFQUFlQSxPQUFRK0IsSUFDaEQySSxtQkFBb0JoQixJQUFDQSxFQUNsQkMsU0FDQTlLLE9BQ0FvTCxRQUNFakssR0FDQ0EsRUFBTVksV0FBVyxhQUNqQlosRUFBTVksV0FBVyxZQUNQLEtBQVZaLElBQ0RBLElBQVcsQ0FDVnFDLFFBQVMsNkZBQTZGckMsU0FHekc0SixXQUFXNUosR0FBcUIsS0FBVkEsRUFBZUEsT0FBUStCLElBQ2hENEksdUJBQXdCcEIsRUFBRVEsVUFDMUJhLHNCQUF1QnJCLEVBQUVJLFNBQ3pCa0IsdUJBQXdCdEIsRUFBRUksU0FDMUJtQix3QkFBeUJ2QixFQUFFQyxNQUFNcEcsY0FBY1EsV0FBV0ssWUFBWWpFLE9BQ3RFK0ssMEJBQTJCeEIsRUFBRUMsTUFDM0JwRyxjQUFjUSxXQUFXTyxjQUFjbkUsT0FFekNnTCw2QkFBOEJ6QixFQUFFQyxNQUM5QnBHLGNBQWNRLFdBQVdRLGlCQUFpQnBFLE9BRTVDaUwsMEJBQTJCMUIsRUFBRUMsTUFDM0JwRyxjQUFjUSxXQUFXUyxjQUFjckUsT0FJekNrTCxjQUFlM0IsRUFBRUksU0FDakJ3QixhQUFjNUIsRUFBRUksU0FDaEJ5QixlQUFnQjdCLEVBQUVJLFNBQ2xCMEIsV0FBWTlCLEVBQUVJLFNBQ2QyQixhQUFjL0IsRUFBRUksU0FDaEI0QixlQUFnQmhDLEVBQUVJLFNBQ2xCNkIsWUFBYWpDLEVBQUVTLEtBQUssQ0FBQyxPQUFRLE1BQU8sTUFBTyxRQUMzQ3lCLGNBQWVsQyxFQUFFUyxLQUFLLENBQUMsUUFBUyxhQUFjLFdBQVksZUFDMUQwQixXQUFZbkMsRUFBRVEsVUFDZDRCLG1CQUFvQnBDLEVBQUVRLFVBQ3RCNkIsY0FBZXJDLEVBQUVXLGNBQ2pCMkIsYUFBY3RDLEVBQUVXLGNBQ2hCNEIsYUFBY3ZDLEVBQUVXLGNBQ2hCNkIsc0JBQXVCeEMsRUFBRVcsY0FDekI4QixxQkFBc0J6QyxFQUFFVyxjQUN4QitCLHFCQUFzQjFDLEVBQUVXLGNBQ3hCZ0Msc0JBQXVCM0MsRUFBRUksU0FDekJ3QyxxQkFBc0I1QyxFQUFFSSxTQUN4QnlDLDZCQUE4QjdDLEVBQUVjLGlCQUdoQ2dDLGtDQUFtQzlDLEVBQUVRLFVBQ3JDdUMsa0NBQW1DL0MsRUFBRVEsVUFDckN3Qyx5QkFBMEJoRCxFQUFFSSxTQUM1QjZDLHNCQUF1QmpELEVBQUVJLFNBQ3pCOEMsdUJBQXdCbEQsRUFBRUksU0FDMUIrQyx5QkFBMEJuRCxFQUFFSSxTQUM1QmdELDJCQUE0QnBELEVBQUVJLFNBRzlCaUQsY0FBZXJELEVBQUVRLFVBQ2pCOEMsWUFBYXRELEVBQUVJLFNBQ2ZtRCxZQUFhdkQsRUFBRVcsY0FDZjZDLG9CQUFxQnhELEVBQUVXLGNBQ3ZCOEMsb0JBQXFCekQsRUFBRVEsVUFHdkJrRCxrQkFBbUIxRCxFQUFFSSxTQUNyQnVELGtCQUFtQjNELEVBQUVXLGNBQ3JCaUQscUJBQXNCNUQsRUFBRWMsaUJBR3hCK0MsNEJBQTZCN0QsRUFBRVEsVUFDL0JzRCxrQ0FBbUM5RCxFQUFFYyxpQkFDckNpRCw0QkFBNkIvRCxFQUFFYyxpQkFDL0JrRCwyQkFBNEJoRSxFQUFFYyxpQkFDOUJtRCxpQ0FBa0NqRSxFQUFFUSxVQUNwQzBELDhCQUErQmxFLEVBQUVJLFNBQ2pDK0QsZ0NBQWlDbkUsRUFBRUksU0FHbkNnRSxrQkFBbUJwRSxFQUFFUSxVQUNyQjZELGlCQUFrQnJFLEVBQUVRLFVBQ3BCOEQsZ0JBQWlCdEUsRUFBRVcsY0FDbkI0RCxxQkFBc0J2RSxFQUFFSSxTQUd4Qm9FLGlCQUFrQnhFLEVBQUVjLGlCQUNwQjJELGlCQUFrQnpFLEVBQUVjLGlCQUNwQjRELGdCQUFpQjFFLEVBQUVXLGNBQ25CZ0UscUJBQXNCM0UsRUFBRWMsaUJBQ3hCOEQsb0JBQXFCNUUsRUFBRWMsaUJBQ3ZCK0QscUJBQXNCN0UsRUFBRWMsaUJBQ3hCZ0Usa0JBQW1COUUsRUFBRWMsaUJBQ3JCaUUsMkJBQTRCL0UsRUFBRWMsaUJBQzlCa0UscUJBQXNCaEYsRUFBRWMsaUJBQ3hCbUUsa0JBQW1CakYsRUFBRVEsVUFHckIwRSxjQUFlL0UsSUFBQ0EsRUFDYkMsU0FDQTlLLE9BQ0FvTCxRQUNFakssR0FDVyxLQUFWQSxJQUNFbUssTUFBTUMsV0FBV3BLLEtBQ2pCb0ssV0FBV3BLLElBQVUsR0FDckJvSyxXQUFXcEssSUFBVSxJQUN4QkEsSUFBVyxDQUNWcUMsUUFBUyxtR0FBbUdyQyxTQUcvRzRKLFdBQVc1SixHQUFxQixLQUFWQSxFQUFlb0ssV0FBV3BLLFFBQVMrQixJQUM1RDJNLGFBQWNuRixFQUFFSSxTQUNoQmdGLGFBQWNwRixFQUFFSSxTQUNoQmlGLG1CQUFvQnJGLEVBQUVRLFVBQ3RCOEUsZ0JBQWlCdEYsRUFBRVEsVUFHbkIrRSxVQUFXdkYsRUFBRVEsVUFDYmdGLFNBQVV4RixFQUFFSSxTQUdacUYsZUFBZ0J6RixFQUFFUyxLQUFLLENBQUMsY0FBZSxhQUFjLFNBQ3JEaUYsOEJBQStCMUYsRUFBRVEsVUFDakNtRixjQUFlM0YsRUFBRVEsVUFDakJvRixzQkFBdUI1RixFQUFFUSxVQUN6QnFGLHlCQUEwQjdGLEVBQUVRLFVBRzVCc0YsYUFBYzlGLEVBQUVRLFVBQ2hCdUYsZUFBZ0IvRixFQUFFUSxVQUNsQndGLGVBQWdCaEcsRUFBRVEsVUFDbEJ5Rix3QkFBeUJqRyxFQUFFUSxVQUMzQjBGLGFBQWNsRyxFQUFFUSxVQUNoQjJGLGNBQWVuRyxFQUFFYyxpQkFDakJzRixxQkFBc0JwRyxFQUFFVyxnQkFHYjBGLEtBQU90RixPQUFPdUYsVUFBVUMsTUFBTW5RLFFBQVFvUSxLQ25QbkQsTUFBTUMsb0JBQW9CQyxNQVF4QixXQUFBQyxDQUFZN04sRUFBUzhOLEdBQ25CQyxRQUVBQyxLQUFLaE8sUUFBVUEsRUFDZmdPLEtBQUsvTixhQUFlRCxFQUVoQjhOLElBQ0ZFLEtBQUtGLFdBQWFBLEVBRXJCLENBU0QsU0FBQUcsQ0FBVUgsR0FHUixPQUZBRSxLQUFLRixXQUFhQSxFQUVYRSxJQUNSLENBVUQsUUFBQUUsQ0FBU3JPLEdBZ0JQLE9BZkFtTyxLQUFLbk8sTUFBUUEsRUFFVEEsRUFBTXNPLE9BQ1JILEtBQUtHLEtBQU90TyxFQUFNc08sTUFHaEJ0TyxFQUFNaU8sYUFDUkUsS0FBS0YsV0FBYWpPLEVBQU1pTyxZQUd0QmpPLEVBQU1LLFFBQ1I4TixLQUFLL04sYUFBZUosRUFBTUcsUUFDMUJnTyxLQUFLOU4sTUFBUUwsRUFBTUssT0FHZDhOLElBQ1IsRUMxQ0gsTUFBTTdLLGNBQWdCaUwsbUJBQW1Cck4sZUFHbkNzTixnQkFBa0JyVSxTQUFTbUosZUFnQjFCLFNBQVNtTCxXQUFXQyxHQUFjLEdBQ3ZDLE9BQU9BLEVBQWNGLGdCQUFrQmxMLGFBQ3pDLENBMkJPLFNBQVNxTCxpQkFBaUJDLEVBQWdCLEdBQUlDLEVBQVUsSUFFN0QsSUFBSUMsRUFBZ0IsQ0FBQSxFQUdoQkMsRUFBYSxDQUFBLEVBcUJqQixPQWxCSUYsR0FBV3ZVLE1BQU1DLFFBQVFzVSxJQUFZQSxFQUFRM1IsU0FFL0M0UixFQUFnQkUsZ0JBQWdCSCxFQUFTdkwsY0FBY0csYUFHdkRzTCxFQUFhRSxtQkFBbUJ0SSxZQUFha0ksSUFJL0NLLHFCQUNFaE8sY0FDQW9DLGNBQ0F3TCxFQUNBQyxFQUNBSCxHQUlLdEwsYUFDVCxDQWVPLFNBQVM2TCxjQUFjQSxFQUFlQyxHQUFjLEdBZ0J6RCxPQWRJQSxJQUVGM1UsT0FBT3dDLEtBQUt1UixpQkFBaUJ2SCxTQUFTek0sV0FDN0JnVSxnQkFBZ0JoVSxFQUFJLElBSTdCNlUsYUFBYWIsZ0JBQWlCclUsU0FBU21KLGlCQUl6QytMLGFBQWFiLGdCQUFpQlcsR0FHdkJYLGVBQ1QsQ0FZTyxTQUFTYSxhQUFhQyxFQUFpQkMsR0FFNUMsR0FBSXpTLFNBQVN3UyxJQUFvQnhTLFNBQVN5UyxHQUN4QyxJQUFLLE1BQU8vVSxFQUFLc0QsS0FBVXJELE9BQU8rVSxRQUFRRCxHQUN4Q0QsRUFBZ0I5VSxHQUNkc0MsU0FBU2dCLEtBQ1IrSSxjQUFjM0wsU0FBU1YsU0FDQ3FGLElBQXpCeVAsRUFBZ0I5VSxHQUNaNlUsYUFBYUMsRUFBZ0I5VSxHQUFNc0QsUUFDekIrQixJQUFWL0IsRUFDRUEsRUFDQXdSLEVBQWdCOVUsSUFBUSxLQUtwQyxPQUFPOFUsQ0FDVCxDQWtCTyxTQUFTRyxnQkFBZ0JDLEdBRTlCLE1BQU1ILEVBQWEsQ0FBQSxFQUduQixHQUFJelMsU0FBUzRTLEdBRVgsSUFBSyxNQUFPbFYsRUFBS3NELEtBQVVyRCxPQUFPK1UsUUFBUUUsR0FBYSxDQUVyRCxNQUFNQyxFQUFrQmhKLFlBQVluTSxHQUNoQ21NLFlBQVluTSxHQUFLZSxNQUFNLEtBQ3ZCLEdBSUpvVSxFQUFnQkMsUUFDZCxDQUFDQyxFQUFLQyxFQUFNQyxJQUNURixFQUFJQyxHQUNISCxFQUFnQnpTLE9BQVMsSUFBTTZTLEVBQVFqUyxFQUFRK1IsRUFBSUMsSUFBUyxJQUNoRVAsRUFFSCxNQUVEblEsSUFDRSxFQUNBLG1GQUtKLE9BQU9tUSxDQUNULENBb0JPLFNBQVNTLGdCQUNkakosT0FDQXZLLFVBQVcsRUFDWHlULGdCQUFpQixHQUVqQixJQUVFLElBQUtuVCxTQUFTaUssU0FBNkIsaUJBQVhBLE9BRTlCLE9BQU8sS0FJVCxNQUFNbUosYUFDYyxpQkFBWG5KLE9BQ0hrSixlQUNFRSxLQUFLLElBQUlwSixXQUNUcUosS0FBS3hDLE1BQU03RyxRQUNiQSxPQUdBc0osbUJBQXFCQyxrQkFDekJKLGFBQ0FELGdCQUNBLEdBSUlNLGNBQWdCTixlQUNsQkcsS0FBS3hDLE1BQ0gwQyxrQkFBa0JKLGFBQWNELGdCQUFnQixJQUNoRCxDQUFDTyxFQUFHMVMsUUFDZSxpQkFBVkEsT0FBc0JBLE1BQU1ZLFdBQVcsWUFDMUN5UixLQUFLLElBQUlyUyxVQUNUQSxRQUVSc1MsS0FBS3hDLE1BQU15QyxvQkFHZixPQUFPN1QsU0FBVzZULG1CQUFxQkUsYUFDeEMsQ0FBQyxNQUFPdlEsR0FFUCxPQUFPLElBQ1IsQ0FDSCxDQXNGQSxTQUFTdU8sbUJBQW1CeEgsR0FDMUIsTUFBTXhFLEVBQVUsQ0FBQSxFQUdoQixJQUFLLE1BQU8rTCxFQUFNdlIsS0FBU3RDLE9BQU8rVSxRQUFRekksR0FDeEMsR0FBSXRNLE9BQU9DLFVBQVVDLGVBQWVDLEtBQUttQyxFQUFNLFNBQVUsQ0FFdkQsTUFBTTBULEVBQVMvQyxLQUFLM1EsRUFBS3NFLFNBRXZCa0IsRUFBUStMLEdBRE5tQyxRQUNjQSxFQUVBMVQsRUFBS2UsS0FFN0IsTUFDTXlFLEVBQVErTCxHQUFRQyxtQkFBbUJ4UixHQUt2QyxPQUFPd0YsQ0FDVCxDQXdCQSxTQUFTMk0scUJBQXFCbkksRUFBUXhFLEVBQVNtTyxFQUFXQyxFQUFRQyxHQUNoRW5XLE9BQU93QyxLQUFLOEosR0FBUUUsU0FBU3pNLElBRTNCLE1BQU0wTSxFQUFRSCxFQUFPdk0sR0FHZnFXLEVBQVlILEdBQWFBLEVBQVVsVyxHQUNuQ3NXLEVBQVNILEdBQVVBLEVBQU9uVyxHQUMxQnVXLEVBQVlILEdBQWFBLEVBQVVwVyxHQUd6QyxRQUEyQixJQUFoQjBNLEVBQU1wSixNQUNmb1IscUJBQXFCaEksRUFBTzNFLEVBQVEvSCxHQUFNcVcsRUFBV0MsRUFBUUMsT0FDeEQsQ0FFREYsVUFDRnRPLEVBQVEvSCxHQUFPcVcsR0FJakIsTUFBTUosRUFBUy9DLEtBQUt4RyxFQUFNN0YsU0FDdEI2RixFQUFNN0YsV0FBV3FNLE1BQWpCeEcsTUFBeUJ1SixJQUMzQmxPLEVBQVEvSCxHQUFPaVcsR0FJYkssVUFDRnZPLEVBQVEvSCxHQUFPc1csR0FJYkMsVUFDRnhPLEVBQVEvSCxHQUFPdVcsRUFFbEIsSUFFTCxDQXNCTyxTQUFTVCxrQkFBa0IvTixFQUFTME4sRUFBZ0JlLEdBaUN6RCxPQUFPWixLQUFLYSxVQUFVMU8sR0FoQ0csQ0FBQ2lPLEVBQUcxUyxLQU8zQixHQUxxQixpQkFBVkEsSUFDVEEsRUFBUUEsRUFBTW5CLFFBS0csbUJBQVZtQixHQUNXLGlCQUFWQSxHQUNOQSxFQUFNWSxXQUFXLGFBQ2pCWixFQUFNVSxTQUFTLEtBQ2pCLENBRUEsR0FBSXlSLEVBRUYsT0FBT2UsRUFFSCxZQUFZbFQsRUFBUSxJQUFJb1QsV0FBVyxPQUFRLGVBRTNDLFdBQVdwVCxFQUFRLElBQUlvVCxXQUFXLE9BQVEsY0FHOUMsTUFBTSxJQUFJbkQsS0FFYixDQUdELE9BQU9qUSxDQUFLLElBSW1Db1QsV0FDL0NGLEVBQXFCLHlCQUEyQixxQkFDaEQsR0FFSixDQWlCQSxTQUFTaEMsZ0JBQWdCSCxFQUFTc0MsR0FFaEMsTUFBTUMsRUFBY3ZDLEVBQVF3QyxXQUN6QkMsR0FBa0MsZUFBMUJBLEVBQUlyVyxRQUFRLEtBQU0sTUFJdkJzVyxFQUFpQkgsR0FBYyxHQUFNdkMsRUFBUXVDLEVBQWMsR0FHakUsR0FBSUcsR0FBa0JKLEVBQW1CN1MsbUJBQ3ZDLElBRUUsT0FBTzBSLGdCQUNMdlIsR0FBQUEsYUFBYW5ELGdCQUFnQmlXLEdBQWlCLFNBQzlDLEVBQ0FKLEVBQW1Cek4sbUJBRXRCLENBQUMsTUFBTzFELEdBQ1BELGFBQ0UsRUFDQUMsRUFDQSxzREFBc0R1UixVQUV6RCxDQUlILE1BQU8sRUFDVCxDQWtCQSxTQUFTdEMsbUJBQW1CdEksRUFBYWtJLEdBRXZDLE1BQU1FLEVBQWEsQ0FBQSxFQUduQixJQUFLLElBQUl5QyxFQUFJLEVBQUdBLEVBQUkzQyxFQUFRM1IsT0FBUXNVLElBQUssQ0FDdkMsTUFBTUMsRUFBUzVDLEVBQVEyQyxHQUFHdlcsUUFBUSxLQUFNLElBR2xDMFUsRUFBa0JoSixFQUFZOEssR0FDaEM5SyxFQUFZOEssR0FBUWxXLE1BQU0sS0FDMUIsR0FHSm9VLEVBQWdCQyxRQUFPLENBQUNDLEVBQUtDLEVBQU1DLEtBQ2pDLEdBQUlKLEVBQWdCelMsT0FBUyxJQUFNNlMsRUFBTyxDQUN4QyxNQUFNalMsRUFBUStRLElBQVUyQyxHQUNuQjFULEdBQ0hzQixJQUNFLEVBQ0EseUNBQXlDcVMseUNBRzdDNUIsRUFBSUMsR0FBUWhTLEdBQVMsSUFDdEIsV0FBd0IrQixJQUFkZ1EsRUFBSUMsS0FDYkQsRUFBSUMsR0FBUSxJQUVkLE9BQU9ELEVBQUlDLEVBQUssR0FDZmYsRUFDSixDQUdELE9BQU9BLENBQ1QsQ0N4akJPMkMsZUFBZUMsTUFBTUMsRUFBS0MsRUFBaUIsSUFDaEQsT0FBTyxJQUFJQyxTQUFRLENBQUNDLEVBQVNDLEtBQzNCQyxtQkFBbUJMLEdBQ2hCTSxJQUFJTixFQUFLQyxHQUFpQk0sSUFDekIsSUFBSUMsRUFBZSxHQUduQkQsRUFBU0UsR0FBRyxRQUFTQyxJQUNuQkYsR0FBZ0JFLENBQUssSUFJdkJILEVBQVNFLEdBQUcsT0FBTyxLQUNaRCxHQUNISixFQUFPLHFDQUVURyxFQUFTSSxLQUFPSCxFQUNoQkwsRUFBUUksRUFBUyxHQUNqQixJQUVIRSxHQUFHLFNBQVVyUyxJQUNaZ1MsRUFBT2hTLEVBQU0sR0FDYixHQUVSLENBd0VBLFNBQVNpUyxtQkFBbUJMLEdBQzFCLE9BQU9BLEVBQUlsVCxXQUFXLFNBQVc4VCxNQUFRQyxJQUMzQyxDQ25HQSxNQUFNQyxNQUFRLENBQ1o5USxPQUFRLDhCQUNSK1EsZUFBZ0IsQ0FBRSxFQUNsQkMsUUFBUyxHQUNUQyxVQUFXLElBZU5uQixlQUFlb0Isb0JBQ3BCQyxFQUNBQyxHQUVBLElBQ0UsSUFBSUMsRUFHSixNQUFNblIsRUFBWW9SLGVBR1pDLEVBQWVoWCxLQUFBQSxLQUFLMkYsRUFBVyxpQkFDL0JzUixFQUFhalgsS0FBQUEsS0FBSzJGLEVBQVcsY0FPbkMsSUFKQ2YsR0FBVUEsV0FBQ2UsSUFBY2QsR0FBU0EsVUFBQ2MsRUFBVyxDQUFFdVIsV0FBVyxLQUl2RHRTLEdBQUFBLFdBQVdvUyxJQUFpQkosRUFBa0JsUixXQUNqRHpDLElBQUksRUFBRyx5REFDUDZULFFBQXVCSyxhQUNyQlAsRUFDQUMsRUFDQUksT0FFRyxDQUNMLElBQUlHLEdBQWdCLEVBR3BCLE1BQU1DLEVBQVdwRCxLQUFLeEMsTUFBTW5QLEdBQVlBLGFBQUMwVSxHQUFlLFFBSXhELEdBQUlLLEVBQVNDLFNBQVduWixNQUFNQyxRQUFRaVosRUFBU0MsU0FBVSxDQUN2RCxNQUFNQyxFQUFZLENBQUEsRUFDbEJGLEVBQVNDLFFBQVF4TSxTQUFTME0sR0FBT0QsRUFBVUMsR0FBSyxJQUNoREgsRUFBU0MsUUFBVUMsQ0FDcEIsQ0FHRCxNQUFNM1IsWUFBRUEsRUFBV0UsY0FBRUEsRUFBYUMsaUJBQUVBLEdBQ2xDNlEsRUFDSWEsRUFDSjdSLEVBQVk3RSxPQUFTK0UsRUFBYy9FLE9BQVNnRixFQUFpQmhGLE9BSzNEc1csRUFBUzdSLFVBQVlvUixFQUFrQnBSLFNBQ3pDdkMsSUFDRSxFQUNBLHlFQUVGbVUsR0FBZ0IsR0FFaEI5WSxPQUFPd0MsS0FBS3VXLEVBQVNDLFNBQVcsQ0FBRSxHQUFFdlcsU0FBVzBXLEdBRS9DeFUsSUFDRSxFQUNBLCtFQUVGbVUsR0FBZ0IsR0FHaEJBLEdBQWlCdFIsR0FBaUIsSUFBSTdFLE1BQU15VyxJQUMxQyxJQUFLTCxFQUFTQyxRQUFRSSxHQUtwQixPQUpBelUsSUFDRSxFQUNBLGVBQWV5VSxpREFFVixDQUNSLElBS0ROLEVBQ0ZOLFFBQXVCSyxhQUNyQlAsRUFDQUMsRUFDQUksSUFHRmhVLElBQUksRUFBRyx1REFHUHNULE1BQU1FLFFBQVVuVSxHQUFBQSxhQUFhMlUsRUFBWSxRQUd6Q0gsRUFBaUJPLEVBQVNDLFFBRzFCZixNQUFNRyxVQUFZaUIsZUFBZXBCLE1BQU1FLFNBRTFDLE9BSUttQixzQkFBc0JoQixFQUFtQkUsRUFDaEQsQ0FBQyxNQUFPalQsR0FDUCxNQUFNLElBQUk4TixZQUNSLDhFQUNBLEtBQ0FPLFNBQVNyTyxFQUNaLENBQ0gsQ0FTTyxTQUFTZ1UsdUJBQ2QsT0FBT3RCLE1BQU1HLFNBQ2YsQ0FXT25CLGVBQWV1Qyx3QkFBd0JDLEdBRTVDLE1BQU0zUixFQUFVa00sYUFHaEJsTSxFQUFRYixXQUFXQyxRQUFVdVMsUUFHdkJwQixvQkFBb0J2USxFQUFRYixXQUFZYSxFQUFReUIsT0FBT00sTUFDL0QsQ0FXTyxTQUFTd1AsZUFBZUssR0FDN0IsT0FBT0EsRUFDSmhOLFVBQVUsRUFBR2dOLEVBQWFDLFFBQVEsT0FDbENuWixRQUFRLEtBQU0sSUFDZEEsUUFBUSxLQUFNLElBQ2RBLFFBQVEsTUFBTyxJQUNmMEIsTUFDTCxDQVlPLFNBQVMwWCxrQkFBa0JDLEdBQ2hDLE9BQU9BLEVBQVdyWixRQUNoQixxRUFDQSxHQUVKLENBb0JPLFNBQVNpWSxlQUNkLE9BQU81WCxnQkFBZ0JtVCxhQUFhL00sV0FBV0ksVUFDakQsQ0F1QkE0UCxlQUFlNkMsdUJBQ2JDLEVBQ0EzQyxFQUNBb0IsRUFDQXdCLEdBQW1CLEdBR2ZELEVBQU9oVyxTQUFTLFNBQ2xCZ1csRUFBU0EsRUFBT3JOLFVBQVUsRUFBR3FOLEVBQU90WCxPQUFTLElBRS9Da0MsSUFBSSxFQUFHLDZCQUE2Qm9WLFFBR3BDLE1BQU1yQyxRQUFpQlIsTUFBTSxHQUFHNkMsT0FBYTNDLEdBRzdDLEdBQTRCLE1BQXhCTSxFQUFTbEUsWUFBOEMsaUJBQWpCa0UsRUFBU0ksS0FBa0IsQ0FDbkUsR0FBSVUsRUFBZ0IsQ0FFbEJBLEVBRG1Cb0Isa0JBQWtCRyxJQUNSLENBQzlCLENBQ0QsT0FBT3JDLEVBQVNJLElBQ2pCLENBR0QsR0FBSWtDLEVBQ0YsTUFBTSxJQUFJM0csWUFDUiwrQkFBK0IwRywyRUFBZ0ZyQyxFQUFTbEUsZUFDeEgsS0FDQUksU0FBUzhELEdBRVgvUyxJQUNFLEVBQ0EsK0JBQStCb1YsNkRBR3JDLENBaUJBOUMsZUFBZXFDLHNCQUFzQmhCLEVBQW1CRSxFQUFpQixJQUN2RSxNQUFNeUIsRUFBYyxDQUNsQi9TLFFBQVNvUixFQUFrQnBSLFFBQzNCOFIsUUFBU1IsR0FJWFAsTUFBTUMsZUFBaUIrQixFQUV2QnRWLElBQUksRUFBRyxtQ0FDUCxJQUNFdVYsR0FBYUEsY0FDWHhZLFVBQUsrVyxlQUFnQixpQkFDckI5QyxLQUFLYSxVQUFVeUQsR0FDZixPQUVILENBQUMsTUFBTzFVLEdBQ1AsTUFBTSxJQUFJOE4sWUFDUiw0Q0FDQSxLQUNBTyxTQUFTck8sRUFDWixDQUNILENBdUJBMFIsZUFBZWtELGNBQ2I3UyxFQUNBRSxFQUNBRSxFQUNBNlEsRUFDQUMsR0FHQSxJQUFJNEIsRUFDSixNQUFNQyxFQUFZOUIsRUFBbUI5TyxLQUMvQjZRLEVBQVkvQixFQUFtQjdPLEtBR3JDLEdBQUkyUSxHQUFhQyxFQUNmLElBQ0VGLEVBQWEsSUFBSUcsZ0JBQUFBLGdCQUFnQixDQUMvQjlRLEtBQU00USxFQUNOM1EsS0FBTTRRLEdBRVQsQ0FBQyxNQUFPL1UsR0FDUCxNQUFNLElBQUk4TixZQUNSLDBDQUNBLEtBQ0FPLFNBQVNyTyxFQUNaLENBSUgsTUFBTTZSLEVBQWlCZ0QsRUFDbkIsQ0FDRUksTUFBT0osRUFDUHRRLFFBQVN5TyxFQUFtQnpPLFNBRTlCLEdBRUUyUSxFQUFtQixJQUNwQm5ULEVBQVk0RixLQUFLNk0sR0FDbEJELHVCQUF1QixHQUFHQyxJQUFVM0MsRUFBZ0JvQixHQUFnQixRQUVuRWhSLEVBQWMwRixLQUFLNk0sR0FDcEJELHVCQUF1QixHQUFHQyxJQUFVM0MsRUFBZ0JvQixRQUVuRDlRLEVBQWN3RixLQUFLNk0sR0FDcEJELHVCQUF1QixHQUFHQyxJQUFVM0MsTUFLeEMsYUFENkJDLFFBQVFxRCxJQUFJRCxJQUNuQi9ZLEtBQUssTUFDN0IsQ0FvQkF1VixlQUFlNEIsYUFBYVAsRUFBbUJDLEVBQW9CSSxHQUVqRSxNQUFNUCxFQUMwQixXQUE5QkUsRUFBa0JwUixRQUNkLEtBQ0EsR0FBR29SLEVBQWtCcFIsVUFHckJDLEVBQVNtUixFQUFrQm5SLFFBQVU4USxNQUFNOVEsT0FFakQsSUFDRSxNQUFNcVIsRUFBaUIsQ0FBQSxFQXVDdkIsT0FyQ0E3VCxJQUNFLEVBQ0EsaURBQWlEeVQsR0FBYSxhQUdoRUgsTUFBTUUsY0FBZ0JnQyxjQUNwQixJQUNLN0IsRUFBa0JoUixZQUFZNEYsS0FBS3lOLEdBQ3BDdkMsRUFBWSxHQUFHalIsS0FBVWlSLEtBQWF1QyxJQUFNLEdBQUd4VCxLQUFVd1QsT0FHN0QsSUFDS3JDLEVBQWtCOVEsY0FBYzBGLEtBQUtnTSxHQUNoQyxRQUFOQSxFQUNJZCxFQUNFLEdBQUdqUixVQUFlaVIsYUFBcUJjLElBQ3ZDLEdBQUcvUixrQkFBdUIrUixJQUM1QmQsRUFDRSxHQUFHalIsS0FBVWlSLGFBQXFCYyxJQUNsQyxHQUFHL1IsYUFBa0IrUixTQUUxQlosRUFBa0I3USxpQkFBaUJ5RixLQUFLNkosR0FDekNxQixFQUNJLEdBQUdqUixXQUFnQmlSLGdCQUF3QnJCLElBQzNDLEdBQUc1UCxzQkFBMkI0UCxPQUd0Q3VCLEVBQWtCNVEsY0FDbEI2USxFQUNBQyxHQUlGUCxNQUFNRyxVQUFZaUIsZUFBZXBCLE1BQU1FLFNBR3ZDK0IsR0FBQUEsY0FBY3ZCLEVBQVlWLE1BQU1FLFNBQ3pCSyxDQUNSLENBQUMsTUFBT2pULEdBQ1AsTUFBTSxJQUFJOE4sWUFDUix1REFDQSxLQUNBTyxTQUFTck8sRUFDWixDQUNILENDbmRPLFNBQVNxVixrQkFDZEMsV0FBV0MsV0FBYSxXQUN0QixNQUFPLENBQUVDLFNBQVUsRUFDdkIsQ0FDQSxDQWNPOUQsZUFBZStELFlBQVlDLEVBQWV2RSxHQUUvQyxNQUFNMUMsV0FBRUEsRUFBVWtILFdBQUVBLEVBQVVDLE1BQUVBLEVBQUtDLEtBQUVBLEdBQVNQLFdBSWhEQSxXQUFXUSxjQUFnQkYsR0FBTSxFQUFPLENBQUUsRUFBRW5ILEtBRzVDL0osT0FBT3FSLGtCQUFtQixFQUMxQkYsRUFBS1AsV0FBV1UsTUFBTXRiLFVBQVcsUUFBUSxTQUFVdWIsRUFBU0MsRUFBYUMsS0FFdkVELEVBQWNOLEVBQU1NLEVBQWEsQ0FDL0JFLFVBQVcsQ0FDVEMsU0FBUyxHQUVYQyxZQUFhLENBQ1hDLE9BQVEsQ0FDTkMsTUFBTyxDQUNMSCxTQUFTLEtBT2ZJLFFBQVMsQ0FBRSxLQUdBRixRQUFVLElBQUl0UCxTQUFRLFNBQVVzUCxHQUMzQ0EsRUFBT0csV0FBWSxDQUN6QixJQUdTaFMsT0FBT2lTLHFCQUNWalMsT0FBT2lTLG1CQUFxQnJCLFdBQVdzQixTQUFTekksS0FBTSxVQUFVLEtBQzlEekosT0FBT3FSLGtCQUFtQixDQUFJLEtBSWxDRSxFQUFRclcsTUFBTXVPLEtBQU0sQ0FBQytILEVBQWFDLEdBQ3RDLElBRUVOLEVBQUtQLFdBQVd1QixPQUFPbmMsVUFBVyxRQUFRLFNBQVV1YixFQUFTYSxFQUFPdlUsR0FDbEUwVCxFQUFRclcsTUFBTXVPLEtBQU0sQ0FBQzJJLEVBQU92VSxHQUNoQyxJQUdFLE1BQU13VSxFQUFvQixDQUN4QkQsTUFBTyxDQUVMSixXQUFXLEVBRVg1VCxPQUFRNFMsRUFBYzVTLE9BQ3RCQyxNQUFPMlMsRUFBYzNTLE9BRXZCcVQsVUFBVyxDQUVUQyxTQUFTLElBS1BILEVBQWMsSUFBSWMsU0FBUyxVQUFVdEIsRUFBY3BULFFBQXJDLEdBR2RpQixFQUFlLElBQUl5VCxTQUFTLFVBQVV0QixFQUFjblMsZUFBckMsR0FHZkQsRUFBZ0IsSUFBSTBULFNBQVMsVUFBVXRCLEVBQWNwUyxnQkFBckMsR0FHaEIyVCxFQUFlckIsR0FDbkIsRUFDQXJTLEVBQ0EyUyxFQUVBYSxHQUlJRyxFQUFnQi9GLEVBQW1CeE4sU0FDckMsSUFBSXFULFNBQVMsVUFBVTdGLEVBQW1CeE4sV0FBMUMsR0FDQSxLQUdBd04sRUFBbUI5UyxZQUNyQixJQUFJMlksU0FBUyxVQUFXN0YsRUFBbUI5UyxXQUEzQyxDQUF1RDZYLEdBSXJENVMsR0FDRnFTLEVBQVdyUyxHQUliZ1MsV0FBV0ksRUFBYzVhLFFBQVEsWUFBYW1jLEVBQWNDLEdBRzVELE1BQU1DLEVBQWlCMUksSUFHdkIsSUFBSyxNQUFNcUIsS0FBUXFILEVBQ21CLG1CQUF6QkEsRUFBZXJILFdBQ2pCcUgsRUFBZXJILEdBSzFCNkYsRUFBV0wsV0FBV1EsZUFHdEJSLFdBQVdRLGNBQWdCLEVBQzdCLENDNUhBLE1BQU1zQixTQUFXM1ksR0FBWUEsYUFDM0J0QyxVQUFLN0MsWUFBVyxZQUFhLGlCQUM3QixRQUlGLElBQUkrZCxRQUFVLEtBbUNQM0YsZUFBZTRGLGNBQWNDLEdBRWxDLE1BQU1uUixNQUFFQSxFQUFLTixNQUFFQSxHQUFVMkksY0FHakJ4SyxPQUFRdVQsS0FBaUJDLEdBQWlCclIsRUFHNUNzUixFQUFnQixDQUNwQnJSLFVBQVVQLEVBQU1LLGtCQUFtQixRQUNuQ3dSLFlBQWEsTUFDYnRZLEtBQU1rWSxHQUFpQixHQUN2QkssY0FBYyxFQUNkQyxlQUFlLEVBQ2ZDLGNBQWMsRUFDZEMsb0JBQW9CLEVBQ3BCQyxnQkFBaUIsUUFDYlIsR0FBZ0JDLEdBSXRCLElBQUtKLFFBQVMsQ0FFWixJQUFJWSxFQUFXLEVBRWYsTUFBTUMsRUFBT3hHLFVBQ1gsSUFDRXRTLElBQ0UsRUFDQSx5REFBeUQ2WSxPQUkzRFosY0FBZ0JsVyxVQUFVZ1gsT0FBT1QsRUFDbEMsQ0FBQyxNQUFPMVgsR0FRUCxHQVBBRCxhQUNFLEVBQ0FDLEVBQ0Esb0RBSUVpWSxFQUFXLElBT2IsTUFBTWpZLEVBTk5aLElBQUksRUFBRyxzQ0FBc0M2WSx1QkFHdkMsSUFBSW5HLFNBQVNLLEdBQWFpRyxXQUFXakcsRUFBVSxhQUMvQytGLEdBSVQsR0FHSCxVQUNRQSxJQUd5QixVQUEzQlIsRUFBY3JSLFVBQ2hCakgsSUFBSSxFQUFHLDZDQUlMb1ksR0FDRnBZLElBQUksRUFBRyw0Q0FFVixDQUFDLE1BQU9ZLEdBQ1AsTUFBTSxJQUFJOE4sWUFDUixnRUFDQSxLQUNBTyxTQUFTck8sRUFDWixDQUVELElBQUtxWCxRQUNILE1BQU0sSUFBSXZKLFlBQVksMkNBQTRDLElBRXJFLENBR0QsT0FBT3VKLE9BQ1QsQ0FRTzNGLGVBQWUyRyxlQUVoQmhCLFNBQVdBLFFBQVFpQixpQkFDZmpCLFFBQVFrQixRQUVoQmxCLFFBQVUsS0FDVmpZLElBQUksRUFBRyxnQ0FDVCxDQWdCT3NTLGVBQWU4RyxRQUFRQyxHQUU1QixJQUFLcEIsVUFBWUEsUUFBUWlCLFVBQ3ZCLE1BQU0sSUFBSXhLLFlBQVksMENBQTJDLEtBZ0JuRSxHQVpBMkssRUFBYUMsV0FBYXJCLFFBQVFtQixnQkFHNUJDLEVBQWFDLEtBQUtDLGlCQUFnQixTQUdsQ0MsZ0JBQWdCSCxFQUFhQyxNQUduQ0csZUFBZUosRUFBYUMsT0FHdkJELEVBQWFDLE1BQVFELEVBQWFDLEtBQUtJLFdBQzFDLE1BQU0sSUFBSWhMLFlBQVksMkNBQTRDLElBRXRFLENBa0JPNEQsZUFBZXFILFVBQVVOLEVBQWNPLEdBQVksR0FDeEQsSUFDRSxHQUFJUCxFQUFhQyxPQUFTRCxFQUFhQyxLQUFLSSxXQWdCMUMsT0FmSUUsU0FFSVAsRUFBYUMsS0FBS08sS0FBSyxjQUFlLENBQzFDQyxVQUFXLDJCQUlQTixnQkFBZ0JILEVBQWFDLGFBRzdCRCxFQUFhQyxLQUFLUyxVQUFTLEtBQy9CMWYsU0FBUzJmLEtBQUtDLFVBQ1osNERBQTRELEtBRzNELENBRVYsQ0FBQyxNQUFPclosR0FDUEQsYUFDRSxFQUNBQyxFQUNBLHlCQUF5QnlZLEVBQWFhLG1EQUl4Q2IsRUFBYWMsVUFBWTlLLGFBQWF2SixLQUFLRyxVQUFZLENBQ3hELENBQ0QsT0FBTyxDQUNULENBaUJPcU0sZUFBZThILGlCQUFpQmQsRUFBTXZILEdBRTNDLE1BQU1zSSxFQUFvQixHQUdwQjdWLEVBQVl1TixFQUFtQnZOLFVBQ3JDLEdBQUlBLEVBQVcsQ0FDYixNQUFNOFYsRUFBYSxHQVVuQixHQVBJOVYsRUFBVStWLElBQ1pELEVBQVdwWixLQUFLLENBQ2RzWixRQUFTaFcsRUFBVStWLEtBS25CL1YsRUFBVWlXLE1BQ1osSUFBSyxNQUFNblosS0FBUWtELEVBQVVpVyxNQUFPLENBQ2xDLE1BQU1DLEdBQVdwWixFQUFLaEMsV0FBVyxRQUdqQ2diLEVBQVdwWixLQUNUd1osRUFDSSxDQUNFRixRQUFTbmIsR0FBQUEsYUFBYW5ELGdCQUFnQm9GLEdBQU8sU0FFL0MsQ0FDRWtSLElBQUtsUixHQUdkLENBR0gsSUFBSyxNQUFNcVosS0FBY0wsRUFDdkIsSUFDRUQsRUFBa0JuWixXQUFXb1ksRUFBS3NCLGFBQWFELEdBQ2hELENBQUMsTUFBTy9aLEdBQ1BELGFBQWEsRUFBR0MsRUFBTyw4Q0FDeEIsQ0FFSDBaLEVBQVd4YyxPQUFTLEVBR3BCLE1BQU0rYyxFQUFjLEdBQ3BCLEdBQUlyVyxFQUFVc1csSUFBSyxDQUNqQixJQUFJQyxFQUFhdlcsRUFBVXNXLElBQUlFLE1BQU0sdUJBQ3JDLEdBQUlELEVBRUYsSUFBSyxJQUFJRSxLQUFpQkYsRUFDcEJFLElBQ0ZBLEVBQWdCQSxFQUNicGYsUUFBUSxPQUFRLElBQ2hCQSxRQUFRLFVBQVcsSUFDbkJBLFFBQVEsS0FBTSxJQUNkQSxRQUFRLEtBQU0sSUFDZEEsUUFBUSxJQUFLLElBQ2JBLFFBQVEsTUFBTyxJQUNmMEIsT0FHQzBkLEVBQWMzYixXQUFXLFFBQzNCdWIsRUFBWTNaLEtBQUssQ0FDZnNSLElBQUt5SSxJQUVFbEosRUFBbUI3UyxvQkFDNUIyYixFQUFZM1osS0FBSyxDQUNmckUsS0FBTUUsS0FBQUEsS0FBSzdDLFlBQVcrZ0IsTUFRaENKLEVBQVkzWixLQUFLLENBQ2ZzWixRQUFTaFcsRUFBVXNXLElBQUlqZixRQUFRLHNCQUF1QixLQUFPLE1BRy9ELElBQUssTUFBTXFmLEtBQWVMLEVBQ3hCLElBQ0VSLEVBQWtCblosV0FBV29ZLEVBQUs2QixZQUFZRCxHQUMvQyxDQUFDLE1BQU90YSxHQUNQRCxhQUNFLEVBQ0FDLEVBQ0EsK0NBRUgsQ0FFSGlhLEVBQVkvYyxPQUFTLENBQ3RCLENBQ0YsQ0FDRCxPQUFPdWMsQ0FDVCxDQWVPL0gsZUFBZThJLG1CQUFtQjlCLEVBQU1lLEdBQzdDLElBQ0UsSUFBSyxNQUFNZ0IsS0FBWWhCLFFBQ2ZnQixFQUFTQyxnQkFJWGhDLEVBQUtTLFVBQVMsS0FFbEIsR0FBMEIsb0JBQWY3RCxXQUE0QixDQUVyQyxNQUFNcUYsRUFBWXJGLFdBQVdzRixPQUc3QixHQUFJdGdCLE1BQU1DLFFBQVFvZ0IsSUFBY0EsRUFBVXpkLE9BRXhDLElBQUssTUFBTTJkLEtBQVlGLEVBQ3JCRSxHQUFZQSxFQUFTQyxVQUVyQnhGLFdBQVdzRixPQUFPcGYsT0FHdkIsQ0FHRCxTQUFVdWYsR0FBbUJ0aEIsU0FBU3VoQixxQkFBcUIsV0FFckQsSUFBTUMsR0FBa0J4aEIsU0FBU3VoQixxQkFBcUIsYUFFbERFLEdBQWlCemhCLFNBQVN1aEIscUJBQXFCLFFBR3pELElBQUssTUFBTUcsSUFBVyxJQUNqQkosS0FDQUUsS0FDQUMsR0FFSEMsRUFBUUMsUUFDVCxHQUVKLENBQUMsTUFBT3BiLEdBQ1BELGFBQWEsRUFBR0MsRUFBTyw4Q0FDeEIsQ0FDSCxDQVlBMFIsZUFBZWtILGdCQUFnQkYsU0FFdkJBLEVBQUsyQyxXQUFXakUsU0FBVSxDQUFFOEIsVUFBVywyQkFHdkNSLEVBQUtzQixhQUFhLENBQUUvZCxLQUFNRSxLQUFJQSxLQUFDK1csZUFBZ0Isc0JBRy9Dd0YsRUFBS1MsU0FBUzlELGdCQUN0QixDQVdBLFNBQVN3RCxlQUFlSCxHQUV0QixNQUFNdFMsTUFBRUEsR0FBVXFJLGFBR2xCaUssRUFBS3JHLEdBQUcsYUFBYVgsVUFHZmdILEVBQUtJLFVBRVIsSUFJQzFTLEVBQU1uQyxRQUFVbUMsRUFBTUcsaUJBQ3hCbVMsRUFBS3JHLEdBQUcsV0FBWWxTLElBQ2xCUixRQUFRUCxJQUFJLFdBQVdlLEVBQVFvUyxTQUFTLEdBRzlDLENDNWNBLElBQUErSSxZQUFlLElBQU0seVhDSU5DLFlBQUMvWSxHQUFRLDhMQVFsQjhZLDhFQUlFOVksd0NDYURrUCxlQUFlOEosZ0JBQWdCOUMsRUFBTWhELEVBQWV2RSxHQUV6RCxNQUFNc0ksRUFBb0IsR0FFMUIsSUFDRSxJQUFJZ0MsR0FBUSxFQUdaLEdBQUkvRixFQUFjbFQsSUFBSyxDQUlyQixHQUhBcEQsSUFBSSxFQUFHLG1DQUdvQixRQUF2QnNXLEVBQWN0YSxLQUNoQixPQUFPc2EsRUFBY2xULElBSXZCaVosR0FBUSxRQUdGL0MsRUFBSzJDLFdBQVdFLFlBQVk3RixFQUFjbFQsS0FBTSxDQUNwRDBXLFVBQVcsb0JBRW5CLE1BQ005WixJQUFJLEVBQUcsMkNBR0RzWixFQUFLUyxTQUFTMUQsWUFBYUMsRUFBZXZFLEdBTWxEc0ksRUFBa0JuWixjQUNOa1osaUJBQWlCZCxFQUFNdkgsSUFJbkMsTUFBTXVLLEVBQU9ELFFBQ0gvQyxFQUFLUyxVQUFVblcsSUFDbkIsTUFBTTJZLEVBQWFsaUIsU0FBU21pQixjQUMxQixzQ0FJSUMsRUFBY0YsRUFBVzdZLE9BQU9nWixRQUFRaGUsTUFBUWtGLEVBQ2hEK1ksRUFBYUosRUFBVzVZLE1BQU0rWSxRQUFRaGUsTUFBUWtGLEVBVXBELE9BTkF2SixTQUFTMmYsS0FBSzRDLE1BQU1DLEtBQU9qWixFQUkzQnZKLFNBQVMyZixLQUFLNEMsTUFBTUUsT0FBUyxNQUV0QixDQUNMTCxjQUNBRSxhQUNELEdBQ0E3VCxXQUFXd04sRUFBYzFTLGNBQ3RCMFYsRUFBS1MsVUFBUyxLQUVsQixNQUFNMEMsWUFBRUEsRUFBV0UsV0FBRUEsR0FBZXJYLE9BQU80USxXQUFXc0YsT0FBTyxHQU83RCxPQUZBbmhCLFNBQVMyZixLQUFLNEMsTUFBTUMsS0FBTyxFQUVwQixDQUNMSixjQUNBRSxhQUNELEtBSURJLEVBQUVBLEVBQUNDLEVBQUVBLFNBQVlDLGVBQWUzRCxHQUdoQzRELEVBQWlCcmUsS0FBS3NlLElBQzFCdGUsS0FBS3VlLEtBQUtkLEVBQUtHLGFBQWVuRyxFQUFjNVMsU0FJeEMyWixFQUFnQnhlLEtBQUtzZSxJQUN6QnRlLEtBQUt1ZSxLQUFLZCxFQUFLSyxZQUFjckcsRUFBYzNTLFFBVTdDLElBQUkyWixFQUVKLGFBUk1oRSxFQUFLaUUsWUFBWSxDQUNyQjdaLE9BQVF3WixFQUNSdlosTUFBTzBaLEVBQ1BHLGtCQUFtQm5CLEVBQVEsRUFBSXZULFdBQVd3TixFQUFjMVMsU0FLbEQwUyxFQUFjdGEsTUFDcEIsSUFBSyxNQUNIc2hCLFFBQWVHLFdBQVduRSxHQUMxQixNQUNGLElBQUssTUFDTCxJQUFLLE9BQ0hnRSxRQUFlSSxhQUNicEUsRUFDQWhELEVBQWN0YSxLQUNkLENBQ0UySCxNQUFPMFosRUFDUDNaLE9BQVF3WixFQUNSSCxJQUNBQyxLQUVGMUcsRUFBY2xTLHNCQUVoQixNQUNGLElBQUssTUFDSGtaLFFBQWVLLFdBQ2JyRSxFQUNBNEQsRUFDQUcsRUFDQS9HLEVBQWNsUyxzQkFFaEIsTUFDRixRQUNFLE1BQU0sSUFBSXNLLFlBQ1IsdUNBQXVDNEgsRUFBY3RhLFFBQ3JELEtBTU4sYUFETW9mLG1CQUFtQjlCLEVBQU1lLEdBQ3hCaUQsQ0FDUixDQUFDLE1BQU8xYyxHQUVQLGFBRE13YSxtQkFBbUI5QixFQUFNZSxHQUN4QnpaLENBQ1IsQ0FDSCxDQWNBMFIsZUFBZTJLLGVBQWUzRCxHQUM1QixPQUFPQSxFQUFLc0UsTUFBTSxvQkFBcUI3QixJQUNyQyxNQUFNZ0IsRUFBRUEsRUFBQ0MsRUFBRUEsRUFBQ3JaLE1BQUVBLEVBQUtELE9BQUVBLEdBQVdxWSxFQUFROEIsd0JBQ3hDLE1BQU8sQ0FDTGQsSUFDQUMsSUFDQXJaLFFBQ0FELE9BQVE3RSxLQUFLaWYsTUFBTXBhLEVBQVMsRUFBSUEsRUFBUyxLQUMxQyxHQUVMLENBYUE0TyxlQUFlbUwsV0FBV25FLEdBQ3hCLE9BQU9BLEVBQUtzRSxNQUNWLGdDQUNDN0IsR0FBWUEsRUFBUWdDLFdBRXpCLENBa0JBekwsZUFBZW9MLGFBQWFwRSxFQUFNdGQsRUFBTWdpQixFQUFNNVosR0FDNUMsT0FBT3NPLFFBQVF1TCxLQUFLLENBQ2xCM0UsRUFBSzRFLFdBQVcsQ0FDZGxpQixPQUNBZ2lCLE9BQ0FHLFNBQVUsU0FDVkMsVUFBVSxFQUNWQyxrQkFBa0IsRUFDbEJDLHVCQUF1QixLQUNWLFFBQVR0aUIsRUFBaUIsQ0FBRXVpQixRQUFTLElBQU8sQ0FBQSxFQUV2Q0MsZUFBd0IsT0FBUnhpQixJQUVsQixJQUFJMFcsU0FBUSxDQUFDK0wsRUFBVTdMLElBQ3JCb0csWUFDRSxJQUFNcEcsRUFBTyxJQUFJbEUsWUFBWSx3QkFBeUIsT0FDdER0SyxHQUF3QixTQUloQyxDQWlCQWtPLGVBQWVxTCxXQUFXckUsRUFBTTVWLEVBQVFDLEVBQU9TLEdBRTdDLGFBRE1rVixFQUFLb0YsaUJBQWlCLFVBQ3JCcEYsRUFBS3FGLElBQUksQ0FFZGpiLE9BQVFBLEVBQVMsRUFDakJDLFFBQ0F3YSxTQUFVLFNBQ1ZoWixRQUFTZixHQUF3QixNQUVyQyxDQ25RQSxJQUFJMEIsS0FBTyxLQUdYLE1BQU04WSxVQUFZLENBQ2hCQyxpQkFBa0IsRUFDbEJDLGlCQUFrQixFQUNsQkMsZUFBZ0IsRUFDaEJDLGVBQWdCLEVBQ2hCQyxtQkFBb0IsRUFDcEJDLHVCQUF3QixFQUN4QkMsMkJBQTRCLEVBQzVCQyxVQUFXLEVBQ1hDLGlCQUFrQixHQXFCYi9NLGVBQWVnTixTQUFTQyxFQUFhcEgsU0FFcENELGNBQWNDLEdBRXBCLElBTUUsR0FMQW5ZLElBQ0UsRUFDQSw4Q0FBOEN1ZixFQUFZeFosbUJBQW1Cd1osRUFBWXZaLGVBR3ZGRixLQUtGLFlBSkE5RixJQUNFLEVBQ0EseUVBTUF1ZixFQUFZeFosV0FBYXdaLEVBQVl2WixhQUN2Q3VaLEVBQVl4WixXQUFhd1osRUFBWXZaLFlBSXZDRixLQUFPLElBQUkwWixLQUFBQSxLQUFLLElBRVhDLFNBQVNGLEdBQ1p2YixJQUFLdWIsRUFBWXhaLFdBQ2pCOUIsSUFBS3NiLEVBQVl2WixXQUNqQjBaLHFCQUFzQkgsRUFBWXJaLGVBQ2xDeVosb0JBQXFCSixFQUFZcFosY0FDakN5WixxQkFBc0JMLEVBQVluWixlQUNsQ3laLGtCQUFtQk4sRUFBWWxaLFlBQy9CeVosMEJBQTJCUCxFQUFZalosb0JBQ3ZDeVosbUJBQW9CUixFQUFZaFosZUFDaEN5WixzQkFBc0IsSUFJeEJsYSxLQUFLbU4sR0FBRyxXQUFXWCxNQUFPK0ksSUFFeEIsTUFBTTRFLFFBQW9CdEcsVUFBVTBCLEdBQVUsR0FDOUNyYixJQUNFLEVBQ0EseUJBQXlCcWIsRUFBU25CLGdEQUFnRCtGLEtBQ25GLElBR0huYSxLQUFLbU4sR0FBRyxrQkFBa0IsQ0FBQ2lOLEVBQVU3RSxLQUNuQ3JiLElBQ0UsRUFDQSx5QkFBeUJxYixFQUFTbkIsMENBRXBDbUIsRUFBUy9CLEtBQU8sSUFBSSxJQUd0QixNQUFNNkcsRUFBbUIsR0FFekIsSUFBSyxJQUFJL04sRUFBSSxFQUFHQSxFQUFJbU4sRUFBWXhaLFdBQVlxTSxJQUMxQyxJQUNFLE1BQU1pSixRQUFpQnZWLEtBQUtzYSxVQUFVQyxRQUN0Q0YsRUFBaUJqZixLQUFLbWEsRUFDdkIsQ0FBQyxNQUFPemEsR0FDUEQsYUFBYSxFQUFHQyxFQUFPLCtDQUN4QixDQUlIdWYsRUFBaUJ0WSxTQUFTd1QsSUFDeEJ2VixLQUFLd2EsUUFBUWpGLEVBQVMsSUFHeEJyYixJQUNFLEVBQ0EsNEJBQTJCbWdCLEVBQWlCcmlCLE9BQVMsU0FBU3FpQixFQUFpQnJpQixvQ0FBc0MsS0FFeEgsQ0FBQyxNQUFPOEMsR0FDUCxNQUFNLElBQUk4TixZQUNSLDZEQUNBLEtBQ0FPLFNBQVNyTyxFQUNaLENBQ0gsQ0FZTzBSLGVBQWVpTyxXQUlwQixHQUhBdmdCLElBQUksRUFBRyw2REFHSDhGLEtBQU0sQ0FFUixJQUFLLE1BQU0wYSxLQUFVMWEsS0FBSzJhLEtBQ3hCM2EsS0FBS3dhLFFBQVFFLEVBQU9uRixVQUlqQnZWLEtBQUs0YSxrQkFDRjVhLEtBQUs0VixVQUNYMWIsSUFBSSxFQUFHLDRDQUVUOEYsS0FBTyxJQUNSLE9BR0ttVCxjQUNSLENBbUJPM0csZUFBZXFPLFNBQVN4ZCxHQUM3QixJQUFJeWQsRUFFSixJQVlFLEdBWEE1Z0IsSUFBSSxFQUFHLGdEQUdMNGUsVUFBVUMsaUJBR1IxYixFQUFRMkMsS0FBS2IsY0FDZjRiLGVBSUcvYSxLQUNILE1BQU0sSUFBSTRJLFlBQ1IsdURBQ0EsS0FLSixNQUFNb1MsRUFBaUIzaUIsY0FHdkIsSUFDRTZCLElBQUksRUFBRyxxQ0FHUDRnQixRQUFxQjlhLEtBQUtzYSxVQUFVQyxRQUdoQ2xkLEVBQVF5QixPQUFPSyxjQUNqQmpGLElBQ0UsRUFDQSxnQkFBZW1ELEVBQVE0ZCxVQUFZLFlBQVk1ZCxFQUFRNGQsZ0JBQWtCLElBQ3pFLGtDQUFrQ0QsU0FHdkMsQ0FBQyxNQUFPbGdCLEdBQ1AsTUFBTSxJQUFJOE4sWUFDUixVQUNFdkwsRUFBUTRkLFVBQVksWUFBWTVkLEVBQVE0ZCxnQkFBa0IsMERBQ0pELFNBQ3hELEtBQ0E3UixTQUFTck8sRUFDWixDQUdELEdBRkFaLElBQUksRUFBRyxxQ0FFRjRnQixFQUFhdEgsS0FHaEIsTUFEQXNILEVBQWF6RyxVQUFZaFgsRUFBUTJDLEtBQUtHLFVBQVksRUFDNUMsSUFBSXlJLFlBQ1IsbUVBQ0EsS0FLSixNQUFNc1MsRUFBWXhqQixpQkFFbEJ3QyxJQUNFLEVBQ0EseUJBQXlCNGdCLEVBQWExRywyQ0FJeEMsTUFBTStHLEVBQWdCOWlCLGNBR2hCbWYsUUFBZWxCLGdCQUNuQndFLEVBQWF0SCxLQUNiblcsRUFBUUgsT0FDUkcsRUFBUWtCLGFBSVYsR0FBSWlaLGFBQWtCM08sTUFtQnBCLEtBTnVCLDBCQUFuQjJPLEVBQU92YyxVQUVUNmYsRUFBYXpHLFVBQVloWCxFQUFRMkMsS0FBS0csVUFBWSxFQUNsRDJhLEVBQWF0SCxLQUFPLE1BSUosaUJBQWhCZ0UsRUFBT3BPLE1BQ1ksMEJBQW5Cb08sRUFBT3ZjLFFBRUQsSUFBSTJOLFlBQ1IsVUFDRXZMLEVBQVE0ZCxVQUFZLFlBQVk1ZCxFQUFRNGQsZ0JBQWtCLG1IQUU1RDlSLFNBQVNxTyxHQUVMLElBQUk1TyxZQUNSLFVBQ0V2TCxFQUFRNGQsVUFBWSxZQUFZNWQsRUFBUTRkLGdCQUFrQixzQ0FDeEJFLFVBQ3BDaFMsU0FBU3FPLEdBS1huYSxFQUFReUIsT0FBT0ssY0FDakJqRixJQUNFLEVBQ0EsZ0JBQWVtRCxFQUFRNGQsVUFBWSxZQUFZNWQsRUFBUTRkLGdCQUFrQixJQUN6RSxzQ0FBc0NFLFVBSzFDbmIsS0FBS3dhLFFBQVFNLEdBSWIsTUFDTU0sRUFEVTFqQixpQkFDYXdqQixFQVM3QixPQVBBcEMsVUFBVVEsV0FBYThCLEVBQ3ZCdEMsVUFBVVMsaUJBQ1JULFVBQVVRLFlBQWNSLFVBQVVFLGlCQUVwQzllLElBQUksRUFBRyw0QkFBNEJraEIsUUFHNUIsQ0FDTDVELFNBQ0FuYSxVQUVILENBQUMsTUFBT3ZDLEdBT1AsT0FORWdlLFVBQVVHLGVBRVI2QixHQUNGOWEsS0FBS3dhLFFBQVFNLEdBR1RoZ0IsQ0FDUCxDQUNILENBcUJPLFNBQVN1Z0IsZUFDZCxPQUFPdkMsU0FDVCxDQVVPLFNBQVN3QyxrQkFDZCxNQUFPLENBQ0xwZCxJQUFLOEIsS0FBSzlCLElBQ1ZDLElBQUs2QixLQUFLN0IsSUFDVndjLEtBQU0zYSxLQUFLdWIsVUFDWEMsVUFBV3hiLEtBQUt5YixVQUNoQkMsV0FBWTFiLEtBQUt1YixVQUFZdmIsS0FBS3liLFVBQ2xDRSxnQkFBaUIzYixLQUFLNGIscUJBQ3RCQyxlQUFnQjdiLEtBQUs4YixvQkFDckJDLG1CQUFvQi9iLEtBQUtnYyx3QkFDekJDLGdCQUFpQmpjLEtBQUtpYyxnQkFBZ0Jqa0IsT0FDdENra0IsWUFDRWxjLEtBQUt1YixVQUNMdmIsS0FBS3liLFVBQ0x6YixLQUFLNGIscUJBQ0w1YixLQUFLOGIsb0JBQ0w5YixLQUFLZ2Msd0JBQ0xoYyxLQUFLaWMsZ0JBQWdCamtCLE9BRTNCLENBU08sU0FBUytpQixjQUNkLE1BQU03YyxJQUNKQSxFQUFHQyxJQUNIQSxFQUFHd2MsS0FDSEEsRUFBSWEsVUFDSkEsRUFBU0UsV0FDVEEsRUFBVUMsZ0JBQ1ZBLEVBQWVFLGVBQ2ZBLEVBQWNFLG1CQUNkQSxFQUFrQkUsZ0JBQ2xCQSxFQUFlQyxZQUNmQSxHQUNFWixrQkFFSnBoQixJQUFJLEVBQUcsMkRBQTJEZ0UsTUFDbEVoRSxJQUFJLEVBQUcsMkRBQTJEaUUsTUFDbEVqRSxJQUFJLEVBQUcsd0NBQXdDeWdCLE1BQy9DemdCLElBQUksRUFBRyx3Q0FBd0NzaEIsTUFDL0N0aEIsSUFDRSxFQUNBLCtEQUErRHdoQixNQUVqRXhoQixJQUNFLEVBQ0EsMERBQTBEeWhCLE1BRTVEemhCLElBQ0UsRUFDQSx5REFBeUQyaEIsTUFFM0QzaEIsSUFDRSxFQUNBLDJEQUEyRDZoQixNQUU3RDdoQixJQUNFLEVBQ0EsMkRBQTJEK2hCLE1BRTdEL2hCLElBQUksRUFBRyx1Q0FBdUNnaUIsS0FDaEQsQ0FXQSxTQUFTdkMsU0FBU0YsR0FDaEIsTUFBTyxDQWNMMEMsT0FBUTNQLFVBRU4sTUFBTStHLEVBQWUsQ0FDbkJhLEdBQUlnSSxLQUFBQSxLQUVKL0gsVUFBV3RiLEtBQUtFLE1BQU1GLEtBQUtzakIsVUFBWTVDLEVBQVl0WixVQUFZLEtBR2pFLElBRUUsTUFBTW1jLEVBQVk1a0IsaUJBY2xCLGFBWE00YixRQUFRQyxHQUdkclosSUFDRSxFQUNBLHlCQUF5QnFaLEVBQWFhLDZDQUNwQzFjLGlCQUFtQjRrQixRQUtoQi9JLENBQ1IsQ0FBQyxNQUFPelksR0FLUCxNQUpBWixJQUNFLEVBQ0EseUJBQXlCcVosRUFBYWEscURBRWxDdFosQ0FDUCxHQWdCSHloQixTQUFVL1AsTUFBTytHLEdBaUJWQSxFQUFhQyxLQVNkRCxFQUFhQyxLQUFLSSxZQUNwQjFaLElBQ0UsRUFDQSx5QkFBeUJxWixFQUFhYSx5REFFakMsR0FJTGIsRUFBYUMsS0FBS2dKLFlBQVlDLFVBQ2hDdmlCLElBQ0UsRUFDQSx5QkFBeUJxWixFQUFhYSx3REFFakMsS0FLUHFGLEVBQVl0WixhQUNWb1QsRUFBYWMsVUFBWW9GLEVBQVl0WixhQUV2Q2pHLElBQ0UsRUFDQSx5QkFBeUJxWixFQUFhYSx5Q0FBeUNxRixFQUFZdFoseUNBRXRGLElBbENQakcsSUFDRSxFQUNBLHlCQUF5QnFaLEVBQWFhLHNEQUVqQyxHQThDWHdCLFFBQVNwSixNQUFPK0csSUFNZCxHQUxBclosSUFDRSxFQUNBLHlCQUF5QnFaLEVBQWFhLDhCQUdwQ2IsRUFBYUMsT0FBU0QsRUFBYUMsS0FBS0ksV0FDMUMsSUFFRUwsRUFBYUMsS0FBS2tKLG1CQUFtQixhQUNyQ25KLEVBQWFDLEtBQUtrSixtQkFBbUIsV0FDckNuSixFQUFhQyxLQUFLa0osbUJBQW1CLHVCQUcvQm5KLEVBQWFDLEtBQUtILE9BQ3pCLENBQUMsTUFBT3ZZLEdBS1AsTUFKQVosSUFDRSxFQUNBLHlCQUF5QnFaLEVBQWFhLG1EQUVsQ3RaLENBQ1AsQ0FDRixFQUdQLENDeGtCTyxTQUFTNmhCLFNBQVN4bEIsR0FFdkIsTUFBTXFJLEVBQVMsSUFBSW9kLE1BQUFBLE1BQU0sSUFBSXBkLE9BTTdCLE9BSGVxZCxVQUFVcmQsR0FHWG1kLFNBQVN4bEIsRUFBTyxDQUFFMmxCLFNBQVUsQ0FBQyxrQkFDN0MsQ0NBQSxJQUFJdGUsb0JBQXFCLEVBcUJsQmdPLGVBQWV1USxhQUFhMWYsR0FFakMsSUFBSUEsSUFBV0EsRUFBUUgsT0F3Q3JCLE1BQU0sSUFBSTBMLFlBQ1Isa0tBQ0EsV0F4Q0lvVSxZQUNKLENBQUU5ZixPQUFRRyxFQUFRSCxPQUFRcUIsWUFBYWxCLEVBQVFrQixjQUMvQ2lPLE1BQU8xUixFQUFPbWlCLEtBRVosR0FBSW5pQixFQUNGLE1BQU1BLEVBSVIsTUFBTTRDLElBQUVBLEVBQUd2SCxRQUFFQSxFQUFPRCxLQUFFQSxHQUFTK21CLEVBQUs1ZixRQUFRSCxPQUc1QyxJQUNNUSxFQUVGK1IsR0FBYUEsY0FDWCxHQUFHdFosRUFBUUUsTUFBTSxLQUFLQyxTQUFXLGNBQ2pDWSxVQUFVK2xCLEVBQUt6RixPQUFRdGhCLElBSXpCdVosR0FBYUEsY0FDWHRaLEdBQVcsU0FBU0QsSUFDWCxRQUFUQSxFQUFpQmtCLE9BQU9DLEtBQUs0bEIsRUFBS3pGLE9BQVEsVUFBWXlGLEVBQUt6RixPQUdoRSxDQUFDLE1BQU8xYyxHQUNQLE1BQU0sSUFBSThOLFlBQ1Isc0NBQ0EsS0FDQU8sU0FBU3JPLEVBQ1osT0FHSzJmLFVBQVUsR0FTeEIsQ0FzQk9qTyxlQUFlMFEsWUFBWTdmLEdBRWhDLEtBQUlBLEdBQVdBLEVBQVFILFFBQVVHLEVBQVFILE9BQU9LLE9BNEU5QyxNQUFNLElBQUlxTCxZQUNSLCtHQUNBLEtBOUVtRCxDQUVyRCxNQUFNdVUsRUFBaUIsR0FHdkIsSUFBSyxJQUFJQyxLQUFRL2YsRUFBUUgsT0FBT0ssTUFBTWxILE1BQU0sTUFBUSxHQUNsRCttQixFQUFPQSxFQUFLL21CLE1BQU0sS0FDRSxJQUFoQittQixFQUFLcGxCLE9BQ1BtbEIsRUFBZS9oQixLQUNiNGhCLFlBQ0UsQ0FDRTlmLE9BQVEsSUFDSEcsRUFBUUgsT0FDWEMsT0FBUWlnQixFQUFLLEdBQ2JqbkIsUUFBU2luQixFQUFLLElBRWhCN2UsWUFBYWxCLEVBQVFrQixjQUV2QixDQUFDekQsRUFBT21pQixLQUVOLEdBQUluaUIsRUFDRixNQUFNQSxFQUlSLE1BQU00QyxJQUFFQSxFQUFHdkgsUUFBRUEsRUFBT0QsS0FBRUEsR0FBUyttQixFQUFLNWYsUUFBUUgsT0FHNUMsSUFDTVEsRUFFRitSLEdBQWFBLGNBQ1gsR0FBR3RaLEVBQVFFLE1BQU0sS0FBS0MsU0FBVyxjQUNqQ1ksVUFBVStsQixFQUFLekYsT0FBUXRoQixJQUl6QnVaLEdBQWFBLGNBQ1h0WixFQUNTLFFBQVRELEVBQ0lrQixPQUFPQyxLQUFLNGxCLEVBQUt6RixPQUFRLFVBQ3pCeUYsRUFBS3pGLE9BR2QsQ0FBQyxNQUFPMWMsR0FDUCxNQUFNLElBQUk4TixZQUNSLHNDQUNBLEtBQ0FPLFNBQVNyTyxFQUNaLE1BS1BaLElBQUksRUFBRyx1REFLWCxNQUFNbWpCLFFBQXFCelEsUUFBUTBRLFdBQVdILFNBR3hDMUMsV0FHTjRDLEVBQWF0YixTQUFRLENBQUN5VixFQUFRM00sS0FFeEIyTSxFQUFPK0YsUUFDVDFpQixhQUNFLEVBQ0EyYyxFQUFPK0YsT0FDUCwrQkFBK0IxUyxFQUFRLHNDQUUxQyxHQUVQLENBTUEsQ0FvQ08yQixlQUFld1EsWUFBWVEsRUFBa0JDLEdBQ2xELElBRUUsSUFBSzdsQixTQUFTNGxCLEdBQ1osTUFBTSxJQUFJNVUsWUFDUixxRkFDQSxLQUtKLE1BQU12TCxFQUFVOE0sYUFBYWxWLFNBQVNzVSxjQUFlLENBQ25Eck0sT0FBUXNnQixFQUFpQnRnQixPQUN6QnFCLFlBQWFpZixFQUFpQmpmLGNBSTFCaVMsRUFBZ0JuVCxFQUFRSCxPQU05QixHQUhBaEQsSUFBSSxFQUFHLDJDQUdzQixPQUF6QnNXLEVBQWNyVCxPQUFpQixDQUdqQyxJQUFJdWdCLEVBRkp4akIsSUFBSSxFQUFHLG1EQUdQLElBRUV3akIsRUFBY25rQixHQUFZQSxhQUN4Qm5ELGdCQUFnQm9hLEVBQWNyVCxRQUM5QixPQUVILENBQUMsTUFBT3JDLEdBQ1AsTUFBTSxJQUFJOE4sWUFDUixtREFDQSxLQUNBTyxTQUFTck8sRUFDWixDQUdELEdBQUkwVixFQUFjclQsT0FBTzdELFNBQVMsUUFFaENrWCxFQUFjbFQsSUFBTW9nQixNQUNmLEtBQUlsTixFQUFjclQsT0FBTzdELFNBQVMsU0FJdkMsTUFBTSxJQUFJc1AsWUFDUixrREFDQSxLQUpGNEgsRUFBY3BULE1BQVFzZ0IsQ0FNdkIsQ0FDRixDQUdELEdBQTBCLE9BQXRCbE4sRUFBY2xULElBQWMsQ0FDOUJwRCxJQUFJLEVBQUcscURBR0xtaEIsZUFBZWpDLHVCQUdqQixNQUFNNUIsUUFBZW1HLGVBQ25CaEIsU0FBU25NLEVBQWNsVCxLQUN2QkQsR0FPRixRQUhFZ2UsZUFBZW5DLGVBR1Z1RSxFQUFZLEtBQU1qRyxFQUMxQixDQUdELEdBQTRCLE9BQXhCaEgsRUFBY3BULE9BQTRDLE9BQTFCb1QsRUFBY25ULFFBQWtCLENBQ2xFbkQsSUFBSSxFQUFHLHNEQUdMbWhCLGVBQWVoQywyQkFHakIsTUFBTTdCLFFBQWVvRyxtQkFDbkJwTixFQUFjcFQsT0FBU29ULEVBQWNuVCxRQUNyQ0EsR0FPRixRQUhFZ2UsZUFBZWxDLG1CQUdWc0UsRUFBWSxLQUFNakcsRUFDMUIsQ0FHRCxPQUFPaUcsRUFDTCxJQUFJN1UsWUFDRixnSkFDQSxLQUdMLENBQUMsTUFBTzlOLEdBQ1AsT0FBTzJpQixFQUFZM2lCLEVBQ3BCLENBQ0gsQ0FTTyxTQUFTK2lCLHdCQUNkLE9BQU9yZixrQkFDVCxDQVVPLFNBQVNzZixzQkFBc0JsbEIsR0FDcEM0RixtQkFBcUI1RixDQUN2QixDQWtCQTRULGVBQWVtUixlQUFlSSxFQUFlMWdCLEdBRTNDLEdBQzJCLGlCQUFsQjBnQixJQUNOQSxFQUFjN08sUUFBUSxTQUFXLEdBQUs2TyxFQUFjN08sUUFBUSxVQUFZLEdBWXpFLE9BVkFoVixJQUFJLEVBQUcsaUNBR1BtRCxFQUFRSCxPQUFPSSxJQUFNeWdCLEVBR3JCMWdCLEVBQVFILE9BQU9FLE1BQVEsS0FDdkJDLEVBQVFILE9BQU9HLFFBQVUsS0FHbEIyZ0IsZUFBZTNnQixHQUV0QixNQUFNLElBQUl1TCxZQUFZLG1DQUFvQyxJQUU5RCxDQWtCQTRELGVBQWVvUixtQkFBbUJHLEVBQWUxZ0IsR0FDL0NuRCxJQUFJLEVBQUcsdUNBR1AsTUFBTWlSLEVBQXFCTCxnQkFDekJpVCxHQUNBLEVBQ0ExZ0IsRUFBUWtCLFlBQVlDLG9CQUl0QixHQUN5QixPQUF2QjJNLEdBQzhCLGlCQUF2QkEsSUFDTkEsRUFBbUIzUixXQUFXLE9BQzlCMlIsRUFBbUI3UixTQUFTLEtBRTdCLE1BQU0sSUFBSXNQLFlBQ1Isb1BBQ0EsS0FXSixPQU5BdkwsRUFBUUgsT0FBT0UsTUFBUStOLEVBR3ZCOU4sRUFBUUgsT0FBT0ksSUFBTSxLQUdkMGdCLGVBQWUzZ0IsRUFDeEIsQ0FjQW1QLGVBQWV3UixlQUFlM2dCLEdBQzVCLE1BQVFILE9BQVFzVCxFQUFlalMsWUFBYTBOLEdBQXVCNU8sRUFrQ25FLE9BL0JBbVQsRUFBY3RhLEtBQU9LLFFBQVFpYSxFQUFjdGEsS0FBTXNhLEVBQWNyYSxTQUcvRHFhLEVBQWNyYSxRQUFVRixXQUFXdWEsRUFBY3RhLEtBQU1zYSxFQUFjcmEsU0FHckVxYSxFQUFjNWEsT0FBU0QsVUFBVTZhLEVBQWM1YSxRQUcvQ3NFLElBQ0UsRUFDQSwrQkFBK0IrUixFQUFtQnpOLG1CQUFxQixVQUFZLGlCQUlyRnlmLG1CQUFtQmhTLEVBQW9CQSxFQUFtQnpOLG9CQUcxRDBmLHNCQUNFMU4sRUFDQXZFLEVBQW1CN1MsbUJBQ25CNlMsRUFBbUJ6TixvQkFJckJuQixFQUFRSCxPQUFTLElBQ1pzVCxLQUNBMk4sZUFBZTNOLElBSWJxSyxTQUFTeGQsRUFDbEIsQ0FxQkEsU0FBUzhnQixlQUFlM04sR0FFdEIsTUFBUW9CLE1BQU93TSxFQUFjbE4sVUFBV21OLEdBQ3RDN04sRUFBY25ULFNBQVd5TixnQkFBZ0IwRixFQUFjcFQsU0FBVSxHQUczRHdVLE1BQU8wTSxFQUFvQnBOLFVBQVdxTixHQUM1Q3pULGdCQUFnQjBGLEVBQWNwUyxpQkFBa0IsR0FHMUN3VCxNQUFPNE0sRUFBbUJ0TixVQUFXdU4sR0FDM0MzVCxnQkFBZ0IwRixFQUFjblMsZ0JBQWlCLEVBTTNDUCxFQUFRbkYsWUFDWkksS0FBS29GLElBQ0gsR0FDQXBGLEtBQUttRixJQUNIc1MsRUFBYzFTLE9BQ1p1Z0IsR0FBa0J2Z0IsT0FDbEJ5Z0IsR0FBd0J6Z0IsT0FDeEIyZ0IsR0FBdUIzZ0IsT0FDdkIwUyxFQUFjdlMsY0FDZCxFQUNGLElBR0osR0E0Qkl1WSxFQUFPLENBQUU1WSxPQXZCYjRTLEVBQWM1UyxRQUNkeWdCLEdBQWtCSyxjQUNsQk4sR0FBY3hnQixRQUNkMmdCLEdBQXdCRyxjQUN4QkosR0FBb0IxZ0IsUUFDcEI2Z0IsR0FBdUJDLGNBQ3ZCRixHQUFtQjVnQixRQUNuQjRTLEVBQWN6UyxlQUNkLElBZXFCRixNQVhyQjJTLEVBQWMzUyxPQUNkd2dCLEdBQWtCTSxhQUNsQlAsR0FBY3ZnQixPQUNkMGdCLEdBQXdCSSxhQUN4QkwsR0FBb0J6Z0IsT0FDcEI0Z0IsR0FBdUJFLGFBQ3ZCSCxHQUFtQjNnQixPQUNuQjJTLEVBQWN4UyxjQUNkLElBRzRCRixTQUc5QixJQUFLLElBQUs4Z0IsRUFBT2htQixLQUFVckQsT0FBTytVLFFBQVFrTSxHQUN4Q0EsRUFBS29JLEdBQ2MsaUJBQVZobUIsR0FBc0JBLEVBQU03QyxRQUFRLFNBQVUsSUFBTTZDLEVBSS9ELE9BQU80ZCxDQUNULENBa0JBLFNBQVN5SCxtQkFBbUJoUyxFQUFvQnpOLEdBRTlDLEdBQUlBLEVBQW9CLENBRXRCLEdBQTRDLGlCQUFqQ3lOLEVBQW1Cdk4sVUFFNUJ1TixFQUFtQnZOLFVBQVltZ0IsaUJBQzdCNVMsRUFBbUJ2TixVQUNuQnVOLEVBQW1CN1Msb0JBQ25CLFFBRUcsSUFBSzZTLEVBQW1Cdk4sVUFDN0IsSUFFRXVOLEVBQW1Cdk4sVUFBWW1nQixpQkFDN0J0bEIsR0FBQUEsYUFBYW5ELGdCQUFnQixrQkFBbUIsUUFDaEQ2VixFQUFtQjdTLG9CQUNuQixFQUVILENBQUMsTUFBTzBCLEdBQ1BaLElBQUksRUFBRyw0REFDUixDQUlILElBRUUrUixFQUFtQjlTLFdBQWFELFdBQzlCK1MsRUFBbUI5UyxXQUNuQjhTLEVBQW1CN1MsbUJBRXRCLENBQUMsTUFBTzBCLEdBQ1BELGFBQWEsRUFBR0MsRUFBTyw4Q0FHdkJtUixFQUFtQjlTLFdBQWEsSUFDakMsQ0FHRCxJQUVFOFMsRUFBbUJ4TixTQUFXdkYsV0FDNUIrUyxFQUFtQnhOLFNBQ25Cd04sRUFBbUI3UyxvQkFDbkIsRUFFSCxDQUFDLE1BQU8wQixHQUNQRCxhQUFhLEVBQUdDLEVBQU8sNENBR3ZCbVIsRUFBbUJ4TixTQUFXLElBQy9CLENBR0csQ0FBQyxVQUFNOUQsR0FBVzNFLFNBQVNpVyxFQUFtQjlTLGFBQ2hEZSxJQUFJLEVBQUcsdURBSUwsQ0FBQyxVQUFNUyxHQUFXM0UsU0FBU2lXLEVBQW1CeE4sV0FDaER2RSxJQUFJLEVBQUcscURBSUwsQ0FBQyxVQUFNUyxHQUFXM0UsU0FBU2lXLEVBQW1Cdk4sWUFDaER4RSxJQUFJLEVBQUcscURBRWIsTUFJSSxHQUNFK1IsRUFBbUJ4TixVQUNuQndOLEVBQW1Cdk4sV0FDbkJ1TixFQUFtQjlTLFdBUW5CLE1BTEE4UyxFQUFtQnhOLFNBQVcsS0FDOUJ3TixFQUFtQnZOLFVBQVksS0FDL0J1TixFQUFtQjlTLFdBQWEsS0FHMUIsSUFBSXlQLFlBQ1Isb0dBQ0EsSUFJUixDQWtCQSxTQUFTaVcsaUJBQ1BuZ0IsRUFBWSxLQUNadEYsRUFDQW9GLEdBR0EsTUFBTXNnQixFQUFlLENBQUMsS0FBTSxNQUFPLFNBRW5DLElBQUlDLEVBQW1CcmdCLEVBQ25Cc2dCLEdBQW1CLEVBR3ZCLEdBQUk1bEIsR0FBc0JzRixFQUFVcEYsU0FBUyxTQUMzQyxJQUNFeWxCLEVBQW1CalUsZ0JBQ2pCdlIsR0FBQUEsYUFBYW5ELGdCQUFnQnNJLEdBQVksU0FDekMsRUFDQUYsRUFFUixDQUFNLE1BQ0EsT0FBTyxJQUNSLE1BR0R1Z0IsRUFBbUJqVSxnQkFBZ0JwTSxHQUFXLEVBQU9GLEdBR2pEdWdCLElBQXFCM2xCLFVBQ2hCMmxCLEVBQWlCcEssTUFLNUIsSUFBSyxNQUFNc0ssS0FBWUYsRUFDaEJELEVBQWE5b0IsU0FBU2lwQixHQUVmRCxJQUNWQSxHQUFtQixVQUZaRCxFQUFpQkUsR0FPNUIsT0FBS0QsR0FLREQsRUFBaUJwSyxRQUNuQm9LLEVBQWlCcEssTUFBUW9LLEVBQWlCcEssTUFBTWxTLEtBQUs1SyxHQUFTQSxFQUFLSixXQUM5RHNuQixFQUFpQnBLLE9BQVNvSyxFQUFpQnBLLE1BQU0zYyxRQUFVLFdBQ3ZEK21CLEVBQWlCcEssT0FLckJvSyxHQVpFLElBYVgsQ0FvQkEsU0FBU2Isc0JBQ1AxTixFQUNBcFgsRUFDQW9GLEdBR0EsQ0FBQyxnQkFBaUIsZ0JBQWdCdUQsU0FBU21kLElBQ3pDLElBRU0xTyxFQUFjME8sS0FHZDlsQixHQUNzQyxpQkFBL0JvWCxFQUFjME8sSUFDckIxTyxFQUFjME8sR0FBYTVsQixTQUFTLFNBR3BDa1gsRUFBYzBPLEdBQWVwVSxnQkFDM0J2UixHQUFBQSxhQUFhbkQsZ0JBQWdCb2EsRUFBYzBPLElBQWUsU0FDMUQsRUFDQTFnQixHQUlGZ1MsRUFBYzBPLEdBQWVwVSxnQkFDM0IwRixFQUFjME8sSUFDZCxFQUNBMWdCLEdBSVAsQ0FBQyxNQUFPMUQsR0FDUEQsYUFDRSxFQUNBQyxFQUNBLGlCQUFpQm9rQix5QkFJbkIxTyxFQUFjME8sR0FBZSxJQUM5QixLQUlDLENBQUMsVUFBTXZrQixHQUFXM0UsU0FBU3dhLEVBQWNwUyxnQkFDM0NsRSxJQUFJLEVBQUcsMERBSUwsQ0FBQyxVQUFNUyxHQUFXM0UsU0FBU3dhLEVBQWNuUyxlQUMzQ25FLElBQUksRUFBRyx3REFFWCxDQ2gwQkEsTUFBTWlsQixTQUFXLEdBU1YsU0FBU0MsU0FBU2hMLEdBQ3ZCK0ssU0FBUy9qQixLQUFLZ1osRUFDaEIsQ0FRTyxTQUFTaUwsaUJBQ2RubEIsSUFBSSxFQUFHLDJEQUNQLElBQUssTUFBTWthLEtBQU0rSyxTQUNmRyxjQUFjbEwsR0FDZG1MLGFBQWFuTCxFQUVqQixDQ2ZBLFNBQVNvTCxtQkFBbUIxa0IsRUFBTzJrQixFQUFTeFMsRUFBVXlTLEdBVXBELE9BUkE3a0IsYUFBYSxFQUFHQyxHQUdtQixnQkFBL0J5TyxhQUFhM0ksTUFBTUMsZ0JBQ2QvRixFQUFNSyxNQUlSdWtCLEVBQUs1a0IsRUFDZCxDQVlBLFNBQVM2a0Isc0JBQXNCN2tCLEVBQU8ya0IsRUFBU3hTLEVBQVV5UyxHQUV2RCxNQUFNemtCLFFBQUVBLEVBQU9FLE1BQUVBLEdBQVVMLEVBR3JCaU8sRUFBYWpPLEVBQU1pTyxZQUFjLElBR3ZDa0UsRUFBUzJTLE9BQU83VyxHQUFZOFcsS0FBSyxDQUFFOVcsYUFBWTlOLFVBQVNFLFNBQzFELENBT2UsU0FBUzJrQixnQkFBZ0JDLEdBRXRDQSxFQUFJQyxJQUFJUixvQkFHUk8sRUFBSUMsSUFBSUwsc0JBQ1YsQ0M1Q2UsU0FBU00sdUJBQXVCRixFQUFLRyxHQUNsRCxJQUVFLEdBQUlBLEVBQW9CbmhCLE9BQVEsQ0FDOUIsTUFBTTlELEVBQ0oseUVBR0lrbEIsRUFBYyxDQUNsQjNnQixPQUFRMGdCLEVBQW9CMWdCLFFBQVUsRUFDdENELFlBQWEyZ0IsRUFBb0IzZ0IsYUFBZSxHQUNoREUsTUFBT3lnQixFQUFvQnpnQixPQUFTLEVBQ3BDQyxXQUFZd2dCLEVBQW9CeGdCLGFBQWMsRUFDOUNDLFFBQVN1Z0IsRUFBb0J2Z0IsU0FBVyxLQUN4Q0MsVUFBV3NnQixFQUFvQnRnQixXQUFhLE1BSTFDdWdCLEVBQVl6Z0IsWUFDZHFnQixFQUFJaGhCLE9BQU8sZUFJYixNQUFNcWhCLEVBQVVDLFVBQVUsQ0FFeEJDLFNBQStCLEdBQXJCSCxFQUFZM2dCLE9BQWMsSUFFcEMrZ0IsTUFBT0osRUFBWTVnQixZQUVuQmloQixRQUFTTCxFQUFZMWdCLE1BQ3JCZ2hCLFFBQVMsQ0FBQ2hCLEVBQVN4UyxLQUNqQkEsRUFBU3lULE9BQU8sQ0FDZGIsS0FBTSxLQUNKNVMsRUFBUzJTLE9BQU8sS0FBS2UsS0FBSyxDQUFFMWxCLFdBQVUsRUFFeEMybEIsUUFBUyxLQUNQM1QsRUFBUzJTLE9BQU8sS0FBS2UsS0FBSzFsQixFQUFRLEdBRXBDLEVBRUo0bEIsS0FBT3BCLEdBR3FCLE9BQXhCVSxFQUFZeGdCLFNBQ2MsT0FBMUJ3Z0IsRUFBWXZnQixXQUNaNmYsRUFBUXFCLE1BQU14ckIsTUFBUTZxQixFQUFZeGdCLFNBQ2xDOGYsRUFBUXFCLE1BQU1DLGVBQWlCWixFQUFZdmdCLFlBRTNDMUYsSUFBSSxFQUFHLDJDQUNBLEtBT2I2bEIsRUFBSUMsSUFBSUksR0FFUmxtQixJQUNFLEVBQ0EsOENBQThDaW1CLEVBQVk1Z0IsNEJBQTRCNGdCLEVBQVkzZ0IsOENBQThDMmdCLEVBQVl6Z0IsY0FFL0osQ0FDRixDQUFDLE1BQU81RSxHQUNQLE1BQU0sSUFBSThOLFlBQ1IseUVBQ0EsS0FDQU8sU0FBU3JPLEVBQ1osQ0FDSCxDQ3pEQSxTQUFTa21CLHNCQUFzQnZCLEVBQVN4UyxFQUFVeVMsR0FDaEQsSUFFRSxNQUFNdUIsRUFBY3hCLEVBQVF5QixRQUFRLGlCQUFtQixHQUd2RCxJQUNHRCxFQUFZanJCLFNBQVMsc0JBQ3JCaXJCLEVBQVlqckIsU0FBUyx1Q0FDckJpckIsRUFBWWpyQixTQUFTLHVCQUV0QixNQUFNLElBQUk0UyxZQUNSLGlIQUNBLEtBS0osT0FBTzhXLEdBQ1IsQ0FBQyxNQUFPNWtCLEdBQ1AsT0FBTzRrQixFQUFLNWtCLEVBQ2IsQ0FDSCxDQW1CQSxTQUFTcW1CLHNCQUFzQjFCLEVBQVN4UyxFQUFVeVMsR0FDaEQsSUFFRSxNQUFNeEwsRUFBT3VMLEVBQVF2TCxLQUdmK0csRUFBWW1CLEtBQUFBLEtBR2xCLElBQUtsSSxHQUFRcGMsY0FBY29jLEdBUXpCLE1BUEFoYSxJQUNFLEVBQ0EseUJBQXlCK2dCLHlCQUN2QndFLEVBQVF5QixRQUFRLG9CQUFzQnpCLEVBQVEyQixXQUFXQywyREFJdkQsSUFBSXpZLFlBQ1IseUJBQXlCcVMsOEpBQ3pCLEtBS0osTUFBTXpjLEVBQXFCcWYsd0JBR3JCemdCLEVBQVEwTixnQkFFWm9KLEVBQUs5VyxPQUFTOFcsRUFBSzdXLFNBQVc2VyxFQUFLL1csUUFBVStXLEVBQUsrSSxNQUVsRCxFQUVBemUsR0FJRixHQUFjLE9BQVZwQixJQUFtQjhXLEVBQUs1VyxJQVExQixNQVBBcEQsSUFDRSxFQUNBLHlCQUF5QitnQix5QkFDdkJ3RSxFQUFReUIsUUFBUSxvQkFBc0J6QixFQUFRMkIsV0FBV0MsMkZBQ21CblcsS0FBS2EsVUFBVW1JLE9BR3pGLElBQUl0TCxZQUNSLFlBQVlxUyxzUkFDWixLQUtKLEdBQUkvRyxFQUFLNVcsS0FBT3JGLHVCQUF1QmljLEVBQUs1VyxLQUMxQyxNQUFNLElBQUlzTCxZQUNSLFlBQVlxUyxpTUFDWixLQTBDSixPQXJDQXdFLEVBQVE2QixpQkFBbUIsQ0FFekJyRyxZQUNBL2QsT0FBUSxDQUNORSxRQUNBRSxJQUFLNFcsRUFBSzVXLElBQ1ZuSCxRQUNFK2QsRUFBSy9kLFNBQ0wsR0FBR3NwQixFQUFROEIsT0FBT0MsVUFBWSxXQUFXdE4sRUFBS2hlLE1BQVEsUUFDeERBLEtBQU1nZSxFQUFLaGUsS0FDWE4sT0FBUXNlLEVBQUt0ZSxPQUNiOEgsSUFBS3dXLEVBQUt4VyxJQUNWQyxXQUFZdVcsRUFBS3ZXLFdBQ2pCQyxPQUFRc1csRUFBS3RXLE9BQ2JDLE1BQU9xVyxFQUFLclcsTUFDWkMsTUFBT29XLEVBQUtwVyxNQUNaTSxjQUFlME0sZ0JBQ2JvSixFQUFLOVYsZUFDTCxFQUNBSSxHQUVGSCxhQUFjeU0sZ0JBQ1pvSixFQUFLN1YsY0FDTCxFQUNBRyxJQUdKRCxZQUFhLENBQ1hDLHFCQUNBcEYsb0JBQW9CLEVBQ3BCRCxXQUFZK2EsRUFBSy9hLFdBQ2pCc0YsU0FBVXlWLEVBQUt6VixTQUNmQyxVQUFXb00sZ0JBQWdCb0osRUFBS3hWLFdBQVcsRUFBTUYsS0FLOUNraEIsR0FDUixDQUFDLE1BQU81a0IsR0FDUCxPQUFPNGtCLEVBQUs1a0IsRUFDYixDQUNILENBT2UsU0FBUzJtQixxQkFBcUIxQixHQUUzQ0EsRUFBSTJCLEtBQUssQ0FBQyxJQUFLLGNBQWVWLHVCQUc5QmpCLEVBQUkyQixLQUFLLENBQUMsSUFBSyxjQUFlUCxzQkFDaEMsQ0M3S0EsTUFBTVEsYUFBZSxDQUNuQkMsSUFBSyxZQUNMQyxLQUFNLGFBQ05DLElBQUssWUFDTGpKLElBQUssa0JBQ0x2YixJQUFLLGlCQWdCUGtQLGVBQWV1VixjQUFjdEMsRUFBU3hTLEVBQVV5UyxHQUM5QyxJQUVFLE1BQU1zQyxFQUFpQjNwQixjQUd2QixJQUFJNHBCLEdBQW9CLEVBQ3hCeEMsRUFBUXlDLE9BQU8vVSxHQUFHLFNBQVVnVixJQUN0QkEsSUFDRkYsR0FBb0IsRUFDckIsSUFJSCxNQUFNdFYsRUFBaUI4UyxFQUFRNkIsaUJBR3pCckcsRUFBWXRPLEVBQWVzTyxVQUdqQy9nQixJQUFJLEVBQUcscUJBQXFCK2dCLDRDQUd0QitCLFlBQVlyUSxHQUFnQixDQUFDN1IsRUFBT21pQixLQUt4QyxHQUhBd0MsRUFBUXlDLE9BQU94RixtQkFBbUIsU0FHOUJ1RixFQUNGL25CLElBQ0UsRUFDQSxxQkFBcUIrZ0IsbUZBSHpCLENBU0EsR0FBSW5nQixFQUNGLE1BQU1BLEVBSVIsSUFBS21pQixJQUFTQSxFQUFLekYsT0FTakIsTUFSQXRkLElBQ0UsRUFDQSxxQkFBcUIrZ0IscUJBQ25Cd0UsRUFBUXlCLFFBQVEsb0JBQ2hCekIsRUFBUTJCLFdBQVdDLG1EQUNpQnBFLEVBQUt6RixXQUd2QyxJQUFJNU8sWUFDUixxQkFBcUJxUyx5R0FDckIsS0FLSixHQUFJZ0MsRUFBS3pGLE9BQVEsQ0FDZnRkLElBQ0UsRUFDQSxxQkFBcUIrZ0IseUNBQWlEK0csVUFJeEUsTUFBTTlyQixLQUFFQSxFQUFJd0gsSUFBRUEsRUFBR0MsV0FBRUEsRUFBVXhILFFBQUVBLEdBQVk4bUIsRUFBSzVmLFFBQVFILE9BR3hELE9BQUlRLEVBQ0t1UCxFQUFTMFQsS0FBS3pwQixVQUFVK2xCLEVBQUt6RixPQUFRdGhCLEtBSTlDK1csRUFBU21WLE9BQU8sZUFBZ0JULGFBQWF6ckIsSUFBUyxhQUdqRHlILEdBQ0hzUCxFQUFTb1YsV0FBV2xzQixHQUlOLFFBQVRELEVBQ0grVyxFQUFTMFQsS0FBSzFELEVBQUt6RixRQUNuQnZLLEVBQVMwVCxLQUFLdnBCLE9BQU9DLEtBQUs0bEIsRUFBS3pGLE9BQVEsV0FDNUMsQ0FsREEsQ0FrREEsR0FFSixDQUFDLE1BQU8xYyxHQUNQLE9BQU80a0IsRUFBSzVrQixFQUNiLENBQ0gsQ0FTZSxTQUFTd25CLGFBQWF2QyxHQUtuQ0EsRUFBSTJCLEtBQUssSUFBS0ssZUFNZGhDLEVBQUkyQixLQUFLLGFBQWNLLGNBQ3pCLENDcElBLE1BQU1RLGdCQUFrQixJQUFJL3FCLEtBR3RCZ3JCLFlBQWN0WCxLQUFLeEMsTUFDdkJuUCxHQUFBQSxhQUFhdEMsS0FBQUEsS0FBSzdDLFlBQVcsZ0JBQWlCLFNBSTFDcXVCLGFBQWUsR0FHZkMsZUFBaUIsSUFHakJDLFdBQWEsR0FVbkIsU0FBU0MsMEJBQ1AsT0FBT0gsYUFBYS9YLFFBQU8sQ0FBQ21ZLEVBQUdDLElBQU1ELEVBQUlDLEdBQUcsR0FBS0wsYUFBYXpxQixNQUNoRSxDQVVBLFNBQVMrcUIsb0JBQ1AsT0FBT0MsYUFBWSxLQUNqQixNQUFNQyxFQUFRNUgsZUFDUjZILEVBQ3VCLElBQTNCRCxFQUFNbEssaUJBQ0YsRUFDQ2tLLEVBQU1qSyxpQkFBbUJpSyxFQUFNbEssaUJBQW9CLElBRTFEMEosYUFBYXJuQixLQUFLOG5CLEdBQ2RULGFBQWF6cUIsT0FBUzJxQixZQUN4QkYsYUFBYW5zQixPQUNkLEdBQ0Fvc0IsZUFDTCxDQVNlLFNBQVNTLGFBQWFwRCxHQUduQ1gsU0FBUzJELHFCQUtUaEQsRUFBSS9TLElBQUksV0FBVyxDQUFDeVMsRUFBU3hTLEVBQVV5UyxLQUNyQyxJQUNFeGxCLElBQUksRUFBRyxxQ0FFUCxNQUFNK29CLEVBQVE1SCxlQUNSK0gsRUFBU1gsYUFBYXpxQixPQUN0QnFyQixFQUFnQlQsMEJBR3RCM1YsRUFBUzBULEtBQUssQ0FFWmYsT0FBUSxLQUNSMEQsU0FBVWYsZ0JBQ1ZnQixPQUFRLEdBQUd4cUIsS0FBS3lxQixPQUFPOXJCLGlCQUFtQjZxQixnQkFBZ0I1cUIsV0FBYSxJQUFPLGNBRzlFOHJCLGNBQWVqQixZQUFZL2xCLFFBQzNCaW5CLGtCQUFtQjVVLHVCQUduQjZVLGtCQUFtQlYsRUFBTTFKLGlCQUN6QnFLLGlCQUFrQlgsRUFBTWxLLGlCQUN4QjhLLGlCQUFrQlosRUFBTWpLLGlCQUN4QjhLLGNBQWViLEVBQU1oSyxlQUNyQjhLLFlBQWNkLEVBQU1qSyxpQkFBbUJpSyxFQUFNbEssaUJBQW9CLElBR2pFL1ksS0FBTXNiLGtCQUdOOEgsU0FDQUMsZ0JBQ0Fwb0IsUUFDRThILE1BQU1zZ0IsS0FBbUJaLGFBQWF6cUIsT0FDbEMsb0VBQ0EsUUFBUW9yQixtQ0FBd0NDLEVBQWNXLFFBQVEsT0FHNUVDLFdBQVloQixFQUFNL0osZUFDbEJnTCxZQUFhakIsRUFBTTlKLG1CQUNuQmdMLG1CQUFvQmxCLEVBQU03Six1QkFDMUJnTCxvQkFBcUJuQixFQUFNNUosNEJBRTlCLENBQUMsTUFBT3ZlLEdBQ1AsT0FBTzRrQixFQUFLNWtCLEVBQ2IsSUFFTCxDQzlHZSxTQUFTdXBCLFNBQVN0RSxHQUkvQkEsRUFBSS9TLElBQUl6RCxhQUFhN0ksR0FBR0MsT0FBUyxLQUFLLENBQUM4ZSxFQUFTeFMsRUFBVXlTLEtBQ3hELElBQ0V4bEIsSUFBSSxFQUFHLHFDQUVQK1MsRUFBU3FYLFNBQVNydEIsS0FBSUEsS0FBQzdDLFlBQVcsU0FBVSxjQUFlLENBQ3pEbXdCLGNBQWMsR0FFakIsQ0FBQyxNQUFPenBCLEdBQ1AsT0FBTzRrQixFQUFLNWtCLEVBQ2IsSUFFTCxDQ2ZlLFNBQVMwcEIsb0JBQW9CekUsR0FLMUNBLEVBQUkyQixLQUFLLCtCQUErQmxWLE1BQU9pVCxFQUFTeFMsRUFBVXlTLEtBQ2hFLElBQ0V4bEIsSUFBSSxFQUFHLDBDQUdQLE1BQU11cUIsRUFBYWpjLEtBQUsvRSx1QkFHeEIsSUFBS2doQixJQUFlQSxFQUFXenNCLE9BQzdCLE1BQU0sSUFBSTRRLFlBQ1IsaUhBQ0EsS0FLSixNQUFNOGIsRUFBUWpGLEVBQVF6UyxJQUFJLFdBRzFCLElBQUswWCxHQUFTQSxJQUFVRCxFQUN0QixNQUFNLElBQUk3YixZQUNSLDJFQUNBLEtBS0osSUFBSW9HLEVBQWF5USxFQUFROEIsT0FBT3ZTLFdBQ2hDLElBQUlBLEVBbUJGLE1BQU0sSUFBSXBHLFlBQVkscUNBQXNDLEtBbEI1RCxVQUVRbUcsd0JBQXdCQyxFQUMvQixDQUFDLE1BQU9sVSxHQUNQLE1BQU0sSUFBSThOLFlBQ1IsNkJBQTZCOU4sRUFBTUcsVUFDbkMsS0FDQWtPLFNBQVNyTyxFQUNaLENBR0RtUyxFQUFTMlMsT0FBTyxLQUFLZSxLQUFLLENBQ3hCNVgsV0FBWSxJQUNaMmEsa0JBQW1CNVUsdUJBQ25CN1QsUUFBUywrQ0FBK0MrVCxNQU03RCxDQUFDLE1BQU9sVSxHQUNQLE9BQU80a0IsRUFBSzVrQixFQUNiLElBRUwsQ0MxQ0EsTUFBTTZwQixjQUFnQixJQUFJQyxJQUdwQjdFLElBQU04RSxVQXNCTHJZLGVBQWVzWSxZQUFZQyxHQUNoQyxJQUVFLE1BQU0xbkIsRUFBVTRNLGNBQWMsQ0FDNUJuTCxPQUFRaW1CLElBT1YsS0FIQUEsRUFBZ0IxbkIsRUFBUXlCLFFBR0xDLFNBQVdnaEIsSUFDNUIsTUFBTSxJQUFJblgsWUFDUixtRkFDQSxLQU1KLE1BQU1vYyxFQUErQyxLQUE1QkQsRUFBYzdsQixZQUFxQixLQUd0RCtsQixFQUFVQyxPQUFPQyxnQkFHakJDLEVBQVNGLE9BQU8sQ0FDcEJELFVBQ0FJLE9BQVEsQ0FDTkMsVUFBV04sS0EyQ2YsR0F0Q0FqRixJQUFJd0YsUUFBUSxnQkFHWnhGLElBQUlDLElBQ0Z3RixLQUFLLENBQ0hDLFFBQVMsQ0FBQyxPQUFRLE1BQU8sY0FNN0IxRixJQUFJQyxLQUFJLENBQUNQLEVBQVN4UyxFQUFVeVMsS0FDMUJ6UyxFQUFTeVksSUFBSSxnQkFBaUIsUUFDOUJoRyxHQUFNLElBSVJLLElBQUlDLElBQ0Y2RSxRQUFRaEYsS0FBSyxDQUNYVSxNQUFPeUUsS0FLWGpGLElBQUlDLElBQ0Y2RSxRQUFRYyxXQUFXLENBQ2pCQyxVQUFVLEVBQ1ZyRixNQUFPeUUsS0FLWGpGLElBQUlDLElBQUlvRixFQUFPUyxRQUdmOUYsSUFBSUMsSUFBSTZFLFFBQVFpQixPQUFPN3VCLEtBQUlBLEtBQUM3QyxZQUFXLGFBR2xDMndCLEVBQWNsbEIsSUFBSUMsTUFBTyxDQUU1QixNQUFNaW1CLEVBQWF4WSxLQUFLeVksYUFBYWpHLEtBR3JDa0csMkJBQTJCRixHQUczQkEsRUFBV0csT0FBT25CLEVBQWM5bEIsS0FBTThsQixFQUFjL2xCLE1BQU0sS0FFeEQybEIsY0FBY2UsSUFBSVgsRUFBYzlsQixLQUFNOG1CLEdBRXRDN3JCLElBQ0UsRUFDQSxtQ0FBbUM2cUIsRUFBYy9sQixRQUFRK2xCLEVBQWM5bEIsUUFDeEUsR0FFSixDQUdELEdBQUk4bEIsRUFBY2xsQixJQUFJZCxPQUFRLENBRTVCLElBQUl6SixFQUFLNndCLEVBRVQsSUFFRTd3QixRQUFZOHdCLFNBQVFBLFNBQ2xCbnZCLEtBQUlBLEtBQUNiLGdCQUFnQjJ1QixFQUFjbGxCLElBQUlFLFVBQVcsY0FDbEQsUUFJRm9tQixRQUFhQyxTQUFRQSxTQUNuQm52QixLQUFJQSxLQUFDYixnQkFBZ0IydUIsRUFBY2xsQixJQUFJRSxVQUFXLGNBQ2xELE9BRUgsQ0FBQyxNQUFPakYsR0FDUFosSUFDRSxFQUNBLHFEQUFxRDZxQixFQUFjbGxCLElBQUlFLHNEQUUxRSxDQUVELEdBQUl6SyxHQUFPNndCLEVBQU0sQ0FFZixNQUFNRSxFQUFjL1ksTUFBTTBZLGFBQWEsQ0FBRTF3QixNQUFLNndCLFFBQVFwRyxLQUd0RGtHLDJCQUEyQkksR0FHM0JBLEVBQVlILE9BQU9uQixFQUFjbGxCLElBQUlaLEtBQU04bEIsRUFBYy9sQixNQUFNLEtBRTdEMmxCLGNBQWNlLElBQUlYLEVBQWNsbEIsSUFBSVosS0FBTW9uQixHQUUxQ25zQixJQUNFLEVBQ0Esb0NBQW9DNnFCLEVBQWMvbEIsUUFBUStsQixFQUFjbGxCLElBQUlaLFFBQzdFLEdBRUosQ0FDRixDQUdEZ2hCLHVCQUF1QkYsSUFBS2dGLEVBQWN6bEIsY0FHMUNtaUIscUJBQXFCMUIsS0FHckJ1QyxhQUFhdkMsS0FDYm9ELGFBQWFwRCxLQUNic0UsU0FBU3RFLEtBQ1R5RSxvQkFBb0J6RSxLQUdwQkQsZ0JBQWdCQyxJQUNqQixDQUFDLE1BQU9qbEIsR0FDUCxNQUFNLElBQUk4TixZQUNSLHFEQUNBLEtBQ0FPLFNBQVNyTyxFQUNaLENBQ0gsQ0FPTyxTQUFTd3JCLGVBRWQsR0FBSTNCLGNBQWNuTyxLQUFPLEVBQUcsQ0FDMUJ0YyxJQUFJLEVBQUcsaUNBR1AsSUFBSyxNQUFPK0UsRUFBTUgsS0FBVzZsQixjQUMzQjdsQixFQUFPdVUsT0FBTSxLQUNYc1IsY0FBYzRCLE9BQU90bkIsR0FDckIvRSxJQUFJLEVBQUcsbUNBQW1DK0UsS0FBUSxHQUd2RCxDQUNILENBU08sU0FBU3VuQixhQUNkLE9BQU83QixhQUNULENBU08sU0FBUzhCLGFBQ2QsT0FBTzVCLE9BQ1QsQ0FTTyxTQUFTNkIsU0FDZCxPQUFPM0csR0FDVCxDQVlPLFNBQVM0RyxtQkFBbUJ6RyxHQUVqQyxNQUFNN2lCLEVBQVU0TSxjQUFjLENBQzVCbkwsT0FBUSxDQUNOUSxhQUFjNGdCLEtBS2xCRCx1QkFBdUJGLElBQUsxaUIsRUFBUXlCLE9BQU9vaEIsb0JBQzdDLENBVU8sU0FBU0YsSUFBSWpwQixLQUFTNnZCLEdBQzNCN0csSUFBSUMsSUFBSWpwQixLQUFTNnZCLEVBQ25CLENBVU8sU0FBUzVaLElBQUlqVyxLQUFTNnZCLEdBQzNCN0csSUFBSS9TLElBQUlqVyxLQUFTNnZCLEVBQ25CLENBVU8sU0FBU2xGLEtBQUszcUIsS0FBUzZ2QixHQUM1QjdHLElBQUkyQixLQUFLM3FCLEtBQVM2dkIsRUFDcEIsQ0FTQSxTQUFTWCwyQkFBMkJubkIsR0FDbENBLEVBQU9xTyxHQUFHLGVBQWUsQ0FBQ3JTLEVBQU9vbkIsS0FDL0JybkIsYUFDRSxFQUNBQyxFQUNBLDBCQUEwQkEsRUFBTUcsK0JBRWxDaW5CLEVBQU90TSxTQUFTLElBR2xCOVcsRUFBT3FPLEdBQUcsU0FBVXJTLElBQ2xCRCxhQUFhLEVBQUdDLEVBQU8sMEJBQTBCQSxFQUFNRyxVQUFVLElBR25FNkQsRUFBT3FPLEdBQUcsY0FBZStVLElBQ3ZCQSxFQUFPL1UsR0FBRyxTQUFVclMsSUFDbEJELGFBQWEsRUFBR0MsRUFBTywwQkFBMEJBLEVBQU1HLFVBQVUsR0FDakUsR0FFTixDQUVBLElBQWU2RCxPQUFBLENBQ2JnbUIsd0JBQ0F3QiwwQkFDQUUsc0JBQ0FDLHNCQUNBQyxjQUNBQyxzQ0FDQTNHLFFBQ0FoVCxRQUNBMFUsV0N2VktsVixlQUFlcWEsZ0JBQWdCQyxFQUFXLFNBRXpDbGEsUUFBUTBRLFdBQVcsQ0FFdkIrQixpQkFHQWlILGVBR0E3TCxhQUlGbGlCLFFBQVF3dUIsS0FBS0QsRUFDZixDQ2VPdGEsZUFBZXdhLFdBQVdDLEVBQWMsSUFFN0MsTUFBTTVwQixFQUFVNE0sY0FBY2dkLEdBQWEsR0FHM0NuSixzQkFBc0J6Z0IsRUFBUWtCLFlBQVlDLG9CQUcxQ25ELFlBQVlnQyxFQUFRM0QsU0FHaEIyRCxFQUFRdUQsTUFBTUUsc0JBQ2hCb21CLG9DQUlJdFosb0JBQW9CdlEsRUFBUWIsV0FBWWEsRUFBUXlCLE9BQU9NLGFBR3ZEb2EsU0FBU25jLEVBQVEyQyxLQUFNM0MsRUFBUXBCLFVBQVU5QixLQUNqRCxDQVNBLFNBQVMrc0IsOEJBQ1BodEIsSUFBSSxFQUFHLHNEQUdQM0IsUUFBUTRVLEdBQUcsUUFBU2dhLElBQ2xCanRCLElBQUksRUFBRyxzQ0FBc0NpdEIsS0FBUSxJQUl2RDV1QixRQUFRNFUsR0FBRyxVQUFVWCxNQUFPcEQsRUFBTStkLEtBQ2hDanRCLElBQUksRUFBRyxpQkFBaUJrUCxzQkFBeUIrZCxZQUMzQ04saUJBQWlCLElBSXpCdHVCLFFBQVE0VSxHQUFHLFdBQVdYLE1BQU9wRCxFQUFNK2QsS0FDakNqdEIsSUFBSSxFQUFHLGlCQUFpQmtQLHNCQUF5QitkLFlBQzNDTixpQkFBaUIsSUFJekJ0dUIsUUFBUTRVLEdBQUcsVUFBVVgsTUFBT3BELEVBQU0rZCxLQUNoQ2p0QixJQUFJLEVBQUcsaUJBQWlCa1Asc0JBQXlCK2QsWUFDM0NOLGlCQUFpQixJQUl6QnR1QixRQUFRNFUsR0FBRyxxQkFBcUJYLE1BQU8xUixFQUFPc08sS0FDNUN2TyxhQUFhLEVBQUdDLEVBQU8saUJBQWlCc08sa0JBQ2xDeWQsZ0JBQWdCLEVBQUUsR0FFNUIsQ0FFQSxJQUFlaGMsTUFBQSxJQUVWL0wsT0FHSHlLLHNCQUNBRSxrQ0FDQWMsZ0NBR0F5YyxzQkFDQWpLLDBCQUNBRyx3QkFDQUYsd0JBR0F2QyxrQkFDQW9NLGdDQUdBM3NCLFFBQ0FXLDBCQUNBWSxZQUFhLFNBQVVuQixHQVNyQm1CLFlBUGdCd08sY0FBYyxDQUM1QnZRLFFBQVMsQ0FDUFksV0FLZ0JaLFFBQVFZLE1BQzdCLEVBQ0RvQixxQkFBc0IsU0FBVS9CLEdBUzlCK0IscUJBUGdCdU8sY0FBYyxDQUM1QnZRLFFBQVMsQ0FDUEMsZUFLeUJELFFBQVFDLFVBQ3RDLEVBQ0RnQyxrQkFBbUIsU0FBVUosRUFBTUMsRUFBTTVCLEdBRXZDLE1BQU15RCxFQUFVNE0sY0FBYyxDQUM1QnZRLFFBQVMsQ0FDUDZCLE9BQ0FDLE9BQ0E1QixZQUtKK0Isa0JBQ0UwQixFQUFRM0QsUUFBUTZCLEtBQ2hCOEIsRUFBUTNELFFBQVE4QixLQUNoQjZCLEVBQVEzRCxRQUFRRSxPQUVuQiJ9 +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),promises=require("fs/promises"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?e:path.join(__dirname$1,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(){return deepCopy(globalOptions)}function updateOptions(e,t=!1){return _mergeOptions(t?deepCopy(globalOptions):globalOptions,e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:path.join(__dirname$1,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIGpvaW4gfSBmcm9tICdwYXRoJztcclxuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCc7XHJcblxyXG5jb25zdCBNQVhfQkFDS09GRl9BVFRFTVBUUyA9IDY7XHJcblxyXG4vLyBUaGUgZGlyZWN0b3J5IHBhdGhcclxuZXhwb3J0IGNvbnN0IF9fZGlybmFtZSA9IGZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLi4vLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbmQgc3RhbmRhcmRpemVzIHRleHQgYnkgcmVwbGFjaW5nIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2VcclxuICogY2hhcmFjdGVycyB3aXRoIGEgc2luZ2xlIHNwYWNlIGFuZCB0cmltbWluZyBhbnkgbGVhZGluZyBvciB0cmFpbGluZ1xyXG4gKiB3aGl0ZXNwYWNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJUZXh0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IC0gVGhlIGlucHV0IHRleHQgdG8gYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtSZWdFeHB9IFtydWxlPS9cXHNcXHMrL2ddIC0gVGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBydWxlIHRvIG1hdGNoXHJcbiAqIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgdGhlICcvXFxzXFxzKy9nJyBSZWdFeHAuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcmVwbGFjZXI9JyAnXSAtIFRoZSBzdHJpbmcgdXNlZCB0byByZXBsYWNlIG11bHRpcGxlXHJcbiAqIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgdGhlICcgJyBzdHJpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjbGVhcmVkIGFuZCBzdGFuZGFyZGl6ZWQgdGV4dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhclRleHQodGV4dCwgcnVsZSA9IC9cXHNcXHMrL2csIHJlcGxhY2VyID0gJyAnKSB7XHJcbiAgcmV0dXJuIHRleHQucmVwbGFjZUFsbChydWxlLCByZXBsYWNlcikudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIGRlZXAgY29weSBvZiB0aGUgZ2l2ZW4gb2JqZWN0IG9yIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZGVlcENvcHlcclxuICpcclxuICogQHBhcmFtIHsoT2JqZWN0fEFycmF5KX0gb2JqQXJyIC0gVGhlIG9iamVjdCBvciBhcnJheSB0byBiZSBkZWVwbHkgY29waWVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxBcnJheSl9IFRoZSBkZWVwIGNvcHkgb2YgdGhlIHByb3ZpZGVkIG9iamVjdCBvciBhcnJheS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBkZWVwQ29weShvYmpBcnIpIHtcclxuICAvLyBJZiB0aGUgYG9iakFycmAgaXMgbnVsbCBvciBub3Qgb2YgdGhlIGBvYmplY3RgIHR5cGUsIHJldHVybiBpdFxyXG4gIGlmIChvYmpBcnIgPT09IG51bGwgfHwgdHlwZW9mIG9iakFyciAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmpBcnI7XHJcbiAgfVxyXG5cclxuICAvLyBQcmVwYXJlIGVpdGhlciBhIG5ldyBhcnJheSBvciBhIG5ldyBvYmplY3RcclxuICBjb25zdCBvYmpBcnJDb3B5ID0gQXJyYXkuaXNBcnJheShvYmpBcnIpID8gW10gOiB7fTtcclxuXHJcbiAgLy8gUmVjdXJzaXZlbHkgY29weSBlYWNoIHByb3BlcnR5XHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqQXJyKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iakFyciwga2V5KSkge1xyXG4gICAgICBvYmpBcnJDb3B5W2tleV0gPSBkZWVwQ29weShvYmpBcnJba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNvcGllZCBvYmplY3RcclxuICByZXR1cm4gb2JqQXJyQ29weTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEltcGxlbWVudHMgYW4gZXhwb25lbnRpYWwgYmFja29mZiBzdHJhdGVneSBmb3IgcmV0cnlpbmcgYSBmdW5jdGlvbiB1bnRpbFxyXG4gKiBhIGNlcnRhaW4gbnVtYmVyIG9mIGF0dGVtcHRzIGFyZSByZWFjaGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGV4cEJhY2tvZmZcclxuICpcclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gLSBUaGUgZnVuY3Rpb24gdG8gYmUgcmV0cmllZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IFthdHRlbXB0PTBdIC0gVGhlIGN1cnJlbnQgYXR0ZW1wdCBudW1iZXIuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGAwYC5cclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0XHJcbiAqIG9mIHRoZSBmdW5jdGlvbiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwQmFja29mZihmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlcGVhdCwgdGhyb3cgYW4gZXJyb3JcclxuICAgIGlmICgrK2F0dGVtcHQgPj0gTUFYX0JBQ0tPRkZfQVRURU1QVFMpIHtcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxyXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc3BvbnNlKSA9PiBzZXRUaW1lb3V0KHJlc3BvbnNlLCBkZWxheUluTXMpKTtcclxuXHJcbiAgICAvLy8gVE8gRE86IENvcnJlY3RcclxuICAgIC8vIC8vIEluZm9ybWF0aW9uIGFib3V0IHRoZSByZXNvdXJjZSB0aW1lb3V0XHJcbiAgICAvLyBsb2coXHJcbiAgICAvLyAgIDMsXHJcbiAgICAvLyAgIGBbdXRpbHNdIFdhaXRlZCAke2RlbGF5SW5Nc31tcyB1bnRpbCBuZXh0IGNhbGwgZm9yIHRoZSByZXNvdXJjZSBvZiBJRDogJHthcmdzWzBdfS5gXHJcbiAgICAvLyApO1xyXG5cclxuICAgIC8vIFRyeSBhZ2FpblxyXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkanVzdHMgdGhlIGNvbnN0cnVjdG9yIG5hbWUgYnkgdHJhbnNmb3JtaW5nIGFuZCBub3JtYWxpemluZyBpdCBiYXNlZFxyXG4gKiBvbiBjb21tb24gY2hhcnQgdHlwZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhDb25zdHJcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnN0ciAtIFRoZSBvcmlnaW5hbCBjb25zdHJ1Y3RvciBuYW1lIHRvIGJlIGZpeGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGNvbnN0cnVjdG9yIG5hbWUsIG9yICdjaGFydCcgaWYgdGhlIGlucHV0XHJcbiAqIGlzIG5vdCByZWNvZ25pemVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeENvbnN0cihjb25zdHIpIHtcclxuICB0cnkge1xyXG4gICAgLy8gRml4IHRoZSBjb25zdHJ1Y3RvciBieSBsb3dlcmluZyBjYXNpbmdcclxuICAgIGNvbnN0IGZpeGVkQ29uc3RyID0gYCR7Y29uc3RyLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnY2hhcnQnLCAnJyl9Q2hhcnRgO1xyXG5cclxuICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgcmVzdWx0IGlzIGp1c3QgJ0NoYXJ0J1xyXG4gICAgaWYgKGZpeGVkQ29uc3RyID09PSAnQ2hhcnQnKSB7XHJcbiAgICAgIGZpeGVkQ29uc3RyLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IsIG90aGVyd2lzZSBkZWZhdWx0IHRvICdjaGFydCdcclxuICAgIHJldHVybiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddLmluY2x1ZGVzKFxyXG4gICAgICBmaXhlZENvbnN0clxyXG4gICAgKVxyXG4gICAgICA/IGZpeGVkQ29uc3RyXHJcbiAgICAgIDogJ2NoYXJ0JztcclxuICB9IGNhdGNoIHtcclxuICAgIC8vIERlZmF1bHQgdG8gJ2NoYXJ0JyBpbiBjYXNlIG9mIGFueSBlcnJvclxyXG4gICAgcmV0dXJuICdjaGFydCc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIG91dGZpbGUgYmFzZWQgb24gcHJvdmlkZWQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeE91dGZpbGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBvdXRmaWxlIC0gVGhlIGZpbGUgcGF0aCBvciBuYW1lLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIG91dGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4T3V0ZmlsZSh0eXBlLCBvdXRmaWxlKSB7XHJcbiAgLy8gR2V0IHRoZSBmaWxlIG5hbWUgZnJvbSB0aGUgYG91dGZpbGVgIG9wdGlvblxyXG4gIGNvbnN0IGZpbGVOYW1lID0gZ2V0QWJzb2x1dGVQYXRoKG91dGZpbGUgfHwgJ2NoYXJ0JylcclxuICAgIC5zcGxpdCgnLicpXHJcbiAgICAuc2hpZnQoKTtcclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCBvdXRmaWxlXHJcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke3R5cGV9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhUeXBlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW291dGZpbGU9bnVsbF0gLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGBudWxsYC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNvcnJlY3RlZCBleHBvcnQgdHlwZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhUeXBlKHR5cGUsIG91dGZpbGUgPSBudWxsKSB7XHJcbiAgLy8gTUlNRSB0eXBlc1xyXG4gIGNvbnN0IG1pbWVUeXBlcyA9IHtcclxuICAgICdpbWFnZS9wbmcnOiAncG5nJyxcclxuICAgICdpbWFnZS9qcGVnJzogJ2pwZWcnLFxyXG4gICAgJ2FwcGxpY2F0aW9uL3BkZic6ICdwZGYnLFxyXG4gICAgJ2ltYWdlL3N2Zyt4bWwnOiAnc3ZnJ1xyXG4gIH07XHJcblxyXG4gIC8vIEdldCBmb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IE9iamVjdC52YWx1ZXMobWltZVR5cGVzKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdHlwZSBhbmQgb3V0ZmlsZSdzIGV4dGVuc2lvbnMgYXJlIHRoZSBzYW1lXHJcbiAgaWYgKG91dGZpbGUpIHtcclxuICAgIGNvbnN0IG91dFR5cGUgPSBvdXRmaWxlLnNwbGl0KCcuJykucG9wKCk7XHJcblxyXG4gICAgLy8gU3VwcG9ydCB0aGUgSlBHIHR5cGVcclxuICAgIGlmIChvdXRUeXBlID09PSAnanBnJykge1xyXG4gICAgICB0eXBlID0gJ2pwZWcnO1xyXG4gICAgfSBlbHNlIGlmIChmb3JtYXRzLmluY2x1ZGVzKG91dFR5cGUpICYmIHR5cGUgIT09IG91dFR5cGUpIHtcclxuICAgICAgdHlwZSA9IG91dFR5cGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IHR5cGVcclxuICByZXR1cm4gbWltZVR5cGVzW3R5cGVdIHx8IGZvcm1hdHMuZmluZCgodCkgPT4gdCA9PT0gdHlwZSkgfHwgJ3BuZyc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIHBhdGggaXMgcmVsYXRpdmUgb3IgYWJzb2x1dGUgYW5kIHJldHVybnMgdGhlIGNvcnJlY3RlZCxcclxuICogYWJzb2x1dGUgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzQWJzb2x1dGVQYXRoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gYmUgY2hlY2tlZCBvbi5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGguXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWJzb2x1dGVQYXRoKHBhdGgpIHtcclxuICByZXR1cm4gaXNBYnNvbHV0ZShwYXRoKSA/IHBhdGggOiBqb2luKF9fZGlybmFtZSwgcGF0aCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBpbnB1dCBkYXRhIHRvIGEgQmFzZTY0IHN0cmluZyBiYXNlZCBvbiB0aGUgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRCYXNlNjRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IC0gVGhlIGlucHV0IHRvIGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmb3JtYXQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQmFzZTY0IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QmFzZTY0KGlucHV0LCB0eXBlKSB7XHJcbiAgLy8gRm9yIHBkZiBhbmQgc3ZnIHR5cGVzIHRoZSBpbnB1dCBtdXN0IGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmcm9tIGEgYnVmZmVyXHJcbiAgaWYgKHR5cGUgPT09ICdwZGYnIHx8IHR5cGUgPT0gJ3N2ZycpIHtcclxuICAgIHJldHVybiBCdWZmZXIuZnJvbShpbnB1dCwgJ3V0ZjgnKS50b1N0cmluZygnYmFzZTY0Jyk7XHJcbiAgfVxyXG5cclxuICAvLyBGb3IgcG5nIGFuZCBqcGVnIGlucHV0IGlzIGFscmVhZHkgYSBCYXNlNjQgc3RyaW5nXHJcbiAgcmV0dXJuIGlucHV0O1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyBzdHJpbmdpZmllZCBkYXRlIHdpdGhvdXQgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGUoKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgR01UIHRleHQgaW5mb3JtYXRpb25cclxuICByZXR1cm4gbmV3IERhdGUoKS50b1N0cmluZygpLnNwbGl0KCcoJylbMF0udHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgc3RvcmVkIHRpbWUgdmFsdWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVRpbWVcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXROZXdEYXRlVGltZSgpIHtcclxuICByZXR1cm4gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIGl0ZW0gaXMgYW4gb2JqZWN0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNPYmplY3RcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIGl0ZW0gdG8gYmUgY2hlY2tlZC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIGl0ZW0gaXMgYW4gb2JqZWN0LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3QoaXRlbSkge1xyXG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaXRlbSkgPT09ICdbb2JqZWN0IE9iamVjdF0nO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdEVtcHR5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGVtcHR5LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3RFbXB0eShpdGVtKSB7XHJcbiAgcmV0dXJuIChcclxuICAgIHR5cGVvZiBpdGVtID09PSAnb2JqZWN0JyAmJlxyXG4gICAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICAgIGl0ZW0gIT09IG51bGwgJiZcclxuICAgIE9iamVjdC5rZXlzKGl0ZW0pLmxlbmd0aCA9PT0gMFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCBpbiB0aGUgZ2l2ZW4gc3RyaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaXRlbSAtIFRoZSBzdHJpbmcgdG8gYmUgY2hlY2tlZCBmb3IgYSBwcml2YXRlIElQIHJhbmdlIFVSTC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoaXRlbSkge1xyXG4gIGNvbnN0IHJlZ2V4UGF0dGVybnMgPSBbXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/bG9jYWxob3N0XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTI3XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xNzJcXC4oMVs2LTldfDJbMC05XXwzWzAtMV0pXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTkyXFwuMTY4XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi9cclxuICBdO1xyXG5cclxuICByZXR1cm4gcmVnZXhQYXR0ZXJucy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3QoaXRlbSkpO1xyXG59XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIGVsYXBzZWQgdGltZSB1c2luZyB0aGUgTm9kZS5qcyBgcHJvY2Vzcy5ocnRpbWUoKWAgbWV0aG9kLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbWVhc3VyZVRpbWVcclxuICpcclxuICogQHJldHVybnMge0Z1bmN0aW9ufSBBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZWxhcHNlZCB0aW1lIGluIG1pbGxpc2Vjb25kcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBtZWFzdXJlVGltZSgpIHtcclxuICBjb25zdCBzdGFydCA9IHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO1xyXG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xyXG59XHJcblxyXG4vKipcclxuICogUm91bmRzIGEgbnVtYmVyIHRvIHRoZSBzcGVjaWZpZWQgcHJlY2lzaW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcm91bmROdW1iZXJcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IHZhbHVlIC0gVGhlIG51bWJlciB0byBiZSByb3VuZGVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcHJlY2lzaW9uIC0gVGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyB0byByb3VuZCB0by5cclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gVGhlIHJvdW5kZWQgbnVtYmVyLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHJvdW5kTnVtYmVyKHZhbHVlLCBwcmVjaXNpb24gPSAxKSB7XHJcbiAgY29uc3QgbXVsdGlwbGllciA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24gfHwgMCk7XHJcbiAgcmV0dXJuIE1hdGgucm91bmQoK3ZhbHVlICogbXVsdGlwbGllcikgLyBtdWx0aXBsaWVyO1xyXG59XHJcblxyXG4vKipcclxuICogQ29udmVydHMgYSB2YWx1ZSB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB0b0Jvb2xlYW5cclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUaGUgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQgdmFsdWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdG9Cb29sZWFuKGl0ZW0pIHtcclxuICByZXR1cm4gWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBXcmFwcyBjdXN0b20gY29kZSB0byBleGVjdXRlIGl0IHNhZmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIHdyYXBBcm91bmRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tIGNvZGUgdG8gYmUgd3JhcHBlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBGbGFnIHRvIGFsbG93IGxvYWRpbmcgY29kZSBmcm9tIGEgZmlsZS5cclxuICogQHBhcmFtIHtib29sZWFufSBbaXNDYWxsYmFjaz1mYWxzZV0gLSBGbGFnIHRoYXQgaW5kaWNhdGVzIHRoZSByZXR1cm5lZCBjb2RlXHJcbiAqIG11c3QgYmUgaW4gYSBjYWxsYmFjayBmb3JtYXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoc3RyaW5nfG51bGwpfSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBudWxsIGlmIHdyYXBwaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHdyYXBBcm91bmQoY3VzdG9tQ29kZSwgYWxsb3dGaWxlUmVzb3VyY2VzLCBpc0NhbGxiYWNrID0gZmFsc2UpIHtcclxuICBpZiAoY3VzdG9tQ29kZSAmJiB0eXBlb2YgY3VzdG9tQ29kZSA9PT0gJ3N0cmluZycpIHtcclxuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcclxuXHJcbiAgICBpZiAoY3VzdG9tQ29kZS5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgICAgLy8gTG9hZCBhIGZpbGUgaWYgdGhlIGZpbGUgcmVzb3VyY2VzIGFyZSBhbGxvd2VkXHJcbiAgICAgIHJldHVybiBhbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgICA/IHdyYXBBcm91bmQoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY3VzdG9tQ29kZSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgICAgaXNDYWxsYmFja1xyXG4gICAgICAgICAgKVxyXG4gICAgICAgIDogbnVsbDtcclxuICAgIH0gZWxzZSBpZiAoXHJcbiAgICAgICFpc0NhbGxiYWNrICYmXHJcbiAgICAgIChjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24gKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFRyZWF0IGEgZnVuY3Rpb24gYXMgYSBzZWxmLWludm9raW5nIGV4cHJlc3Npb25cclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gT3IgcmV0dXJuIGFzIGEgc3RyaW5naWZpZWQgY29kZVxyXG4gICAgcmV0dXJuIGN1c3RvbUNvZGUucmVwbGFjZSgvOyQvLCAnJyk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgX19kaXJuYW1lLFxyXG4gIGNsZWFyVGV4dCxcclxuICBkZWVwQ29weSxcclxuICBleHBCYWNrb2ZmLFxyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBnZXROZXdEYXRlLFxyXG4gIGdldE5ld0RhdGVUaW1lLFxyXG4gIGlzT2JqZWN0LFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBtZWFzdXJlVGltZSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQSBtb2R1bGUgZm9yIG1hbmFnaW5nIGxvZ2dpbmcgZnVuY3Rpb25hbGl0eSB3aXRoIGN1c3RvbWl6YWJsZVxyXG4gKiBsb2cgbGV2ZWxzLCBjb25zb2xlIGFuZCBmaWxlIGxvZ2dpbmcgb3B0aW9ucywgYW5kIGVycm9yIGhhbmRsaW5nIHN1cHBvcnQuXHJcbiAqIFRoZSBtb2R1bGUgYWxzbyBlbnN1cmVzIHRoYXQgZmlsZS1iYXNlZCBsb2dzIGFyZSBzdG9yZWQgaW4gYSBzdHJ1Y3R1cmVkXHJcbiAqIGRpcmVjdG9yeSwgY3JlYXRpbmcgdGhlIG5lY2Vzc2FyeSBwYXRocyBhdXRvbWF0aWNhbGx5IGlmIHRoZXkgZG8gbm90IGV4aXN0LlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWJzb2x1dGVQYXRoLCBnZXROZXdEYXRlIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG4vLyBUaGUgYXZhaWxhYmxlIGNvbG9yc1xyXG5jb25zdCBjb2xvcnMgPSBbJ3JlZCcsICd5ZWxsb3cnLCAnYmx1ZScsICdncmF5JywgJ2dyZWVuJ107XHJcblxyXG4vLyBUaGUgZGVmYXVsdCBsb2dnaW5nIGNvbmZpZ1xyXG5jb25zdCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBGdWxsIHBhdGggdG8gdGhlIGxvZyBmaWxlXHJcbiAgcGF0aFRvTG9nOiAnJyxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdXHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhIG1lc3NhZ2Ugd2l0aCBhIHNwZWNpZmllZCBsb2cgbGV2ZWwuIEFjY2VwdHMgYSB2YXJpYWJsZSBudW1iZXJcclxuICogb2YgYXJndW1lbnRzLiBUaGUgYXJndW1lbnRzIGFmdGVyIHRoZSBgbGV2ZWxgIGFyZSBwYXNzZWQgdG8gYGNvbnNvbGUubG9nYFxyXG4gKiBhbmQvb3IgdXNlZCB0byBjb25zdHJ1Y3QgYW5kIGFwcGVuZCBtZXNzYWdlcyB0byBhIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Li4udW5rbm93bn0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZ1xyXG4gKiBsZXZlbCBhbmQgdGhlIHJlbWFpbmluZyBhcmUgc3RyaW5ncyB1c2VkIHRvIGJ1aWxkIHRoZSBsb2cgbWVzc2FnZS5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZyguLi5hcmdzKSB7XHJcbiAgY29uc3QgW25ld0xldmVsLCAuLi50ZXh0c10gPSBhcmdzO1xyXG5cclxuICAvLyBDdXJyZW50IGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgbGV2ZWxzRGVzYywgbGV2ZWwgfSA9IGxvZ2dpbmc7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZSBvciBpcyBpdCBhIGJlbmNobWFyayBsb2dcclxuICBpZiAoXHJcbiAgICBuZXdMZXZlbCAhPT0gNSAmJlxyXG4gICAgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aClcclxuICApIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtnZXROZXdEYXRlKCl9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgYW4gZXJyb3IgbWVzc2FnZSBhbG9uZyB3aXRoIGl0cyBzdGFjayB0cmFjZS4gT3B0aW9uYWxseSwgYSBjdXN0b20gbWVzc2FnZVxyXG4gKiBjYW4gYmUgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dXaXRoU3RhY2tcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IG5ld0xldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHN0YWNrIHRyYWNlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGluY2x1ZGVkXHJcbiAqIGluIHRoZSBsb2cgYWxvbmdzaWRlIHRoZSBlcnJvci5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZ1dpdGhTdGFjayhuZXdMZXZlbCwgZXJyb3IsIGN1c3RvbU1lc3NhZ2UpIHtcclxuICAvLyBHZXQgdGhlIG1haW4gbWVzc2FnZVxyXG4gIGNvbnN0IG1haW5NZXNzYWdlID0gY3VzdG9tTWVzc2FnZSB8fCAoZXJyb3IgJiYgZXJyb3IubWVzc2FnZSkgfHwgJyc7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlXHJcbiAgaWYgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aCkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIEFkZCB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9IGVycm9yICYmIGVycm9yLnN0YWNrO1xyXG5cclxuICAvLyBDb21iaW5lIGN1c3RvbSBtZXNzYWdlIG9yIGVycm9yIG1lc3NhZ2Ugd2l0aCBlcnJvciBzdGFjayBtZXNzYWdlLCBpZiBleGlzdHNcclxuICBjb25zdCB0ZXh0cyA9IFttYWluTWVzc2FnZV07XHJcbiAgaWYgKHN0YWNrTWVzc2FnZSkge1xyXG4gICAgdGV4dHMucHVzaCgnXFxuJywgc3RhY2tNZXNzYWdlKTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgdGV4dHMuc2hpZnQoKVtjb2xvcnNbbmV3TGV2ZWwgLSAxXV0sXHJcbiAgICAgICAgLi4udGV4dHNcclxuICAgICAgXSlcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgbG9nZ2luZyBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaW5pdExvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGxvZ2dpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGxvZ2dpbmdgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaW5pdExvZ2dpbmcobG9nZ2luZ09wdGlvbnMpIHtcclxuICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBgbG9nZ2luZ09wdGlvbnNgIG9iamVjdFxyXG4gIGNvbnN0IHsgbGV2ZWwsIGRlc3QsIGZpbGUsIHRvQ29uc29sZSwgdG9GaWxlIH0gPSBsb2dnaW5nT3B0aW9ucztcclxuXHJcbiAgLy8gUmVzZXQgZmxhZ3MgdG8gdGhlIGRlZmF1bHQgdmFsdWVzXHJcbiAgbG9nZ2luZy5wYXRoQ3JlYXRlZCA9IGZhbHNlO1xyXG4gIGxvZ2dpbmcucGF0aFRvTG9nID0gJyc7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nZ2luZyBsZXZlbFxyXG4gIHNldExvZ0xldmVsKGxldmVsKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb25zb2xlIGxvZ2dpbmdcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpO1xyXG5cclxuICAvLyBTZXQgdGhlIGZpbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKGAwYCA9IG5vIGxvZ2dpbmcsXHJcbiAqIGAxYCA9IGVycm9yLCBgMmAgPSB3YXJuaW5nLCBgM2AgPSBub3RpY2UsIGA0YCA9IHZlcmJvc2UsIG9yIGA1YCA9IGJlbmNobWFyaykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRMb2dMZXZlbFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgbG9nIGxldmVsIHRvIGJlIHNldC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRMb2dMZXZlbChsZXZlbCkge1xyXG4gIGlmIChcclxuICAgIE51bWJlci5pc0ludGVnZXIobGV2ZWwpICYmXHJcbiAgICBsZXZlbCA+PSAwICYmXHJcbiAgICBsZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoXHJcbiAgKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYGxldmVsYCBvcHRpb25cclxuICAgIGxvZ2dpbmcubGV2ZWwgPSBsZXZlbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGNvbnNvbGUgbG9nZ2luZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUNvbnNvbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9Db25zb2xlIC0gVGhlIGZsYWcgZm9yIHNldHRpbmcgdGhlIGxvZ2dpbmcgdG8gdGhlIGNvbnNvbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmcodG9Db25zb2xlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0NvbnNvbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9Db25zb2xlID0gISF0b0NvbnNvbGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlIG5hbWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVGaWxlTG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZGVzdCAtIFRoZSBkZXN0aW5hdGlvbiBwYXRoIHdoZXJlIHRoZSBsb2cgZmlsZSBzaG91bGRcclxuICogYmUgc2F2ZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlIC0gVGhlIG5hbWUgb2YgdGhlIGxvZyBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvRmlsZSAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9nZ2luZyBzaG91bGRcclxuICogYmUgZGlyZWN0ZWQgdG8gYSBmaWxlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgdG9GaWxlYCBvcHRpb25cclxuICBsb2dnaW5nLnRvRmlsZSA9ICEhdG9GaWxlO1xyXG5cclxuICAvLyBTZXQgdGhlIGBkZXN0YCBhbmQgYGZpbGVgIG9wdGlvbnMgb25seSBpZiB0aGUgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWRcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIGxvZ2dpbmcuZGVzdCA9IGRlc3QgfHwgJyc7XHJcbiAgICBsb2dnaW5nLmZpbGUgPSBmaWxlIHx8ICcnO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgdGhlIHByb3ZpZGVkIHRleHRzIHRvIGEgZmlsZSwgaWYgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWQuIEl0IGNyZWF0ZXNcclxuICogdGhlIG5lY2Vzc2FyeSBkaXJlY3Rvcnkgc3RydWN0dXJlIGlmIG5vdCBhbHJlYWR5IGNyZWF0ZWQgYW5kIGFwcGVuZHNcclxuICogdGhlIGNvbnRlbnQsIGluY2x1ZGluZyBhbiBvcHRpb25hbCBwcmVmaXgsIHRvIHRoZSBzcGVjaWZpZWQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9nVG9GaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHRleHRzIC0gQW4gYXJyYXkgb2YgdGV4dHMgdG8gYmUgbG9nZ2VkLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4IC0gQW4gb3B0aW9uYWwgcHJlZml4IHRvIGJlIGFkZGVkIHRvIGVhY2ggbG9nIGVudHJ5LlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KSB7XHJcbiAgaWYgKCFsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAvLyBDcmVhdGUgaWYgZG9lcyBub3QgZXhpc3RcclxuICAgICFleGlzdHNTeW5jKGdldEFic29sdXRlUGF0aChsb2dnaW5nLmRlc3QpKSAmJlxyXG4gICAgICBta2RpclN5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpO1xyXG5cclxuICAgIC8vIENyZWF0ZSB0aGUgZnVsbCBwYXRoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyA9IGdldEFic29sdXRlUGF0aChqb2luKGxvZ2dpbmcuZGVzdCwgbG9nZ2luZy5maWxlKSk7XHJcblxyXG4gICAgLy8gV2Ugbm93IGFzc3VtZSB0aGUgcGF0aCBpcyBhdmFpbGFibGUsIGUuZy4gaXQncyB0aGUgcmVzcG9uc2liaWxpdHlcclxuICAgIC8vIG9mIHRoZSB1c2VyIHRvIGNyZWF0ZSB0aGUgcGF0aCB3aXRoIHRoZSBjb3JyZWN0IGFjY2VzcyByaWdodHMuXHJcbiAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICB9XHJcblxyXG4gIC8vIEFkZCB0aGUgY29udGVudCB0byBhIGZpbGVcclxuICBhcHBlbmRGaWxlKFxyXG4gICAgbG9nZ2luZy5wYXRoVG9Mb2csXHJcbiAgICBbcHJlZml4XS5jb25jYXQodGV4dHMpLmpvaW4oJyAnKSArICdcXG4nLFxyXG4gICAgKGVycm9yKSA9PiB7XHJcbiAgICAgIGlmIChlcnJvciAmJiBsb2dnaW5nLnRvRmlsZSAmJiBsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmcsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmdcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvbmZpZ3VyYXRpb24gbWFuYWdlbWVudCBtb2R1bGUgZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuXHJcbiAqIFByb3ZpZGVzIGRlZmF1bHQgY29uZmlndXJhdGlvbnMgdGhhdCBzdXBwb3J0IGVudmlyb25tZW50IHZhcmlhYmxlcywgQ0xJXHJcbiAqIGFyZ3VtZW50cywgYW5kIGludGVyYWN0aXZlIHByb21wdHMgZm9yIGN1c3RvbWl6YXRpb24gb2Ygb3B0aW9ucyBhbmQgZmVhdHVyZXMuXHJcbiAqIEFkZGl0aW9uYWxseSwgaXQgbWFwcyBsZWdhY3kgb3B0aW9ucyB0byBtb2Rlcm4gc3RydWN0dXJlcywgZ2VuZXJhdGVzIG5lc3RlZFxyXG4gKiBhcmd1bWVudCBtYXBwaW5ncywgYW5kIGRpc3BsYXlzIENMSSB1c2FnZSBpbmZvcm1hdGlvbi5cclxuICovXHJcblxyXG4vKipcclxuICogVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYWxsIGF2YWlsYWJsZSBvcHRpb25zLCBvcmdhbml6ZWRcclxuICogYnkgc2VjdGlvbnMuXHJcbiAqXHJcbiAqIFRoaXMgb2JqZWN0IGluY2x1ZGVzOlxyXG4gKiAtIERlZmF1bHQgdmFsdWVzIGZvciBlYWNoIG9wdGlvblxyXG4gKiAtIERhdGEgdHlwZXMgZm9yIHZhbGlkYXRpb25cclxuICogLSBOYW1lcyBvZiBjb3JyZXNwb25kaW5nIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiAtIERlc2NyaXB0aW9ucyBvZiBlYWNoIHByb3BlcnR5XHJcbiAqIC0gSW5mb3JtYXRpb24gdXNlZCBmb3IgcHJvbXB0cyBpbiBpbnRlcmFjdGl2ZSBjb25maWd1cmF0aW9uXHJcbiAqIC0gW09wdGlvbmFsXSBDb3JyZXNwb25kaW5nIENMSSBhcmd1bWVudCBuYW1lcyBmb3IgQ0xJIHVzYWdlXHJcbiAqIC0gW09wdGlvbmFsXSBMZWdhY3kgbmFtZXMgZnJvbSB0aGUgcHJldmlvdXMgUGhhbnRvbUpTLWJhc2VkIHNlcnZlclxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZmF1bHRDb25maWcgPSB7XHJcbiAgcHVwcGV0ZWVyOiB7XHJcbiAgICBhcmdzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJy0tYWxsb3ctcnVubmluZy1pbnNlY3VyZS1jb250ZW50JyxcclxuICAgICAgICAnLS1hc2gtbm8tbnVkZ2VzJyxcclxuICAgICAgICAnLS1hdXRvcGxheS1wb2xpY3k9dXNlci1nZXN0dXJlLXJlcXVpcmVkJyxcclxuICAgICAgICAnLS1ibG9jay1uZXctd2ViLWNvbnRlbnRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWFjY2VsZXJhdGVkLTJkLWNhbnZhcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLW5ldHdvcmtpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1icmVha3BhZCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jaGVja2VyLWltYWdpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC1leHRlbnNpb25zLXdpdGgtYmFja2dyb3VuZC1wYWdlcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtdXBkYXRlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRlZmF1bHQtYXBwcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZXYtc2htLXVzYWdlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRvbWFpbi1yZWxpYWJpbGl0eScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1leHRlbnNpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWZlYXR1cmVzPUNhbGN1bGF0ZU5hdGl2ZVdpbk9jY2x1c2lvbixJbnRlcmVzdEZlZWRDb250ZW50U3VnZ2VzdGlvbnMsV2ViT1RQJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1sb2dnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXBvcHVwLWJsb2NraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1yZW5kZXJlci1iYWNrZ3JvdW5kaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlYXJjaC1lbmdpbmUtY2hvaWNlLXNjcmVlbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNpdGUtaXNvbGF0aW9uLXRyaWFscycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zcGVlY2gtYXBpJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXN5bmMnLFxyXG4gICAgICAgICctLWVuYWJsZS11bnNhZmUtd2ViZ3B1JyxcclxuICAgICAgICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcclxuICAgICAgICAnLS1oaWRlLXNjcm9sbGJhcnMnLFxyXG4gICAgICAgICctLW1ldHJpY3MtcmVjb3JkaW5nLW9ubHknLFxyXG4gICAgICAgICctLW11dGUtYXVkaW8nLFxyXG4gICAgICAgICctLW5vLWRlZmF1bHQtYnJvd3Nlci1jaGVjaycsXHJcbiAgICAgICAgJy0tbm8tZmlyc3QtcnVuJyxcclxuICAgICAgICAnLS1uby1waW5ncycsXHJcbiAgICAgICAgJy0tbm8tc2FuZGJveCcsXHJcbiAgICAgICAgJy0tbm8tc3RhcnR1cC13aW5kb3cnLFxyXG4gICAgICAgICctLW5vLXp5Z290ZScsXHJcbiAgICAgICAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxyXG4gICAgICAgICctLXByb2Nlc3MtcGVyLXRhYicsXHJcbiAgICAgICAgJy0tdXNlLW1vY2sta2V5Y2hhaW4nXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdQVVBQRVRFRVJfQVJHUycsXHJcbiAgICAgIGNsaU5hbWU6ICdwdXBwZXRlZXJBcmdzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBcnJheSBvZiBQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgICBzZXBhcmF0b3I6ICc7J1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBoaWdoY2hhcnRzOiB7XHJcbiAgICB2ZXJzaW9uOiB7XHJcbiAgICAgIHZhbHVlOiAnbGF0ZXN0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgdmVyc2lvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNkblVybDoge1xyXG4gICAgICB2YWx1ZTogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmb3JjZUZldGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmxhZyB0byByZWZldGNoIHNjcmlwdHMgYWZ0ZXIgZWFjaCBzZXJ2ZXIgcmVydW4nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhY2hlUGF0aDoge1xyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DQUNIRV9QQVRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEaXJlY3RvcnkgcGF0aCBmb3IgY2FjaGVkIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2hpZ2hjaGFydHMnLCAnaGlnaGNoYXJ0cy1tb3JlJywgJ2hpZ2hjaGFydHMtM2QnXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbW9kdWxlU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdzdG9jaycsXHJcbiAgICAgICAgJ21hcCcsXHJcbiAgICAgICAgJ2dhbnR0JyxcclxuICAgICAgICAnZXhwb3J0aW5nJyxcclxuICAgICAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgICAgICdhY2Nlc3NpYmlsaXR5JyxcclxuICAgICAgICAvLyAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgICAgICdib29zdCcsXHJcbiAgICAgICAgJ2RhdGEnLFxyXG4gICAgICAgICdkYXRhLXRvb2xzJyxcclxuICAgICAgICAnZHJhZ2dhYmxlLXBvaW50cycsXHJcbiAgICAgICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAgICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICAgICAnaGVhdG1hcCcsXHJcbiAgICAgICAgJ3RpbGVtYXAnLFxyXG4gICAgICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAgICAgJ3RpbWVsaW5lJyxcclxuICAgICAgICAndHJlZW1hcCcsXHJcbiAgICAgICAgJ3RyZWVncmFwaCcsXHJcbiAgICAgICAgJ2l0ZW0tc2VyaWVzJyxcclxuICAgICAgICAnZHJpbGxkb3duJyxcclxuICAgICAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAgICAgJ2J1bGxldCcsXHJcbiAgICAgICAgJ2Z1bm5lbCcsXHJcbiAgICAgICAgJ2Z1bm5lbDNkJyxcclxuICAgICAgICAnZ2VvaGVhdG1hcCcsXHJcbiAgICAgICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAgICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAgICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzZXJpZXMtb24tcG9pbnQnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgLy8gJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaScsXHJcbiAgICAgICAgJ2Zsb3dtYXAnLFxyXG4gICAgICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAgICAgJ25hdmlnYXRvcicsXHJcbiAgICAgICAgJ3RleHRwYXRoJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBtb2R1bGUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogWydpbmRpY2F0b3JzLWFsbCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBpbmRpY2F0b3Igc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQuanMvMi4zMC4xL21vbWVudC5taW4uanMnLFxyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQtdGltZXpvbmUvMC41LjQ1L21vbWVudC10aW1lem9uZS13aXRoLWRhdGEubWluLmpzJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DVVNUT01fU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBjdXN0b20gc2NyaXB0cyBvciBkZXBlbmRlbmNpZXMgdG8gZmV0Y2gnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGV4cG9ydDoge1xyXG4gICAgaW5maWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0lORklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnB1dCBmaWxlbmFtZSB3aXRoIHR5cGUsIGZvcm1hdHRlZCBjb3JyZWN0bHkgYXMgSlNPTiBvciBTVkcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbnN0cjoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5TVFInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3ZlcnJpZGVzIHRoZSBgaW5maWxlYCB3aXRoIEpTT04sIHN0cmluZ2lmaWVkIEpTT04sIG9yIFNWRyBpbnB1dCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09QVElPTlMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FsaWFzIGZvciB0aGUgYGluc3RyYCBvcHRpb24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzdmc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU1ZHJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTVkcgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCB0byByZW5kZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CQVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdCYXRjaCBqb2Igc3RyaW5nIHdpdGggaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09VVEZJTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3V0cHV0IGZpbGVuYW1lIHdpdGggdHlwZS4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcgYW5kIGlnbm9yZXMgYHR5cGVgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIGV4cG9ydCBmb3JtYXQuIENhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBwbmcnLFxyXG4gICAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0NPTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDaGFydCBjb25zdHJ1Y3Rvci4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgICBoaW50OiAnRGVmYXVsdDogY2hhcnQnLFxyXG4gICAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGI2NDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CNjQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gdGhlIGNoYXJ0IHNob3VsZCBiZSByZWNlaXZlZCBpbiBCYXNlNjQgZm9ybWF0IGluc3RlYWQgb2YgYmluYXJ5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBub0Rvd25sb2FkOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX05PX0RPV05MT0FEJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZXRoZXIgb3Igbm90IHRvIGluY2x1ZGUgb3IgZXhjbHVkZSBhdHRhY2htZW50IGhlYWRlcnMgaW4gdGhlIHJlc3BvbnNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSEVJR0hUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1NDQUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0RlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgbWluOiAwLjEsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9HTE9CQUxfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggZ2xvYmFsIG9wdGlvbnMgZm9yIEhpZ2hjaGFydHMuc2V0T3B0aW9ucycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVEhFTUVfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHdlYnBhZ2UgcmVuZGVyaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgZHVyaW5nIGV4cG9ydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FsbG93cyBvciBkaXNhbGxvd3MgaW5qZWN0aW9uIG9mIGZpbGVzeXN0ZW0gcmVzb3VyY2VzIChkaXNhYmxlZCBpbiBzZXJ2ZXIgbW9kZSknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DQUxMQkFDSycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIENhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgLmpzIGZpbGVuYW1lJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZXMgYXMgSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgZmlsZW5hbWUsIGNvbnRhaW5pbmcgZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSB3aXRoIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdQcm9tcHQtYmFzZWQgb3B0aW9uIHNldHRpbmcsIHNhdmVkIHRvIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVTZXJ2ZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0cyB0aGUgc2VydmVyIHdoZW4gdHJ1ZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaG9zdDoge1xyXG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIb3N0bmFtZSBvZiB0aGUgc2VydmVyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUG9ydCBudW1iZXIgZm9yIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHVwbG9hZExpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiAzLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9VUExPQURfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVxdWVzdCBib2R5IHNpemUgaW4gTUInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAnc2VydmVyQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Rpc3BsYXlzIG9yIG5vdCBhY3Rpb24gZHVyYXRpb25zIGluIG1pbGxpc2Vjb25kcyBkdXJpbmcgc2VydmVyIHJlcXVlc3RzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwcm94eToge1xyXG4gICAgICBob3N0OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eUhvc3QnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnSG9zdCBvZiB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB3aW5kb3c6IHtcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RpbWUgd2luZG93IGluIG1pbnV0ZXMgZm9yIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnRGVsYXkgZHVyYXRpb24gYmV0d2VlbiBzdWNjZXNzaXZlIHJlcXVlc3RzIGJlZm9yZSByZWFjaGluZyB0aGUgbGltaXQnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcEtleToge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0tleSB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwVG9rZW5gJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBUb2tlbjoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVG9rZW4gdG8gYnlwYXNzIHRoZSByYXRlIGxpbWl0ZXIsIHVzZWQgd2l0aCBgc2tpcEtleWAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdGb3JjZXMgdGhlIHNlcnZlciB0byB1c2UgSFRUUFMgb25seSB3aGVuIHRydWUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IGZvciB0aGUgU1NMIHNlcnZlcicsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xDZXJ0UGF0aCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWluaW11bSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICB3b3JrTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDQwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTnVtYmVyIG9mIHRhc2tzIGEgd29ya2VyIGNhbiBoYW5kbGUgYmVmb3JlIHJlc3RhcnRpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpZGxlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMzAwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGlkbGUgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAyMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGJlZm9yZSByZXRyeWluZyByZXNvdXJjZSBjcmVhdGlvbiBvbiBmYWlsdXJlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgdG8gY2hlY2sgYW5kIGRlc3Ryb3kgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1Nob3dzIHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdMb2dnaW5nIHZlcmJvc2l0eSBsZXZlbCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICByb3VuZDogMCxcclxuICAgICAgICBtaW46IDAsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnTG9nIGZpbGUgbmFtZS4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCB0byBiZSBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZXN0OiB7XHJcbiAgICAgIHZhbHVlOiAnbG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFJlcXVpcmVzIGBsb2dUb0ZpbGVgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBjb25zb2xlIGxvZ2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvRmlsZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICB1aToge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVVpJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGVuZHBvaW50IHJvdXRlIGZvciB0aGUgVUknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBOb2RlLmpzIGVudmlyb25tZW50IHR5cGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0Rpc3BsYXkgb3Igc2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoYXJkUmVzZXRQYWdlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfSEFSRF9SRVNFVF9QQUdFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaGV0aGVyIG9yIG5vdCB0byByZXNldCB0aGUgcGFnZSBjb250ZW50IGVudGlyZWx5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBicm93c2VyU2hlbGxNb2RlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHNldCB0aGUgYnJvd3NlciB0byBydW4gaW4gc2hlbGwgbW9kZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBkZWJ1Zzoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZURlYnVnJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSB1bmRlcmx5aW5nIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlYWRsZXNzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBoZWFkbGVzcyBtb2RlIGR1cmluZyBkZWJ1Z2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRldnRvb2xzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgRGV2VG9vbHMgaW4gaGVhZGZ1bCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGxpc3RlbmluZyB0byBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGR1bXBpbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RVTVBJTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdSZWRpcmVjdHMgb3Igbm90IGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gcHJvY2Vzcy5zdGRvdXQgYW5kIHByb2Nlc3Muc3RkZXJyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzbG93TW86IHtcclxuICAgICAgdmFsdWU6IDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVsYXlzIFB1cHBldGVlciBvcGVyYXRpb25zIGJ5IHRoZSBzcGVjaWZpZWQgbWlsbGlzZWNvbmRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IHVzZWQgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLy8gUHJvcGVydGllcyBuZXN0aW5nIGxldmVsIG9mIGFsbCBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRQcm9wcyA9IF9jcmVhdGVOZXN0ZWRQcm9wcyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIFByb3BlcnRpZXMgbmFtZXMgdGhhdCBzaG91bGQgbm90IGJlIHJlY3Vyc2l2ZWx5IG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IF9jcmVhdGVBYnNvbHV0ZVByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdlbmVyYXRlcyBhIG1hcHBpbmcgb2YgbmVzdGVkIGFyZ3VtZW50IGNoYWlucyBmcm9tIGEgbmVzdGVkXHJcbiAqIGNvbmZpZyBvYmplY3QuIFRoaXMgZnVuY3Rpb24gdHJhdmVyc2VzIGEgbmVzdGVkIG9iamVjdCBhbmQgY3JlYXRlcyBhIG1hcHBpbmdcclxuICogd2hlcmUgZWFjaCBrZXkgaXMgYW4gYXJndW1lbnQgbmFtZSAoZWl0aGVyIGZyb20gYGNsaU5hbWVgLCBgbGVnYWN5TmFtZWAsXHJcbiAqIG9yIHRoZSBvcmlnaW5hbCBrZXkpIGFuZCBlYWNoIHZhbHVlIGlzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY2hhaW5cclxuICogb2YgbmVzdGVkIHByb3BlcnRpZXMgbGVhZGluZyB0byB0aGF0IGFyZ3VtZW50LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbbmVzdGVkUHJvcHM9e31dIC0gVGhlIGFjY3VtdWxhdG9yIG9iamVjdCBmb3Igc3RvcmluZ1xyXG4gKiB0aGUgcmVzdWx0aW5nIGFyZ3VtZW50cyBjaGFpbnMuICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcHJvcENoYWluPScnXSAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzLFxyXG4gKiB1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbi4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gb2JqZWN0IG1hcHBpbmcgYXJndW1lbnQgbmFtZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZ1xyXG4gKiBuZXN0ZWQgcHJvcGVydHkgY2hhaW5zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzKGNvbmZpZywgbmVzdGVkUHJvcHMgPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgX2NyZWF0ZU5lc3RlZFByb3BzKGVudHJ5LCBuZXN0ZWRQcm9wcywgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBuZXN0ZWRQcm9wc1tlbnRyeS5jbGlOYW1lIHx8IGtleV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgaWYgKGVudHJ5LmxlZ2FjeU5hbWUgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2tleX1gLnN1YnN0cmluZygxKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIG9iamVjdCB3aXRoIG5lc3RlZCBhcmd1bWVudCBjaGFpbnNcclxuICByZXR1cm4gbmVzdGVkUHJvcHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSBnYXRoZXJzIHRoZSBuYW1lcyBvZiBwcm9wZXJ0aWVzIGZyb20gYSBjb25maWd1cmF0aW9uIG9iamVjdCB0aGF0XHJcbiAqIGNhbiBiZSB0cmVhdGVkIGFzIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZXNlIHByb3BlcnRpZXMgaGF2ZSB2YWx1ZXMgdGhhdFxyXG4gKiBhcmUgb2JqZWN0cyBhbmQgZG8gbm90IGNvbnRhaW4gZnVydGhlciBuZXN0ZWQgZGVwdGggd2hlbiBtZXJnaW5nIGFuIG9iamVjdFxyXG4gKiBjb250YWluaW5nIHRoZXNlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbYWJzb2x1dGVQcm9wcz1bXV0gLSBBbiBhcnJheSB0byBjb2xsZWN0IHRoZSBuYW1lc1xyXG4gKiBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxzdHJpbmc+fSBBbiBhcnJheSBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZVxyXG4gKiBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZUFic29sdXRlUHJvcHMoY29uZmlnLCBhYnNvbHV0ZVByb3BzID0gW10pIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudHlwZXMgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzXHJcbiAgICAgIF9jcmVhdGVBYnNvbHV0ZVByb3BzKGVudHJ5LCBhYnNvbHV0ZVByb3BzKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIHRoZSBvcHRpb24gY2FuIGJlIGFuIG9iamVjdCwgc2F2ZSBpdHMgdHlwZSBpbiB0aGUgYXJyYXlcclxuICAgICAgaWYgKGVudHJ5LnR5cGVzLmluY2x1ZGVzKCdPYmplY3QnKSkge1xyXG4gICAgICAgIGFic29sdXRlUHJvcHMucHVzaChrZXkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgYXJyYXkgd2l0aCB0aGUgbmFtZXMgb2YgYWJzb2x1dGUgcHJvcGVydGllc1xyXG4gIHJldHVybiBhYnNvbHV0ZVByb3BzO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRQcm9wcyxcclxuICBhYnNvbHV0ZVByb3BzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZFxyXG4gKiB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBgZW52c2AuIFdlIHNob3VsZCBub3QgdXNlIHRoZSBgcHJvY2Vzcy5lbnZgXHJcbiAqIGRpcmVjdGx5IGluIHRoZSBhcHBsaWNhdGlvbiBhcyB0aGVzZSB3b3VsZCBub3QgYmUgcGFyc2VkIHByb3Blcmx5LlxyXG4gKlxyXG4gKiBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSBwYXJzZWQgYW5kIHZhbGlkYXRlZCBvbmx5IG9uY2Ugd2hlblxyXG4gKiB0aGUgYXBwbGljYXRpb24gc3RhcnRzLiBXZSBzaG91bGQgd3JpdGUgYSBjdXN0b20gdmFsaWRhdG9yIG9yIGEgdHJhbnNmb3JtZXJcclxuICogZm9yIGVhY2ggb2YgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gcHVwcGV0ZWVyXHJcbiAgUFVQUEVURUVSX0FSR1M6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIGhpZ2hjaGFydHNcclxuICBISUdIQ0hBUlRTX1ZFUlNJT046IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PiAvXihsYXRlc3R8XFxkKyhcXC5cXGQrKXswLDJ9KSQvLnRlc3QodmFsdWUpIHx8IHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vJykgfHxcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcclxuICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgSElHSENIQVJUU19DRE5fVVJMLiBJdCBzaG91bGQgc3RhcnQgd2l0aCBodHRwOi8vIG9yIGh0dHBzOi8vLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTOiB2LmFycmF5KGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jb3JlU2NyaXB0cy52YWx1ZSksXHJcbiAgSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5tb2R1bGVTY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9JTkZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0lOU1RSOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9TVkc6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0JBVENIOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PVVRGSUxFOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9UWVBFOiB2LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLFxyXG4gIEVYUE9SVF9DT05TVFI6IHYuZW51bShbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddKSxcclxuICBFWFBPUlRfQjY0OiB2LmJvb2xlYW4oKSxcclxuICBFWFBPUlRfTk9fRE9XTkxPQUQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0dMT0JBTF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9USEVNRV9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19DVVNUT01fQ09ERTogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ0FMTEJBQ0s6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX1JFU09VUkNFUzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfTE9BRF9DT05GSUc6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUc6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlclxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1VQTE9BRF9MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgTE9HR0lOR19UT19GSUxFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gdWlcclxuICBVSV9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PREVfRU5WOiB2LmVudW0oWydkZXZlbG9wbWVudCcsICdwcm9kdWN0aW9uJywgJ3Rlc3QnXSksXHJcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0hBUkRfUkVTRVRfUEFHRTogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgTWFuYWdlcyBjb25maWd1cmF0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGJ5IGxvYWRpbmdcclxuICogYW5kIG1lcmdpbmcgb3B0aW9ucyBmcm9tIG11bHRpcGxlIHNvdXJjZXMsIHN1Y2ggYXMgZGVmYXVsdCBzZXR0aW5ncyxcclxuICogZW52aXJvbm1lbnQgdmFyaWFibGVzLCB1c2VyLXByb3ZpZGVkIG9wdGlvbnMsIGFuZCBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKiBFbnN1cmVzIHRoZSBnbG9iYWwgb3B0aW9ucyBhcmUgdXAtdG8tZGF0ZSB3aXRoIHRoZSBoaWdoZXN0IHByaW9yaXR5IHZhbHVlcy5cclxuICogUHJvdmlkZXMgZnVuY3Rpb25zIGZvciBhY2Nlc3NpbmcgYW5kIHVwZGF0aW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZGVlcENvcHksIGdldEFic29sdXRlUGF0aCwgaXNPYmplY3QgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCB7IGFic29sdXRlUHJvcHMsIG5lc3RlZFByb3BzLCBkZWZhdWx0Q29uZmlnIH0gZnJvbSAnLi9zY2hlbWFzL2NvbmZpZy5qcyc7XHJcblxyXG4vLyBTZXRzIHRoZSBnbG9iYWwgb3B0aW9ucyB3aXRoIGluaXRpYWwgdmFsdWVzIGZyb20gdGhlIGRlZmF1bHQgY29uZmlnXHJcbmNvbnN0IGdsb2JhbE9wdGlvbnMgPSBfaW5pdE9wdGlvbnMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoKSB7XHJcbiAgcmV0dXJuIGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIHVwZGF0ZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgbmV3IG9wdGlvbnMgdG8gYmVcclxuICogbWVyZ2VkIGludG8gdGhlIGdsb2JhbCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PWZhbHNlXSAtIERldGVybWluZXMgd2hldGhlciB0byBtZXJnZSB0aGUgbmV3XHJcbiAqIG9wdGlvbnMgaW50byBhIGNvcHkgb2YgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYHRydWVgKSBvciBkaXJlY3RseSBpbnRvXHJcbiAqIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3QgKGBmYWxzZWApLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBvcHRpb25zIG9iamVjdCwgZWl0aGVyIHRoZSBtb2RpZmllZCBnbG9iYWxcclxuICogb3B0aW9ucyBvciBhIG1vZGlmaWVkIGNvcHksIGJhc2VkIG9uIHRoZSB2YWx1ZSBvZiBgZ2V0Q29weWAuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdXBkYXRlT3B0aW9ucyhuZXdPcHRpb25zLCBnZXRDb3B5ID0gZmFsc2UpIHtcclxuICAvLyBNZXJnZSBuZXcgb3B0aW9ucyB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb3IgaXRzIGNvcHkgYW5kIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgcmV0dXJuIF9tZXJnZU9wdGlvbnMoXHJcbiAgICBnZXRDb3B5ID8gZGVlcENvcHkoZ2xvYmFsT3B0aW9ucykgOiBnbG9iYWxPcHRpb25zLFxyXG4gICAgbmV3T3B0aW9uc1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHZhbHVlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEksIGtlZXBpbmdcclxuICogdGhlIHByaW5jaXBsZSBvZiBvcHRpb25zIGxvYWQgcHJpb3JpdHkuIFRoaXMgZnVuY3Rpb24gYWNjZXB0cyBhIGBjbGlBcmdzYFxyXG4gKiBhcnJheSBjb250YWluaW5nIGFyZ3VtZW50cyBmcm9tIHRoZSBDTEksIHdoaWNoIHdpbGwgYmUgdmFsaWRhdGVkIGFuZCBhcHBsaWVkXHJcbiAqIGlmIHByb3ZpZGVkLlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSBhIGN1c3RvbSBKU09OIGZpbGUgKGxvYWRlZCBieSB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uKS5cclxuICogMi4gVmFsdWVzIGZyb20gdGhlIGNvbW1hbmQgbGluZSBpbnRlcmZhY2UgKENMSSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRDbGlPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBBbiBhcnJheSBvZiBjb21tYW5kIGxpbmUgYXJndW1lbnRzIHVzZWRcclxuICogZm9yIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIHVwZGF0ZWQgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCByZWZsZWN0aW5nIHRoZSBtZXJnZWRcclxuICogY29uZmlndXJhdGlvbiBmcm9tIHNvdXJjZXMgcHJvdmlkZWQgdGhyb3VnaCB0aGUgQ0xJLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldENsaU9wdGlvbnMoY2xpQXJncykge1xyXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcclxuICBpZiAoY2xpQXJncyAmJiBBcnJheS5pc0FycmF5KGNsaUFyZ3MpICYmIGNsaUFyZ3MubGVuZ3RoKSB7XHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBjdXN0b20gSlNPTiBsb2FkZWQgdmlhIHRoZSBgLS1sb2FkQ29uZmlnYFxyXG4gICAgY29uc3QgY29uZmlnT3B0aW9ucyA9IF9sb2FkQ29uZmlnRmlsZShjbGlBcmdzLCBnbG9iYWxPcHRpb25zLmN1c3RvbUxvZ2ljKTtcclxuXHJcbiAgICAvLyBVcGRhdGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCB0aGUgdmFsdWVzIGZyb20gdGhlIGBjb25maWdPcHRpb25zYFxyXG4gICAgdXBkYXRlT3B0aW9ucyhjb25maWdPcHRpb25zKTtcclxuXHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBDTElcclxuICAgIGNvbnN0IGNsaU9wdGlvbnMgPSBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNsaU9wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGdsb2JhbCBvcHRpb25zXHJcbiAgcmV0dXJuIGdsb2JhbE9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNYXBzIG9sZC1zdHJ1Y3R1cmVkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyAoUGhhbnRvbUpTKSB0byBhIG5ldyBmb3JtYXRcclxuICogKFB1cHBldGVlcikuIFRoaXMgZnVuY3Rpb24gY29udmVydHMgZmxhdCwgb2xkLXN0cnVjdHVyZWQgb3B0aW9ucyBpbnRvXHJcbiAqIGEgbmV3LCBuZXN0ZWQgY29uZmlndXJhdGlvbiBmb3JtYXQgYmFzZWQgb24gYSBwcmVkZWZpbmVkIG1hcHBpbmdcclxuICogKGBuZXN0ZWRQcm9wc2ApLiBUaGUgbmV3IGZvcm1hdCBpcyB1c2VkIGZvciBQdXBwZXRlZXIsIHdoaWxlIHRoZSBvbGQgZm9ybWF0XHJcbiAqIHdhcyB1c2VkIGZvciBQaGFudG9tSlMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtYXBUb05ld09wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9sZE9wdGlvbnMgLSBUaGUgb2xkLCBmbGF0IGNvbmZpZ3VyYXRpb24gb3B0aW9uc1xyXG4gKiB0byBiZSBjb252ZXJ0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgbmV3IG9iamVjdCBjb250YWluaW5nIG9wdGlvbnMgc3RydWN0dXJlZCBhY2NvcmRpbmdcclxuICogdG8gdGhlIG1hcHBpbmcgZGVmaW5lZCBpbiBgbmVzdGVkUHJvcHNgIG9yIGFuIGVtcHR5IG9iamVjdCBpZiB0aGUgcHJvdmlkZWRcclxuICogYG9sZE9wdGlvbnNgIGlzIG5vdCBhIGNvcnJlY3Qgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9ucyhvbGRPcHRpb25zKSB7XHJcbiAgLy8gQW4gb2JqZWN0IGZvciB0aGUgbmV3IHN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gIGNvbnN0IG5ld09wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgdmFsdWUgaXMgYSBjb3JyZWN0IG9iamVjdFxyXG4gIGlmIChpc09iamVjdChvbGRPcHRpb25zKSkge1xyXG4gICAgLy8gSXRlcmF0ZSBvdmVyIGVhY2gga2V5LXZhbHVlIHBhaXIgaW4gdGhlIG9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnNcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG9sZE9wdGlvbnMpKSB7XHJcbiAgICAgIC8vIElmIHRoZXJlIGlzIGEgbmVzdGVkIG1hcHBpbmcsIHNwbGl0IGl0IGludG8gYSBwcm9wZXJ0aWVzIGNoYWluXHJcbiAgICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW2tleV1cclxuICAgICAgICA/IG5lc3RlZFByb3BzW2tleV0uc3BsaXQoJy4nKVxyXG4gICAgICAgIDogW107XHJcblxyXG4gICAgICAvLyBJZiBpdCBpcyB0aGUgbGFzdCBwcm9wZXJ0eSBpbiB0aGUgY2hhaW4sIGFzc2lnbiB0aGUgdmFsdWUsIG90aGVyd2lzZSxcclxuICAgICAgLy8gY3JlYXRlIG9yIHJldXNlIHRoZSBuZXN0ZWQgb2JqZWN0XHJcbiAgICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoXHJcbiAgICAgICAgKG9iaiwgcHJvcCwgaW5kZXgpID0+XHJcbiAgICAgICAgICAob2JqW3Byb3BdID1cclxuICAgICAgICAgICAgcHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4ID8gdmFsdWUgOiBvYmpbcHJvcF0gfHwge30pLFxyXG4gICAgICAgIG5ld09wdGlvbnNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICAnW2NvbmZpZ10gTm8gY29ycmVjdCBvYmplY3Qgd2l0aCBvcHRpb25zIHdhcyBwcm92aWRlZC4gUmV0dXJuaW5nIGFuIGVtcHR5IGFycmF5LidcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIG5ldywgc3RydWN0dXJlZCBvcHRpb25zIG9iamVjdFxyXG4gIHJldHVybiBuZXdPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogVmFsaWRhdGVzLCBwYXJzZXMsIGFuZCBjaGVja3MgaWYgdGhlIHByb3ZpZGVkIGNvbmZpZyBpcyBhbGxvd2VkIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnXHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gY29uZmlnIC0gVGhlIGNvbmZpZyB0byBiZSB2YWxpZGF0ZWQgYW5kIHBhcnNlZCBhcyBhIHNldFxyXG4gKiBvZiBvcHRpb25zLiBNdXN0IGJlIGVpdGhlciBhbiBvYmplY3Qgb3IgYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3RvU3RyaW5nPWZhbHNlXSAtIFdoZXRoZXIgdG8gcmV0dXJuIGEgc3RyaW5naWZpZWQgdmVyc2lvblxyXG4gKiBvZiB0aGUgcGFyc2VkIGNvbmZpZy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICogQHBhcmFtIHtib29sZWFufSBbYWxsb3dGdW5jdGlvbnM9ZmFsc2VdIC0gV2hldGhlciB0byBhbGxvdyBmdW5jdGlvbnNcclxuICogaW4gdGhlIHBhcnNlZCBjb25maWcuIElmIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkLiBPdGhlcndpc2UsIHdoZW5cclxuICogYSBmdW5jdGlvbiBpcyBmb3VuZCwgbnVsbCBpcyByZXR1cm5lZC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8c3RyaW5nfG51bGwpfSBSZXR1cm5zIGEgcGFyc2VkIHNldCBvZiBvcHRpb25zIG9iamVjdCxcclxuICogYSBzdHJpbmdpZmllZCBzZXQgb2Ygb3B0aW9ucyBvYmplY3QgaWYgdGhlIGB0b1N0cmluZ2AgaXMgdHJ1ZSwgYW5kIG51bGxcclxuICogaWYgdGhlIGNvbmZpZyBpcyBub3QgYSB2YWxpZCBzZXQgb2Ygb3B0aW9ucyBvciBwYXJzaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzQWxsb3dlZENvbmZpZyhcclxuICBjb25maWcsXHJcbiAgdG9TdHJpbmcgPSBmYWxzZSxcclxuICBhbGxvd0Z1bmN0aW9ucyA9IGZhbHNlXHJcbikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBBY2NlcHQgb25seSBvYmplY3RzIGFuZCBzdHJpbmdzXHJcbiAgICBpZiAoIWlzT2JqZWN0KGNvbmZpZykgJiYgdHlwZW9mIGNvbmZpZyAhPT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gUmV0dXJuIG51bGwgaWYgYW55IG90aGVyIHR5cGVcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBvYmplY3QgcmVwcmVzZW50YXRpb24gb2YgdGhlIG9yaWdpbmFsIGNvbmZpZ1xyXG4gICAgY29uc3Qgb2JqZWN0Q29uZmlnID1cclxuICAgICAgdHlwZW9mIGNvbmZpZyA9PT0gJ3N0cmluZydcclxuICAgICAgICA/IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IGV2YWwoYCgke2NvbmZpZ30pYClcclxuICAgICAgICAgIDogSlNPTi5wYXJzZShjb25maWcpXHJcbiAgICAgICAgOiBjb25maWc7XHJcblxyXG4gICAgLy8gUHJlc2VydmUgb3IgcmVtb3ZlIHBvdGVudGlhbCBmdW5jdGlvbnMgYmFzZWQgb24gdGhlIGBhbGxvd0Z1bmN0aW9uc2AgZmxhZ1xyXG4gICAgY29uc3Qgc3RyaW5naWZpZWRPcHRpb25zID0gX29wdGlvbnNTdHJpbmdpZnkoXHJcbiAgICAgIG9iamVjdENvbmZpZyxcclxuICAgICAgYWxsb3dGdW5jdGlvbnMsXHJcbiAgICAgIGZhbHNlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFBhcnNlIHRoZSBjb25maWcgdG8gY2hlY2sgaWYgaXQgaXMgdmFsaWQgc2V0IG9mIG9wdGlvbnNcclxuICAgIGNvbnN0IHBhcnNlZE9wdGlvbnMgPSBhbGxvd0Z1bmN0aW9uc1xyXG4gICAgICA/IEpTT04ucGFyc2UoXHJcbiAgICAgICAgICBfb3B0aW9uc1N0cmluZ2lmeShvYmplY3RDb25maWcsIGFsbG93RnVuY3Rpb25zLCB0cnVlKSxcclxuICAgICAgICAgIChfLCB2YWx1ZSkgPT5cclxuICAgICAgICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJiB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpXHJcbiAgICAgICAgICAgICAgPyBldmFsKGAoJHt2YWx1ZX0pYClcclxuICAgICAgICAgICAgICA6IHZhbHVlXHJcbiAgICAgICAgKVxyXG4gICAgICA6IEpTT04ucGFyc2Uoc3RyaW5naWZpZWRPcHRpb25zKTtcclxuXHJcbiAgICAvLyBSZXR1cm4gc3RyaW5naWZpZWQgb3Igb2JqZWN0IG9wdGlvbnMgYmFzZWQgb24gdGhlIGB0b1N0cmluZ2AgZmxhZ1xyXG4gICAgcmV0dXJuIHRvU3RyaW5nID8gc3RyaW5naWZpZWRPcHRpb25zIDogcGFyc2VkT3B0aW9ucztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gUmV0dXJuIG51bGwgaWYgcGFyc2luZyBmYWlsc1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbywgdmVyc2lvbiwgYW5kIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludExpY2Vuc2VcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludExpY2Vuc2UoKSB7XHJcbiAgLy8gUHJpbnQgdGhlIGxvZ28gYW5kIHZlcnNpb24gaW5mb3JtYXRpb25cclxuICBwcmludFZlcnNpb24oKTtcclxuXHJcbiAgLy8gUHJpbnQgdGhlIGxpY2Vuc2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdUaGlzIHNvZnR3YXJlIHJlcXVpcmVzIGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGZvciBjb21tZXJjaWFsIHVzZS5cXG4nXHJcbiAgICAgIC55ZWxsb3csXHJcbiAgICAnXFxuRm9yIGEgZnVsbCBsaXN0IG9mIENMSSBvcHRpb25zLCB0eXBlOicsXHJcbiAgICAnXFxuaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyIC0taGVscFxcbicuZ3JlZW4sXHJcbiAgICAnXFxuSWYgeW91IGRvIG5vdCBoYXZlIGEgbGljZW5zZSwgb25lIGNhbiBiZSBvYnRhaW5lZCBoZXJlOicsXHJcbiAgICAnXFxuaHR0cHM6Ly9zaG9wLmhpZ2hzb2Z0LmNvbS9cXG4nLmdyZWVuLFxyXG4gICAgJ1xcblRvIGN1c3RvbWl6ZSB5b3VyIGluc3RhbGxhdGlvbiwgcGxlYXNlIHJlZmVyIHRvIHRoZSBSRUFETUUgZmlsZSBhdDonLFxyXG4gICAgJ1xcbmh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWVcXG4nLmdyZWVuXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByaW50cyB1c2FnZSBpbmZvcm1hdGlvbiBmb3IgQ0xJIGFyZ3VtZW50cywgZGlzcGxheWluZyBhdmFpbGFibGUgb3B0aW9uc1xyXG4gKiBhbmQgdGhlaXIgZGVzY3JpcHRpb25zLiBJdCBjYW4gbGlzdCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5IGlmIGNhdGVnb3JpZXNcclxuICogY29udGFpbiBuZXN0ZWQgb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50VXNhZ2VcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFVzYWdlKCkge1xyXG4gIC8vIERpc3BsYXkgUkVBRE1FIGFuZCBnZW5lcmFsIHVzYWdlIGluZm9ybWF0aW9uXHJcbiAgY29uc29sZS5sb2coXHJcbiAgICAnXFxuVXNhZ2Ugb2YgQ0xJIGFyZ3VtZW50czonLmJvbGQsXHJcbiAgICAnXFxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nLFxyXG4gICAgYFxcbkZvciBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uLCB2aXNpdCB0aGUgUkVBRE1FIGZpbGUgYXQ6ICR7J2h0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWUnLmdyZWVufS5cXG5gXHJcbiAgKTtcclxuXHJcbiAgLy8gSXRlcmF0ZSB0aHJvdWdoIGVhY2ggY2F0ZWdvcnkgaW4gdGhlIGBkZWZhdWx0Q29uZmlnYCBhbmQgZGlzcGxheSB1c2FnZSBpbmZvXHJcbiAgT2JqZWN0LmtleXMoZGVmYXVsdENvbmZpZykuZm9yRWFjaCgoY2F0ZWdvcnkpID0+IHtcclxuICAgIGNvbnNvbGUubG9nKGAke2NhdGVnb3J5LnRvVXBwZXJDYXNlKCl9YC5ib2xkLnJlZCk7XHJcbiAgICBfY3ljbGVDYXRlZ29yaWVzKGRlZmF1bHRDb25maWdbY2F0ZWdvcnldKTtcclxuICAgIGNvbnNvbGUubG9nKCcnKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByaW50cyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGxvZ28gb3IgdGV4dCB3aXRoIHRoZSB2ZXJzaW9uXHJcbiAqIGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW25vTG9nbz1mYWxzZV0gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24sIHdpdGhvdXQgdGhlIGxvZ28uIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRWZXJzaW9uKG5vTG9nbyA9IGZhbHNlKSB7XHJcbiAgLy8gR2V0IHBhY2thZ2UgdmVyc2lvbiBlaXRoZXIgZnJvbSBgLmVudmAgb3IgZnJvbSBgcGFja2FnZS5qc29uYFxyXG4gIGNvbnN0IHBhY2thZ2VWZXJzaW9uID0gSlNPTi5wYXJzZShcclxuICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ3BhY2thZ2UuanNvbicpLCAndXRmOCcpXHJcbiAgKS52ZXJzaW9uO1xyXG5cclxuICAvLyBQcmludCB0ZXh0IG9ubHlcclxuICBpZiAobm9Mb2dvKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHYke3BhY2thZ2VWZXJzaW9ufWApO1xyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBQcmludCB0aGUgbG9nb1xyXG4gICAgY29uc29sZS5sb2coXHJcbiAgICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ21zZycsICdzdGFydHVwLm1zZycpLCAndXRmOCcpLnRvU3RyaW5nKClcclxuICAgICAgICAuYm9sZC55ZWxsb3csXHJcbiAgICAgIGB2JHtwYWNrYWdlVmVyc2lvbn1cXG5gLmJvbGRcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgYW5kIHJldHVybnMgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCBiYXNlZCBvbiB0aGUgcHJvdmlkZWRcclxuICogY29uZmlndXJhdGlvbiwgc2V0dGluZyB2YWx1ZXMgZnJvbSBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogVGhlIHByaW9yaXR5IG9yZGVyIGZvciBzZXR0aW5nIHZhbHVlcyBpczpcclxuICpcclxuICogMS4gVmFsdWVzIGZyb20gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSAoZGVmYXVsdHMpLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgKHNwZWNpZmllZCBpbiB0aGUgYC5lbnZgIGZpbGUpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2luaXRPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgdXNlZCBmb3IgaW5pdGlhbGl6aW5nXHJcbiAqIHRoZSBnbG9iYWwgb3B0aW9ucy4gSXQgc2hvdWxkIGluY2x1ZGUgbmVzdGVkIHByb3BlcnRpZXMgd2l0aCBhIGB2YWx1ZWBcclxuICogYW5kIGFuIGBlbnZMaW5rYCBmb3IgbGlua2luZyB0byBlbnZpcm9ubWVudCB2YXJpYWJsZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBpbml0aWFsaXplZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHBvcHVsYXRlZCB3aXRoXHJcbiAqIHZhbHVlcyBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5XHJcbiAqIG9yZGVyLlxyXG4gKi9cclxuZnVuY3Rpb24gX2luaXRPcHRpb25zKGNvbmZpZykge1xyXG4gIC8vIEluaXQgdGhlIG9iamVjdCBmb3Igb3B0aW9uc1xyXG4gIGNvbnN0IG9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gU3RhcnQgaW5pdGlhbGl6aW5nIHRoZSBgb3B0aW9uc2Agb2JqZWN0IHJlY3Vyc2l2ZWx5XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoY29uZmlnKSkge1xyXG4gICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChpdGVtLCAndmFsdWUnKSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGNvcnJlY3QgdmFsdWUgYmFzZWQgb24gdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5IG9yZGVyXHJcbiAgICAgIGlmIChlbnZzW2l0ZW0uZW52TGlua10gIT09IHVuZGVmaW5lZCAmJiBlbnZzW2l0ZW0uZW52TGlua10gIT09IG51bGwpIHtcclxuICAgICAgICAvLyBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIHZhbHVlXHJcbiAgICAgICAgb3B0aW9uc1tuYW1lXSA9IGVudnNbaXRlbS5lbnZMaW5rXTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaGUgdmFsdWUgZnJvbSB0aGUgY29uZmlnIGZpbGVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gaXRlbS52YWx1ZTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIGEgc2VjdGlvbiBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICBvcHRpb25zW25hbWVdID0gX2luaXRPcHRpb25zKGl0ZW0pO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBjcmVhdGVkIGBvcHRpb25zYCBvYmplY3RcclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGNvbnNpZGVyaW5nIGFic29sdXRlIHByb3BlcnRpZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcmlnaW5hbE9wdGlvbnMgLSBPcmlnaW5hbCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBuZXdPcHRpb25zIC0gTmV3IGNvbmZpZ3VyYXRpb24gb3B0aW9ucyB0byBiZSBtZXJnZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IE1lcmdlZCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX21lcmdlT3B0aW9ucyhvcmlnaW5hbE9wdGlvbnMsIG5ld09wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgYG9yaWdpbmFsT3B0aW9uc2AgYW5kIGBuZXdPcHRpb25zYCBhcmUgY29ycmVjdCBvYmplY3RzXHJcbiAgaWYgKGlzT2JqZWN0KG9yaWdpbmFsT3B0aW9ucykgJiYgaXNPYmplY3QobmV3T3B0aW9ucykpIHtcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICAgIG9yaWdpbmFsT3B0aW9uc1trZXldID1cclxuICAgICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXHJcbiAgICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgPyBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9uc1trZXldLCB2YWx1ZSlcclxuICAgICAgICAgIDogdmFsdWUgIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICAgIDogb3JpZ2luYWxPcHRpb25zW2tleV0gfHwgbnVsbDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb3JpZ2luYWwgKG1vZGlmaWVkIG9yIG5vdCkgb3B0aW9uc1xyXG4gIHJldHVybiBvcmlnaW5hbE9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmdcclxuICogd2l0aCB0aGUgb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy4gSW4gb3JkZXIgZm9yIGEgZnVuY3Rpb25cclxuICogdG8gYmUgcHJlc2VydmVkLCBpdCBuZWVkcyB0byBmb2xsb3cgdGhlIGZvcm1hdCBgZnVuY3Rpb24gKC4uLikgey4uLn1gLlxyXG4gKiBTdWNoIGEgZnVuY3Rpb24gY2FuIGFsc28gYmUgc3RyaW5naWZpZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWRcclxuICogaW4gdGhlIG91dHB1dC4gT3RoZXJ3aXNlIGFuIGVycm9yIGlzIHRocm93bi5cclxuICogQHBhcmFtIHtib29sZWFufSBzdHJpbmdpZnlGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBzYXZlZFxyXG4gKiBhcyBzdHJpbmdzLiBUaGUgYGFsbG93RnVuY3Rpb25zYCBtdXN0IGJlIHNldCB0byB0cnVlIGFzIHdlbGwgZm9yIHRoaXMgdG8gdGFrZVxyXG4gKiBhbiBlZmZlY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgd2hlbiBmdW5jdGlvbnMgYXJlIG5vdCBhbGxvd2VkIGJ1dCBhcmVcclxuICogZm91bmQgaW4gcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX29wdGlvbnNTdHJpbmdpZnkob3B0aW9ucywgYWxsb3dGdW5jdGlvbnMsIHN0cmluZ2lmeUZ1bmN0aW9ucykge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAoXywgdmFsdWUpID0+IHtcclxuICAgIC8vIFRyaW0gc3RyaW5nIHZhbHVlc1xyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgdmFsdWUgaXMgYSBmdW5jdGlvbiBvciBzdHJpbmdpZmllZCBmdW5jdGlvblxyXG4gICAgaWYgKFxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgfHxcclxuICAgICAgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpICYmXHJcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBJZiBhbGxvd0Z1bmN0aW9ucyBpcyBzZXQgdG8gdHJ1ZSwgcHJlc2VydmUgZnVuY3Rpb25zXHJcbiAgICAgIGlmIChhbGxvd0Z1bmN0aW9ucykge1xyXG4gICAgICAgIC8vIEJhc2VkIG9uIHRoZSBgc3RyaW5naWZ5RnVuY3Rpb25zYCBvcHRpb25zLCBzZXQgZnVuY3Rpb24gdmFsdWVzXHJcbiAgICAgICAgcmV0dXJuIHN0cmluZ2lmeUZ1bmN0aW9uc1xyXG4gICAgICAgICAgPyAvLyBBcyBzdHJpbmdpZmllZCBmdW5jdGlvbnNcclxuICAgICAgICAgICAgYFwiRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOXCJgXHJcbiAgICAgICAgICA6IC8vIEFzIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOYDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaHJvdyBhbiBlcnJvciBvdGhlcndpc2VcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgc2ltcGx5IHJldHVybiB0aGUgdmFsdWVcclxuICAgIHJldHVybiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIHN0cmluZ2lmeUZ1bmN0aW9ucyA/IC9cXFxcXCJFWFBfRlVOfEVYUF9GVU5cXFxcXCIvZyA6IC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHByb3ZpZGVkIHZpYVxyXG4gKiB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGluIHRoZSBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2xvYWRDb25maWdGaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIHRvIHNlYXJjaFxyXG4gKiBmb3IgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgZmlsZSBwYXRoLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGxvYWRlZCBmcm9tIHRoZSBzcGVjaWZpZWRcclxuICogZmlsZSwgb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBmaWxlIGlzIG5vdCBmb3VuZCwgaW52YWxpZCwgb3IgYW4gZXJyb3JcclxuICogb2NjdXJzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiB2YWx1ZVxyXG4gIGNvbnN0IGNvbmZpZ0ZpbGVOYW1lID0gY29uZmlnSW5kZXggPiAtMSAmJiBjbGlBcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lICYmIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIExvYWQgYW4gb3B0aW9uYWwgY3VzdG9tIEpTT04gY29uZmlnIGZpbGVcclxuICAgICAgcmV0dXJuIGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGNvbmZpZ0ZpbGVOYW1lKSwgJ3V0ZjgnKSxcclxuICAgICAgICBmYWxzZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgMixcclxuICAgICAgICBlcnJvcixcclxuICAgICAgICBgW2NvbmZpZ10gVW5hYmxlIHRvIGxvYWQgdGhlIGNvbmZpZ3VyYXRpb24gZnJvbSB0aGUgJHtjb25maWdGaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBObyBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gcmV0dXJuXHJcbiAgcmV0dXJuIHt9O1xyXG59XHJcblxyXG4vKipcclxuICogUGFyc2VzIGNvbW1hbmQtbGluZSBhcmd1bWVudHMgYW5kIHBhaXJzIGVhY2ggYXJndW1lbnQgd2l0aCBpdHMgY29ycmVzcG9uZGluZ1xyXG4gKiBvcHRpb24gaW4gdGhlIGNvbmZpZ3VyYXRpb24uIFRoZSB2YWx1ZXMgYXJlIHN0cnVjdHVyZWQgaW50byBhIG5lc3RlZCBvcHRpb25zXHJcbiAqIG9iamVjdCwgYmFzZWQgb24gcHJlZGVmaW5lZCBtYXBwaW5ncy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBuZXN0ZWRQcm9wcyAtIEFuIGFycmF5IG9mIG5lc3RpbmcgbGV2ZWwgZm9yIGFsbFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjbGlBcmdzIC0gQW4gYXJyYXkgb2YgY29tbWFuZC1saW5lIGFyZ3VtZW50c1xyXG4gKiBjb250YWluaW5nIG9wdGlvbnMgYW5kIHRoZWlyIGFzc29jaWF0ZWQgdmFsdWVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0IHdoZXJlIGVhY2ggb3B0aW9uIGZyb21cclxuICogdGhlIGNvbW1hbmQtbGluZSBpcyBwYWlyZWQgd2l0aCBpdHMgdmFsdWUsIHN0cnVjdHVyZWQgaW50byBuZXN0ZWQgb2JqZWN0c1xyXG4gKiBhcyBkZWZpbmVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX3BhaXJBcmd1bWVudFZhbHVlKG5lc3RlZFByb3BzLCBjbGlBcmdzKSB7XHJcbiAgLy8gQW4gZW1wdHkgb2JqZWN0IHRvIGNvbGxlY3QgYW5kIHN0cnVjdHVyaXplIGRhdGEgZnJvbSB0aGUgYXJnc1xyXG4gIGNvbnN0IGNsaU9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gQ3ljbGUgdGhyb3VnaCBhbGwgQ0xJIGFyZ3MgYW5kIGZpbHRlciB0aGVtXHJcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBjbGlBcmdzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICBjb25zdCBvcHRpb24gPSBjbGlBcmdzW2ldLnJlcGxhY2UoLy0vZywgJycpO1xyXG5cclxuICAgIC8vIEZpbmQgdGhlIHJpZ2h0IHBsYWNlIGZvciBwcm9wZXJ0eSdzIHZhbHVlXHJcbiAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRQcm9wc1tvcHRpb25dXHJcbiAgICAgID8gbmVzdGVkUHJvcHNbb3B0aW9uXS5zcGxpdCgnLicpXHJcbiAgICAgIDogW107XHJcblxyXG4gICAgLy8gQ3JlYXRlIG9wdGlvbnMgb2JqZWN0IHdpdGggdmFsdWVzIGZyb20gQ0xJIGZvciBsYXRlciBwYXJzaW5nIGFuZCBtZXJnaW5nXHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XHJcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcclxuICAgICAgICBjb25zdCB2YWx1ZSA9IGNsaUFyZ3NbKytpXTtcclxuICAgICAgICBpZiAoIXZhbHVlKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGBbY29uZmlnXSBNaXNzaW5nIHZhbHVlIGZvciB0aGUgQ0xJICctLSR7b3B0aW9ufScgYXJndW1lbnQuIFVzaW5nIHRoZSBkZWZhdWx0IHZhbHVlLmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIG9ialtwcm9wXSA9IHZhbHVlIHx8IG51bGw7XHJcbiAgICAgIH0gZWxzZSBpZiAob2JqW3Byb3BdID09PSB1bmRlZmluZWQpIHtcclxuICAgICAgICBvYmpbcHJvcF0gPSB7fTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xyXG4gICAgfSwgY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcGFyc2VkIENMSSBvcHRpb25zXHJcbiAgcmV0dXJuIGNsaU9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSB0cmF2ZXJzZXMgdGhlIG9wdGlvbnMgb2JqZWN0IHRvIHByaW50IHRoZSB1c2FnZSBpbmZvcm1hdGlvblxyXG4gKiBmb3IgZWFjaCBvcHRpb24gY2F0ZWdvcnkgYW5kIGluZGl2aWR1YWwgb3B0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2N5Y2xlQ2F0ZWdvcmllc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIENMSSBvcHRpb25zLiBJdCBtYXlcclxuICogaW5jbHVkZSBuZXN0ZWQgY2F0ZWdvcmllcyBhbmQgaW5kaXZpZHVhbCBvcHRpb25zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb25zKSB7XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgb3B0aW9uXSBvZiBPYmplY3QuZW50cmllcyhvcHRpb25zKSkge1xyXG4gICAgLy8gSWYgdGhlIGN1cnJlbnQgZW50cnkgaXMgYSBjYXRlZ29yeSBhbmQgbm90IGEgbGVhZiBvcHRpb24sIHJlY3Vyc2UgaW50byBpdFxyXG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob3B0aW9uLCAndmFsdWUnKSkge1xyXG4gICAgICBfY3ljbGVDYXRlZ29yaWVzKG9wdGlvbik7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBQcmVwYXJlIGRlc2NyaXB0aW9uXHJcbiAgICAgIGNvbnN0IGRlc2NOYW1lID0gYCAtLSR7b3B0aW9uLmNsaU5hbWUgfHwgbmFtZX1gO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB2YWx1ZVxyXG4gICAgICBsZXQgb3B0aW9uVmFsdWUgPSBvcHRpb24udmFsdWU7XHJcblxyXG4gICAgICAvLyBQcmVwYXJlIHZhbHVlIGZvciBvcHRpb24gdGhhdCBpcyBub3QgbnVsbCBhbmQgaXMgYXJyYXkgb2Ygc3RyaW5nc1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmdbXScpKSB7XHJcbiAgICAgICAgb3B0aW9uVmFsdWUgPVxyXG4gICAgICAgICAgJ1snICsgb3B0aW9uVmFsdWUubWFwKChpdGVtKSA9PiBgJyR7aXRlbX0nYCkuam9pbignLCAnKSArICddJztcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGEgc3RyaW5nXHJcbiAgICAgIGlmIChvcHRpb25WYWx1ZSAhPT0gbnVsbCAmJiBvcHRpb24udHlwZXMuaW5jbHVkZXMoJ3N0cmluZycpKSB7XHJcbiAgICAgICAgb3B0aW9uVmFsdWUgPSBgJyR7b3B0aW9uVmFsdWV9J2A7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERpc3BsYXkgY29ycmVjdGx5IGFsaWduZWQgbWVzc2FnZXNcclxuICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgZGVzY05hbWUuZ3JlZW4sXHJcbiAgICAgICAgYCR7KCc8JyArIG9wdGlvbi50eXBlcy5qb2luKCd8JykgKyAnPicpLnllbGxvd31gLFxyXG4gICAgICAgIGAke1N0cmluZyhvcHRpb25WYWx1ZSkuYm9sZH1gLmJsdWUsXHJcbiAgICAgICAgYC0gJHtvcHRpb24uZGVzY3JpcHRpb259LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHVwZGF0ZU9wdGlvbnMsXHJcbiAgc2V0Q2xpT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcbiAgaXNBbGxvd2VkQ29uZmlnLFxyXG4gIHByaW50TGljZW5zZSxcclxuICBwcmludFVzYWdlLFxyXG4gIHByaW50VmVyc2lvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgSFRUUCB1dGlsaXR5IG1vZHVsZSBmb3IgZmV0Y2hpbmcgYW5kIHBvc3RpbmcgZGF0YS4gU3VwcG9ydHMgYm90aFxyXG4gKiBIVFRQIGFuZCBIVFRQUyBwcm90b2NvbHMsIHByb3ZpZGluZyBtZXRob2RzIHRvIG1ha2UgR0VUIGFuZCBQT1NUIHJlcXVlc3RzXHJcbiAqIHdpdGggY3VzdG9taXphYmxlIG9wdGlvbnMuIEluY2x1ZGVzIHByb3RvY29sIGRldGVybWluYXRpb24gYmFzZWQgb24gVVJMXHJcbiAqIGFuZCBhdWdtZW50cyByZXNwb25zZSBvYmplY3RzIHdpdGggYSAndGV4dCcgcHJvcGVydHkgZm9yIGVhc2llciBkYXRhIGFjY2Vzcy5cclxuICovXHJcblxyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGRhdGEgZnJvbSB0aGUgc3BlY2lmaWVkIFVSTCB1c2luZyBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBmZXRjaFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBmZXRjaCBkYXRhIGZyb20uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmV0Y2godXJsLCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpXHJcbiAgICAgIC5nZXQodXJsLCByZXF1ZXN0T3B0aW9ucywgKHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2VEYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFyZXNwb25zZURhdGEpIHtcclxuICAgICAgICAgICAgcmVqZWN0KCdOb3RoaW5nIHdhcyBmZXRjaGVkIGZyb20gdGhlIFVSTC4nKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogU2VuZHMgYSBQT1NUIHJlcXVlc3QgdG8gdGhlIHNwZWNpZmllZCBVUkwgd2l0aCB0aGUgcHJvdmlkZWQgSlNPTiBib2R5IHVzaW5nXHJcbiAqIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHBvc3RcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gc2VuZCB0aGUgUE9TVCByZXF1ZXN0IHRvLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2JvZHk9e31dIC0gVGhlIEpTT04gYm9keSB0byBpbmNsdWRlIGluIHRoZSBQT1NUIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtyZXF1ZXN0T3B0aW9ucz17fV0gLSBPcHRpb25zIGZvciB0aGUgSFRUUC9IVFRQUyByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBIVFRQL0hUVFBTIHJlc3BvbnNlXHJcbiAqIG9iamVjdCB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0KHVybCwgYm9keSA9IHt9LCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIGNvbnN0IGRhdGEgPSBKU09OLnN0cmluZ2lmeShib2R5KTtcclxuXHJcbiAgICAvLyBTZXQgZGVmYXVsdCBoZWFkZXJzIGFuZCBtZXJnZSB3aXRoIHJlcXVlc3RPcHRpb25zXHJcbiAgICBjb25zdCBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihcclxuICAgICAge1xyXG4gICAgICAgIG1ldGhvZDogJ1BPU1QnLFxyXG4gICAgICAgIGhlYWRlcnM6IHtcclxuICAgICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXHJcbiAgICAgICAgICAnQ29udGVudC1MZW5ndGgnOiBkYXRhLmxlbmd0aFxyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcmVxdWVzdE9wdGlvbnNcclxuICAgICk7XHJcblxyXG4gICAgY29uc3QgcmVxdWVzdCA9IF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpXHJcbiAgICAgIC5yZXF1ZXN0KHVybCwgb3B0aW9ucywgKHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2VEYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgLy8gV3JpdGUgdGhlIHJlcXVlc3QgYm9keSBhbmQgZW5kIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LndyaXRlKGRhdGEpO1xyXG4gICAgcmVxdWVzdC5lbmQoKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgdGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBVUkwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZGV0ZXJtaW5lIHRoZSBwcm90b2NvbC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIChodHRwIG9yIGh0dHBzKS5cclxuICovXHJcbmZ1bmN0aW9uIF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpIHtcclxuICByZXR1cm4gdXJsLnN0YXJ0c1dpdGgoJ2h0dHBzJykgPyBodHRwcyA6IGh0dHA7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBmZXRjaCxcclxuICBwb3N0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEEgY3VzdG9tIGVycm9yIGNsYXNzIGZvciBoYW5kbGluZyBleHBvcnQtcmVsYXRlZCBlcnJvcnMuIEV4dGVuZHMgdGhlIG5hdGl2ZVxyXG4gKiBgRXJyb3JgIGNsYXNzIHRvIGluY2x1ZGUgYWRkaXRpb25hbCBwcm9wZXJ0aWVzIGxpa2Ugc3RhdHVzIGNvZGUgYW5kIHN0YWNrXHJcbiAqIHRyYWNlIGRldGFpbHMuXHJcbiAqL1xyXG5jbGFzcyBFeHBvcnRFcnJvciBleHRlbmRzIEVycm9yIHtcclxuICAvKipcclxuICAgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIHRoZSBgRXhwb3J0RXJyb3JgLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSBUaGUgZXJyb3IgbWVzc2FnZSB0byBiZSBkaXNwbGF5ZWQuXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBPcHRpb25hbCBIVFRQIHN0YXR1cyBjb2RlIGFzc29jaWF0ZWRcclxuICAgKiB3aXRoIHRoZSBlcnJvciAoZS5nLiwgNDAwLCA1MDApLlxyXG4gICAqL1xyXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UsIHN0YXR1c0NvZGUpIHtcclxuICAgIHN1cGVyKCk7XHJcblxyXG4gICAgdGhpcy5tZXNzYWdlID0gbWVzc2FnZTtcclxuICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gbWVzc2FnZTtcclxuXHJcbiAgICBpZiAoc3RhdHVzQ29kZSkge1xyXG4gICAgICB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXNDb2RlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0cyBvciB1cGRhdGVzIHRoZSBIVFRQIHN0YXR1cyBjb2RlIGZvciB0aGUgZXJyb3IuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzQ29kZSAtIFRoZSBIVFRQIHN0YXR1cyBjb2RlIHRvIGFzc2lnbiB0byB0aGUgZXJyb3IuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyB7RXhwb3J0RXJyb3J9IFRoZSB1cGRhdGVkIGluc3RhbmNlIG9mIHRoZSBgRXhwb3J0RXJyb3JgIGNsYXNzLlxyXG4gICAqL1xyXG4gIHNldFN0YXR1cyhzdGF0dXNDb2RlKSB7XHJcbiAgICB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXNDb2RlO1xyXG5cclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0cyBhZGRpdGlvbmFsIGVycm9yIGRldGFpbHMgYmFzZWQgb24gYW4gZXhpc3RpbmcgZXJyb3Igb2JqZWN0LlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBBbiBlcnJvciBvYmplY3QgY29udGFpbmluZyBkZXRhaWxzIHRvIHBvcHVsYXRlXHJcbiAgICogdGhlIGBFeHBvcnRFcnJvcmAgaW5zdGFuY2UuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyB7RXhwb3J0RXJyb3J9IFRoZSB1cGRhdGVkIGluc3RhbmNlIG9mIHRoZSBgRXhwb3J0RXJyb3JgIGNsYXNzLlxyXG4gICAqL1xyXG4gIHNldEVycm9yKGVycm9yKSB7XHJcbiAgICB0aGlzLmVycm9yID0gZXJyb3I7XHJcblxyXG4gICAgaWYgKGVycm9yLm5hbWUpIHtcclxuICAgICAgdGhpcy5uYW1lID0gZXJyb3IubmFtZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhdHVzQ29kZSkge1xyXG4gICAgICB0aGlzLnN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChlcnJvci5zdGFjaykge1xyXG4gICAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XHJcbiAgICAgIHRoaXMuc3RhY2sgPSBlcnJvci5zdGFjaztcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IEV4cG9ydEVycm9yO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhlIGNhY2hlIG1hbmFnZXIgaXMgcmVzcG9uc2libGUgZm9yIGhhbmRsaW5nIGFuZCBtYW5hZ2luZ1xyXG4gKiB0aGUgSGlnaGNoYXJ0cyBsaWJyYXJ5IGFsb25nIHdpdGggaXRzIGRlcGVuZGVuY2llcy4gSXQgZW5zdXJlcyB0aGF0IHRoZXNlXHJcbiAqIHJlc291cmNlcyBhcmUgc3RvcmVkIGFuZCByZXRyaWV2ZWQgZWZmaWNpZW50bHkgdG8gb3B0aW1pemUgcGVyZm9ybWFuY2VcclxuICogYW5kIHJlZHVjZSByZWR1bmRhbnQgbmV0d29yayByZXF1ZXN0cy4gVGhlIGNhY2hlIGlzIHN0b3JlZCBpbiB0aGUgYC5jYWNoZWBcclxuICogZGlyZWN0b3J5IGJ5IGRlZmF1bHQsIHdoaWNoIHNlcnZlcyBhcyBhIGRlZGljYXRlZCBmb2xkZXIgZm9yIGtlZXBpbmcgY2FjaGVkXHJcbiAqIGZpbGVzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGV4aXN0c1N5bmMsIG1rZGlyU3luYywgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBIdHRwc1Byb3h5QWdlbnQgfSBmcm9tICdodHRwcy1wcm94eS1hZ2VudCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBmZXRjaCB9IGZyb20gJy4vZmV0Y2guanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGluaXRpYWwgY2FjaGUgdGVtcGxhdGVcclxuY29uc3QgY2FjaGUgPSB7XHJcbiAgY2RuVXJsOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICBhY3RpdmVNYW5pZmVzdDoge30sXHJcbiAgc291cmNlczogJycsXHJcbiAgaGNWZXJzaW9uOiAnJ1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyB0aGUgY2FjaGUgZm9yIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLCB1cGRhdGVzIHRoZSBjYWNoZSBpZiBuZWVkZWQsXHJcbiAqIGFuZCBsb2FkcyB0aGUgc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjaGVja0FuZFVwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgc2VydmVyLnByb3h5YCBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGUoXHJcbiAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgc2VydmVyUHJveHlPcHRpb25zXHJcbikge1xyXG4gIHRyeSB7XHJcbiAgICBsZXQgZmV0Y2hlZE1vZHVsZXM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjYWNoZSBwYXRoXHJcbiAgICBjb25zdCBjYWNoZVBhdGggPSBnZXRDYWNoZVBhdGgoKTtcclxuXHJcbiAgICAvLyBQcmVwYXJlIHBhdGhzIHRvIG1hbmlmZXN0IGFuZCBzb3VyY2VzIGZyb20gdGhlIGNhY2hlIGZvbGRlclxyXG4gICAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgICBjb25zdCBzb3VyY2VQYXRoID0gam9pbihjYWNoZVBhdGgsICdzb3VyY2VzLmpzJyk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBjYWNoZSBkZXN0aW5hdGlvbiBpZiBpdCBkb2Vzbid0IGV4aXN0IGFscmVhZHlcclxuICAgICFleGlzdHNTeW5jKGNhY2hlUGF0aCkgJiYgbWtkaXJTeW5jKGNhY2hlUGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XHJcblxyXG4gICAgLy8gRmV0Y2ggYWxsIHRoZSBzY3JpcHRzIGVpdGhlciBpZiB0aGUgYG1hbmlmZXN0Lmpzb25gIGRvZXMgbm90IGV4aXN0XHJcbiAgICAvLyBvciBpZiB0aGUgYGZvcmNlRmV0Y2hgIG9wdGlvbiBpcyBlbmFibGVkXHJcbiAgICBpZiAoIWV4aXN0c1N5bmMobWFuaWZlc3RQYXRoKSB8fCBoaWdoY2hhcnRzT3B0aW9ucy5mb3JjZUZldGNoKSB7XHJcbiAgICAgIGxvZygzLCAnW2NhY2hlXSBGZXRjaGluZyBhbmQgY2FjaGluZyBIaWdoY2hhcnRzIGRlcGVuZGVuY2llcy4nKTtcclxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCBfdXBkYXRlQ2FjaGUoXHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgIHNvdXJjZVBhdGhcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XHJcblxyXG4gICAgICAvLyBSZWFkIHRoZSBtYW5pZmVzdCBKU09OXHJcbiAgICAgIGNvbnN0IG1hbmlmZXN0ID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMobWFuaWZlc3RQYXRoKSwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBtb2R1bGVzIGlzIGFuIGFycmF5LCBpZiBzbywgd2UgcmV3cml0ZSBpdCB0byBhIG1hcCB0byBtYWtlXHJcbiAgICAgIC8vIGl0IGVhc2llciB0byByZXNvbHZlIG1vZHVsZXMuXHJcbiAgICAgIGlmIChtYW5pZmVzdC5tb2R1bGVzICYmIEFycmF5LmlzQXJyYXkobWFuaWZlc3QubW9kdWxlcykpIHtcclxuICAgICAgICBjb25zdCBtb2R1bGVNYXAgPSB7fTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzLmZvckVhY2goKG0pID0+IChtb2R1bGVNYXBbbV0gPSAxKSk7XHJcbiAgICAgICAgbWFuaWZlc3QubW9kdWxlcyA9IG1vZHVsZU1hcDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSBhY3R1YWwgbnVtYmVyIG9mIHNjcmlwdHMgdG8gYmUgZmV0Y2hlZFxyXG4gICAgICBjb25zdCB7IGNvcmVTY3JpcHRzLCBtb2R1bGVTY3JpcHRzLCBpbmRpY2F0b3JTY3JpcHRzIH0gPVxyXG4gICAgICAgIGhpZ2hjaGFydHNPcHRpb25zO1xyXG4gICAgICBjb25zdCBudW1iZXJPZk1vZHVsZXMgPVxyXG4gICAgICAgIGNvcmVTY3JpcHRzLmxlbmd0aCArIG1vZHVsZVNjcmlwdHMubGVuZ3RoICsgaW5kaWNhdG9yU2NyaXB0cy5sZW5ndGg7XHJcblxyXG4gICAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgaGlnaGNoYXJ0cyBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAgIC8vIElmIHRoZXJlIGFyZSBjaGFuZ2VzLCBmZXRjaCByZXF1ZXN0ZWQgbW9kdWxlcyBhbmQgcHJvZHVjdHMsXHJcbiAgICAgIC8vIGFuZCBiYWtlIHRoZW0gaW50byBhIGdpYW50IGJsb2IuIFNhdmUgdGhlIGJsb2IuXHJcbiAgICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgICdbY2FjaGVdIEEgSGlnaGNoYXJ0cyB2ZXJzaW9uIG1pc21hdGNoIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC4nXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgICAgfSBlbHNlIGlmIChcclxuICAgICAgICBPYmplY3Qua2V5cyhtYW5pZmVzdC5tb2R1bGVzIHx8IHt9KS5sZW5ndGggIT09IG51bWJlck9mTW9kdWxlc1xyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gVGhlIGNhY2hlIGFuZCB0aGUgcmVxdWVzdGVkIG1vZHVsZXMgZG8gbm90IG1hdGNoLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENoZWNrIGVhY2ggbW9kdWxlLCBpZiBhbnl0aGluZyBpcyBtaXNzaW5nIHJlZmV0Y2ggZXZlcnl0aGluZ1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSAobW9kdWxlU2NyaXB0cyB8fCBbXSkuc29tZSgobW9kdWxlTmFtZSkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFtYW5pZmVzdC5tb2R1bGVzW21vZHVsZU5hbWVdKSB7XHJcbiAgICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICAgIGBbY2FjaGVdIFRoZSAke21vZHVsZU5hbWV9IGlzIG1pc3NpbmcgaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLmBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFVwZGF0ZSBjYWNoZSBpZiBuZWVkZWRcclxuICAgICAgaWYgKHJlcXVlc3RVcGRhdGUpIHtcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICAgIGhpZ2hjaGFydHNPcHRpb25zLFxyXG4gICAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDMsICdbY2FjaGVdIERlcGVuZGVuY3kgY2FjaGUgaXMgdXAgdG8gZGF0ZSwgcHJvY2VlZGluZy4nKTtcclxuXHJcbiAgICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xyXG4gICAgICAgIGNhY2hlLnNvdXJjZXMgPSByZWFkRmlsZVN5bmMoc291cmNlUGF0aCwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IGN1cnJlbnQgbW9kdWxlcyBtYXBcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XHJcblxyXG4gICAgICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICAgICAgY2FjaGUuaGNWZXJzaW9uID0gZXh0cmFjdFZlcnNpb24oY2FjaGUuc291cmNlcyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcclxuICAgIC8vIGluIGEgc2xpZ2h0bHkgZGlmZmVyZW50IGZvcm1hdFxyXG4gICAgYXdhaXQgX3NhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHNPcHRpb25zLCBmZXRjaGVkTW9kdWxlcyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gQ291bGQgbm90IGNvbmZpZ3VyZSBjYWNoZSBhbmQgY3JlYXRlIG9yIHVwZGF0ZSB0aGUgY29uZmlnIG1hbmlmZXN0LicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgdmVyc2lvbiBvZiBIaWdoY2hhcnRzIGZyb20gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0SGlnaGNoYXJ0c1ZlcnNpb25cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNhY2hlZCBIaWdoY2hhcnRzIHZlcnNpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSB7XHJcbiAgcmV0dXJuIGNhY2hlLmhjVmVyc2lvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBpbiB0aGUgYXBwbGllZCBjb25maWd1cmF0aW9uIGFuZCBjaGVja3NcclxuICogdGhlIGNhY2hlIGZvciB0aGUgbmV3IHZlcnNpb24uXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IG5ld1ZlcnNpb24gLSBUaGUgbmV3IEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uKG5ld1ZlcnNpb24pIHtcclxuICAvLyBVcGRhdGUgdG8gdGhlIG5ldyB2ZXJzaW9uXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgaGlnaGNoYXJ0czoge1xyXG4gICAgICB2ZXJzaW9uOiBuZXdWZXJzaW9uXHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIENoZWNrIGlmIGNhY2hlIG5lZWRzIHRvIGJlIHVwZGF0ZWRcclxuICBhd2FpdCBjaGVja0FuZFVwZGF0ZUNhY2hlKG9wdGlvbnMuaGlnaGNoYXJ0cywgb3B0aW9ucy5zZXJ2ZXIucHJveHkpO1xyXG59XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgSGlnaGNoYXJ0cyB2ZXJzaW9uIGZyb20gdGhlIGNhY2hlJ3Mgc291cmNlcyBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHRyYWN0VmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2FjaGVTb3VyY2VzIC0gVGhlIGNhY2hlIHNvdXJjZXMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgZXh0cmFjdGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0VmVyc2lvbihjYWNoZVNvdXJjZXMpIHtcclxuICByZXR1cm4gY2FjaGVTb3VyY2VzXHJcbiAgICAuc3Vic3RyaW5nKDAsIGNhY2hlU291cmNlcy5pbmRleE9mKCcqLycpKVxyXG4gICAgLnJlcGxhY2UoJy8qJywgJycpXHJcbiAgICAucmVwbGFjZSgnKi8nLCAnJylcclxuICAgIC5yZXBsYWNlKC9cXG4vZywgJycpXHJcbiAgICAudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgdGhlIEhpZ2hjaGFydHMgbW9kdWxlIG5hbWUgYmFzZWQgb24gdGhlIHNjcmlwdFBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0UGF0aCAtIFRoZSBwYXRoIG9mIHRoZSBzY3JpcHQgZnJvbSB3aGljaCB0aGUgbW9kdWxlXHJcbiAqIG5hbWUgd2lsbCBiZSBleHRyYWN0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgbW9kdWxlIG5hbWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0UGF0aCkge1xyXG4gIHJldHVybiBzY3JpcHRQYXRoLnJlcGxhY2UoXHJcbiAgICAvKC4qKVxcL3woLiopbW9kdWxlc1xcL3xzdG9ja1xcLyguKilpbmRpY2F0b3JzXFwvfG1hcHNcXC8oLiopbW9kdWxlc1xcLy9naSxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBjYWNoZSBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY2FjaGUgb2JqZWN0IGNvbnRhaW5pbmcgdmFyaW91cyBjYWNoZWQgZGF0YS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZSgpIHtcclxuICByZXR1cm4gY2FjaGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBjYWNoZSBwYXRoIGZvciBIaWdoY2hhcnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBhYnNvbHV0ZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnkgZm9yIEhpZ2hjaGFydHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoKCkge1xyXG4gIHJldHVybiBnZXRBYnNvbHV0ZVBhdGgoZ2V0T3B0aW9ucygpLmhpZ2hjaGFydHMuY2FjaGVQYXRoLCAndXRmOCcpOyAvLyAjNTYyXHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGEgc2luZ2xlIHNjcmlwdCBhbmQgdXBkYXRlcyB0aGUgYGZldGNoZWRNb2R1bGVzYCBhY2NvcmRpbmdseS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHQgLSBBIHBhdGggdG8gc2NyaXB0IHRvIGdldC5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gQWRkaXRpb25hbCBvcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnRcclxuICogdG8gdXNlIGZvciBhIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtzaG91bGRUaHJvd0Vycm9yPWZhbHNlXSAtIEEgZmxhZyB0byBpbmRpY2F0ZSBpZiB0aGUgZXJyb3JcclxuICogc2hvdWxkIGJlIHRocm93bi4gVGhpcyBzaG91bGQgYmUgdXNlZCBvbmx5IGZvciB0aGUgY29yZSBzY3JpcHRzLiBUaGUgZGVmYXVsdFxyXG4gKiB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgdGV4dCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgZmV0Y2hlZCBzY3JpcHQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhIHByb2JsZW1cclxuICogd2l0aCBmZXRjaGluZyB0aGUgc2NyaXB0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChcclxuICBzY3JpcHQsXHJcbiAgcmVxdWVzdE9wdGlvbnMsXHJcbiAgZmV0Y2hlZE1vZHVsZXMsXHJcbiAgc2hvdWxkVGhyb3dFcnJvciA9IGZhbHNlXHJcbikge1xyXG4gIC8vIEdldCByaWQgb2YgdGhlIC5qcyBmcm9tIHRoZSBjdXN0b20gc3RyaW5nc1xyXG4gIGlmIChzY3JpcHQuZW5kc1dpdGgoJy5qcycpKSB7XHJcbiAgICBzY3JpcHQgPSBzY3JpcHQuc3Vic3RyaW5nKDAsIHNjcmlwdC5sZW5ndGggLSAzKTtcclxuICB9XHJcbiAgbG9nKDQsIGBbY2FjaGVdIEZldGNoaW5nIHNjcmlwdCAtICR7c2NyaXB0fS5qc2ApO1xyXG5cclxuICAvLyBGZXRjaCB0aGUgc2NyaXB0XHJcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHtzY3JpcHR9LmpzYCwgcmVxdWVzdE9wdGlvbnMpO1xyXG5cclxuICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAgaWYgKHJlc3BvbnNlLnN0YXR1c0NvZGUgPT09IDIwMCAmJiB0eXBlb2YgcmVzcG9uc2UudGV4dCA9PSAnc3RyaW5nJykge1xyXG4gICAgaWYgKGZldGNoZWRNb2R1bGVzKSB7XHJcbiAgICAgIGNvbnN0IG1vZHVsZU5hbWUgPSBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHQpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlc1ttb2R1bGVOYW1lXSA9IDE7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gcmVzcG9uc2UudGV4dDtcclxuICB9XHJcblxyXG4gIC8vIEJhc2VkIG9uIHRoZSBgc2hvdWxkVGhyb3dFcnJvcmAgZmxhZywgZGVjaWRlIGhvdyB0byBzZXJ2ZSBlcnJvciBtZXNzYWdlXHJcbiAgaWYgKHNob3VsZFRocm93RXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbiAoc3RhdHVzIGNvZGU6ICR7cmVzcG9uc2Uuc3RhdHVzQ29kZX0pLmAsXHJcbiAgICAgIDQwNFxyXG4gICAgKS5zZXRFcnJvcihyZXNwb25zZSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxvZyhcclxuICAgICAgMixcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNhdmVzIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCBmZXRjaGVkIG1vZHVsZXMgdG8gdGhlIGNhY2hlIG1hbmlmZXN0XHJcbiAqIGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NhdmVDb25maWdUb01hbmlmZXN0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2ZldGNoZWRNb2R1bGVzPXt9XSAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzID0ge30pIHtcclxuICBjb25zdCBuZXdNYW5pZmVzdCA9IHtcclxuICAgIHZlcnNpb246IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24sXHJcbiAgICBtb2R1bGVzOiBmZXRjaGVkTW9kdWxlc1xyXG4gIH07XHJcblxyXG4gIC8vIFVwZGF0ZSBjYWNoZSBvYmplY3Qgd2l0aCB0aGUgY3VycmVudCBtb2R1bGVzXHJcbiAgY2FjaGUuYWN0aXZlTWFuaWZlc3QgPSBuZXdNYW5pZmVzdDtcclxuXHJcbiAgbG9nKDMsICdbY2FjaGVdIFdyaXRpbmcgYSBuZXcgbWFuaWZlc3QuJyk7XHJcbiAgdHJ5IHtcclxuICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgIGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdtYW5pZmVzdC5qc29uJyksXHJcbiAgICAgIEpTT04uc3RyaW5naWZ5KG5ld01hbmlmZXN0KSxcclxuICAgICAgJ3V0ZjgnXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIEVycm9yIHdyaXRpbmcgdGhlIGNhY2hlIG1hbmlmZXN0LicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBIaWdoY2hhcnRzIGBzY3JpcHRzYCBhbmQgYGN1c3RvbVNjcmlwdHNgIGZyb20gdGhlIGdpdmVuIENETnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2ZldGNoU2NyaXB0c1xyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjb3JlU2NyaXB0cyAtIEhpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBtb2R1bGVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBtb2R1bGVzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjdXN0b21TY3JpcHRzIC0gQ3VzdG9tIHNjcmlwdCBwYXRocyB0byBmZXRjaCAoZnVsbFxyXG4gKiBVUkxzKS5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZmV0Y2hlZCBzY3JpcHRzXHJcbiAqIGNvbnRlbnQgam9pbmVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIHdoaWxlXHJcbiAqIHNldHRpbmcgYW4gSFRUUCBBZ2VudCBmb3IgcHJveHkuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzKFxyXG4gIGNvcmVTY3JpcHRzLFxyXG4gIG1vZHVsZVNjcmlwdHMsXHJcbiAgY3VzdG9tU2NyaXB0cyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgZmV0Y2hlZE1vZHVsZXNcclxuKSB7XHJcbiAgLy8gQ29uZmlndXJlIHByb3h5IGlmIGV4aXN0c1xyXG4gIGxldCBwcm94eUFnZW50O1xyXG4gIGNvbnN0IHByb3h5SG9zdCA9IHNlcnZlclByb3h5T3B0aW9ucy5ob3N0O1xyXG4gIGNvbnN0IHByb3h5UG9ydCA9IHNlcnZlclByb3h5T3B0aW9ucy5wb3J0O1xyXG5cclxuICAvLyBUcnkgdG8gY3JlYXRlIGEgUHJveHkgQWdlbnRcclxuICBpZiAocHJveHlIb3N0ICYmIHByb3h5UG9ydCkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgcHJveHlBZ2VudCA9IG5ldyBIdHRwc1Byb3h5QWdlbnQoe1xyXG4gICAgICAgIGhvc3Q6IHByb3h5SG9zdCxcclxuICAgICAgICBwb3J0OiBwcm94eVBvcnRcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tjYWNoZV0gQ291bGQgbm90IGNyZWF0ZSBhIFByb3h5IEFnZW50LicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gSWYgZXhpc3RzLCBhZGQgcHJveHkgYWdlbnQgdG8gcmVxdWVzdCBvcHRpb25zXHJcbiAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSBwcm94eUFnZW50XHJcbiAgICA/IHtcclxuICAgICAgICBhZ2VudDogcHJveHlBZ2VudCxcclxuICAgICAgICB0aW1lb3V0OiBzZXJ2ZXJQcm94eU9wdGlvbnMudGltZW91dFxyXG4gICAgICB9XHJcbiAgICA6IHt9O1xyXG5cclxuICBjb25zdCBhbGxGZXRjaFByb21pc2VzID0gW1xyXG4gICAgLi4uY29yZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcywgdHJ1ZSlcclxuICAgICksXHJcbiAgICAuLi5tb2R1bGVTY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMpXHJcbiAgICApLFxyXG4gICAgLi4uY3VzdG9tU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMpXHJcbiAgICApXHJcbiAgXTtcclxuXHJcbiAgY29uc3QgZmV0Y2hlZFNjcmlwdHMgPSBhd2FpdCBQcm9taXNlLmFsbChhbGxGZXRjaFByb21pc2VzKTtcclxuICByZXR1cm4gZmV0Y2hlZFNjcmlwdHMuam9pbignO1xcbicpO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgbG9jYWwgY2FjaGUgd2l0aCBIaWdoY2hhcnRzIHNjcmlwdHMgYW5kIHRoZWlyIHZlcnNpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF91cGRhdGVDYWNoZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaGlnaGNoYXJ0c09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgaGlnaGNoYXJ0c2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzb3VyY2VQYXRoIC0gVGhlIHBhdGggdG8gdGhlIHNvdXJjZSBmaWxlIGluIHRoZSBjYWNoZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0IHJlcHJlc2VudGluZ1xyXG4gKiB0aGUgZmV0Y2hlZCBtb2R1bGVzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gaXNzdWUgdXBkYXRpbmdcclxuICogdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfdXBkYXRlQ2FjaGUoaGlnaGNoYXJ0c09wdGlvbnMsIHNlcnZlclByb3h5T3B0aW9ucywgc291cmNlUGF0aCkge1xyXG4gIC8vIEdldCBIaWdoY2hhcnRzIHZlcnNpb24gZm9yIHNjcmlwdHNcclxuICBjb25zdCBoY1ZlcnNpb24gPVxyXG4gICAgaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbiA9PT0gJ2xhdGVzdCdcclxuICAgICAgPyBudWxsXHJcbiAgICAgIDogYCR7aGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbn1gO1xyXG5cclxuICAvLyBHZXQgdGhlIENETiB1cmwgZm9yIHNjcmlwdHNcclxuICBjb25zdCBjZG5VcmwgPSBoaWdoY2hhcnRzT3B0aW9ucy5jZG5VcmwgfHwgY2FjaGUuY2RuVXJsO1xyXG5cclxuICB0cnkge1xyXG4gICAgY29uc3QgZmV0Y2hlZE1vZHVsZXMgPSB7fTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbY2FjaGVdIFVwZGF0aW5nIGNhY2hlIHZlcnNpb24gdG8gSGlnaGNoYXJ0czogJHtoY1ZlcnNpb24gfHwgJ2xhdGVzdCd9LmBcclxuICAgICk7XHJcblxyXG4gICAgY2FjaGUuc291cmNlcyA9IGF3YWl0IF9mZXRjaFNjcmlwdHMoXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5jb3JlU2NyaXB0cy5tYXAoKGMpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb24gPyBgJHtjZG5Vcmx9LyR7aGNWZXJzaW9ufS8ke2N9YCA6IGAke2NkblVybH0vJHtjfWBcclxuICAgICAgICApXHJcbiAgICAgIF0sXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5tb2R1bGVTY3JpcHRzLm1hcCgobSkgPT5cclxuICAgICAgICAgIG0gPT09ICdtYXAnXHJcbiAgICAgICAgICAgID8gaGNWZXJzaW9uXHJcbiAgICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L21hcHMvJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbWFwcy9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgIDogaGNWZXJzaW9uXHJcbiAgICAgICAgICAgICAgPyBgJHtjZG5Vcmx9LyR7aGNWZXJzaW9ufS9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgICAgOiBgJHtjZG5Vcmx9L21vZHVsZXMvJHttfWBcclxuICAgICAgICApLFxyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmluZGljYXRvclNjcmlwdHMubWFwKChpKSA9PlxyXG4gICAgICAgICAgaGNWZXJzaW9uXHJcbiAgICAgICAgICAgID8gYCR7Y2RuVXJsfS9zdG9jay8ke2hjVmVyc2lvbn0vaW5kaWNhdG9ycy8ke2l9YFxyXG4gICAgICAgICAgICA6IGAke2NkblVybH0vc3RvY2svaW5kaWNhdG9ycy8ke2l9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgaGlnaGNoYXJ0c09wdGlvbnMuY3VzdG9tU2NyaXB0cyxcclxuICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICBmZXRjaGVkTW9kdWxlc1xyXG4gICAgKTtcclxuXHJcbiAgICAvLyBFeHRyYWN0IGFuZCBzYXZlIHZlcnNpb24gb2YgY3VycmVudGx5IHVzZWQgSGlnaGNoYXJ0c1xyXG4gICAgY2FjaGUuaGNWZXJzaW9uID0gZXh0cmFjdFZlcnNpb24oY2FjaGUuc291cmNlcyk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgZmV0Y2hlZCBtb2R1bGVzIGludG8gY2FjaGVzJyBzb3VyY2UgSlNPTlxyXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcclxuICAgIHJldHVybiBmZXRjaGVkTW9kdWxlcztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBVbmFibGUgdG8gdXBkYXRlIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgY2hlY2tBbmRVcGRhdGVDYWNoZSxcclxuICBnZXRIaWdoY2hhcnRzVmVyc2lvbixcclxuICB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbixcclxuICBleHRyYWN0VmVyc2lvbixcclxuICBleHRyYWN0TW9kdWxlTmFtZSxcclxuICBnZXRDYWNoZSxcclxuICBnZXRDYWNoZVBhdGhcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1ldGhvZHMgZm9yIGluaXRpYWxpemluZyBIaWdoY2hhcnRzIHdpdGggY3VzdG9taXplZFxyXG4gKiBhbmltYXRpb24gc2V0dGluZ3MgYW5kIHRyaWdnZXJpbmcgdGhlIGNyZWF0aW9uIG9mIEhpZ2hjaGFydHMgY2hhcnRzIHdpdGhcclxuICogZXhwb3J0LXNwZWNpZmljIGNvbmZpZ3VyYXRpb25zIGluIHRoZSBwYWdlIGNvbnRleHQuIFN1cHBvcnRzIGR5bmFtaWMgb3B0aW9uXHJcbiAqIG1lcmdpbmcsIGN1c3RvbSBsb2dpYyBpbmplY3Rpb24sIGFuZCBjb250cm9sIG92ZXIgcmVuZGVyaW5nIGJlaGF2aW9ycy4gVXNlZFxyXG4gKiBieSB0aGUgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqL1xyXG5cclxuLyogZXNsaW50LWRpc2FibGUgbm8tdW5kZWYgKi9cclxuXHJcbi8qKlxyXG4gKiBTZXR0aW5nIHRoZSBgSGlnaGNoYXJ0cy5hbmltT2JqZWN0YCBmdW5jdGlvbi4gQ2FsbGVkIHdoZW4gaW5pdGluZyB0aGUgcGFnZS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0c1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0cygpIHtcclxuICBIaWdoY2hhcnRzLmFuaW1PYmplY3QgPSBmdW5jdGlvbiAoKSB7XHJcbiAgICByZXR1cm4geyBkdXJhdGlvbjogMCB9O1xyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIHRoZSBhY3R1YWwgSGlnaGNoYXJ0cyBjaGFydCBvbiBhIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY3JlYXRlQ2hhcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUNoYXJ0KGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEdldCByZXF1aXJlZCBmdW5jdGlvbnNcclxuICBjb25zdCB7IGdldE9wdGlvbnMsIHNldE9wdGlvbnMsIG1lcmdlLCB3cmFwIH0gPSBIaWdoY2hhcnRzO1xyXG5cclxuICAvLyBDcmVhdGUgYSBzZXBhcmF0ZSBvYmplY3QgZm9yIGEgcG90ZW50aWFsIGBzZXRPcHRpb25zYCB1c2FnZXMgaW4gb3JkZXJcclxuICAvLyB0byBwcmV2ZW50IGZyb20gcG9sbHV0aW5nIG90aGVyIGV4cG9ydHMgdGhhdCBjYW4gaGFwcGVuIG9uIHRoZSBzYW1lIHBhZ2VcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSBtZXJnZShmYWxzZSwge30sIGdldE9wdGlvbnMoKSk7XHJcblxyXG4gIC8vIE5PVEU6IElzIHRoaXMgdXNlZCBmb3IgYW55dGhpbmcgdXNlZnVsP1xyXG4gIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gZmFsc2U7XHJcbiAgd3JhcChIaWdoY2hhcnRzLkNoYXJ0LnByb3RvdHlwZSwgJ2luaXQnLCBmdW5jdGlvbiAocHJvY2VlZCwgdXNlck9wdGlvbnMsIGNiKSB7XHJcbiAgICAvLyBPdmVycmlkZSB0aGUgYHVzZXJPcHRpb25zYCB3aXRoIGltYWdlIGZyaWVuZGx5IG9wdGlvbnNcclxuICAgIHVzZXJPcHRpb25zID0gbWVyZ2UodXNlck9wdGlvbnMsIHtcclxuICAgICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgfSxcclxuICAgICAgcGxvdE9wdGlvbnM6IHtcclxuICAgICAgICBzZXJpZXM6IHtcclxuICAgICAgICAgIGxhYmVsOiB7XHJcbiAgICAgICAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICAvKiBFeHBlY3RzIHRvb2x0aXAgaW4gdGhlIGB1c2VyT3B0aW9uc2Agd2hlbiBgZm9yRXhwb3J0YCBpcyB0cnVlLlxyXG4gICAgICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL2hpZ2hjaGFydHMvYmxvYi8zYWQ0MzBhMzUzYjgwNTZiOWU3NjRhYTRlNWNkNjgyOGFhNDc5ZGIyL2pzL3BhcnRzL0NoYXJ0LmpzI0wyNDFcclxuICAgICAgICAqL1xyXG4gICAgICB0b29sdGlwOiB7fVxyXG4gICAgfSk7XHJcblxyXG4gICAgKHVzZXJPcHRpb25zLnNlcmllcyB8fCBbXSkuZm9yRWFjaChmdW5jdGlvbiAoc2VyaWVzKSB7XHJcbiAgICAgIHNlcmllcy5hbmltYXRpb24gPSBmYWxzZTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEFkZCBmbGFnIHRvIGtub3cgaWYgY2hhcnQgcmVuZGVyIGhhcyBiZWVuIGNhbGxlZC5cclxuICAgIGlmICghd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlcikge1xyXG4gICAgICB3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyID0gSGlnaGNoYXJ0cy5hZGRFdmVudCh0aGlzLCAncmVuZGVyJywgKCkgPT4ge1xyXG4gICAgICAgIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gdHJ1ZTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbdXNlck9wdGlvbnMsIGNiXSk7XHJcbiAgfSk7XHJcblxyXG4gIHdyYXAoSGlnaGNoYXJ0cy5TZXJpZXMucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCBjaGFydCwgb3B0aW9ucykge1xyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbY2hhcnQsIG9wdGlvbnNdKTtcclxuICB9KTtcclxuXHJcbiAgLy8gU29tZSBtYW5kYXRvcnkgYWRkaXRpb25hbCBgY2hhcnRgIGFuZCBgZXhwb3J0aW5nYCBvcHRpb25zXHJcbiAgY29uc3QgYWRkaXRpb25hbE9wdGlvbnMgPSB7XHJcbiAgICBjaGFydDoge1xyXG4gICAgICAvLyBCeSBkZWZhdWx0IGFuaW1hdGlvbiBpcyBkaXNhYmxlZFxyXG4gICAgICBhbmltYXRpb246IGZhbHNlLFxyXG4gICAgICAvLyBHZXQgdGhlIHJpZ2h0IHNpemUgdmFsdWVzXHJcbiAgICAgIGhlaWdodDogZXhwb3J0T3B0aW9ucy5oZWlnaHQsXHJcbiAgICAgIHdpZHRoOiBleHBvcnRPcHRpb25zLndpZHRoXHJcbiAgICB9LFxyXG4gICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgIC8vIE5vIG5lZWQgZm9yIHRoZSBleHBvcnRpbmcgYnV0dG9uXHJcbiAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IHRoZSBpbnB1dCB0byBleHBvcnQgZnJvbSB0aGUgYGluc3RyYCBvcHRpb25cclxuICBjb25zdCB1c2VyT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy5pbnN0cn1gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvblxyXG4gIGNvbnN0IHRoZW1lT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy5nbG9iYWxPcHRpb25zfWApKCk7XHJcblxyXG4gIC8vIE1lcmdlIHRoZSBmb2xsb3dpbmcgb3B0aW9ucyBvYmplY3RzIHRvIGNyZWF0ZSBmaW5hbCBvcHRpb25zXHJcbiAgY29uc3QgZmluYWxPcHRpb25zID0gbWVyZ2UoXHJcbiAgICBmYWxzZSxcclxuICAgIHRoZW1lT3B0aW9ucyxcclxuICAgIHVzZXJPcHRpb25zLFxyXG4gICAgLy8gUGxhY2VkIGl0IGhlcmUgaW5zdGVhZCBpbiB0aGUgaW5pdCBiZWNhdXNlIG9mIHRoZSBzaXplIGlzc3Vlc1xyXG4gICAgYWRkaXRpb25hbE9wdGlvbnNcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gIGNvbnN0IGZpbmFsQ2FsbGJhY2sgPSBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2tcclxuICAgID8gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2t9YCkoKVxyXG4gICAgOiBudWxsO1xyXG5cclxuICAvLyBUcmlnZ2VyIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSB7XHJcbiAgICBuZXcgRnVuY3Rpb24oJ29wdGlvbnMnLCBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkodXNlck9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBnbG9iYWwgb3B0aW9ucyBpZiBleGlzdFxyXG4gIGlmIChnbG9iYWxPcHRpb25zKSB7XHJcbiAgICBzZXRPcHRpb25zKGdsb2JhbE9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsbCB0aGUgY2hhcnQgY3JlYXRpb25cclxuICBIaWdoY2hhcnRzW2V4cG9ydE9wdGlvbnMuY29uc3RyXSgnY29udGFpbmVyJywgZmluYWxPcHRpb25zLCBmaW5hbENhbGxiYWNrKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBjdXJyZW50IGdsb2JhbCBvcHRpb25zXHJcbiAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIENsZWFyIGl0IGp1c3QgaW4gY2FzZSAoZS5nLiB0aGUgYHNldE9wdGlvbnNgIHdhcyB1c2VkIGluIHRoZSBgY3VzdG9tQ29kZWApXHJcbiAgZm9yIChjb25zdCBwcm9wIGluIGRlZmF1bHRPcHRpb25zKSB7XHJcbiAgICBpZiAodHlwZW9mIGRlZmF1bHRPcHRpb25zW3Byb3BdICE9PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgIGRlbGV0ZSBkZWZhdWx0T3B0aW9uc1twcm9wXTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgZGVmYXVsdCBvcHRpb25zIGJhY2tcclxuICBzZXRPcHRpb25zKEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaik7XHJcblxyXG4gIC8vIEVtcHR5IHRoZSBjdXN0b20gZ2xvYmFsIG9wdGlvbnMgb2JqZWN0XHJcbiAgSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqID0ge307XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzZXR1cEhpZ2hjaGFydHMsXHJcbiAgY3JlYXRlQ2hhcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgbWFuYWdpbmcgUHVwcGV0ZWVyIGJyb3dzZXJcclxuICogaW5zdGFuY2UsIGNyZWF0aW5nIGFuZCBjbGVhcmluZyBwYWdlcywgaW5qZWN0aW5nIGN1c3RvbSByZXNvdXJjZXMsXHJcbiAqIGFuZCBzZXR0aW5nIHVwIEhpZ2hjaGFydHMgZm9yIHNlcnZlci1zaWRlIHJlbmRlcmluZy4gVGhlIG1vZHVsZSBlbnN1cmVzXHJcbiAqIHRoYXQgcmVzb3VyY2VzIGFyZSBjb3JyZWN0bHkgbWFuYWdlZCBhbmQgY2FuIGhhbmRsZSBmYWlsdXJlcyBkdXJpbmdcclxuICogb3BlcmF0aW9ucyBsaWtlIGxhdW5jaGluZyB0aGUgYnJvd3NlciBvciBjcmVhdGluZyBuZXcgcGFnZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgcHVwcGV0ZWVyIGZyb20gJ3B1cHBldGVlcic7XHJcblxyXG5pbXBvcnQgeyBnZXRDYWNoZVBhdGggfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgc2V0dXBIaWdoY2hhcnRzIH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgcGFnZXNcclxuY29uc3QgdGVtcGxhdGUgPSByZWFkRmlsZVN5bmMoXHJcbiAgam9pbihfX2Rpcm5hbWUsICd0ZW1wbGF0ZXMnLCAndGVtcGxhdGUuaHRtbCcpLFxyXG4gICd1dGY4J1xyXG4pO1xyXG5cclxuLy8gVG8gc2F2ZSB0aGUgYnJvd3NlclxyXG5sZXQgYnJvd3NlciA9IG51bGw7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBleGlzdGluZyBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJyb3dzZXJcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEJyb3dzZXIoKSB7XHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBObyB2YWxpZCBicm93c2VyIGhhcyBiZWVuIGNyZWF0ZWQuJywgNTAwKTtcclxuICB9XHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVCcm93c2VyXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyXHJcbiAqIGxhdW5jaC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyXHJcbiAqIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBtYXggcmV0cmllcyB0byBvcGVuXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhcmUgcmVhY2hlZCwgb3IgaWYgbm8gYnJvd3NlciBpbnN0YW5jZSBpcyBmb3VuZCBhZnRlclxyXG4gKiByZXRyaWVzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncykge1xyXG4gIC8vIEdldCBgZGVidWdgIGFuZCBgb3RoZXJgIG9wdGlvbnNcclxuICBjb25zdCB7IGRlYnVnLCBvdGhlciB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZW5hYmxlOiBlbmFibGVkRGVidWcsIC4uLmRlYnVnT3B0aW9ucyB9ID0gZGVidWc7XHJcblxyXG4gIC8vIExhdW5jaCBvcHRpb25zIGZvciB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGNvbnN0IGxhdW5jaE9wdGlvbnMgPSB7XHJcbiAgICBoZWFkbGVzczogb3RoZXIuYnJvd3NlclNoZWxsTW9kZSA/ICdzaGVsbCcgOiB0cnVlLFxyXG4gICAgdXNlckRhdGFEaXI6ICd0bXAnLFxyXG4gICAgYXJnczogcHVwcGV0ZWVyQXJncyB8fCBbXSxcclxuICAgIGhhbmRsZVNJR0lOVDogZmFsc2UsXHJcbiAgICBoYW5kbGVTSUdURVJNOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR0hVUDogZmFsc2UsXHJcbiAgICB3YWl0Rm9ySW5pdGlhbFBhZ2U6IGZhbHNlLFxyXG4gICAgZGVmYXVsdFZpZXdwb3J0OiBudWxsLFxyXG4gICAgLi4uKGVuYWJsZWREZWJ1ZyAmJiBkZWJ1Z09wdGlvbnMpXHJcbiAgfTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlclxyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgLy8gQSBjb3VudGVyIGZvciB0aGUgYnJvd3NlcidzIGxhdW5jaCByZXRyaWVzXHJcbiAgICBsZXQgdHJ5Q291bnQgPSAwO1xyXG5cclxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbYnJvd3Nlcl0gQXR0ZW1wdGluZyB0byBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIExhdW5jaCB0aGUgYnJvd3NlclxyXG4gICAgICAgIGJyb3dzZXIgPSBhd2FpdCBwdXBwZXRlZXIubGF1bmNoKGxhdW5jaE9wdGlvbnMpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuXHJcbiAgICAgICAgICAvLyBXYWl0IGZvciBhIDQgc2Vjb25kcyBiZWZvcmUgdHJ5aW5nIGFnYWluXHJcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIDQwMDApKTtcclxuICAgICAgICAgIGF3YWl0IG9wZW4oKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIGF3YWl0IG9wZW4oKTtcclxuXHJcbiAgICAgIC8vIFNoZWxsIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChsYXVuY2hPcHRpb25zLmhlYWRsZXNzID09PSAnc2hlbGwnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBzaGVsbCBtb2RlLmApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEZWJ1ZyBtb2RlIGluZm9ybVxyXG4gICAgICBpZiAoZW5hYmxlZERlYnVnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1ticm93c2VyXSBNYXhpbXVtIHJldHJpZXMgdG8gb3BlbiBhIGJyb3dzZXIgaW5zdGFuY2UgcmVhY2hlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJywgNTAwKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGJyb3dzZXIgaW5zdGFuY2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyB0aGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgaWYgaXQgaXMgY29ubmVjdGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsb3NlQnJvd3NlclxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsb3NlQnJvd3NlcigpIHtcclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciB3aGVuIGNvbm5lY3RlZFxyXG4gIGlmIChicm93c2VyICYmIGJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgfVxyXG4gIGJyb3dzZXIgPSBudWxsO1xyXG4gIGxvZyg0LCAnW2Jyb3dzZXJdIENsb3NlZCB0aGUgYnJvd3Nlci4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBuZXcgUHVwcGV0ZWVyIHBhZ2Ugd2l0aGluIGFuIGV4aXN0aW5nIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqIFRoZSBmdW5jdGlvbiBjcmVhdGVzIGEgbmV3IHBhZ2UsIGRpc2FibGVzIGNhY2hpbmcsIHNldHMgY29udGVudCB1c2luZ1xyXG4gKiB0aGUgYF9zZXRQYWdlQ29udGVudCgpYCwgYW5kIHJldHVybnMgdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gbmV3UGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBgaWRgLFxyXG4gKiBgd29ya0NvdW50YCwgYW5kIGBwYWdlYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG5vIHZhbGlkIGJyb3dzZXJcclxuICogaGFzIGJlZW4gY29ubmVjdGVkIG9yIGlmIGEgcGFnZSBpcyBpbnZhbGlkIG9yIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBuZXdQYWdlKHBvb2xSZXNvdXJjZSkge1xyXG4gIC8vIEVycm9yIGluIGNhc2Ugb2Ygbm8gY29ubmVjdGVkIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIgfHwgIWJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFticm93c2VyXSBCcm93c2VyIGlzIG5vdCB5ZXQgY29ubmVjdGVkLmAsIDUwMCk7XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBwYWdlXHJcbiAgcG9vbFJlc291cmNlLnBhZ2UgPSBhd2FpdCBicm93c2VyLm5ld1BhZ2UoKTtcclxuXHJcbiAgLy8gRGlzYWJsZSBjYWNoZVxyXG4gIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IF9zZXRQYWdlQ29udGVudChwb29sUmVzb3VyY2UucGFnZSk7XHJcblxyXG4gIC8vIFNldCBwYWdlIGV2ZW50c1xyXG4gIF9zZXRQYWdlRXZlbnRzKHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIHBhZ2UgaXMgY29ycmVjdGx5IGNyZWF0ZWRcclxuICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlIHx8IHBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIFRoZSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLicsIDQwMCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLiBMb2dzXHJcbiAqIHRocm93biBlcnJvciBpZiBjbGVhcmluZyBvZiBhIHBhZ2UncyBjb250ZW50IGZhaWxzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBwYWdlIGFuZCBpZC5cclxuICogQHBhcmFtIHtib29sZWFufSBbaGFyZFJlc2V0PWZhbHNlXSAtIEEgZmxhZyBpbmRpY2F0aW5nIHRoZSB0eXBlIG9mIGNsZWFyaW5nXHJcbiAqIHRvIGJlIHBlcmZvcm1lZC4gSWYgdHJ1ZSwgbmF2aWdhdGVzIHRvIGBhYm91dDpibGFua2AgYW5kIHJlc2V0cyBjb250ZW50XHJcbiAqIGFuZCBzY3JpcHRzLiBJZiBmYWxzZSwgY2xlYXJzIHRoZSBib2R5IGNvbnRlbnQgYnkgc2V0dGluZyBhIHByZWRlZmluZWQgSFRNTFxyXG4gKiBzdHJ1Y3R1cmUuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0cnVlIHdoZW4gcGFnZVxyXG4gKiBpcyBjb3JyZWN0bHkgY2xlYXJlZCBhbmQgZmFsc2Ugd2hlbiBpdCBpcyBub3QuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlKHBvb2xSZXNvdXJjZSwgaGFyZFJlc2V0ID0gZmFsc2UpIHtcclxuICB0cnkge1xyXG4gICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIGlmIChoYXJkUmVzZXQpIHtcclxuICAgICAgICAvLyBOYXZpZ2F0ZSB0byBgYWJvdXQ6YmxhbmtgXHJcbiAgICAgICAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2UuZ290bygnYWJvdXQ6YmxhbmsnLCB7XHJcbiAgICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBTZXQgdGhlIGNvbnRlbnQgYW5kIGFuZCBzY3JpcHRzIGFnYWluXHJcbiAgICAgICAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBDbGVhciBib2R5IGNvbnRlbnRcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LmlubmVySFRNTCA9XHJcbiAgICAgICAgICAgICc8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+PGRpdiBpZD1cImNvbnRhaW5lclwiPjwvZGl2PjwvZGl2Pic7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgMixcclxuICAgICAgZXJyb3IsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIENvbnRlbnQgb2YgdGhlIHBhZ2UgY291bGQgbm90IGJlIGNsZWFyZWQuYFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgcG9vbFJlc291cmNlLndvcmtDb3VudCA9IGdldE9wdGlvbnMoKS5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgfVxyXG4gIHJldHVybiBmYWxzZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgY3VzdG9tIEpTIGFuZCBDU1MgcmVzb3VyY2VzIHRvIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHJlc291cmNlcyB3aWxsXHJcbiAqIGJlIGFkZGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxBcnJheS48T2JqZWN0Pj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIGFycmF5XHJcbiAqIG9mIGluamVjdGVkIHJlc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheVxyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIC8vIFVzZSByZXNvdXJjZXNcclxuICBjb25zdCByZXNvdXJjZXMgPSBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzO1xyXG4gIGlmIChyZXNvdXJjZXMpIHtcclxuICAgIGNvbnN0IGluamVjdGVkSnMgPSBbXTtcclxuXHJcbiAgICAvLyBMb2FkIGN1c3RvbSBKUyBjb2RlXHJcbiAgICBpZiAocmVzb3VyY2VzLmpzKSB7XHJcbiAgICAgIGluamVjdGVkSnMucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmpzXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExvYWQgc2NyaXB0cyBmcm9tIGFsbCBjdXN0b20gZmlsZXNcclxuICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICAgIGNvbnN0IGlzTG9jYWwgPSAhZmlsZS5zdGFydHNXaXRoKCdodHRwJykgPyB0cnVlIDogZmFsc2U7XHJcblxyXG4gICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBzY3JpcHQgZnJvbSByZXNvdXJjZXMnIGZpbGVzXHJcbiAgICAgICAgaW5qZWN0ZWRKcy5wdXNoKFxyXG4gICAgICAgICAgaXNMb2NhbFxyXG4gICAgICAgICAgICA/IHtcclxuICAgICAgICAgICAgICAgIGNvbnRlbnQ6IHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoZmlsZSksICd1dGY4JylcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIDoge1xyXG4gICAgICAgICAgICAgICAgdXJsOiBmaWxlXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBmb3IgKGNvbnN0IGpzUmVzb3VyY2Ugb2YgaW5qZWN0ZWRKcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoanNSZXNvdXJjZSkpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFticm93c2VyXSBUaGUgSlMgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgaW5qZWN0ZWRKcy5sZW5ndGggPSAwO1xyXG5cclxuICAgIC8vIExvYWQgQ1NTXHJcbiAgICBjb25zdCBpbmplY3RlZENzcyA9IFtdO1xyXG4gICAgaWYgKHJlc291cmNlcy5jc3MpIHtcclxuICAgICAgbGV0IGNzc0ltcG9ydHMgPSByZXNvdXJjZXMuY3NzLm1hdGNoKC9AaW1wb3J0XFxzKihbXjtdKik7L2cpO1xyXG4gICAgICBpZiAoY3NzSW1wb3J0cykge1xyXG4gICAgICAgIC8vIEhhbmRsZSBjc3Mgc2VjdGlvblxyXG4gICAgICAgIGZvciAobGV0IGNzc0ltcG9ydFBhdGggb2YgY3NzSW1wb3J0cykge1xyXG4gICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGgpIHtcclxuICAgICAgICAgICAgY3NzSW1wb3J0UGF0aCA9IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICAucmVwbGFjZSgndXJsKCcsICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCdAaW1wb3J0JywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoL1wiL2csICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC8nL2csICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC87LywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoL1xcKS9nLCAnJylcclxuICAgICAgICAgICAgICAudHJpbSgpO1xyXG5cclxuICAgICAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIGNzcyBmcm9tIHJlc291cmNlc1xyXG4gICAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aC5zdGFydHNXaXRoKCdodHRwJykpIHtcclxuICAgICAgICAgICAgICBpbmplY3RlZENzcy5wdXNoKHtcclxuICAgICAgICAgICAgICAgIHVybDogY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9IGVsc2UgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgICAgICAgICBpbmplY3RlZENzcy5wdXNoKHtcclxuICAgICAgICAgICAgICAgIHBhdGg6IGpvaW4oX19kaXJuYW1lLCBjc3NJbXBvcnRQYXRoKVxyXG4gICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBUaGUgcmVzdCBvZiB0aGUgQ1NTIHNlY3Rpb24gd2lsbCBiZSBjb250ZW50IGJ5IG5vd1xyXG4gICAgICBpbmplY3RlZENzcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuY3NzLnJlcGxhY2UoL0BpbXBvcnRcXHMqKFteO10qKTsvZywgJycpIHx8ICcgJ1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIGZvciAoY29uc3QgY3NzUmVzb3VyY2Ugb2YgaW5qZWN0ZWRDc3MpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFN0eWxlVGFnKGNzc1Jlc291cmNlKSk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgICAgIGBbYnJvd3Nlcl0gVGhlIENTUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGluamVjdGVkQ3NzLmxlbmd0aCA9IDA7XHJcbiAgICB9XHJcbiAgfVxyXG4gIHJldHVybiBpbmplY3RlZFJlc291cmNlcztcclxufVxyXG5cclxuLyoqXHJcbiAqIENsZWFycyBvdXQgYWxsIHN0YXRlIHNldCBvbiB0aGUgcGFnZSB3aXRoIGBhZGRTY3JpcHRUYWdgIGFuZCBgYWRkU3R5bGVUYWdgLlxyXG4gKiBSZW1vdmVzIGluamVjdGVkIHJlc291cmNlcyBhbmQgcmVzZXRzIENTUyBhbmQgc2NyaXB0IHRhZ3Mgb24gdGhlIHBhZ2UuXHJcbiAqIEFkZGl0aW9uYWxseSwgaXQgZGVzdHJveXMgcHJldmlvdXNseSBleGlzdGluZyBjaGFydHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCBmcm9tIHdoaWNoIHJlc291cmNlcyB3aWxsXHJcbiAqIGJlIGNsZWFyZWQuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPE9iamVjdD59IGluamVjdGVkUmVzb3VyY2VzIC0gQXJyYXkgb2YgaW5qZWN0ZWQgcmVzb3VyY2VzXHJcbiAqIHRvIGJlIGNsZWFyZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgdHJ5IHtcclxuICAgIGZvciAoY29uc3QgcmVzb3VyY2Ugb2YgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICAgICAgYXdhaXQgcmVzb3VyY2UuZGlzcG9zZSgpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0cyBhZnRlciBleHBvcnQgaXMgZG9uZSBhbmQgcmVzZXQgYWxsIENTUyBhbmQgc2NyaXB0IHRhZ3NcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAvLyBXZSBhcmUgbm90IGd1YXJhbnRlZWQgdGhhdCBIaWdoY2hhcnRzIGlzIGxvYWRlZCwgd2hlbiBkb2luZyBTVkcgZXhwb3J0c1xyXG4gICAgICBpZiAodHlwZW9mIEhpZ2hjaGFydHMgIT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgY29uc3Qgb2xkQ2hhcnRzID0gSGlnaGNoYXJ0cy5jaGFydHM7XHJcblxyXG4gICAgICAgIC8vIENoZWNrIGluIGFueSBhbHJlYWR5IGV4aXN0aW5nIGNoYXJ0c1xyXG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KG9sZENoYXJ0cykgJiYgb2xkQ2hhcnRzLmxlbmd0aCkge1xyXG4gICAgICAgICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzXHJcbiAgICAgICAgICBmb3IgKGNvbnN0IG9sZENoYXJ0IG9mIG9sZENoYXJ0cykge1xyXG4gICAgICAgICAgICBvbGRDaGFydCAmJiBvbGRDaGFydC5kZXN0cm95KCk7XHJcbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgICBIaWdoY2hhcnRzLmNoYXJ0cy5zaGlmdCgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5zY3JpcHRzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3NjcmlwdCcpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWywgLi4uc3R5bGVzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3N0eWxlJyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4ubGlua3NUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnbGluaycpO1xyXG5cclxuICAgICAgLy8gUmVtb3ZlIHRhZ3NcclxuICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIFtcclxuICAgICAgICAuLi5zY3JpcHRzVG9SZW1vdmUsXHJcbiAgICAgICAgLi4uc3R5bGVzVG9SZW1vdmUsXHJcbiAgICAgICAgLi4ubGlua3NUb1JlbW92ZVxyXG4gICAgICBdKSB7XHJcbiAgICAgICAgZWxlbWVudC5yZW1vdmUoKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFticm93c2VyXSBDb3VsZCBub3QgY2xlYXIgcGFnZSdzIHJlc291cmNlcy5gKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb250ZW50IGZvciBhIFB1cHBldGVlciBQYWdlIHVzaW5nIGEgcHJlZGVmaW5lZCB0ZW1wbGF0ZVxyXG4gKiBhbmQgYWRkaXRpb25hbCBzY3JpcHRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9zZXRQYWdlQ29udGVudFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggdGhlIGNvbnRlbnRcclxuICogaXMgYmVpbmcgc2V0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3NldFBhZ2VDb250ZW50KHBhZ2UpIHtcclxuICAvLyBTZXQgdGhlIGluaXRpYWwgcGFnZSBjb250ZW50XHJcbiAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHRlbXBsYXRlLCB7IHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnIH0pO1xyXG5cclxuICAvLyBBZGQgYWxsIHJlZ2lzdGVyZWQgSGlnY2hhcnRzIHNjcmlwdHMsIHF1aXRlIGRlbWFuZGluZ1xyXG4gIGF3YWl0IHBhZ2UuYWRkU2NyaXB0VGFnKHsgcGF0aDogam9pbihnZXRDYWNoZVBhdGgoKSwgJ3NvdXJjZXMuanMnKSB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSBpbml0aWFsIGBhbmltT2JqZWN0YCBmb3IgSGlnaGNoYXJ0c1xyXG4gIGF3YWl0IHBhZ2UuZXZhbHVhdGUoc2V0dXBIaWdoY2hhcnRzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCBldmVudHMgKGxpa2UgYHBhZ2VlcnJvcmAgYW5kIGBjb25zb2xlYCkgZm9yIGEgUHVwcGV0ZWVyIFBhZ2UgaW4gb3JkZXJcclxuICogdG8gY2F0Y2ggYW5kIGRpc3BsYXkgZXJyb3JzIGFuZCBjb25zb2xlIGxvZ3MgZnJvbSB0aGUgd2luZG93IGNvbnRleHQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfc2V0UGFnZUV2ZW50c1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggdGhlIGxpc3RlbmVyc1xyXG4gKiBhcmUgYmVpbmcgc2V0LlxyXG4gKi9cclxuZnVuY3Rpb24gX3NldFBhZ2VFdmVudHMocGFnZSkge1xyXG4gIC8vIEdldCBgZGVidWdgIG9wdGlvbnNcclxuICBjb25zdCB7IGRlYnVnIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIFNldCB0aGUgYHBhZ2VlcnJvcmAgbGlzdGVuZXJcclxuICBwYWdlLm9uKCdwYWdlZXJyb3InLCBhc3luYyAoKSA9PiB7XHJcbiAgICAvLyBJdCB3b3VsZCBzZWVtIGxpa2UgdGhpcyBtYXkgZmlyZSBhdCB0aGUgc2FtZSB0aW1lIG9yIHNob3J0bHkgYmVmb3JlXHJcbiAgICAvLyBhIHBhZ2UgaXMgY2xvc2VkLlxyXG4gICAgaWYgKHBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGNvbnNvbGVgIGxpc3RlbmVyLCBpZiBuZWVkZWRcclxuICBpZiAoZGVidWcuZW5hYmxlICYmIGRlYnVnLmxpc3RlblRvQ29uc29sZSkge1xyXG4gICAgcGFnZS5vbignY29uc29sZScsIChtZXNzYWdlKSA9PiB7XHJcbiAgICAgIGNvbnNvbGUubG9nKGBbZGVidWddICR7bWVzc2FnZS50ZXh0KCl9YCk7XHJcbiAgICB9KTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRCcm93c2VyLFxyXG4gIGNyZWF0ZUJyb3dzZXIsXHJcbiAgY2xvc2VCcm93c2VyLFxyXG4gIG5ld1BhZ2UsXHJcbiAgY2xlYXJQYWdlLFxyXG4gIGFkZFBhZ2VSZXNvdXJjZXMsXHJcbiAgY2xlYXJQYWdlUmVzb3VyY2VzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIFRoZSBDU1MgdG8gYmUgdXNlZCBvbiB0aGUgZXhwb3J0ZWQgcGFnZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIENTUyBjb25maWd1cmF0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKCkgPT4gYFxyXG5cclxuaHRtbCwgYm9keSB7XHJcbiAgbWFyZ2luOiAwO1xyXG4gIHBhZGRpbmc6IDA7XHJcbiAgYm94LXNpemluZzogYm9yZGVyLWJveDtcclxufVxyXG5cclxuI3RhYmxlLWRpdiwgI3NsaWRlcnMsICNkYXRhdGFibGUsICNjb250cm9scywgLmxkLXJvdyB7XHJcbiAgZGlzcGxheTogbm9uZTtcclxuICBoZWlnaHQ6IDA7XHJcbn1cclxuXHJcbiNjaGFydC1jb250YWluZXIge1xyXG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XHJcbiAgbWFyZ2luOiAwO1xyXG4gIG92ZXJmbG93OiBhdXRvO1xyXG4gIGZvbnQtc2l6ZTogMDtcclxufVxyXG5cclxuI2NoYXJ0LWNvbnRhaW5lciA+IGZpZ3VyZSwgZGl2IHtcclxuICBtYXJnaW4tdG9wOiAwICFpbXBvcnRhbnQ7XHJcbiAgbWFyZ2luLWJvdHRvbTogMCAhaW1wb3J0YW50O1xyXG59XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCBjc3NUZW1wbGF0ZSBmcm9tICcuL2Nzcy5qcyc7XHJcblxyXG4vKipcclxuICogVGhlIFNWRyB0ZW1wbGF0ZSB0byB1c2Ugd2hlbiBsb2FkaW5nIFNWRyBjb250ZW50IHRvIGJlIGV4cG9ydGVkLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc3ZnIC0gVGhlIFNWRyBpbnB1dCBjb250ZW50IHRvIGJlIGV4cG9ydGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgU1ZHIHRlbXBsYXRlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKHN2ZykgPT4gYFxyXG48IURPQ1RZUEUgaHRtbD5cclxuPGh0bWwgbGFuZz0nZW4tVVMnPlxyXG4gIDxoZWFkPlxyXG4gICAgPG1ldGEgaHR0cC1lcXVpdj1cIkNvbnRlbnQtVHlwZVwiIGNvbnRlbnQ9XCJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLThcIj5cclxuICAgIDx0aXRsZT5IaWdoY2hhcnRzIEV4cG9ydDwvdGl0bGU+XHJcbiAgPC9oZWFkPlxyXG4gIDxzdHlsZT5cclxuICAgICR7Y3NzVGVtcGxhdGUoKX1cclxuICA8L3N0eWxlPlxyXG4gIDxib2R5PlxyXG4gICAgPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPlxyXG4gICAgICAke3N2Z31cclxuICAgIDwvZGl2PlxyXG4gIDwvYm9keT5cclxuPC9odG1sPlxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIGhhbmRsZXMgY2hhcnQgZXhwb3J0IGZ1bmN0aW9uYWxpdHkgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKiBJdCBzdXBwb3J0cyBleHBvcnRpbmcgY2hhcnRzIGFzIFNWRywgUE5HLCBKUEVHLCBhbmQgUERGIGZvcm1hdHMuIFRoZSBtb2R1bGVcclxuICogbWFuYWdlcyBwYWdlIHJlc291cmNlcywgc2V0cyB1cCB0aGUgZXhwb3J0IGVudmlyb25tZW50LCBhbmQgcHJvY2Vzc2VzIGNoYXJ0XHJcbiAqIGNvbmZpZ3VyYXRpb25zIG9yIFNWRyBpbnB1dHMgZm9yIHJlbmRlcmluZy4gRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlXHJcbiAqIHVzaW5nIFB1cHBldGVlci5cclxuICovXHJcblxyXG5pbXBvcnQgeyBhZGRQYWdlUmVzb3VyY2VzLCBjbGVhclBhZ2VSZXNvdXJjZXMgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBjcmVhdGVDaGFydCB9IGZyb20gJy4vaGlnaGNoYXJ0cy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBzdmdUZW1wbGF0ZSBmcm9tICcuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L3N2Z0V4cG9ydC5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZSB1c2luZyBQdXBwZXRlZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcHVwcGV0ZWVyRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPChzdHJpbmd8QnVmZmVyfEV4cG9ydEVycm9yKT59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzXHJcbiAqIHRvIHRoZSBleHBvcnRlZCBkYXRhIG9yIHJlamVjdGluZyB3aXRoIGFuIGBFeHBvcnRFcnJvcmAuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBleHBvcnQgdG8gYW4gdW5zdXBwb3J0ZWRcclxuICogb3V0cHV0IGZvcm1hdCBvY2N1cnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHVwcGV0ZWVyRXhwb3J0KHBhZ2UsIGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheSAoYWRkaXRpb25hbCBKUyBhbmQgQ1NTKVxyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsZXQgaXNTVkcgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBEZWNpZGUgb24gdGhlIGV4cG9ydCBtZXRob2RcclxuICAgIGlmIChleHBvcnRPcHRpb25zLnN2Zykge1xyXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBgdHlwZWAgaXMgYWxzbyBTVkcsIHJldHVybiB0aGUgaW5wdXRcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgICAgICByZXR1cm4gZXhwb3J0T3B0aW9ucy5zdmc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIE1hcmsgYXMgU1ZHIGV4cG9ydCBmb3IgdGhlIGxhdGVyIHNpemUgY29ycmVjdGlvbnNcclxuICAgICAgaXNTVkcgPSB0cnVlO1xyXG5cclxuICAgICAgLy8gU1ZHIGV4cG9ydFxyXG4gICAgICBhd2FpdCBwYWdlLnNldENvbnRlbnQoc3ZnVGVtcGxhdGUoZXhwb3J0T3B0aW9ucy5zdmcpLCB7XHJcbiAgICAgICAgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCdcclxuICAgICAgfSk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIEpTT04gY29uZmlnLicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRcclxuICAgICAgYXdhaXQgcGFnZS5ldmFsdWF0ZShjcmVhdGVDaGFydCwgZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBLZWVwcyB0cmFjayBvZiBhbGwgcmVzb3VyY2VzIGFkZGVkIG9uIHRoZSBwYWdlIHdpdGggYWRkWFhYVGFnLiBldGNcclxuICAgIC8vIEl0J3MgVklUQUwgdGhhdCBhbGwgYWRkZWQgcmVzb3VyY2VzIGVuZHMgdXAgaGVyZSBzbyB3ZSBjYW4gY2xlYXIgdGhpbmdzXHJcbiAgICAvLyBvdXQgd2hlbiBkb2luZyBhIG5ldyBleHBvcnQgaW4gdGhlIHNhbWUgcGFnZSFcclxuICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goXHJcbiAgICAgIC4uLihhd2FpdCBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIGN1c3RvbUxvZ2ljT3B0aW9ucykpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgcmVhbCBjaGFydCBzaXplIGFuZCBzZXQgdGhlIHpvb20gYWNjb3JkaW5nbHlcclxuICAgIGNvbnN0IHNpemUgPSBpc1NWR1xyXG4gICAgICA/IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKHNjYWxlKSA9PiB7XHJcbiAgICAgICAgICBjb25zdCBzdmdFbGVtZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcclxuICAgICAgICAgICAgJyNjaGFydC1jb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnXHJcbiAgICAgICAgICApO1xyXG5cclxuICAgICAgICAgIC8vIEdldCB0aGUgdmFsdWVzIGNvcnJlY3RseSBzY2FsZWRcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0SGVpZ2h0ID0gc3ZnRWxlbWVudC5oZWlnaHQuYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xyXG4gICAgICAgICAgY29uc3QgY2hhcnRXaWR0aCA9IHN2Z0VsZW1lbnQud2lkdGguYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xyXG5cclxuICAgICAgICAgIC8vIEluIGNhc2Ugb2YgU1ZHIHRoZSB6b29tIG11c3QgYmUgc2V0IGRpcmVjdGx5IGZvciBib2R5IGFzIHNjYWxlXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IHNjYWxlO1xyXG5cclxuICAgICAgICAgIC8vIFNldCB0aGUgbWFyZ2luIHRvIDBweFxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLm1hcmdpbiA9ICcwcHgnO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0sIHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSkpXHJcbiAgICAgIDogYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGNvbnN0IHsgY2hhcnRIZWlnaHQsIGNoYXJ0V2lkdGggfSA9IHdpbmRvdy5IaWdoY2hhcnRzLmNoYXJ0c1swXTtcclxuXHJcbiAgICAgICAgICAvLyBObyBuZWVkIGZvciBzdWNoIHNjYWxlIG1hbmlwdWxhdGlvbiBpbiBjYXNlIG9mIG90aGVyIHR5cGVzXHJcbiAgICAgICAgICAvLyBvZiBleHBvcnRzLiBSZXNldCB0aGUgem9vbSBmb3Igb3RoZXIgZXhwb3J0cyB0aGFuIHRvIFNWR3NcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gMTtcclxuXHJcbiAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICBjaGFydEhlaWdodCxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aFxyXG4gICAgICAgICAgfTtcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNsaXAgcmVnaW9uIGZvciB0aGUgcGFnZVxyXG4gICAgY29uc3QgeyB4LCB5IH0gPSBhd2FpdCBfZ2V0Q2xpcFJlZ2lvbihwYWdlKTtcclxuXHJcbiAgICAvLyBTZXQgZmluYWwgYGhlaWdodGAgZm9yIHZpZXdwb3J0XHJcbiAgICBjb25zdCB2aWV3cG9ydEhlaWdodCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydEhlaWdodCB8fCBleHBvcnRPcHRpb25zLmhlaWdodClcclxuICAgICk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGB3aWR0aGAgZm9yIHZpZXdwb3J0XHJcbiAgICBjb25zdCB2aWV3cG9ydFdpZHRoID0gTWF0aC5hYnMoXHJcbiAgICAgIE1hdGguY2VpbChzaXplLmNoYXJ0V2lkdGggfHwgZXhwb3J0T3B0aW9ucy53aWR0aClcclxuICAgICk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBmaW5hbCB2aWV3cG9ydCBub3cgdGhhdCB3ZSBoYXZlIHRoZSByZWFsIGhlaWdodFxyXG4gICAgYXdhaXQgcGFnZS5zZXRWaWV3cG9ydCh7XHJcbiAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICBkZXZpY2VTY2FsZUZhY3RvcjogaXNTVkcgPyAxIDogcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKVxyXG4gICAgfSk7XHJcblxyXG4gICAgbGV0IHJlc3VsdDtcclxuICAgIC8vIFJhc3Rlcml6YXRpb24gcHJvY2Vzc1xyXG4gICAgc3dpdGNoIChleHBvcnRPcHRpb25zLnR5cGUpIHtcclxuICAgICAgY2FzZSAnc3ZnJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlU1ZHKHBhZ2UpO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBjYXNlICdwbmcnOlxyXG4gICAgICBjYXNlICdqcGVnJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlSW1hZ2UoXHJcbiAgICAgICAgICBwYWdlLFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy50eXBlLFxyXG4gICAgICAgICAge1xyXG4gICAgICAgICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgICAgeCxcclxuICAgICAgICAgICAgeVxyXG4gICAgICAgICAgfSxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBjYXNlICdwZGYnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVQREYoXHJcbiAgICAgICAgICBwYWdlLFxyXG4gICAgICAgICAgdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtleHBvcnRdIFVuc3VwcG9ydGVkIG91dHB1dCBmb3JtYXQ6ICR7ZXhwb3J0T3B0aW9ucy50eXBlfS5gLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDbGVhciBwcmV2aW91c2x5IGluamVjdGVkIEpTIGFuZCBDU1MgcmVzb3VyY2VzXHJcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIHJlc3VsdDtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiBlcnJvcjtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGNsaXBwaW5nIHJlZ2lvbiBjb29yZGluYXRlcyBvZiB0aGUgc3BlY2lmaWVkIHBhZ2UgZWxlbWVudFxyXG4gKiB3aXRoIHRoZSAnY2hhcnQtY29udGFpbmVyJyBpZC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZ2V0Q2xpcFJlZ2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHhgLCBgeWAsIGB3aWR0aGAsIGFuZCBgaGVpZ2h0YCBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2dldENsaXBSZWdpb24ocGFnZSkge1xyXG4gIHJldHVybiBwYWdlLiRldmFsKCcjY2hhcnQtY29udGFpbmVyJywgKGVsZW1lbnQpID0+IHtcclxuICAgIGNvbnN0IHsgeCwgeSwgd2lkdGgsIGhlaWdodCB9ID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgIHJldHVybiB7XHJcbiAgICAgIHgsXHJcbiAgICAgIHksXHJcbiAgICAgIHdpZHRoLFxyXG4gICAgICBoZWlnaHQ6IE1hdGgudHJ1bmMoaGVpZ2h0ID4gMSA/IGhlaWdodCA6IDUwMClcclxuICAgIH07XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIFNWRyBieSBldmFsdWF0aW5nIHRoZSBgb3V0ZXJIVE1MYCBvZiB0aGUgZmlyc3QgJ3N2ZycgZWxlbWVudFxyXG4gKiBpbnNpZGUgYW4gZWxlbWVudCB3aXRoIHRoZSBpZCAnY29udGFpbmVyJy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlU1ZHXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgU1ZHIHN0cmluZy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9jcmVhdGVTVkcocGFnZSkge1xyXG4gIHJldHVybiBwYWdlLiRldmFsKFxyXG4gICAgJyNjb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnLFxyXG4gICAgKGVsZW1lbnQpID0+IGVsZW1lbnQub3V0ZXJIVE1MXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gaW1hZ2UgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBgc2NyZWVuc2hvdGAgZnVuY3Rpb25hbGl0eSB3aXRoXHJcbiAqIHNwZWNpZmllZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVJbWFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBJbWFnZSB0eXBlLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2xpcCAtIENsaXBwaW5nIHJlZ2lvbiBjb29yZGluYXRlcy5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBpbWFnZSBidWZmZXJcclxuICogb3IgcmVqZWN0aW5nIHdpdGggYW4gYEV4cG9ydEVycm9yYCBmb3IgdGltZW91dC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9jcmVhdGVJbWFnZShwYWdlLCB0eXBlLCBjbGlwLCByYXN0ZXJpemF0aW9uVGltZW91dCkge1xyXG4gIHJldHVybiBQcm9taXNlLnJhY2UoW1xyXG4gICAgcGFnZS5zY3JlZW5zaG90KHtcclxuICAgICAgdHlwZSxcclxuICAgICAgY2xpcCxcclxuICAgICAgZW5jb2Rpbmc6ICdiYXNlNjQnLFxyXG4gICAgICBmdWxsUGFnZTogZmFsc2UsXHJcbiAgICAgIG9wdGltaXplRm9yU3BlZWQ6IHRydWUsXHJcbiAgICAgIGNhcHR1cmVCZXlvbmRWaWV3cG9ydDogdHJ1ZSxcclxuICAgICAgLi4uKHR5cGUgIT09ICdwbmcnID8geyBxdWFsaXR5OiA4MCB9IDoge30pLFxyXG4gICAgICAvLyBBbHdheXMgcmVuZGVyIG9uIGEgdHJhbnNwYXJlbnQgcGFnZSBpZiB0aGUgZXhwZWN0ZWQgdHlwZSBmb3JtYXQgaXMgUE5HXHJcbiAgICAgIG9taXRCYWNrZ3JvdW5kOiB0eXBlID09ICdwbmcnIC8vICM0NDcsICM0NjNcclxuICAgIH0pLFxyXG4gICAgbmV3IFByb21pc2UoKF9yZXNvbHZlLCByZWplY3QpID0+XHJcbiAgICAgIHNldFRpbWVvdXQoXHJcbiAgICAgICAgKCkgPT4gcmVqZWN0KG5ldyBFeHBvcnRFcnJvcignUmFzdGVyaXphdGlvbiB0aW1lb3V0JywgNDA4KSksXHJcbiAgICAgICAgcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxyXG4gICAgICApXHJcbiAgICApXHJcbiAgXSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUERGIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgYHBkZmAgZnVuY3Rpb25hbGl0eSB3aXRoIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVQREZcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHQgLSBQREYgaGVpZ2h0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGggLSBQREYgd2lkdGguXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgUERGIGJ1ZmZlci5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9jcmVhdGVQREYocGFnZSwgaGVpZ2h0LCB3aWR0aCwgcmFzdGVyaXphdGlvblRpbWVvdXQpIHtcclxuICBhd2FpdCBwYWdlLmVtdWxhdGVNZWRpYVR5cGUoJ3NjcmVlbicpO1xyXG4gIHJldHVybiBwYWdlLnBkZih7XHJcbiAgICAvLyBUaGlzIHdpbGwgcmVtb3ZlIGFuIGV4dHJhIGVtcHR5IHBhZ2UgaW4gUERGIGV4cG9ydHNcclxuICAgIGhlaWdodDogaGVpZ2h0ICsgMSxcclxuICAgIHdpZHRoLFxyXG4gICAgZW5jb2Rpbmc6ICdiYXNlNjQnLFxyXG4gICAgdGltZW91dDogcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgcHVwcGV0ZWVyRXhwb3J0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBwcm92aWRlcyBhIHdvcmtlciBwb29sIGltcGxlbWVudGF0aW9uIGZvciBtYW5hZ2luZ1xyXG4gKiB0aGUgYnJvd3NlciBpbnN0YW5jZSBhbmQgcGFnZXMsIHNwZWNpZmljYWxseSBkZXNpZ25lZCBmb3IgdXNlIHdpdGhcclxuICogdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci4gSXQgb3B0aW1pemVzIHJlc291cmNlcyB1c2FnZSBhbmQgcGVyZm9ybWFuY2VcclxuICogYnkgbWFpbnRhaW5pbmcgYSBwb29sIG9mIHdvcmtlcnMgdGhhdCBjYW4gaGFuZGxlIGNvbmN1cnJlbnQgZXhwb3J0IHRhc2tzXHJcbiAqIHVzaW5nIFB1cHBldGVlci5cclxuICovXHJcblxyXG5pbXBvcnQgeyBQb29sIH0gZnJvbSAndGFybic7XHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7IGNyZWF0ZUJyb3dzZXIsIGNsb3NlQnJvd3NlciwgbmV3UGFnZSwgY2xlYXJQYWdlIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgcHVwcGV0ZWVyRXhwb3J0IH0gZnJvbSAnLi9leHBvcnQuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0TmV3RGF0ZVRpbWUsIG1lYXN1cmVUaW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIHBvb2wgaW5zdGFuY2VcclxubGV0IHBvb2wgPSBudWxsO1xyXG5cclxuLy8gUG9vbCBzdGF0aXN0aWNzXHJcbmNvbnN0IHBvb2xTdGF0cyA9IHtcclxuICBleHBvcnRzQXR0ZW1wdGVkOiAwLFxyXG4gIGV4cG9ydHNQZXJmb3JtZWQ6IDAsXHJcbiAgZXhwb3J0c0Ryb3BwZWQ6IDAsXHJcbiAgZXhwb3J0c0Zyb21Tdmc6IDAsXHJcbiAgZXhwb3J0c0Zyb21PcHRpb25zOiAwLFxyXG4gIGV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHM6IDAsXHJcbiAgZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHM6IDAsXHJcbiAgdGltZVNwZW50OiAwLFxyXG4gIHRpbWVTcGVudEF2ZXJhZ2U6IDBcclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyB0aGUgZXhwb3J0IHBvb2wgd2l0aCB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiwgY3JlYXRpbmdcclxuICogYSBicm93c2VyIGluc3RhbmNlIGFuZCBzZXR0aW5nIHVwIHdvcmtlciByZXNvdXJjZXMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdFBvb2xcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyXHJcbiAqIGxhdW5jaC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGVuZGluZyB0aGUgZnVuY3Rpb25cclxuICogZXhlY3V0aW9uIHdoZW4gYW4gYWxyZWFkeSBpbml0aWFsaXplZCBwb29sIG9mIHJlc291cmNlcyBpcyBmb3VuZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGNvdWxkIG5vdCBjcmVhdGUgdGhlIHBvb2xcclxuICogb2Ygd29ya2Vycy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0UG9vbChwb29sT3B0aW9ucywgcHVwcGV0ZWVyQXJncykge1xyXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgcHVwcGV0ZWVyIGFyZ3VtZW50c1xyXG4gIGF3YWl0IGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncyk7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gSW5pdGlhbGl6aW5nIHBvb2wgd2l0aCB3b3JrZXJzOiBtaW4gJHtwb29sT3B0aW9ucy5taW5Xb3JrZXJzfSwgbWF4ICR7cG9vbE9wdGlvbnMubWF4V29ya2Vyc30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBpZiAocG9vbCkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICAnW3Bvb2xdIEFscmVhZHkgaW5pdGlhbGl6ZWQsIHBsZWFzZSBraWxsIGl0IGJlZm9yZSBjcmVhdGluZyBhIG5ldyBvbmUuJ1xyXG4gICAgICApO1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcCBhbiBleWUgb24gYSBjb3JyZWN0IG1pbiBhbmQgbWF4IHdvcmtlcnMgbnVtYmVyXHJcbiAgICBpZiAocG9vbE9wdGlvbnMubWluV29ya2VycyA+IHBvb2xPcHRpb25zLm1heFdvcmtlcnMpIHtcclxuICAgICAgcG9vbE9wdGlvbnMubWluV29ya2VycyA9IHBvb2xPcHRpb25zLm1heFdvcmtlcnM7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgcG9vbCBhbG9uZyB3aXRoIGEgbWluaW1hbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBwb29sID0gbmV3IFBvb2woe1xyXG4gICAgICAvLyBHZXQgdGhlIGBjcmVhdGVgLCBgdmFsaWRhdGVgLCBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9uc1xyXG4gICAgICAuLi5fZmFjdG9yeShwb29sT3B0aW9ucyksXHJcbiAgICAgIG1pbjogcG9vbE9wdGlvbnMubWluV29ya2VycyxcclxuICAgICAgbWF4OiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzLFxyXG4gICAgICBhY3F1aXJlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuYWNxdWlyZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmNyZWF0ZVRpbWVvdXQsXHJcbiAgICAgIGRlc3Ryb3lUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5kZXN0cm95VGltZW91dCxcclxuICAgICAgaWRsZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmlkbGVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVSZXRyeUludGVydmFsLFxyXG4gICAgICByZWFwSW50ZXJ2YWxNaWxsaXM6IHBvb2xPcHRpb25zLnJlYXBlckludGVydmFsLFxyXG4gICAgICBwcm9wYWdhdGVDcmVhdGVFcnJvcjogZmFsc2VcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFNldCBldmVudHNcclxuICAgIHBvb2wub24oJ3JlbGVhc2UnLCBhc3luYyAocmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gQ2xlYXIgcGFnZVxyXG4gICAgICBjb25zdCBjbGVhclN0YXR1cyA9IGF3YWl0IGNsZWFyUGFnZShyZXNvdXJjZSwgZmFsc2UpO1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cmVzb3VyY2UuaWR9XSAtIFJlbGVhc2luZyBhIHdvcmtlci4gQ2xlYXIgcGFnZSBzdGF0dXM6ICR7Y2xlYXJTdGF0dXN9LmBcclxuICAgICAgKTtcclxuICAgIH0pO1xyXG5cclxuICAgIHBvb2wub24oJ2Rlc3Ryb3lTdWNjZXNzJywgKF9ldmVudElkLCByZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cmVzb3VyY2UuaWR9XSAtIERlc3Ryb3llZCBhIHdvcmtlciBzdWNjZXNzZnVsbHkuYFxyXG4gICAgICApO1xyXG4gICAgICByZXNvdXJjZS5wYWdlID0gbnVsbDtcclxuICAgIH0pO1xyXG5cclxuICAgIGNvbnN0IGluaXRpYWxSZXNvdXJjZXMgPSBbXTtcclxuICAgIC8vIENyZWF0ZSBhbiBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcG9vbE9wdGlvbnMubWluV29ya2VyczsgaSsrKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG4gICAgICAgIGluaXRpYWxSZXNvdXJjZXMucHVzaChyZXNvdXJjZSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW3Bvb2xdIENvdWxkIG5vdCBjcmVhdGUgYW4gaW5pdGlhbCByZXNvdXJjZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlcyBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBpbml0aWFsUmVzb3VyY2VzLmZvckVhY2goKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIHBvb2wucmVsZWFzZShyZXNvdXJjZSk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gVGhlIHBvb2wgaXMgcmVhZHkke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RoID8gYCB3aXRoICR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGh9IGluaXRpYWwgcmVzb3VyY2VzIHdhaXRpbmcuYCA6ICcuJ31gXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcG9vbF0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgY3JlYXRlIHRoZSBwb29sIG9mIHdvcmtlcnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBUZXJtaW5hdGVzIGFsbCB3b3JrZXJzIGluIHRoZSBwb29sLCBkZXN0cm95cyB0aGUgcG9vbCwgYW5kIGNsb3NlcyB0aGUgYnJvd3NlclxyXG4gKiBpbnN0YW5jZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBraWxsUG9vbFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSBhbGwgd29ya2VycyBhcmVcclxuICogdGVybWluYXRlZCwgdGhlIHBvb2wgaXMgZGVzdHJveWVkLCBhbmQgdGhlIGJyb3dzZXIgaXMgc3VjY2Vzc2Z1bGx5IGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBraWxsUG9vbCgpIHtcclxuICBsb2coMywgJ1twb29sXSBLaWxsaW5nIHBvb2wgd2l0aCBhbGwgd29ya2VycyBhbmQgY2xvc2luZyBicm93c2VyLicpO1xyXG5cclxuICAvLyBJZiBzdGlsbCBhbGl2ZSwgZGVzdHJveSB0aGUgcG9vbCBvZiBwYWdlcyBiZWZvcmUgY2xvc2luZyBhIGJyb3dzZXJcclxuICBpZiAocG9vbCkge1xyXG4gICAgLy8gRnJlZSB1cCBub3QgcmVsZWFzZWQgd29ya2Vyc1xyXG4gICAgZm9yIChjb25zdCB3b3JrZXIgb2YgcG9vbC51c2VkKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXIucmVzb3VyY2UpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgdGhlIHBvb2wgaWYgaXQgaXMgc3RpbGwgYXZhaWxhYmxlXHJcbiAgICBpZiAoIXBvb2wuZGVzdHJveWVkKSB7XHJcbiAgICAgIGF3YWl0IHBvb2wuZGVzdHJveSgpO1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBEZXN0cm95ZWQgdGhlIHBvb2wgb2YgcmVzb3VyY2VzLicpO1xyXG4gICAgfVxyXG4gICAgcG9vbCA9IG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGF3YWl0IGNsb3NlQnJvd3NlcigpO1xyXG59XHJcblxyXG4vKipcclxuICogUHJvY2Vzc2VzIHRoZSBleHBvcnQgd29yayB1c2luZyBhIHdvcmtlciBmcm9tIHRoZSBwb29sLiBBY3F1aXJlcyBhIHdvcmtlclxyXG4gKiBoYW5kbGUgZnJvbSB0aGUgcG9vbCwgcGVyZm9ybXMgdGhlIGV4cG9ydCB1c2luZyBwdXBwZXRlZXIsIGFuZCByZWxlYXNlc1xyXG4gKiB0aGUgd29ya2VyIGhhbmRsZSBiYWNrIHRvIHRoZSBwb29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHBvc3RXb3JrXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgY29tcGxldGUgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBleHBvcnQgcmVzdWx0XHJcbiAqIGFuZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcG9zdFdvcmsob3B0aW9ucykge1xyXG4gIGxldCB3b3JrZXJIYW5kbGU7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsb2coNCwgJ1twb29sXSBXb3JrIHJlY2VpdmVkLCBzdGFydGluZyB0byBwcm9jZXNzLicpO1xyXG5cclxuICAgIC8vIEFuIGV4cG9ydCBhdHRlbXB0IGNvdW50ZWRcclxuICAgICsrcG9vbFN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQ7XHJcblxyXG4gICAgLy8gRGlzcGxheSB0aGUgcG9vbCBpbmZvcm1hdGlvbiBpZiBuZWVkZWRcclxuICAgIGlmIChvcHRpb25zLnBvb2wuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGdldFBvb2xJbmZvKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaW4gY2FzZSBvZiBsYWNraW5nIHRoZSBwb29sIGluc3RhbmNlXHJcbiAgICBpZiAoIXBvb2wpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbcG9vbF0gV29yayByZWNlaXZlZCwgYnV0IHBvb2wgaGFzIG5vdCBiZWVuIHN0YXJ0ZWQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaGUgYWNxdWlyZSBjb3VudGVyXHJcbiAgICBjb25zdCBhY3F1aXJlQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gVHJ5IHRvIGFjcXVpcmUgdGhlIHdvcmtlciBhbG9uZyB3aXRoIHRoZSBpZCwgd29ya3MgY291bnQgYW5kIHBhZ2VcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgICAvLyBBY3F1aXJlIGEgcG9vbCByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIHBhZ2UgYWNxdWlyZSB0aW1lXHJcbiAgICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICA1LFxyXG4gICAgICAgICAgYFtiZW5jaG1hcmtdICR7b3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnfWAsXHJcbiAgICAgICAgICBgQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZSB0b29rICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgIH1FcnJvciBlbmNvdW50ZXJlZCB3aGVuIGFjcXVpcmluZyBhbiBhdmFpbGFibGUgZW50cnk6ICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuICAgIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmVkIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuXHJcbiAgICBpZiAoIXdvcmtlckhhbmRsZS5wYWdlKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICAgIHdvcmtlckhhbmRsZS53b3JrQ291bnQgPSBvcHRpb25zLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbcG9vbF0gUmVzb2x2ZWQgd29ya2VyIHBhZ2UgaXMgaW52YWxpZDogdGhlIHBvb2wgc2V0dXAgaXMgd29ua3kuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBTYXZlIHRoZSBzdGFydCB0aW1lXHJcbiAgICBjb25zdCB3b3JrU3RhcnQgPSBnZXROZXdEYXRlVGltZSgpO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgNCxcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3dvcmtlckhhbmRsZS5pZH1dIC0gU3RhcnRpbmcgd29yayBvbiB0aGlzIHBvb2wgZW50cnkuYFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTdGFydCBtZWFzdXJpbmcgZXhwb3J0IHRpbWVcclxuICAgIGNvbnN0IGV4cG9ydENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFBlcmZvcm0gYW4gZXhwb3J0IG9uIGEgcHVwcGV0ZWVyIGxldmVsXHJcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwdXBwZXRlZXJFeHBvcnQoXHJcbiAgICAgIHdvcmtlckhhbmRsZS5wYWdlLFxyXG4gICAgICBvcHRpb25zLmV4cG9ydCxcclxuICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiBpdCdzIGFuIGVycm9yXHJcbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgRXJyb3IpIHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSWYgdGhlcmUncyBhIHJhc3Rlcml6YXRpb24gdGltZW91dCwgd2Ugd2FudCBuZWVkIHRvIGZsdXNoIHRoZSBwYWdlLlxyXG4gICAgICAvLyBUaGlzIGlzIGJlY2F1c2UgdGhlIHBhZ2UgbWF5IGJlIGluIGEgc3RhdGUgd2hlcmUgaXQncyB3YWl0aW5nIGZvclxyXG4gICAgICAvLyB0aGUgc2NyZWVuc2hvdCB0byBmaW5pc2ggZXZlbiB0aG91Z2ggdGhlIHRpbWVvdXQgaGFzIG9jY3VyZWQuXHJcbiAgICAgIC8vIFdoaWNoIG9mIGNvdXJzZSBjYXVzZXMgYSBsb3Qgb2YgaXNzdWVzIHdpdGggdGhlIGV2ZW50IHN5c3RlbSxcclxuICAgICAgLy8gYW5kIHBhZ2UgY29uc2lzdGVuY3kuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIE9ubHkgcGFnZS5zY3JlZW5zaG90IHdpbGwgdGhyb3cgdGhpcywgdGltZW91dHMgZm9yIFBERidzIGFyZVxyXG4gICAgICAvLyBoYW5kbGVkIGJ5IHRoZSBwYWdlLnBkZiBmdW5jdGlvbiBpdHNlbGYuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIC4uLnllcywgdGhpcyBpcyB1Z2x5LlxyXG4gICAgICBpZiAocmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnKSB7XHJcbiAgICAgICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UgPSBudWxsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoXHJcbiAgICAgICAgcmVzdWx0Lm5hbWUgPT09ICdUaW1lb3V0RXJyb3InIHx8XHJcbiAgICAgICAgcmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnXHJcbiAgICAgICkge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgICB9UmFzdGVyaXphdGlvbiB0aW1lb3V0OiB5b3VyIGNoYXJ0IG1heSBiZSB0b28gY29tcGxleCBvciBsYXJnZSwgYW5kIGZhaWxlZCB0byByZW5kZXIgd2l0aGluIHRoZSBhbGxvdHRlZCB0aW1lLmBcclxuICAgICAgICApLnNldEVycm9yKHJlc3VsdCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICAgIH1FcnJvciBlbmNvdW50ZXJlZCBkdXJpbmcgZXhwb3J0OiAke2V4cG9ydENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgdGhlIFB1cHBldGVlciBleHBvcnQgdGltZVxyXG4gICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNSxcclxuICAgICAgICBgW2JlbmNobWFya10gJHtvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJyd9YCxcclxuICAgICAgICBgRXhwb3J0aW5nIGEgY2hhcnQgc3VjZXNzZnVsbHkgdG9vayAke2V4cG9ydENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgcmVzb3VyY2UgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcblxyXG4gICAgLy8gVXNlZCBmb3Igc3RhdGlzdGljcyBpbiBhdmVyYWdlVGltZSBhbmQgcHJvY2Vzc2VkV29ya0NvdW50LCB3aGljaFxyXG4gICAgLy8gaW4gdHVybiBpcyB1c2VkIGJ5IHRoZSAvaGVhbHRoIHJvdXRlLlxyXG4gICAgY29uc3Qgd29ya0VuZCA9IGdldE5ld0RhdGVUaW1lKCk7XHJcbiAgICBjb25zdCBleHBvcnRUaW1lID0gd29ya0VuZCAtIHdvcmtTdGFydDtcclxuXHJcbiAgICBwb29sU3RhdHMudGltZVNwZW50ICs9IGV4cG9ydFRpbWU7XHJcbiAgICBwb29sU3RhdHMudGltZVNwZW50QXZlcmFnZSA9XHJcbiAgICAgIHBvb2xTdGF0cy50aW1lU3BlbnQgLyArK3Bvb2xTdGF0cy5leHBvcnRzUGVyZm9ybWVkO1xyXG5cclxuICAgIGxvZyg0LCBgW3Bvb2xdIFdvcmsgY29tcGxldGVkIGluICR7ZXhwb3J0VGltZX1tcy5gKTtcclxuXHJcbiAgICAvLyBPdGhlcndpc2UgcmV0dXJuIHRoZSByZXN1bHRcclxuICAgIHJldHVybiB7XHJcbiAgICAgIHJlc3VsdCxcclxuICAgICAgb3B0aW9uc1xyXG4gICAgfTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgKytwb29sU3RhdHMuZXhwb3J0c0Ryb3BwZWQ7XHJcblxyXG4gICAgaWYgKHdvcmtlckhhbmRsZSkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuICAgIH1cclxuXHJcbiAgICB0aHJvdyBlcnJvcjtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xcclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8bnVsbCl9IFRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UgaWYgaW5pdGlhbGl6ZWQsIG9yIG51bGxcclxuICogaWYgdGhlIHBvb2wgaGFzIG5vdCBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbCgpIHtcclxuICByZXR1cm4gcG9vbDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIHN0YXRpc3RpYyBvZiBhIHBvb2wgaW5zdGFjZSBhYm91dCBleHBvcnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbFN0YXRzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjdXJyZW50IHBvb2wgc3RhdGlzdGljcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sU3RhdHMoKSB7XHJcbiAgcmV0dXJuIHBvb2xTdGF0cztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBwb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LCBpbmNsdWRpbmcgbWluaW11bSBhbmQgbWF4aW11bVxyXG4gKiB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmUgcmVxdWVzdHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sSW5mb0pTT05cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gUG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mb0pTT04oKSB7XHJcbiAgcmV0dXJuIHtcclxuICAgIG1pbjogcG9vbC5taW4sXHJcbiAgICBtYXg6IHBvb2wubWF4LFxyXG4gICAgdXNlZDogcG9vbC5udW1Vc2VkKCksXHJcbiAgICBhdmFpbGFibGU6IHBvb2wubnVtRnJlZSgpLFxyXG4gICAgYWxsQ3JlYXRlZDogcG9vbC5udW1Vc2VkKCkgKyBwb29sLm51bUZyZWUoKSxcclxuICAgIHBlbmRpbmdBY3F1aXJlczogcG9vbC5udW1QZW5kaW5nQWNxdWlyZXMoKSxcclxuICAgIHBlbmRpbmdDcmVhdGVzOiBwb29sLm51bVBlbmRpbmdDcmVhdGVzKCksXHJcbiAgICBwZW5kaW5nVmFsaWRhdGlvbnM6IHBvb2wubnVtUGVuZGluZ1ZhbGlkYXRpb25zKCksXHJcbiAgICBwZW5kaW5nRGVzdHJveXM6IHBvb2wucGVuZGluZ0Rlc3Ryb3lzLmxlbmd0aCxcclxuICAgIGFic29sdXRlQWxsOlxyXG4gICAgICBwb29sLm51bVVzZWQoKSArXHJcbiAgICAgIHBvb2wubnVtRnJlZSgpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nQWNxdWlyZXMoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ0NyZWF0ZXMoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ1ZhbGlkYXRpb25zKCkgK1xyXG4gICAgICBwb29sLnBlbmRpbmdEZXN0cm95cy5sZW5ndGhcclxuICB9O1xyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgcG9vbCwgaW5jbHVkaW5nIHRoZSBtaW5pbXVtXHJcbiAqIGFuZCBtYXhpbXVtIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZVxyXG4gKiByZXF1ZXN0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xJbmZvXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm8oKSB7XHJcbiAgY29uc3Qge1xyXG4gICAgbWluLFxyXG4gICAgbWF4LFxyXG4gICAgdXNlZCxcclxuICAgIGF2YWlsYWJsZSxcclxuICAgIGFsbENyZWF0ZWQsXHJcbiAgICBwZW5kaW5nQWNxdWlyZXMsXHJcbiAgICBwZW5kaW5nQ3JlYXRlcyxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9ucyxcclxuICAgIHBlbmRpbmdEZXN0cm95cyxcclxuICAgIGFic29sdXRlQWxsXHJcbiAgfSA9IGdldFBvb2xJbmZvSlNPTigpO1xyXG5cclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWluaW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttaW59LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtYXhpbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21heH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiB1c2VkIHJlc291cmNlczogJHt1c2VkfS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGZyZWUgcmVzb3VyY2VzOiAke2F2YWlsYWJsZX0uYCk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgY3JlYXRlZCAodXNlZCBhbmQgZnJlZSkgcmVzb3VyY2VzOiAke2FsbENyZWF0ZWR9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgYWNxdWlyZWQ6ICR7cGVuZGluZ0FjcXVpcmVzfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGNyZWF0ZWQ6ICR7cGVuZGluZ0NyZWF0ZXN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgdmFsaWRhdGVkOiAke3BlbmRpbmdWYWxpZGF0aW9uc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBkZXN0cm95ZWQ6ICR7cGVuZGluZ0Rlc3Ryb3lzfS5gXHJcbiAgKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCByZXNvdXJjZXM6ICR7YWJzb2x1dGVBbGx9LmApO1xyXG59XHJcblxyXG4vKipcclxuICogRmFjdG9yeSBmdW5jdGlvbiB0aGF0IHJldHVybnMgYW4gb2JqZWN0IHdpdGggYGNyZWF0ZWAsIGB2YWxpZGF0ZWAsXHJcbiAqIGFuZCBgZGVzdHJveWAgZnVuY3Rpb25zIGZvciB0aGUgcG9vbCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9mYWN0b3J5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2ZhY3RvcnkocG9vbE9wdGlvbnMpIHtcclxuICByZXR1cm4ge1xyXG4gICAgLyoqXHJcbiAgICAgKiBDcmVhdGVzIGEgbmV3IHdvcmtlciBwYWdlIGZvciB0aGUgZXhwb3J0IHBvb2wuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gY3JlYXRlXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0XHJcbiAgICAgKiBjb250YWluaW5nIHRoZSB3b3JrZXIgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCBpbml0aWFsXHJcbiAgICAgKiB3b3JrIGNvdW50LlxyXG4gICAgICpcclxuICAgICAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBlcnJvciBkdXJpbmdcclxuICAgICAqIHRoZSBjcmVhdGlvbiBvZiB0aGUgbmV3IHBhZ2UuXHJcbiAgICAgKi9cclxuICAgIGNyZWF0ZTogYXN5bmMgKCkgPT4ge1xyXG4gICAgICAvLyBJbml0IHRoZSByZXNvdXJjZSB3aXRoIHVuaXF1ZSBpZCBhbmQgd29yayBjb3VudFxyXG4gICAgICBjb25zdCBwb29sUmVzb3VyY2UgPSB7XHJcbiAgICAgICAgaWQ6IHV1aWQoKSxcclxuICAgICAgICAvLyBUcnkgdG8gZGlzdHJpYnV0ZSB0aGUgaW5pdGlhbCB3b3JrIGNvdW50XHJcbiAgICAgICAgd29ya0NvdW50OiBNYXRoLnJvdW5kKE1hdGgucmFuZG9tKCkgKiAocG9vbE9wdGlvbnMud29ya0xpbWl0IC8gMikpXHJcbiAgICAgIH07XHJcblxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIFN0YXJ0IG1lYXN1cmluZyBhIHBhZ2UgY3JlYXRpb24gdGltZVxyXG4gICAgICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IGdldE5ld0RhdGVUaW1lKCk7XHJcblxyXG4gICAgICAgIC8vIENyZWF0ZSBhIG5ldyBwYWdlXHJcbiAgICAgICAgYXdhaXQgbmV3UGFnZShwb29sUmVzb3VyY2UpO1xyXG5cclxuICAgICAgICAvLyBNZWFzdXJlIHRoZSB0aW1lIG9mIGZ1bGwgY3JlYXRpb24gYW5kIGNvbmZpZ3VyYXRpb24gb2YgYSBwYWdlXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFN1Y2Nlc3NmdWxseSBjcmVhdGVkIGEgd29ya2VyLCB0b29rICR7XHJcbiAgICAgICAgICAgIGdldE5ld0RhdGVUaW1lKCkgLSBzdGFydERhdGVcclxuICAgICAgICAgIH1tcy5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0dXJuIHJlYWR5IHBvb2wgcmVzb3VyY2VcclxuICAgICAgICByZXR1cm4gcG9vbFJlc291cmNlO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBFcnJvciBlbmNvdW50ZXJlZCB3aGVuIGNyZWF0aW5nIGEgbmV3IHBhZ2UuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBWYWxpZGF0ZXMgYSB3b3JrZXIgcGFnZSBpbiB0aGUgZXhwb3J0IHBvb2wsIGNoZWNraW5nIGlmIGl0IGhhcyBleGNlZWRlZFxyXG4gICAgICogdGhlIHdvcmsgbGltaXQuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gdmFsaWRhdGVcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICAgKiB0aGUgd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0cnVlIGlmIHRoZSB3b3JrZXJcclxuICAgICAqIGlzIHZhbGlkIGFuZCB3aXRoaW4gdGhlIHdvcmsgbGltaXQ7IG90aGVyd2lzZSwgdG8gZmFsc2UuXHJcbiAgICAgKi9cclxuICAgIHZhbGlkYXRlOiBhc3luYyAocG9vbFJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIEluIGNlcnRhaW4gY2FzZXMgYWNxdWlyaW5nIHRocm93cyBhIFRhcmdldENsb3NlRXJyb3IsIHdoaWNoIG1heVxyXG4gICAgICAvLyBiZSBjYXVzZWQgYnkgdHdvIHRoaW5nczpcclxuICAgICAgLy8gLSBUaGUgcGFnZSBpcyBjbG9zZWQgYW5kIGF0dGVtcHRlZCB0byBiZSByZXVzZWQuXHJcbiAgICAgIC8vIC0gTG9zdCBjb250YWN0IHdpdGggdGhlIGJyb3dzZXIuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIFdoYXQgd2UncmUgc2VlaW5nIGluIGxvZ3MgaXMgdGhhdCBzdWNjZXNzaXZlIGV4cG9ydHMgdHlwaWNhbGx5XHJcbiAgICAgIC8vIHN1Y2NlZWRzLCBhbmQgdGhlIHNlcnZlciByZWNvdmVycywgaW5kaWNhdGluZyB0aGF0IGl0J3MgbGlrZWx5XHJcbiAgICAgIC8vIHRoZSBmaXJzdCBjYXNlLiBUaGlzIGlzIGFuIGF0dGVtcHQgYXQgYWxsaWV2YXRpbmcgdGhlIGlzc3VlIGJ5XHJcbiAgICAgIC8vIHNpbXBseSBub3QgdmFsaWRhdGluZyB0aGUgd29ya2VyIGlmIHRoZSBwYWdlIGlzIG51bGwgb3IgY2xvc2VkLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBUaGUgYWN0dWFsIHJlc3VsdCBmcm9tIHdoZW4gdGhpcyBoYXBwZW5lZCwgd2FzIHRoYXQgYSB3b3JrZXIgd291bGRcclxuICAgICAgLy8gYmUgY29tcGxldGVseSBsb2NrZWQsIHN0b3BwaW5nIGl0IGZyb20gYmVpbmcgYWNxdWlyZWQgdW50aWxcclxuICAgICAgLy8gaXRzIHdvcmsgY291bnQgcmVhY2hlZCB0aGUgbGltaXQuXHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHBhZ2VgIGlzIHZhbGlkXHJcbiAgICAgIGlmICghcG9vbFJlc291cmNlLnBhZ2UpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKG5vIHZhbGlkIHBhZ2UgaXMgZm91bmQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBwYWdlYCBpcyBjbG9zZWRcclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKHBhZ2UgaXMgY2xvc2VkIG9yIGludmFsaWQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBtYWluRnJhbWVgIGlzIGRldGFjaGVkXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZS5tYWluRnJhbWUoKS5kZXRhY2hlZCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAocGFnZSdzIGZyYW1lIGlzIGRldGFjaGVkKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgd29ya0xpbWl0YCBpcyBleGNlZWRlZFxyXG4gICAgICBpZiAoXHJcbiAgICAgICAgcG9vbE9wdGlvbnMud29ya0xpbWl0ICYmXHJcbiAgICAgICAgKytwb29sUmVzb3VyY2Uud29ya0NvdW50ID4gcG9vbE9wdGlvbnMud29ya0xpbWl0XHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAoZXhjZWVkZWQgdGhlICR7cG9vbE9wdGlvbnMud29ya0xpbWl0fSB3b3JrcyBwZXIgcmVzb3VyY2UgbGltaXQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVGhlIGBwb29sUmVzb3VyY2VgIGlzIHZhbGlkYXRlZFxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH0sXHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBEZXN0cm95cyBhIHdvcmtlciBlbnRyeSBpbiB0aGUgZXhwb3J0IHBvb2wsIGNsb3NpbmcgaXRzIGFzc29jaWF0ZWQgcGFnZS5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiBkZXN0cm95XHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHBvb2xSZXNvdXJjZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAgICogdGhlIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cclxuICAgICAqL1xyXG4gICAgZGVzdHJveTogYXN5bmMgKHBvb2xSZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBEZXN0cm95aW5nIGEgd29ya2VyLmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZSAmJiAhcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAvLyBSZW1vdmUgYWxsIGF0dGFjaGVkIGV2ZW50IGxpc3RlbmVycyBmcm9tIHRoZSByZXNvdXJjZVxyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdwYWdlZXJyb3InKTtcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygnY29uc29sZScpO1xyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdmcmFtZWRldGFjaGVkJyk7XHJcblxyXG4gICAgICAgICAgLy8gV2UgbmVlZCB0byB3YWl0IGFyb3VuZCBmb3IgdGhpc1xyXG4gICAgICAgICAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2UuY2xvc2UoKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAzLFxyXG4gICAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBQYWdlIGNvdWxkIG5vdCBiZSBjbG9zZWQgdXBvbiBkZXN0cm95aW5nLmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9O1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgaW5pdFBvb2wsXHJcbiAga2lsbFBvb2wsXHJcbiAgcG9zdFdvcmssXHJcbiAgZ2V0UG9vbCxcclxuICBnZXRQb29sU3RhdHMsXHJcbiAgZ2V0UG9vbEluZm8sXHJcbiAgZ2V0UG9vbEluZm9KU09OXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBVc2VkIHRvIHNhbml0aXplIHRoZSBzdHJpbmdzIGNvbWluZyBmcm9tIHRoZSBleHBvcnRpbmcgbW9kdWxlXHJcbiAqIHRvIHByZXZlbnQgWFNTIGF0dGFja3MgKHdpdGggdGhlIERPTVB1cmlmeSBsaWJyYXJ5KS5cclxuICovXHJcblxyXG5pbXBvcnQgRE9NUHVyaWZ5IGZyb20gJ2RvbXB1cmlmeSc7XHJcbmltcG9ydCB7IEpTRE9NIH0gZnJvbSAnanNkb20nO1xyXG5cclxuLyoqXHJcbiAqIFNhbml0aXplcyBhIGdpdmVuIEhUTUwgc3RyaW5nIGJ5IHJlbW92aW5nIDxzY3JpcHQ+IHRhZ3MuIFRoaXMgZnVuY3Rpb24gdXNlc1xyXG4gKiBhIHJlZ3VsYXIgZXhwcmVzc2lvbiB0byBmaW5kIGFuZCByZW1vdmUgYWxsIG9jY3VycmVuY2VzIG9mIDxzY3JpcHQ+PC9zY3JpcHQ+XHJcbiAqIHRhZ3MgYW5kIGFueSBjb250ZW50IHdpdGhpbiB0aGVtLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2FuaXRpemVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IC0gVGhlIEhUTUwgc3RyaW5nIHRvIGJlIHNhbml0aXplZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIHNhbml0aXplZCBIVE1MIHN0cmluZy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzYW5pdGl6ZShpbnB1dCkge1xyXG4gIC8vIEdldCB0aGUgdmlydHVhbCBET01cclxuICBjb25zdCB3aW5kb3cgPSBuZXcgSlNET00oJycpLndpbmRvdztcclxuXHJcbiAgLy8gQ3JlYXRlIGEgcHVyaWZ5aW5nIGluc3RhbmNlXHJcbiAgY29uc3QgcHVyaWZ5ID0gRE9NUHVyaWZ5KHdpbmRvdyk7XHJcblxyXG4gIC8vIFJldHVybiBzYW5pdGl6ZWQgaW5wdXRcclxuICByZXR1cm4gcHVyaWZ5LnNhbml0aXplKGlucHV0LCB7IEFERF9UQUdTOiBbJ2ZvcmVpZ25PYmplY3QnXSB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNhbml0aXplXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBwcm92aWRlcyBmdW5jdGlvbnMgdG8gcHJlcGFyZSBmb3IgdGhlIGV4cG9ydGluZyBjaGFydHNcclxuICogaW50byB2YXJpb3VzIGltYWdlIG91dHB1dCBmb3JtYXRzIHN1Y2ggYXMgSlBFRywgUE5HLCBQREYsIGFuZCBTVkdzLlxyXG4gKiBJdCBzdXBwb3J0cyBzaW5nbGUgYW5kIGJhdGNoIGV4cG9ydCBvcGVyYXRpb25zIGFuZCBhbGxvd3MgY3VzdG9taXphdGlvblxyXG4gKiB0aHJvdWdoIG9wdGlvbnMgcGFzc2VkIGZyb20gY29uZmlndXJhdGlvbnMgb3IgQVBJcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcblxyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcsIHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRQb29sU3RhdHMsIGtpbGxQb29sLCBwb3N0V29yayB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IHNhbml0aXplIH0gZnJvbSAnLi9zYW5pdGl6ZS5qcyc7XHJcbmltcG9ydCB7XHJcbiAgZml4Q29uc3RyLFxyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIGlzT2JqZWN0LFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHdyYXBBcm91bmRcclxufSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgZ2xvYmFsIGZsYWcgZm9yIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uXHJcbmxldCBhbGxvd0NvZGVFeGVjdXRpb24gPSBmYWxzZTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBvcHRpb25zIGFuZCBzYXZlc1xyXG4gKiB0aGUgcmVzdWx0aW5nIGltYWdlIHRvIHRoZSBwcm92aWRlZCBvdXRwdXQgZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzaW5nbGVFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgYG9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3NcclxuICogZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhbiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGVcclxuICogc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gVGhlIG9iamVjdCBtdXN0IGNvbnRhaW4gYXQgbGVhc3Qgb25lXHJcbiAqIG9mIHRoZSBmb2xsb3dpbmcgYGV4cG9ydGAgcHJvcGVydGllczogYGluZmlsZWAsIGBpbnN0cmAsIGBvcHRpb25zYCwgb3IgYHN2Z2BcclxuICogdG8gZ2VuZXJhdGUgYSB2YWxpZCBpbWFnZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIHNpbmdsZSBleHBvcnRcclxuICogcHJvY2VzcyBpcyBjb21wbGV0ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2luZ2xlRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQpIHtcclxuICAgIC8vIFBlcmZvcm0gYW4gZXhwb3J0XHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChcclxuICAgICAgeyBleHBvcnQ6IG9wdGlvbnMuZXhwb3J0LCBjdXN0b21Mb2dpYzogb3B0aW9ucy5jdXN0b21Mb2dpYyB9LFxyXG4gICAgICBhc3luYyAoZXJyb3IsIGRhdGEpID0+IHtcclxuICAgICAgICAvLyBFeGl0IHByb2Nlc3Mgd2hlbiBlcnJvciBleGlzdHNcclxuICAgICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBgYjY0YCwgYG91dGZpbGVgLCBhbmQgYHR5cGVgIGZvciBhIGNoYXJ0XHJcbiAgICAgICAgY29uc3QgeyBiNjQsIG91dGZpbGUsIHR5cGUgfSA9IGRhdGEub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlc3VsdFxyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpZiAoYjY0KSB7XHJcbiAgICAgICAgICAgIC8vIEFzIGEgQmFzZTY0IHN0cmluZyB0byBhIHR4dCBmaWxlXHJcbiAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgYCR7b3V0ZmlsZS5zcGxpdCgnLicpLnNoaWZ0KCkgfHwgJ2NoYXJ0J30udHh0YCxcclxuICAgICAgICAgICAgICBnZXRCYXNlNjQoZGF0YS5yZXN1bHQsIHR5cGUpXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAvLyBBcyBhIGNvcnJlY3QgaW1hZ2UgZm9ybWF0XHJcbiAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgb3V0ZmlsZSB8fCBgY2hhcnQuJHt0eXBlfWAsXHJcbiAgICAgICAgICAgICAgdHlwZSAhPT0gJ3N2ZycgPyBCdWZmZXIuZnJvbShkYXRhLnJlc3VsdCwgJ2Jhc2U2NCcpIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICA1MDBcclxuICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gS2lsbCBwb29sIGFuZCBjbG9zZSBicm93c2VyIGFmdGVyIGZpbmlzaGluZyBzaW5nbGUgZXhwb3J0XHJcbiAgICAgICAgYXdhaXQga2lsbFBvb2woKTtcclxuICAgICAgfVxyXG4gICAgKTtcclxuICB9IGVsc2Uge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NoYXJ0XSBObyBleHBlY3RlZCBgZXhwb3J0YCBvcHRpb25zIHdlcmUgZm91bmQuIFBsZWFzZSBwcm92aWRlIG9uZSBvZiB0aGUgZm9sbG93aW5nIG9wdGlvbnM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuJyxcclxuICAgICAgNDAwXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIGJhdGNoIGV4cG9ydCBwcm9jZXNzIGZvciBtdWx0aXBsZSBjaGFydHMgYmFzZWQgb24gaW5mb3JtYXRpb25cclxuICogcHJvdmlkZWQgaW4gdGhlIGBiYXRjaGAgb3B0aW9uLiBUaGUgYGJhdGNoYCBpcyBhIHN0cmluZyBpbiB0aGUgZm9sbG93aW5nXHJcbiAqIGZvcm1hdDogXCJpbmZpbGUxLmpzb249b3V0ZmlsZTEucG5nO2luZmlsZTIuanNvbj1vdXRmaWxlMi5wbmc7Li4uXCIuIFJlc3VsdHNcclxuICogYXJlIHNhdmVkIHRvIHRoZSBzcGVjaWZpZWQgb3V0cHV0IGZpbGVzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGJhdGNoRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIEl0IG11c3QgY29udGFpbiB0aGUgYGJhdGNoYCBvcHRpb24gZnJvbVxyXG4gKiB0aGUgYGV4cG9ydGAgc2VjdGlvbiB0byBnZW5lcmF0ZSB2YWxpZCBpbWFnZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBiYXRjaCBleHBvcnRcclxuICogcHJvY2Vzc2VzIGFyZSBjb21wbGV0ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIGFueSBvZiB0aGUgYmF0Y2ggZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYmF0Y2hFeHBvcnQob3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBleHBvcnQgbWFrZXMgc2Vuc2VcclxuICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLmV4cG9ydCAmJiBvcHRpb25zLmV4cG9ydC5iYXRjaCkge1xyXG4gICAgLy8gQW4gYXJyYXkgZm9yIGNvbGxlY3RpbmcgYmF0Y2ggZXhwb3J0c1xyXG4gICAgY29uc3QgYmF0Y2hGdW5jdGlvbnMgPSBbXTtcclxuXHJcbiAgICAvLyBTcGxpdCBhbmQgcGFpciB0aGUgYGJhdGNoYCBhcmd1bWVudHNcclxuICAgIGZvciAobGV0IHBhaXIgb2Ygb3B0aW9ucy5leHBvcnQuYmF0Y2guc3BsaXQoJzsnKSB8fCBbXSkge1xyXG4gICAgICBwYWlyID0gcGFpci5zcGxpdCgnPScpO1xyXG4gICAgICBpZiAocGFpci5sZW5ndGggPT09IDIpIHtcclxuICAgICAgICBiYXRjaEZ1bmN0aW9ucy5wdXNoKFxyXG4gICAgICAgICAgc3RhcnRFeHBvcnQoXHJcbiAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICBleHBvcnQ6IHtcclxuICAgICAgICAgICAgICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICAgICAgICAgICAgaW5maWxlOiBwYWlyWzBdLFxyXG4gICAgICAgICAgICAgICAgb3V0ZmlsZTogcGFpclsxXVxyXG4gICAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgICAgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgICAgICAgIG91dGZpbGUsXHJcbiAgICAgICAgICAgICAgICAgICAgdHlwZSAhPT0gJ3N2ZydcclxuICAgICAgICAgICAgICAgICAgICAgID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKVxyXG4gICAgICAgICAgICAgICAgICAgICAgOiBkYXRhLnJlc3VsdFxyXG4gICAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAgICAgICAgICdbY2hhcnRdIEVycm9yIHdoaWxlIHNhdmluZyBhIGNoYXJ0LicsXHJcbiAgICAgICAgICAgICAgICAgIDUwMFxyXG4gICAgICAgICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICApXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMiwgJ1tjaGFydF0gTm8gY29ycmVjdCBwYWlyIGZvdW5kIGZvciB0aGUgYmF0Y2ggZXhwb3J0LicpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQXdhaXQgYWxsIGV4cG9ydHMgYXJlIGRvbmVcclxuICAgIGNvbnN0IGJhdGNoUmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChiYXRjaEZ1bmN0aW9ucyk7XHJcblxyXG4gICAgLy8gS2lsbCBwb29sIGFuZCBjbG9zZSBicm93c2VyIGFmdGVyIGZpbmlzaGluZyBiYXRjaCBleHBvcnRcclxuICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcblxyXG4gICAgLy8gTG9nIGVycm9ycyBpZiBmb3VuZFxyXG4gICAgYmF0Y2hSZXN1bHRzLmZvckVhY2goKHJlc3VsdCwgaW5kZXgpID0+IHtcclxuICAgICAgLy8gTG9nIHRoZSBlcnJvciB3aXRoIHN0YWNrIGFib3V0IHRoZSBzcGVjaWZpYyBiYXRjaCBleHBvcnRcclxuICAgICAgaWYgKHJlc3VsdC5yZWFzb24pIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAxLFxyXG4gICAgICAgICAgcmVzdWx0LnJlYXNvbixcclxuICAgICAgICAgIGBbY2hhcnRdIEJhdGNoIGV4cG9ydCBudW1iZXIgJHtpbmRleCArIDF9IGNvdWxkIG5vdCBiZSBjb3JyZWN0bHkgY29tcGxldGVkLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGVsc2Uge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NoYXJ0XSBObyBleHBlY3RlZCBgZXhwb3J0YCBvcHRpb25zIHdlcmUgZm91bmQuIFBsZWFzZSBwcm92aWRlIHRoZSBgYmF0Y2hgIG9wdGlvbiB0byBnZW5lcmF0ZSB2YWxpZCBpbWFnZXMuJyxcclxuICAgICAgNDAwXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhbiBleHBvcnQgcHJvY2Vzcy4gVGhlIGBleHBvcnRpbmdPcHRpb25zYCBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXRcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgcGFydGlhbFxyXG4gKiBvcHRpb25zIGFyZSBwcm92aWRlZCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICpcclxuICogVGhlIGBlbmRDYWxsYmFja2AgZnVuY3Rpb24gaXMgaW52b2tlZCB1cG9uIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBleHBvcnQsXHJcbiAqIGVpdGhlciBzdWNjZXNzZnVsbHkgb3Igd2l0aCBhbiBlcnJvci4gVGhlIGBlcnJvcmAgb2JqZWN0IGlzIHByb3ZpZGVkXHJcbiAqIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIGNvbnRhaW5pbmdcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5XHJcbiAqIGFuZCB0aGUgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0aW5nT3B0aW9ucyAtIFRoZSBgZXhwb3J0aW5nT3B0aW9uc2Agb2JqZWN0LCB3aGljaFxyXG4gKiBzaG91bGQgaW5jbHVkZSBzZXR0aW5ncyBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuXHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJZiB0aGUgcHJvdmlkZWRcclxuICogb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZW5kQ2FsbGJhY2sgLSBUaGUgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCB1cG9uXHJcbiAqIGZpbmFsaXppbmcgdGhlIGV4cG9ydCBwcm9jZXNzIG9yIHVwb24gZW5jb3VudGVyaW5nIGFuIGVycm9yLiBUaGUgZmlyc3RcclxuICogYXJndW1lbnQgaXMgdGhlIGBlcnJvcmAgb2JqZWN0LCBhbmQgdGhlIHNlY29uZCBhcmd1bWVudCBpcyB0aGUgYGRhdGFgIG9iamVjdCxcclxuICogd2hpY2ggaW5jbHVkZXMgdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgXHJcbiAqIHByb3BlcnR5IGFuZCB0aGUgZnVsbCBzZXQgb2Ygb3B0aW9ucyBpbiB0aGUgYG9wdGlvbnNgIHByb3BlcnR5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gVGhpcyBmdW5jdGlvbiBkb2VzIG5vdCByZXR1cm4gYSB2YWx1ZSBkaXJlY3RseS5cclxuICogSW5zdGVhZCwgaXQgY29tbXVuaWNhdGVzIHJlc3VsdHMgdmlhIHRoZSBgZW5kQ2FsbGJhY2tgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYSBwcm9ibGVtIHdpdGhcclxuICogcHJvY2Vzc2luZyBpbnB1dCBvZiBhbnkgdHlwZS4gVGhlIGVycm9yIGlzIHBhc3NlZCBpbnRvIHRoZSBgZW5kQ2FsbGJhY2tgXHJcbiAqIGZ1bmN0aW9uIGFuZCBwcm9jZXNzZWQgdGhlcmUuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc3RhcnRFeHBvcnQoZXhwb3J0aW5nT3B0aW9ucywgZW5kQ2FsbGJhY2spIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgb3B0aW9ucyBpcyBhbiBvYmplY3RcclxuICAgIGlmICghaXNPYmplY3QoZXhwb3J0aW5nT3B0aW9ucykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgcHJvdmlkZWQgYGV4cG9ydGluZ09wdGlvbnNgLiBOZWVkcyB0byBiZSBhbiBvYmplY3QuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBNZXJnZSBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gdGhlIGNvcHkgb2YgdGhlIGluc3RhbmNlIG9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKFxyXG4gICAgICB7XHJcbiAgICAgICAgZXhwb3J0OiBleHBvcnRpbmdPcHRpb25zLmV4cG9ydCxcclxuICAgICAgICBjdXN0b21Mb2dpYzogZXhwb3J0aW5nT3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICByZXR1cm4gcG9zdFdvcmsob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBmb3IgY2hhcnQgZXhwb3J0cyBiYXNlZFxyXG4gKiBvbiB0aGUgcHJvdmlkZWQgZXhwb3J0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICogMS4gVGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBgc2NhbGVgIGZyb20gdGhlIGBleHBvcnRPcHRpb25zYC5cclxuICogMi4gT3B0aW9ucyBmcm9tIHRoZSBjaGFydCBjb25maWd1cmF0aW9uIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBnbG9iYWwgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgdGhlbWUgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCBzZWN0aW9ucykuXHJcbiAqIDUuIEZhbGxiYWNrIGRlZmF1bHQgdmFsdWVzIChgaGVpZ2h0ID0gNDAwYCwgYHdpZHRoID0gNjAwYCwgYHNjYWxlID0gMWApLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZpbmRDaGFydFNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb2RlIGV4ZWN1dGlvblxyXG4gKiBpcyBub3QgYWxsb3dlZCBidXQgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYXJlIHN0aWxsIHByb3ZpZGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgLy8gSW4gY2FzZSBvZiBhbGxvd2luZyBjb2RlIGV4ZWN1dGlvblxyXG4gIGlmIChhbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIC8vIFByb2Nlc3MgdGhlIGByZXNvdXJjZXNgIG9wdGlvblxyXG4gICAgaWYgKHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID09PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBDdXN0b20gc3RyaW5naWZpZWQgcmVzb3VyY2VzXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gTG9hZCB0aGUgZGVmYXVsdCBvbmVcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoJ3Jlc291cmNlcy5qc29uJyksICd1dGY4JyksXHJcbiAgICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IGByZXNvdXJjZXMuanNvbmAgZmlsZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGN1c3RvbSBjb2RlIGFuZCB3cmFwIGFyb3VuZCBpdCBpbiBhIHNlbGYgaW52b2tpbmcgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1tjaGFydF0gVGhlIGBjdXN0b21Db2RlYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gVHJ5IHRvIGxvYWQgY2FsbGJhY2sgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2ssXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY2FsbGJhY2tgIGNhbm5vdCBiZSBsb2FkZWQuJyk7XHJcblxyXG4gICAgICAvLyBJbiBjYXNlIG9mIGFuIGVycm9yLCBzZXQgdGhlIG9wdGlvbiB3aXRoIG51bGxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGN1c3RvbUNvZGVgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGN1c3RvbUNvZGVgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGNhbGxiYWNrYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgY2FsbGJhY2tgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHJlc291cmNlc2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gSWYgdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIGZsYWcgaXMgc2V0IHRvIGZhbHNlLCB3ZSBzaG91bGQgcmVmdXNlXHJcbiAgICAvLyB0aGUgdXNhZ2Ugb2YgdGhlIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgLCBhbmQgYGN1c3RvbUNvZGVgIG9wdGlvbnMuXHJcbiAgICAvLyBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbCByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LlxyXG4gICAgaWYgKFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFJlc2V0IGFsbCBjdXN0b20gY29kZSBvcHRpb25zXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcblxyXG4gICAgICAvLyBTZW5kIGEgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydCB0aGVzZSBzZXR0aW5nc1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnLCBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gLFxyXG4gICAgICAgIDQwM1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZnJvbSB0aGUgYHJlc291cmNlc2Agb3B0aW9uIGZvciBleHBvcnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFtyZXNvdXJjZXM9bnVsbF0gLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuXHJcbiAqIENhbiBiZSBlaXRoZXIgYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiwgYSBwYXRoIHRvIGEgSlNPTiBmaWxlLFxyXG4gKiBvciBudWxsLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgbnVsbGAuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2FkaW5nXHJcbiAqIHJlc291cmNlcyBmcm9tIGZpbGVzIGlzIGFsbG93ZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGhhbmRsZWQgcmVzb3VyY2VzIG9yIG51bGwgaWYgbm8gdmFsaWQgcmVzb3VyY2VzXHJcbiAqIGFyZSBmb3VuZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgcmVzb3VyY2VzID0gbnVsbCxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIExpc3Qgb2YgYWxsb3dlZCBzZWN0aW9ucyBpbiB0aGUgcmVzb3VyY2VzIEpTT05cclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKHJlc291cmNlcyksICd1dGY4JyksXHJcbiAgICAgICAgZmFsc2UsXHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxyXG4gICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhyZXNvdXJjZXMsIGZhbHNlLCBhbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIGZpbGVzIHNlY3Rpb25cclxuICAgIGlmIChoYW5kbGVkUmVzb3VyY2VzICYmICFhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaWx0ZXIgZnJvbSB1bm5lY2Vzc2FyeSBwcm9wZXJ0aWVzXHJcbiAgZm9yIChjb25zdCBwcm9wTmFtZSBpbiBoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXNbcHJvcE5hbWVdO1xyXG4gICAgfSBlbHNlIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiBhbGxvd2VkIHByb3BlcnRpZXMgaXMgcHJlc2VudFxyXG4gIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgbG9hZGluZyBhbmQgdmFsaWRhdGlvbiBvZiB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYFxyXG4gKiBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIElmIHRoZSBvcHRpb24gaXMgYSBzdHJpbmcgYW5kIHJlZmVyZW5jZXMgYSBKU09OIGZpbGVcclxuICogKHdoZW4gdGhlIGBhbGxvd0ZpbGVSZXNvdXJjZXNgIGlzIHRydWUpLCBpdCByZWFkcyBhbmQgcGFyc2VzIHRoZSBmaWxlLlxyXG4gKiBPdGhlcndpc2UsIGl0IGF0dGVtcHRzIHRvIHBhcnNlIHRoZSBzdHJpbmcgb3Igb2JqZWN0IGFzIEpTT04uIElmIGFueSBlcnJvcnNcclxuICogb2NjdXIgZHVyaW5nIHRoaXMgcHJvY2VzcywgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC4gSWYgdGhlcmUgaXMgYW4gZXJyb3JcclxuICogbG9hZGluZyBvciBwYXJzaW5nIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3IgYHRoZW1lT3B0aW9uc2AsIHRoZSBlcnJvciBpcyBsb2dnZWRcclxuICogYW5kIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBlbmFibGluZyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzcGVjaWZpZWQgRXhwcmVzcyBhcHAuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXRcclxuICogdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCByYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWQgYW5kIHRoZSBhcHAgZXhpc3RzXHJcbiAgICBpZiAoYXBwICYmIHJhdGVMaW1pdGluZ09wdGlvbnMuZW5hYmxlKSB7XHJcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPVxyXG4gICAgICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgICAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICAgICAgd2luZG93OiByYXRlTGltaXRpbmdPcHRpb25zLndpbmRvdyB8fCAxLFxyXG4gICAgICAgIG1heFJlcXVlc3RzOiByYXRlTGltaXRpbmdPcHRpb25zLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgICAgIGRlbGF5OiByYXRlTGltaXRpbmdPcHRpb25zLmRlbGF5IHx8IDAsXHJcbiAgICAgICAgdHJ1c3RQcm94eTogcmF0ZUxpbWl0aW5nT3B0aW9ucy50cnVzdFByb3h5IHx8IGZhbHNlLFxyXG4gICAgICAgIHNraXBLZXk6IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcEtleSB8fCBudWxsLFxyXG4gICAgICAgIHNraXBUb2tlbjogcmF0ZUxpbWl0aW5nT3B0aW9ucy5za2lwVG9rZW4gfHwgbnVsbFxyXG4gICAgICB9O1xyXG5cclxuICAgICAgLy8gU2V0IGlmIGJlaGluZCBhIHByb3h5XHJcbiAgICAgIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICAgICAgYXBwLmVuYWJsZSgndHJ1c3QgcHJveHknKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gICAgICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcclxuICAgICAgICAvLyBUaW1lIGZyYW1lIGZvciB3aGljaCByZXF1ZXN0cyBhcmUgY2hlY2tlZCBhbmQgcmVtZW1iZXJlZFxyXG4gICAgICAgIHdpbmRvd01zOiByYXRlT3B0aW9ucy53aW5kb3cgKiA2MCAqIDEwMDAsXHJcbiAgICAgICAgLy8gTGltaXQgZWFjaCBJUCB0byAxMDAgcmVxdWVzdHMgcGVyIGB3aW5kb3dNc2BcclxuICAgICAgICBsaW1pdDogcmF0ZU9wdGlvbnMubWF4UmVxdWVzdHMsXHJcbiAgICAgICAgLy8gRGlzYWJsZSBkZWxheWluZywgZnVsbCBzcGVlZCB1bnRpbCB0aGUgbWF4IGxpbWl0IGlzIHJlYWNoZWRcclxuICAgICAgICBkZWxheU1zOiByYXRlT3B0aW9ucy5kZWxheSxcclxuICAgICAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlLmZvcm1hdCh7XHJcbiAgICAgICAgICAgIGpzb246ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZSB9KTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgICAgIC8vIEFsbG93IGJ5cGFzc2luZyB0aGUgbGltaXRlciBpZiBhIHZhbGlkIGtleS90b2tlbiBoYXMgYmVlbiBzZW50XHJcbiAgICAgICAgICBpZiAoXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IG51bGwgJiZcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkuYWNjZXNzX3Rva2VuID09PSByYXRlT3B0aW9ucy5za2lwVG9rZW5cclxuICAgICAgICAgICkge1xyXG4gICAgICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcblxyXG4gICAgICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gICAgICBhcHAudXNlKGxpbWl0ZXIpO1xyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtyYXRlIGxpbWl0aW5nXSBFbmFibGVkIHJhdGUgbGltaXRpbmcgd2l0aCAke3JhdGVPcHRpb25zLm1heFJlcXVlc3RzfSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcmF0ZSBsaW1pdGluZ10gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciB2YWxpZGF0aW5nIGluY29taW5nIEhUVFAgcmVxdWVzdHNcclxuICogaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi4gVGhpcyBtb2R1bGUgZW5zdXJlcyB0aGF0IHJlcXVlc3RzIGNvbnRhaW5cclxuICogYXBwcm9wcmlhdGUgY29udGVudCB0eXBlcyBhbmQgdmFsaWQgcmVxdWVzdCBib2RpZXMsIGluY2x1ZGluZyBwcm9wZXIgSlNPTlxyXG4gKiBzdHJ1Y3R1cmVzIGFuZCBjaGFydCBkYXRhIGZvciBleHBvcnRzLiBJdCBjaGVja3MgZm9yIHBvdGVudGlhbCBpc3N1ZXMgc3VjaFxyXG4gKiBhcyBtaXNzaW5nIG9yIG1hbGZvcm1lZCBkYXRhLCBwcml2YXRlIHJhbmdlIFVSTHMgaW4gU1ZHIHBheWxvYWRzLCBhbmQgYWxsb3dzXHJcbiAqIGZvciBmbGV4aWJsZSBvcHRpb25zIHZhbGlkYXRpb24uIFRoZSBtaWRkbGV3YXJlIGxvZ3MgZGV0YWlsZWQgaW5mb3JtYXRpb25cclxuICogYW5kIGhhbmRsZXMgZXJyb3JzIHJlbGF0ZWQgdG8gaW5jb3JyZWN0IHBheWxvYWRzLCBjaGFydCBkYXRhLCBhbmQgcHJpdmF0ZSBVUkxcclxuICogdXNhZ2UuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uIH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpc09iamVjdEVtcHR5LCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgY29udGVudC10eXBlIGhlYWRlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBjb250ZW50LXR5cGVcclxuICogaXMgbm90IGNvcnJlY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSBjb250ZW50IHR5cGUgaGVhZGVyXHJcbiAgICBjb25zdCBjb250ZW50VHlwZSA9IHJlcXVlc3QuaGVhZGVyc1snY29udGVudC10eXBlJ10gfHwgJyc7XHJcblxyXG4gICAgLy8gQWxsb3cgb25seSBKU09OLCBVUkwtZW5jb2RlZCBhbmQgZm9ybSBkYXRhIHdpdGhvdXQgZmlsZXMgdHlwZXMgb2YgZGF0YVxyXG4gICAgaWYgKFxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL2pzb24nKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnbXVsdGlwYXJ0L2Zvcm0tZGF0YScpXHJcbiAgICApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbdmFsaWRhdGlvbl0gQ29udGVudC1UeXBlIG11c3QgYmUgYXBwbGljYXRpb24vanNvbiwgYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkLCBvciBtdWx0aXBhcnQvZm9ybS1kYXRhLicsXHJcbiAgICAgICAgNDE1XHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYHJlcXVlc3RCb2R5TWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIHJlcXVlc3QncyBib2R5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGJvZHkgaXMgbm90IGNvcnJlY3QuXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY2hhcnQgZGF0YSBmcm9tIHRoZSBib2R5XHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaW4gY2FzZSBvZiB0aGUgcHJpdmF0ZSByYW5nZVxyXG4gKiB1cmwgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGJvZHlcclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHV1aWQoKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgZW1wdHkuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGJvZHkgaXMgcmVxdWlyZWQuIFBsZWFzZSBlbnN1cmUgdGhhdCB5b3VyIENvbnRlbnQtVHlwZSBoZWFkZXIgaXMgY29ycmVjdC4gQWNjZXB0ZWQgdHlwZXMgYXJlICdhcHBsaWNhdGlvbi9qc29uJyBhbmQgJ211bHRpcGFydC9mb3JtLWRhdGEnLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uIGZvciB0aGUgc2VydmVyXHJcbiAgICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb24gPSBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKTtcclxuXHJcbiAgICAvLyBGaW5kIGEgY29ycmVjdCBjaGFydCBvcHRpb25zXHJcbiAgICBjb25zdCBpbnN0ciA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgLy8gVXNlIG9uZSBvZiB0aGUgYmVsb3dcclxuICAgICAgYm9keS5pbnN0ciB8fCBib2R5Lm9wdGlvbnMgfHwgYm9keS5pbmZpbGUgfHwgYm9keS5kYXRhLFxyXG4gICAgICAvLyBTdHJpbmdpZnkgb3B0aW9uc1xyXG4gICAgICB0cnVlLFxyXG4gICAgICAvLyBBbGxvdyBvciBkaXNhbGxvdyBmdW5jdGlvbnNcclxuICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRoZXJlIGlzIG5vIGNvcnJlY3QgY2hhcnQgZGF0YVxyXG4gICAgaWYgKGluc3RyID09PSBudWxsICYmICFib2R5LnN2Zykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIG1pc3NpbmcgY29ycmVjdCBjaGFydCBkYXRhIGZvciBleHBvcnQ6ICR7SlNPTi5zdHJpbmdpZnkoYm9keSl9LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGVzdCBvZiB4bGluazpocmVmIGVsZW1lbnRzIGZyb20gcGF5bG9hZCdzIFNWRyBmYWlsc1xyXG4gICAgaWYgKGJvZHkuc3ZnICYmIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoYm9keS5zdmcpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBTVkcgcG90ZW50aWFsbHkgY29udGFpbiBhdCBsZWFzdCBvbmUgZm9yYmlkZGVuIFVSTCBpbiAneGxpbms6aHJlZicgZWxlbWVudC4gUGxlYXNlIHJldmlldyB0aGUgU1ZHIGNvbnRlbnQgYW5kIGVuc3VyZSB0aGF0IGFsbCByZWZlcmVuY2VkIFVSTHMgY29tcGx5IHdpdGggc2VjdXJpdHkgcG9saWNpZXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3Qgb3B0aW9ucyBhbmQgc3RvcmUgcGFyc2VkIHN0cnVjdHVyZSBpbiB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC52YWxpZGF0ZWRPcHRpb25zID0ge1xyXG4gICAgICAvLyBTZXQgdGhlIGNyZWF0ZWQgSUQgYXMgYSBgcmVxdWVzdElkYCBwcm9wZXJ0eSBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICByZXF1ZXN0SWQsXHJcbiAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIHN2ZzogYm9keS5zdmcsXHJcbiAgICAgICAgb3V0ZmlsZTpcclxuICAgICAgICAgIGJvZHkub3V0ZmlsZSB8fFxyXG4gICAgICAgICAgYCR7cmVxdWVzdC5wYXJhbXMuZmlsZW5hbWUgfHwgJ2NoYXJ0J30uJHtib2R5LnR5cGUgfHwgJ3BuZyd9YCxcclxuICAgICAgICB0eXBlOiBib2R5LnR5cGUsXHJcbiAgICAgICAgY29uc3RyOiBib2R5LmNvbnN0cixcclxuICAgICAgICBiNjQ6IGJvZHkuYjY0LFxyXG4gICAgICAgIG5vRG93bmxvYWQ6IGJvZHkubm9Eb3dubG9hZCxcclxuICAgICAgICBoZWlnaHQ6IGJvZHkuaGVpZ2h0LFxyXG4gICAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxyXG4gICAgICAgIHNjYWxlOiBib2R5LnNjYWxlLFxyXG4gICAgICAgIGdsb2JhbE9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkuZ2xvYmFsT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApLFxyXG4gICAgICAgIHRoZW1lT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS50aGVtZU9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKVxyXG4gICAgICB9LFxyXG4gICAgICBjdXN0b21Mb2dpYzoge1xyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvbixcclxuICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IGZhbHNlLFxyXG4gICAgICAgIGN1c3RvbUNvZGU6IGJvZHkuY3VzdG9tQ29kZSxcclxuICAgICAgICBjYWxsYmFjazogYm9keS5jYWxsYmFjayxcclxuICAgICAgICByZXNvdXJjZXM6IGlzQWxsb3dlZENvbmZpZyhib2R5LnJlc291cmNlcywgdHJ1ZSwgYWxsb3dDb2RlRXhlY3V0aW9uKVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIC8vIENhbGwgdGhlIG5leHQgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApIHtcclxuICAvLyBBZGQgY29udGVudCB0eXBlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIGNvbnRlbnRUeXBlTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCByZXF1ZXN0IGJvZHkgcmVxdWVzdCB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCByZXF1ZXN0Qm9keU1pZGRsZXdhcmUpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIHRoZSBleHBvcnQgcm91dGVzIGFuZCBsb2dpYyBmb3IgaGFuZGxpbmcgY2hhcnQgZXhwb3J0XHJcbiAqIHJlcXVlc3RzIGluIGFuIEV4cHJlc3Mgc2VydmVyLiBUaGlzIG1vZHVsZSBwcm9jZXNzZXMgaW5jb21pbmcgcmVxdWVzdHNcclxuICogdG8gZXhwb3J0IGNoYXJ0cyBpbiB2YXJpb3VzIGZvcm1hdHMgKGUuZy4gSlBFRywgUE5HLCBQREYsIFNWRykuIEl0IGludGVncmF0ZXNcclxuICogd2l0aCBIaWdoY2hhcnRzJyBjb3JlIGZ1bmN0aW9uYWxpdGllcyBhbmQgc3VwcG9ydHMgYm90aCBpbW1lZGlhdGUgZG93bmxvYWRcclxuICogcmVzcG9uc2VzIGFuZCBCYXNlNjQtZW5jb2RlZCBjb250ZW50IHJldHVybnMuIFRoZSBjb2RlIGFsc28gZmVhdHVyZXNcclxuICogYmVuY2htYXJraW5nIGZvciBwZXJmb3JtYW5jZSBtb25pdG9yaW5nLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHN0YXJ0RXhwb3J0IH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRCYXNlNjQsIG1lYXN1cmVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBSZXZlcnNlZCBNSU1FIHR5cGVzXHJcbmNvbnN0IHJldmVyc2VkTWltZSA9IHtcclxuICBwbmc6ICdpbWFnZS9wbmcnLFxyXG4gIGpwZWc6ICdpbWFnZS9qcGVnJyxcclxuICBnaWY6ICdpbWFnZS9naWYnLFxyXG4gIHBkZjogJ2FwcGxpY2F0aW9uL3BkZicsXHJcbiAgc3ZnOiAnaW1hZ2Uvc3ZnK3htbCdcclxufTtcclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleHBvcnQgcmVxdWVzdHMgZnJvbSB0aGUgY2xpZW50LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnQocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gU3RhcnQgY291bnRpbmcgdGltZSBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gSW4gY2FzZSB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQsIGZvcmNlIHRvIGFib3J0IGZ1cnRoZXIgYWN0aW9uc1xyXG4gICAgbGV0IGNvbm5lY3Rpb25BYm9ydGVkID0gZmFsc2U7XHJcbiAgICByZXF1ZXN0LnNvY2tldC5vbignY2xvc2UnLCAoaGFkRXJyb3JzKSA9PiB7XHJcbiAgICAgIGlmIChoYWRFcnJvcnMpIHtcclxuICAgICAgICBjb25uZWN0aW9uQWJvcnRlZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgb3B0aW9ucyBwcmV2aW91c2x5IHZhbGlkYXRlZCBpbiB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucztcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3QgaWRcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHJlcXVlc3RPcHRpb25zLnJlcXVlc3RJZDtcclxuXHJcbiAgICAvLyBJbmZvIGFib3V0IGFuIGluY29taW5nIHJlcXVlc3Qgd2l0aCBjb3JyZWN0IGRhdGFcclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3QuYCk7XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChyZXF1ZXN0T3B0aW9ucywgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgIC8vIFJlbW92ZSB0aGUgY2xvc2UgZXZlbnQgZnJvbSB0aGUgc29ja2V0XHJcbiAgICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBjb25uZWN0aW9uIHdhcyBjbG9zZWQsIGRvIG5vdGhpbmdcclxuICAgICAgaWYgKGNvbm5lY3Rpb25BYm9ydGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gYmVmb3JlIHRoZSBjaGFydCBmaW5pc2hlZCBwcm9jZXNzaW5nLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZXJyb3IsIGxvZyBpdCBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCBsb2cgdGhlIG1lc3NhZ2UgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKCFkYXRhIHx8ICFkYXRhLnJlc3VsdCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8XHJcbiAgICAgICAgICAgIHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHJlc3VsdCBpcyAke2RhdGEucmVzdWx0fS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBVbmV4cGVjdGVkIHJldHVybiBvZiB0aGUgZXhwb3J0IHJlc3VsdCBmcm9tIHRoZSBjaGFydCBnZW5lcmF0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciByZXF1ZXN0IGRhdGEuYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFJldHVybiB0aGUgcmVzdWx0IGluIGFuIGFwcHJvcHJpYXRlIGZvcm1hdFxyXG4gICAgICBpZiAoZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3MgdG9vayAke3JlcXVlc3RDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYHR5cGVgLCBgYjY0YCwgYG5vRG93bmxvYWRgLCBhbmQgYG91dGZpbGVgIGZyb20gb3B0aW9uc1xyXG4gICAgICAgIGNvbnN0IHsgdHlwZSwgYjY0LCBub0Rvd25sb2FkLCBvdXRmaWxlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBJZiBvbmx5IEJhc2U2NCBpcyByZXF1aXJlZCwgcmV0dXJuIGl0XHJcbiAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTZXQgY29ycmVjdCBjb250ZW50IHR5cGVcclxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XHJcblxyXG4gICAgICAgIC8vIERlY2lkZSB3aGV0aGVyIHRvIGRvd25sb2FkIG9yIG5vdCBjaGFydCBmaWxlXHJcbiAgICAgICAgaWYgKCFub0Rvd25sb2FkKSB7XHJcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KG91dGZpbGUpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgU1ZHLCByZXR1cm4gcGxhaW4gY29udGVudCwgb3RoZXJ3aXNlIGEgYjY0IHN0cmluZyBmcm9tIGEgYnVmZmVyXHJcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXHJcbiAgICAgICAgICA/IHJlc3BvbnNlLnNlbmQoZGF0YS5yZXN1bHQpXHJcbiAgICAgICAgICA6IHJlc3BvbnNlLnNlbmQoQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYGV4cG9ydGAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZXhwb3J0Um91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gZXhwb3J0Um91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy8nIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyBhdCB0aGUgcm9vdFxyXG4gICAqIGVuZHBvaW50LlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvJywgcmVxdWVzdEV4cG9ydCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy86ZmlsZW5hbWUnIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyB3aXRoXHJcbiAgICogYSBzcGVjaWZpZWQgZmlsZW5hbWUgcGFyYW1ldGVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgcmVxdWVzdEV4cG9ydCk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmVyIGhlYWx0aCBtb25pdG9yaW5nLCBpbmNsdWRpbmdcclxuICogdXB0aW1lLCBzdWNjZXNzIHJhdGVzLCBhbmQgb3RoZXIgc2VydmVyIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBnZXRQb29sSW5mb0pTT04gfSBmcm9tICcuLi8uLi9wb29sLmpzJztcclxuaW1wb3J0IHsgYWRkVGltZXIgfSBmcm9tICcuLi8uLi90aW1lci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0TmV3RGF0ZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vLyBTZXQgdGhlIHN0YXJ0IGRhdGUgb2YgdGhlIHNlcnZlclxyXG5jb25zdCBzZXJ2ZXJTdGFydFRpbWUgPSBuZXcgRGF0ZSgpO1xyXG5cclxuLy8gR2V0IHRoZSBgcGFja2FnZS5qc29uYCBjb250ZW50XHJcbmNvbnN0IHBhY2thZ2VGaWxlID0gSlNPTi5wYXJzZShcclxuICByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICdwYWNrYWdlLmpzb24nKSwgJ3V0ZjgnKVxyXG4pO1xyXG5cclxuLy8gQW4gYXJyYXkgZm9yIHN1Y2Nlc3MgcmF0ZSByYXRpb3NcclxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XHJcblxyXG4vLyBSZWNvcmQgZXZlcnkgbWludXRlXHJcbmNvbnN0IHJlY29yZEludGVydmFsID0gNjAgKiAxMDAwO1xyXG5cclxuLy8gMzAgbWludXRlc1xyXG5jb25zdCB3aW5kb3dTaXplID0gMzA7XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgYHN1Y2Nlc3NSYXRlc2BcclxuICogYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBBIG1vdmluZyBhdmVyYWdlIGZvciBzdWNjZXNzIHJhdGlvIG9mIHRoZSBzZXJ2ZXIgZXhwb3J0cy5cclxuICovXHJcbmZ1bmN0aW9uIF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCkge1xyXG4gIHJldHVybiBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIHRoZSBpbnRlcnZhbCByZXNwb25zaWJsZSBmb3IgY2FsY3VsYXRpbmcgY3VycmVudCBzdWNjZXNzIHJhdGUgcmF0aW9cclxuICogYW5kIGNvbGxlY3RzIHJlY29yZHMgdG8gdGhlIGBzdWNjZXNzUmF0ZXNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3N0YXJ0U3VjY2Vzc1JhdGVcclxuICpcclxuICogQHJldHVybnMge05vZGVKUy5UaW1lb3V0fSBJZCBvZiBhbiBpbnRlcnZhbC5cclxuICovXHJcbmZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlKCkge1xyXG4gIHJldHVybiBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICBjb25zdCBzdGF0cyA9IGdldFBvb2xTdGF0cygpO1xyXG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID1cclxuICAgICAgc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCA9PT0gMFxyXG4gICAgICAgID8gMVxyXG4gICAgICAgIDogKHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQgLyBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkKSAqIDEwMDtcclxuXHJcbiAgICBzdWNjZXNzUmF0ZXMucHVzaChzdWNjZXNzUmF0aW8pO1xyXG4gICAgaWYgKHN1Y2Nlc3NSYXRlcy5sZW5ndGggPiB3aW5kb3dTaXplKSB7XHJcbiAgICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xyXG4gICAgfVxyXG4gIH0sIHJlY29yZEludGVydmFsKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBoZWFsdGhgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGhlYWx0aFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGhlYWx0aFJvdXRlcyhhcHApIHtcclxuICAvLyBTdGFydCBwcm9jZXNzaW5nIHN1Y2Nlc3MgcmF0ZSByYXRpbyBpbnRlcnZhbCBhbmQgc2F2ZSBpdHMgaWQgdG8gdGhlIGFycmF5XHJcbiAgLy8gZm9yIHRoZSBncmFjZWZ1bCBjbGVhcmluZyBvbiBzaHV0ZG93biB3aXRoIGluamVjdGVkIGBhZGRUaW1lcmAgZnVudGlvblxyXG4gIGFkZFRpbWVyKF9zdGFydFN1Y2Nlc3NSYXRlKCkpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy9oZWFsdGgnIC0gQSByb3V0ZSBmb3IgZ2V0dGluZyB0aGUgYmFzaWMgc3RhdHMgb2YgdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KCcvaGVhbHRoJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1toZWFsdGhdIFJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgICAgY29uc3QgbW92aW5nQXZlcmFnZSA9IF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgICAvLyBTZW5kIHRoZSBzZXJ2ZXIncyBzdGF0aXN0aWNzXHJcbiAgICAgIHJlc3BvbnNlLnNlbmQoe1xyXG4gICAgICAgIC8vIFN0YXR1cyBhbmQgdGltZXNcclxuICAgICAgICBzdGF0dXM6ICdPSycsXHJcbiAgICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcclxuICAgICAgICB1cHRpbWU6IGAke01hdGguZmxvb3IoKGdldE5ld0RhdGVUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MCl9IG1pbnV0ZXNgLFxyXG5cclxuICAgICAgICAvLyBWZXJzaW9uc1xyXG4gICAgICAgIHNlcnZlclZlcnNpb246IHBhY2thZ2VGaWxlLnZlcnNpb24sXHJcbiAgICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IGdldEhpZ2hjaGFydHNWZXJzaW9uKCksXHJcblxyXG4gICAgICAgIC8vIEV4cG9ydHNcclxuICAgICAgICBhdmVyYWdlRXhwb3J0VGltZTogc3RhdHMudGltZVNwZW50QXZlcmFnZSxcclxuICAgICAgICBhdHRlbXB0ZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkLFxyXG4gICAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQsXHJcbiAgICAgICAgZmFpbGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0Ryb3BwZWQsXHJcbiAgICAgICAgc3VjZXNzUmF0aW86IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDAsXHJcblxyXG4gICAgICAgIC8vIFBvb2xcclxuICAgICAgICBwb29sOiBnZXRQb29sSW5mb0pTT04oKSxcclxuXHJcbiAgICAgICAgLy8gTW92aW5nIGF2ZXJhZ2VcclxuICAgICAgICBwZXJpb2QsXHJcbiAgICAgICAgbW92aW5nQXZlcmFnZSxcclxuICAgICAgICBtZXNzYWdlOlxyXG4gICAgICAgICAgaXNOYU4obW92aW5nQXZlcmFnZSkgfHwgIXN1Y2Nlc3NSYXRlcy5sZW5ndGhcclxuICAgICAgICAgICAgPyAnVG9vIGVhcmx5IHRvIHJlcG9ydC4gTm8gZXhwb3J0cyBtYWRlIHlldC4gUGxlYXNlIGNoZWNrIGJhY2sgc29vbi4nXHJcbiAgICAgICAgICAgIDogYExhc3QgJHtwZXJpb2R9IG1pbnV0ZXMgaGFkIGEgc3VjY2VzcyByYXRlIG9mICR7bW92aW5nQXZlcmFnZS50b0ZpeGVkKDIpfSUuYCxcclxuXHJcbiAgICAgICAgLy8gU1ZHIGFuZCBKU09OIGV4cG9ydHNcclxuICAgICAgICBzdmdFeHBvcnRzOiBzdGF0cy5leHBvcnRzRnJvbVN2ZyxcclxuICAgICAgICBqc29uRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zLFxyXG4gICAgICAgIHN2Z0V4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyxcclxuICAgICAgICBqc29uRXhwb3J0c0F0dGVtcHRzOiBzdGF0cy5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0c1xyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciBzZXJ2aW5nIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXJcclxuICogd2hlbiBlbmFibGVkLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHVpYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1aVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHVpUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIEdFVCAnLycgLSBBIHJvdXRlIGZvciBhIFVJIHdoZW4gZW5hYmxlZCBvbiB0aGUgZXhwb3J0IHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KGdldE9wdGlvbnMoKS51aS5yb3V0ZSB8fCAnLycsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdWldIFJldHVybmluZyBVSSBmb3IgdGhlIGV4cG9ydC4nKTtcclxuXHJcbiAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwge1xyXG4gICAgICAgIGFjY2VwdFJhbmdlczogZmFsc2VcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3IgdXBkYXRpbmcgdGhlIEhpZ2hjaGFydHMgdmVyc2lvblxyXG4gKiBvbiB0aGUgc2VydmVyLCB3aXRoIGF1dGhlbnRpY2F0aW9uIGFuZCB2YWxpZGF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGdldEhpZ2hjaGFydHNWZXJzaW9uLCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHZlcnNpb25fY2hhbmdlYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nIC0gQSByb3V0ZSBmb3IgY2hhbmdpbmdcclxuICAgKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicsIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdmVyc2lvbl0gQ2hhbmdpbmcgSGlnaGNoYXJ0cyB2ZXJzaW9uLicpO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBkaXJlY3RseSBmcm9tIGVudnNcclxuICAgICAgY29uc3QgYWRtaW5Ub2tlbiA9IGVudnMuSElHSENIQVJUU19BRE1JTl9UT0tFTjtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBleGlzdGVuY2Ugb2YgdGhlIHRva2VuXHJcbiAgICAgIGlmICghYWRtaW5Ub2tlbiB8fCAhYWRtaW5Ub2tlbi5sZW5ndGgpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIFRoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBmcm9tIHRoZSBoYy1hdXRoIGhlYWRlclxyXG4gICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbdmVyc2lvbl0gSW52YWxpZCBvciBtaXNzaW5nIHRva2VuOiBTZXQgdGhlIHRva2VuIGluIHRoZSBoYy1hdXRoIGhlYWRlci4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xyXG4gICAgICBsZXQgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XHJcbiAgICAgIGlmIChuZXdWZXJzaW9uKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFVwZGF0ZSB2ZXJzaW9uXHJcbiAgICAgICAgICBhd2FpdCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICBgW3ZlcnNpb25dIFZlcnNpb24gY2hhbmdlOiAke2Vycm9yLm1lc3NhZ2V9YCxcclxuICAgICAgICAgICAgNDAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFN1Y2Nlc3NcclxuICAgICAgICByZXNwb25zZS5zdGF0dXMoMjAwKS5zZW5kKHtcclxuICAgICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcclxuICAgICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG4gICAgICAgICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIEhpZ2hjaGFydHMgdG8gdmVyc2lvbjogJHtuZXdWZXJzaW9ufS5gXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gTm8gdmVyc2lvbiBzcGVjaWZpZWRcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1t2ZXJzaW9uXSBObyBuZXcgdmVyc2lvbiBzdXBwbGllZC4nLCA0MDApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEEgbW9kdWxlIHRoYXQgc2V0cyB1cCBhbmQgbWFuYWdlcyBIVFRQIGFuZCBIVFRQUyBzZXJ2ZXJzXHJcbiAqIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBoYW5kbGVzIHNlcnZlciBpbml0aWFsaXphdGlvbixcclxuICogY29uZmlndXJhdGlvbiwgZXJyb3IgaGFuZGxpbmcsIG1pZGRsZXdhcmUgc2V0dXAsIHJvdXRlIGRlZmluaXRpb24sIGFuZCByYXRlXHJcbiAqIGxpbWl0aW5nLiBUaGUgbW9kdWxlIGV4cG9ydHMgZnVuY3Rpb25zIHRvIHN0YXJ0LCBzdG9wLCBhbmQgbWFuYWdlIHNlcnZlclxyXG4gKiBpbnN0YW5jZXMsIGFzIHdlbGwgYXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIGRlZmluaW5nIHJvdXRlcyBhbmQgYXR0YWNoaW5nXHJcbiAqIG1pZGRsZXdhcmVzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlIH0gZnJvbSAnZnMvcHJvbWlzZXMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgY29ycyBmcm9tICdjb3JzJztcclxuaW1wb3J0IGV4cHJlc3MgZnJvbSAnZXhwcmVzcyc7XHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5pbXBvcnQgbXVsdGVyIGZyb20gJ211bHRlcic7XHJcblxyXG5pbXBvcnQgeyB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGdldEFic29sdXRlUGF0aCB9IGZyb20gJy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBlcnJvck1pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy9lcnJvci5qcyc7XHJcbmltcG9ydCByYXRlTGltaXRpbmdNaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzJztcclxuaW1wb3J0IHZhbGlkYXRpb25NaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvdmFsaWRhdGlvbi5qcyc7XHJcblxyXG5pbXBvcnQgZXhwb3J0Um91dGVzIGZyb20gJy4vcm91dGVzL2V4cG9ydC5qcyc7XHJcbmltcG9ydCBoZWFsdGhSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvaGVhbHRoLmpzJztcclxuaW1wb3J0IHVpUm91dGVzIGZyb20gJy4vcm91dGVzL3VpLmpzJztcclxuaW1wb3J0IHZlcnNpb25DaGFuZ2VSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvdmVyc2lvbkNoYW5nZS5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIEFycmF5IG9mIGFuIGFjdGl2ZSBzZXJ2ZXJzXHJcbmNvbnN0IGFjdGl2ZVNlcnZlcnMgPSBuZXcgTWFwKCk7XHJcblxyXG4vLyBDcmVhdGUgZXhwcmVzcyBhcHBcclxuY29uc3QgYXBwID0gZXhwcmVzcygpO1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhbiBIVFRQIGFuZC9vciBIVFRQUyBzZXJ2ZXIgYmFzZWQgb24gdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24uXHJcbiAqIFRoZSBgc2VydmVyT3B0aW9uc2Agb2JqZWN0IGNvbnRhaW5zIHNlcnZlci1yZWxhdGVkIHByb3BlcnRpZXMgKHJlZmVyXHJcbiAqIHRvIHRoZSBgc2VydmVyYCBzZWN0aW9uIGluIHRoZSBgLi9saWIvc2NoZW1hcy9jb25maWcuanNgIGZpbGUgZm9yIGRldGFpbHMpLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHN0YXJ0U2VydmVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHNlcnZlcmBcclxuICogb3B0aW9ucy4gVGhpcyBvYmplY3QgbWF5IGluY2x1ZGUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiB0aGUgYHNlcnZlcmBcclxuICogb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgZGVmYXVsdFxyXG4gKiB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBlaXRoZXJcclxuICogbm90IGVuYWJsZWQgb3Igbm8gdmFsaWQgRXhwcmVzcyBhcHAgaXMgZm91bmQsIHNpZ25hbGluZyB0aGUgZW5kIG9mIHRoZVxyXG4gKiBmdW5jdGlvbidzIGV4ZWN1dGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBzZXJ2ZXIgY2Fubm90XHJcbiAqIGJlIGNvbmZpZ3VyZWQgYW5kIHN0YXJ0ZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc3RhcnRTZXJ2ZXIoc2VydmVyT3B0aW9ucykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIHNlcnZlcjogc2VydmVyT3B0aW9uc1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gVXNlIHZhbGlkYXRlZCBvcHRpb25zXHJcbiAgICBzZXJ2ZXJPcHRpb25zID0gb3B0aW9ucy5zZXJ2ZXI7XHJcblxyXG4gICAgLy8gU3RvcCBpZiBub3QgZW5hYmxlZFxyXG4gICAgaWYgKCFzZXJ2ZXJPcHRpb25zLmVuYWJsZSB8fCAhYXBwKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3NlcnZlcl0gU2VydmVyIGNhbm5vdCBiZSBzdGFydGVkIChub3QgZW5hYmxlZCBvciBubyBjb3JyZWN0IEV4cHJlc3MgYXBwIGZvdW5kKS4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRvbyBiaWcgbGltaXRzIGxlYWQgdG8gdGltZW91dHMgaW4gdGhlIGV4cG9ydCBwcm9jZXNzIHdoZW5cclxuICAgIC8vIHRoZSByYXN0ZXJpemF0aW9uIHRpbWVvdXQgaXMgc2V0IHRvbyBsb3dcclxuICAgIGNvbnN0IHVwbG9hZExpbWl0Qnl0ZXMgPSBzZXJ2ZXJPcHRpb25zLnVwbG9hZExpbWl0ICogMTAyNCAqIDEwMjQ7XHJcblxyXG4gICAgLy8gTWVtb3J5IHN0b3JhZ2UgZm9yIG11bHRlciBwYWNrYWdlXHJcbiAgICBjb25zdCBzdG9yYWdlID0gbXVsdGVyLm1lbW9yeVN0b3JhZ2UoKTtcclxuXHJcbiAgICAvLyBFbmFibGUgcGFyc2luZyBvZiBmb3JtIGRhdGEgKGZpbGVzKSB3aXRoIG11bHRlciBwYWNrYWdlXHJcbiAgICBjb25zdCB1cGxvYWQgPSBtdWx0ZXIoe1xyXG4gICAgICBzdG9yYWdlLFxyXG4gICAgICBsaW1pdHM6IHtcclxuICAgICAgICBmaWVsZFNpemU6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gRGlzYWJsZSB0aGUgWC1Qb3dlcmVkLUJ5IGhlYWRlclxyXG4gICAgYXBwLmRpc2FibGUoJ3gtcG93ZXJlZC1ieScpO1xyXG5cclxuICAgIC8vIEVuYWJsZSBDT1JTIHN1cHBvcnRcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGNvcnMoe1xyXG4gICAgICAgIG1ldGhvZHM6IFsnUE9TVCcsICdHRVQnLCAnT1BUSU9OUyddXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldHRpbmcgYSBsb3Qgb2YgYFJhbmdlTm90U2F0aXNmaWFibGVFcnJvcmAgZXhjZXB0aW9ucyAoZXZlbiB0aG91Z2ggdGhpc1xyXG4gICAgLy8gaXMgYSBkZXByZWNhdGVkIG9wdGlvbnMsIGxldCdzIHRyeSB0byBzZXQgaXQgdG8gZmFsc2UpXHJcbiAgICBhcHAudXNlKChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgICByZXNwb25zZS5zZXQoJ0FjY2VwdC1SYW5nZXMnLCAnbm9uZScpO1xyXG4gICAgICBuZXh0KCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBFbmFibGUgYm9keSBwYXJzZXIgZm9yIEpTT04gZGF0YVxyXG4gICAgYXBwLnVzZShcclxuICAgICAgZXhwcmVzcy5qc29uKHtcclxuICAgICAgICBsaW1pdDogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBFbmFibGUgYm9keSBwYXJzZXIgZm9yIFVSTC1lbmNvZGVkIGZvcm0gZGF0YVxyXG4gICAgYXBwLnVzZShcclxuICAgICAgZXhwcmVzcy51cmxlbmNvZGVkKHtcclxuICAgICAgICBleHRlbmRlZDogdHJ1ZSxcclxuICAgICAgICBsaW1pdDogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBVc2Ugb25seSBub24tZmlsZSBtdWx0aXBhcnQgZm9ybSBmaWVsZHNcclxuICAgIGFwcC51c2UodXBsb2FkLm5vbmUoKSk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHN0YXRpYyBmb2xkZXIncyByb3V0ZVxyXG4gICAgYXBwLnVzZShleHByZXNzLnN0YXRpYyhqb2luKF9fZGlybmFtZSwgJ3B1YmxpYycpKSk7XHJcblxyXG4gICAgLy8gTGlzdGVuIEhUVFAgc2VydmVyXHJcbiAgICBpZiAoIXNlcnZlck9wdGlvbnMuc3NsLmZvcmNlKSB7XHJcbiAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQKVxyXG4gICAgICBjb25zdCBodHRwU2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoYXBwKTtcclxuXHJcbiAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAvLyBMaXN0ZW5cclxuICAgICAgaHR0cFNlcnZlci5saXN0ZW4oc2VydmVyT3B0aW9ucy5wb3J0LCBzZXJ2ZXJPcHRpb25zLmhvc3QsICgpID0+IHtcclxuICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUCBzZXJ2ZXJcclxuICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJPcHRpb25zLnBvcnQsIGh0dHBTZXJ2ZXIpO1xyXG5cclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFN0YXJ0ZWQgSFRUUCBzZXJ2ZXIgb24gJHtzZXJ2ZXJPcHRpb25zLmhvc3R9OiR7c2VydmVyT3B0aW9ucy5wb3J0fS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTGlzdGVuIEhUVFBTIHNlcnZlclxyXG4gICAgaWYgKHNlcnZlck9wdGlvbnMuc3NsLmVuYWJsZSkge1xyXG4gICAgICAvLyBTZXQgdXAgYW4gU1NMIHNlcnZlciBhbHNvXHJcbiAgICAgIGxldCBrZXksIGNlcnQ7XHJcblxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIEdldCB0aGUgU1NMIGtleVxyXG4gICAgICAgIGtleSA9IGF3YWl0IHJlYWRGaWxlKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmtleScpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wgY2VydGlmaWNhdGVcclxuICAgICAgICBjZXJ0ID0gYXdhaXQgcmVhZEZpbGUoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIuY3J0JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gVW5hYmxlIHRvIGxvYWQga2V5L2NlcnRpZmljYXRlIGZyb20gdGhlICcke3NlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRofScgcGF0aC4gQ291bGQgbm90IHJ1biBzZWN1cmVkIGxheWVyIHNlcnZlci5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKGtleSAmJiBjZXJ0KSB7XHJcbiAgICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFBTKVxyXG4gICAgICAgIGNvbnN0IGh0dHBzU2VydmVyID0gaHR0cHMuY3JlYXRlU2VydmVyKHsga2V5LCBjZXJ0IH0sIGFwcCk7XHJcblxyXG4gICAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgIC8vIExpc3RlblxyXG4gICAgICAgIGh0dHBzU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBzZXJ2ZXJPcHRpb25zLmhvc3QsICgpID0+IHtcclxuICAgICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQUyBzZXJ2ZXJcclxuICAgICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMuc3NsLnBvcnQsIGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0fS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBzZXJ2ZXJPcHRpb25zLnJhdGVMaW1pdGluZyk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSB2YWxpZGF0aW9uIGhhbmRsZXJcclxuICAgIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHJvdXRlc1xyXG4gICAgZXhwb3J0Um91dGVzKGFwcCk7XHJcbiAgICBoZWFsdGhSb3V0ZXMoYXBwKTtcclxuICAgIHVpUm91dGVzKGFwcCk7XHJcbiAgICB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXHJcbiAgICBlcnJvck1pZGRsZXdhcmUoYXBwKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbG9zZVNlcnZlcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbG9zZVNlcnZlcnMoKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgYXJlIHNlcnZlcnMgd29ya2luZ1xyXG4gIGlmIChhY3RpdmVTZXJ2ZXJzLnNpemUgPiAwKSB7XHJcbiAgICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NpbmcgYWxsIHNlcnZlcnMuYCk7XHJcblxyXG4gICAgLy8gQ2xvc2UgZWFjaCBvbmUgb2Ygc2VydmVyc1xyXG4gICAgZm9yIChjb25zdCBbcG9ydCwgc2VydmVyXSBvZiBhY3RpdmVTZXJ2ZXJzKSB7XHJcbiAgICAgIHNlcnZlci5jbG9zZSgoKSA9PiB7XHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5kZWxldGUocG9ydCk7XHJcbiAgICAgICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zZWQgc2VydmVyIG9uIHBvcnQ6ICR7cG9ydH0uYCk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRTZXJ2ZXJzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtBcnJheS48T2JqZWN0Pn0gU2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VydmVycygpIHtcclxuICByZXR1cm4gYWN0aXZlU2VydmVycztcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEV4cHJlc3NcclxuICpcclxuICogQHJldHVybnMge0V4cHJlc3N9IFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEV4cHJlc3MoKSB7XHJcbiAgcmV0dXJuIGV4cHJlc3M7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QXBwXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXBwKCkge1xyXG4gIHJldHVybiBhcHA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXRcclxuICogb2YgdGhlIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlc1xyXG4gKiB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlUmF0ZUxpbWl0aW5nKHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgc2VydmVyOiB7XHJcbiAgICAgIHJhdGVMaW1pdGluZzogcmF0ZUxpbWl0aW5nT3B0aW9uc1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9uc1xyXG4gIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBvcHRpb25zLnNlcnZlci5yYXRlTGltaXRpbmdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFwcGx5IG1pZGRsZXdhcmUocykgdG8gYSBzcGVjaWZpYyBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXNlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggR0VUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIFBPU1QgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5wb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEF0dGFjaCBlcnJvciBoYW5kbGVycyB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnNcclxuICpcclxuICogQHBhcmFtIHsoaHR0cC5TZXJ2ZXJ8aHR0cHMuU2VydmVyKX0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoc2VydmVyKSB7XHJcbiAgc2VydmVyLm9uKCdjbGllbnRFcnJvcicsIChlcnJvciwgc29ja2V0KSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDEsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9LCBkZXN0cm95aW5nIHNvY2tldC5gXHJcbiAgICApO1xyXG4gICAgc29ja2V0LmRlc3Ryb3koKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignY29ubmVjdGlvbicsIChzb2NrZXQpID0+IHtcclxuICAgIHNvY2tldC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGNsb3NlU2VydmVycyxcclxuICBnZXRTZXJ2ZXJzLFxyXG4gIGdldEV4cHJlc3MsXHJcbiAgZ2V0QXBwLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhhbmRsZXMgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciwgZW5zdXJpbmdcclxuICogcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzIHN1Y2ggYXMgYnJvd3NlciwgcGFnZXMsIHNlcnZlcnMsIGFuZCB0aW1lcnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBjbGVhckFsbFRpbWVycyB9IGZyb20gJy4vdGltZXIuanMnO1xyXG5cclxuaW1wb3J0IHsgY2xvc2VTZXJ2ZXJzIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBjbGVhbnVwIG9wZXJhdGlvbnMgdG8gZW5zdXJlIGEgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIHByb2Nlc3MuXHJcbiAqIFRoaXMgaW5jbHVkZXMgY2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgdGltZW91dHMvaW50ZXJ2YWxzLCBjbG9zaW5nIGFjdGl2ZVxyXG4gKiBzZXJ2ZXJzLCB0ZXJtaW5hdGluZyByZXNvdXJjZXMgKHBhZ2VzKSBvZiB0aGUgcG9vbCwgcG9vbCBpdHNlbGYsIGFuZCBjbG9zaW5nXHJcbiAqIHRoZSBicm93c2VyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbZXhpdENvZGU9MF0gLSBUaGUgZXhpdCBjb2RlIHRvIHVzZSB3aXRoIGBwcm9jZXNzLmV4aXQoKWAuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGAwYC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXAoZXhpdENvZGUgPSAwKSB7XHJcbiAgLy8gQXdhaXQgZnJlZWluZyBhbGwgcmVzb3VyY2VzXHJcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcclxuICAgIC8vIENsZWFyIGFsbCBvbmdvaW5nIGludGVydmFsc1xyXG4gICAgY2xlYXJBbGxUaW1lcnMoKSxcclxuXHJcbiAgICAvLyBHZXQgYXZhaWxhYmxlIHNlcnZlciBpbnN0YW5jZXMgKEhUVFAvSFRUUFMpIGFuZCBjbG9zZSB0aGVtXHJcbiAgICBjbG9zZVNlcnZlcnMoKSxcclxuXHJcbiAgICAvLyBDbG9zZSBhbiBhY3RpdmUgcG9vbCBhbG9uZyB3aXRoIGl0cyB3b3JrZXJzIGFuZCB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gICAga2lsbFBvb2woKVxyXG4gIF0pO1xyXG5cclxuICAvLyBFeGl0IHByb2Nlc3Mgd2l0aCBhIGNvcnJlY3QgY29kZVxyXG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaHV0ZG93bkNsZWFuVXBcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvcmUgbW9kdWxlIGZvciBpbml0aWFsaXppbmcgYW5kIG1hbmFnaW5nIHRoZSBIaWdoY2hhcnRzIEV4cG9ydFxyXG4gKiBTZXJ2ZXIuIFByb3ZpZGVzIGZ1bmN0aW9uYWxpdGllcyBmb3IgY29uZmlndXJpbmcgZXhwb3J0cywgc2V0dGluZyB1cCBzZXJ2ZXJcclxuICogb3BlcmF0aW9ucywgbG9nZ2luZywgc2NyaXB0cyBjYWNoaW5nLCByZXNvdXJjZSBwb29saW5nLCBhbmQgZ3JhY2VmdWwgcHJvY2Vzc1xyXG4gKiBjbGVhbnVwLlxyXG4gKi9cclxuXHJcbmltcG9ydCAnY29sb3JzJztcclxuXHJcbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMsIG1hcFRvTmV3T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHtcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIGluaXRMb2dnaW5nLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG4gIHNldExvZ0xldmVsXHJcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpbml0UG9vbCwga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzaHV0ZG93bkNsZWFuVXAgfSBmcm9tICcuL3Jlc291cmNlUmVsZWFzZS5qcyc7XHJcblxyXG5pbXBvcnQgc2VydmVyIGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwcm9jZXNzLiBUYXNrcyBzdWNoIGFzIGNvbmZpZ3VyaW5nIGxvZ2dpbmcsIGNoZWNraW5nXHJcbiAqIHRoZSBjYWNoZSBhbmQgc291cmNlcywgYW5kIGluaXRpYWxpemluZyB0aGUgcmVzb3VyY2UgcG9vbCBvY2N1ciBkdXJpbmcgdGhpc1xyXG4gKiBzdGFnZS5cclxuICpcclxuICogVGhpcyBmdW5jdGlvbiBtdXN0IGJlIGNhbGxlZCBiZWZvcmUgYXR0ZW1wdGluZyB0byBleHBvcnQgY2hhcnRzIG9yIHNldFxyXG4gKiB1cCBhIHNlcnZlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpbml0T3B0aW9ucyAtIFRoZSBgaW5pdE9wdGlvbnNgIG9iamVjdCwgd2hpY2ggbWF5XHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmdcclxuICogdmFsdWVzIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0RXhwb3J0KGluaXRPcHRpb25zKSB7XHJcbiAgLy8gSW5pdCBhbmQgdXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKGluaXRPcHRpb25zKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBwZXIgZXhwb3J0IG1vZHVsZSBzY29wZVxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gIC8vIEluaXQgdGhlIGxvZ2dpbmdcclxuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAob3B0aW9ucy5vdGhlci5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXHJcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMsIG9wdGlvbnMuc2VydmVyLnByb3h5KTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgcG9vbFxyXG4gIGF3YWl0IGluaXRQb29sKG9wdGlvbnMucG9vbCwgb3B0aW9ucy5wdXBwZXRlZXIuYXJncyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXHJcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nXHJcbiAqIGFuZCAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVyc1xyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCkge1xyXG4gIGxvZygzLCAnW3Byb2Nlc3NdIEF0dGFjaGluZyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdleGl0J1xyXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gUHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0uYCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSU5UJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdURVJNJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICd1bmNhdWdodEV4Y2VwdGlvbidcclxuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGVycm9yLmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDEpO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgLy8gU2VydmVyXHJcbiAgLi4uc2VydmVyLFxyXG5cclxuICAvLyBPcHRpb25zXHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuXHJcbiAgLy8gRXhwb3J0aW5nXHJcbiAgaW5pdEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcblxyXG4gIC8vIFJlbGVhc2VcclxuICBraWxsUG9vbCxcclxuICBzaHV0ZG93bkNsZWFuVXAsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsOiBmdW5jdGlvbiAobGV2ZWwpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGxldmVsXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICBzZXRMb2dMZXZlbChvcHRpb25zLmxvZ2dpbmcubGV2ZWwpO1xyXG4gIH0sXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmc6IGZ1bmN0aW9uICh0b0NvbnNvbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIHRvQ29uc29sZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlQ29uc29sZUxvZ2dpbmcob3B0aW9ucy5sb2dnaW5nLnRvQ29uc29sZSk7XHJcbiAgfSxcclxuICBlbmFibGVGaWxlTG9nZ2luZzogZnVuY3Rpb24gKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBsb2dnaW5nOiB7XHJcbiAgICAgICAgZGVzdCxcclxuICAgICAgICBmaWxlLFxyXG4gICAgICAgIHRvRmlsZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy5kZXN0LFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZmlsZSxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLnRvRmlsZVxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcbiJdLCJuYW1lcyI6WyJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInRhZ05hbWUiLCJ0b1VwcGVyQ2FzZSIsInNyYyIsImJhc2VVUkkiLCJkZWVwQ29weSIsIm9iakFyciIsIm9iakFyckNvcHkiLCJBcnJheSIsImlzQXJyYXkiLCJrZXkiLCJPYmplY3QiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJmaXhDb25zdHIiLCJjb25zdHIiLCJmaXhlZENvbnN0ciIsInRvTG93ZXJDYXNlIiwicmVwbGFjZSIsImluY2x1ZGVzIiwiZml4T3V0ZmlsZSIsInR5cGUiLCJvdXRmaWxlIiwiZ2V0QWJzb2x1dGVQYXRoIiwic3BsaXQiLCJzaGlmdCIsImZpeFR5cGUiLCJtaW1lVHlwZXMiLCJmb3JtYXRzIiwidmFsdWVzIiwib3V0VHlwZSIsInBvcCIsImZpbmQiLCJ0IiwicGF0aCIsImlzQWJzb2x1dGUiLCJqb2luIiwiZ2V0QmFzZTY0IiwiaW5wdXQiLCJCdWZmZXIiLCJmcm9tIiwidG9TdHJpbmciLCJnZXROZXdEYXRlIiwiRGF0ZSIsInRyaW0iLCJnZXROZXdEYXRlVGltZSIsImdldFRpbWUiLCJpc09iamVjdCIsIml0ZW0iLCJpc09iamVjdEVtcHR5Iiwia2V5cyIsImxlbmd0aCIsImlzUHJpdmF0ZVJhbmdlVXJsRm91bmQiLCJzb21lIiwicGF0dGVybiIsInRlc3QiLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwicHJvY2VzcyIsImhydGltZSIsImJpZ2ludCIsIk51bWJlciIsInJvdW5kTnVtYmVyIiwidmFsdWUiLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwiTWF0aCIsInBvdyIsInJvdW5kIiwid3JhcEFyb3VuZCIsImN1c3RvbUNvZGUiLCJhbGxvd0ZpbGVSZXNvdXJjZXMiLCJpc0NhbGxiYWNrIiwiZW5kc1dpdGgiLCJyZWFkRmlsZVN5bmMiLCJzdGFydHNXaXRoIiwiY29sb3JzIiwibG9nZ2luZyIsInRvQ29uc29sZSIsInRvRmlsZSIsInBhdGhDcmVhdGVkIiwicGF0aFRvTG9nIiwibGV2ZWxzRGVzYyIsInRpdGxlIiwiY29sb3IiLCJsb2ciLCJhcmdzIiwibmV3TGV2ZWwiLCJ0ZXh0cyIsImxldmVsIiwicHJlZml4IiwiX2xvZ1RvRmlsZSIsImNvbnNvbGUiLCJhcHBseSIsInVuZGVmaW5lZCIsImNvbmNhdCIsImxvZ1dpdGhTdGFjayIsImVycm9yIiwiY3VzdG9tTWVzc2FnZSIsIm1haW5NZXNzYWdlIiwibWVzc2FnZSIsInN0YWNrTWVzc2FnZSIsInN0YWNrIiwicHVzaCIsImluaXRMb2dnaW5nIiwibG9nZ2luZ09wdGlvbnMiLCJkZXN0IiwiZmlsZSIsInNldExvZ0xldmVsIiwiZW5hYmxlQ29uc29sZUxvZ2dpbmciLCJlbmFibGVGaWxlTG9nZ2luZyIsImlzSW50ZWdlciIsImV4aXN0c1N5bmMiLCJta2RpclN5bmMiLCJhcHBlbmRGaWxlIiwiZGVmYXVsdENvbmZpZyIsInB1cHBldGVlciIsInR5cGVzIiwiZW52TGluayIsImNsaU5hbWUiLCJkZXNjcmlwdGlvbiIsInByb21wdE9wdGlvbnMiLCJzZXBhcmF0b3IiLCJoaWdoY2hhcnRzIiwidmVyc2lvbiIsImNkblVybCIsImZvcmNlRmV0Y2giLCJjYWNoZVBhdGgiLCJjb3JlU2NyaXB0cyIsImluc3RydWN0aW9ucyIsIm1vZHVsZVNjcmlwdHMiLCJpbmRpY2F0b3JTY3JpcHRzIiwiY3VzdG9tU2NyaXB0cyIsImV4cG9ydCIsImluZmlsZSIsImluc3RyIiwib3B0aW9ucyIsInN2ZyIsImJhdGNoIiwiaGludCIsImNob2ljZXMiLCJiNjQiLCJub0Rvd25sb2FkIiwiaGVpZ2h0Iiwid2lkdGgiLCJzY2FsZSIsImRlZmF1bHRIZWlnaHQiLCJkZWZhdWx0V2lkdGgiLCJkZWZhdWx0U2NhbGUiLCJtaW4iLCJtYXgiLCJnbG9iYWxPcHRpb25zIiwidGhlbWVPcHRpb25zIiwicmFzdGVyaXphdGlvblRpbWVvdXQiLCJjdXN0b21Mb2dpYyIsImFsbG93Q29kZUV4ZWN1dGlvbiIsImNhbGxiYWNrIiwicmVzb3VyY2VzIiwibG9hZENvbmZpZyIsImxlZ2FjeU5hbWUiLCJjcmVhdGVDb25maWciLCJzZXJ2ZXIiLCJlbmFibGUiLCJob3N0IiwicG9ydCIsInVwbG9hZExpbWl0IiwiYmVuY2htYXJraW5nIiwicHJveHkiLCJ0aW1lb3V0IiwicmF0ZUxpbWl0aW5nIiwibWF4UmVxdWVzdHMiLCJ3aW5kb3ciLCJkZWxheSIsInRydXN0UHJveHkiLCJza2lwS2V5Iiwic2tpcFRva2VuIiwic3NsIiwiZm9yY2UiLCJjZXJ0UGF0aCIsInBvb2wiLCJtaW5Xb3JrZXJzIiwibWF4V29ya2VycyIsIndvcmtMaW1pdCIsImFjcXVpcmVUaW1lb3V0IiwiY3JlYXRlVGltZW91dCIsImRlc3Ryb3lUaW1lb3V0IiwiaWRsZVRpbWVvdXQiLCJjcmVhdGVSZXRyeUludGVydmFsIiwicmVhcGVySW50ZXJ2YWwiLCJ1aSIsInJvdXRlIiwib3RoZXIiLCJub2RlRW52IiwibGlzdGVuVG9Qcm9jZXNzRXhpdHMiLCJub0xvZ28iLCJoYXJkUmVzZXRQYWdlIiwiYnJvd3NlclNoZWxsTW9kZSIsImRlYnVnIiwiaGVhZGxlc3MiLCJkZXZ0b29scyIsImxpc3RlblRvQ29uc29sZSIsImR1bXBpbyIsInNsb3dNbyIsImRlYnVnZ2luZ1BvcnQiLCJuZXN0ZWRQcm9wcyIsIl9jcmVhdGVOZXN0ZWRQcm9wcyIsImFic29sdXRlUHJvcHMiLCJfY3JlYXRlQWJzb2x1dGVQcm9wcyIsImNvbmZpZyIsInByb3BDaGFpbiIsImZvckVhY2giLCJlbnRyeSIsInN1YnN0cmluZyIsImRvdGVudiIsInYiLCJhcnJheSIsImZpbHRlckFycmF5IiwieiIsInN0cmluZyIsInRyYW5zZm9ybSIsIm1hcCIsImZpbHRlciIsImJvb2xlYW4iLCJlbnVtIiwicmVmaW5lIiwicG9zaXRpdmVOdW0iLCJpc05hTiIsInBhcnNlRmxvYXQiLCJub25OZWdhdGl2ZU51bSIsIkNvbmZpZyIsIm9iamVjdCIsIlBVUFBFVEVFUl9BUkdTIiwiSElHSENIQVJUU19WRVJTSU9OIiwiSElHSENIQVJUU19DRE5fVVJMIiwiSElHSENIQVJUU19GT1JDRV9GRVRDSCIsIkhJR0hDSEFSVFNfQ0FDSEVfUEFUSCIsIkhJR0hDSEFSVFNfQURNSU5fVE9LRU4iLCJISUdIQ0hBUlRTX0NPUkVfU0NSSVBUUyIsIkhJR0hDSEFSVFNfTU9EVUxFX1NDUklQVFMiLCJISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTIiwiSElHSENIQVJUU19DVVNUT01fU0NSSVBUUyIsIkVYUE9SVF9JTkZJTEUiLCJFWFBPUlRfSU5TVFIiLCJFWFBPUlRfT1BUSU9OUyIsIkVYUE9SVF9TVkciLCJFWFBPUlRfQkFUQ0giLCJFWFBPUlRfT1VURklMRSIsIkVYUE9SVF9UWVBFIiwiRVhQT1JUX0NPTlNUUiIsIkVYUE9SVF9CNjQiLCJFWFBPUlRfTk9fRE9XTkxPQUQiLCJFWFBPUlRfSEVJR0hUIiwiRVhQT1JUX1dJRFRIIiwiRVhQT1JUX1NDQUxFIiwiRVhQT1JUX0RFRkFVTFRfSEVJR0hUIiwiRVhQT1JUX0RFRkFVTFRfV0lEVEgiLCJFWFBPUlRfREVGQVVMVF9TQ0FMRSIsIkVYUE9SVF9HTE9CQUxfT1BUSU9OUyIsIkVYUE9SVF9USEVNRV9PUFRJT05TIiwiRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCIsIkNVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTiIsIkNVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUyIsIkNVU1RPTV9MT0dJQ19DVVNUT01fQ09ERSIsIkNVU1RPTV9MT0dJQ19DQUxMQkFDSyIsIkNVU1RPTV9MT0dJQ19SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfTE9BRF9DT05GSUciLCJDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRyIsIlNFUlZFUl9FTkFCTEUiLCJTRVJWRVJfSE9TVCIsIlNFUlZFUl9QT1JUIiwiU0VSVkVSX1VQTE9BRF9MSU1JVCIsIlNFUlZFUl9CRU5DSE1BUktJTkciLCJTRVJWRVJfUFJPWFlfSE9TVCIsIlNFUlZFUl9QUk9YWV9QT1JUIiwiU0VSVkVSX1BST1hZX1RJTUVPVVQiLCJTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUiLCJTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFMiLCJTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1ciLCJTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOIiwiU0VSVkVSX1NTTF9FTkFCTEUiLCJTRVJWRVJfU1NMX0ZPUkNFIiwiU0VSVkVSX1NTTF9QT1JUIiwiU0VSVkVSX1NTTF9DRVJUX1BBVEgiLCJQT09MX01JTl9XT1JLRVJTIiwiUE9PTF9NQVhfV09SS0VSUyIsIlBPT0xfV09SS19MSU1JVCIsIlBPT0xfQUNRVUlSRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfVElNRU9VVCIsIlBPT0xfREVTVFJPWV9USU1FT1VUIiwiUE9PTF9JRExFX1RJTUVPVVQiLCJQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCIsIlBPT0xfUkVBUEVSX0lOVEVSVkFMIiwiUE9PTF9CRU5DSE1BUktJTkciLCJMT0dHSU5HX0xFVkVMIiwiTE9HR0lOR19GSUxFIiwiTE9HR0lOR19ERVNUIiwiTE9HR0lOR19UT19DT05TT0xFIiwiTE9HR0lOR19UT19GSUxFIiwiVUlfRU5BQkxFIiwiVUlfUk9VVEUiLCJPVEhFUl9OT0RFX0VOViIsIk9USEVSX0xJU1RFTl9UT19QUk9DRVNTX0VYSVRTIiwiT1RIRVJfTk9fTE9HTyIsIk9USEVSX0hBUkRfUkVTRVRfUEFHRSIsIk9USEVSX0JST1dTRVJfU0hFTExfTU9ERSIsIkRFQlVHX0VOQUJMRSIsIkRFQlVHX0hFQURMRVNTIiwiREVCVUdfREVWVE9PTFMiLCJERUJVR19MSVNURU5fVE9fQ09OU09MRSIsIkRFQlVHX0RVTVBJTyIsIkRFQlVHX1NMT1dfTU8iLCJERUJVR19ERUJVR0dJTkdfUE9SVCIsImVudnMiLCJwYXJ0aWFsIiwicGFyc2UiLCJlbnYiLCJfaW5pdE9wdGlvbnMiLCJnZXRPcHRpb25zIiwidXBkYXRlT3B0aW9ucyIsIm5ld09wdGlvbnMiLCJnZXRDb3B5IiwiX21lcmdlT3B0aW9ucyIsIm1hcFRvTmV3T3B0aW9ucyIsIm9sZE9wdGlvbnMiLCJlbnRyaWVzIiwicHJvcGVydGllc0NoYWluIiwicmVkdWNlIiwib2JqIiwicHJvcCIsImluZGV4IiwiaXNBbGxvd2VkQ29uZmlnIiwiYWxsb3dGdW5jdGlvbnMiLCJvYmplY3RDb25maWciLCJldmFsIiwiSlNPTiIsInN0cmluZ2lmaWVkT3B0aW9ucyIsIl9vcHRpb25zU3RyaW5naWZ5IiwicGFyc2VkT3B0aW9ucyIsIl8iLCJuYW1lIiwib3JpZ2luYWxPcHRpb25zIiwic3RyaW5naWZ5RnVuY3Rpb25zIiwic3RyaW5naWZ5IiwicmVwbGFjZUFsbCIsIkVycm9yIiwiYXN5bmMiLCJmZXRjaCIsInVybCIsInJlcXVlc3RPcHRpb25zIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJfZ2V0UHJvdG9jb2xNb2R1bGUiLCJnZXQiLCJyZXNwb25zZSIsInJlc3BvbnNlRGF0YSIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiaHR0cHMiLCJodHRwIiwiRXhwb3J0RXJyb3IiLCJjb25zdHJ1Y3RvciIsInN0YXR1c0NvZGUiLCJzdXBlciIsInRoaXMiLCJzZXRTdGF0dXMiLCJzZXRFcnJvciIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsImkiLCJzZXR1cEhpZ2hjaGFydHMiLCJIaWdoY2hhcnRzIiwiYW5pbU9iamVjdCIsImR1cmF0aW9uIiwiY3JlYXRlQ2hhcnQiLCJleHBvcnRPcHRpb25zIiwiY3VzdG9tTG9naWNPcHRpb25zIiwic2V0T3B0aW9ucyIsIm1lcmdlIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJpc1JlbmRlckNvbXBsZXRlIiwiQ2hhcnQiLCJwcm9jZWVkIiwidXNlck9wdGlvbnMiLCJjYiIsImV4cG9ydGluZyIsImVuYWJsZWQiLCJwbG90T3B0aW9ucyIsInNlcmllcyIsImxhYmVsIiwidG9vbHRpcCIsImFuaW1hdGlvbiIsIm9uSGlnaGNoYXJ0c1JlbmRlciIsImFkZEV2ZW50IiwiU2VyaWVzIiwiY2hhcnQiLCJhZGRpdGlvbmFsT3B0aW9ucyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImRlZmF1bHRPcHRpb25zIiwidGVtcGxhdGUiLCJicm93c2VyIiwiY3JlYXRlQnJvd3NlciIsInB1cHBldGVlckFyZ3MiLCJlbmFibGVkRGVidWciLCJkZWJ1Z09wdGlvbnMiLCJsYXVuY2hPcHRpb25zIiwidXNlckRhdGFEaXIiLCJoYW5kbGVTSUdJTlQiLCJoYW5kbGVTSUdURVJNIiwiaGFuZGxlU0lHSFVQIiwid2FpdEZvckluaXRpYWxQYWdlIiwiZGVmYXVsdFZpZXdwb3J0IiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwic2V0VGltZW91dCIsImNsb3NlQnJvd3NlciIsImNvbm5lY3RlZCIsImNsb3NlIiwibmV3UGFnZSIsInBvb2xSZXNvdXJjZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJfc2V0UGFnZUNvbnRlbnQiLCJfc2V0UGFnZUV2ZW50cyIsImlzQ2xvc2VkIiwiY2xlYXJQYWdlIiwiaGFyZFJlc2V0IiwiZ290byIsIndhaXRVbnRpbCIsImV2YWx1YXRlIiwiYm9keSIsImlubmVySFRNTCIsImlkIiwid29ya0NvdW50IiwiYWRkUGFnZVJlc291cmNlcyIsImluamVjdGVkUmVzb3VyY2VzIiwiaW5qZWN0ZWRKcyIsImpzIiwiY29udGVudCIsImZpbGVzIiwiaXNMb2NhbCIsImpzUmVzb3VyY2UiLCJhZGRTY3JpcHRUYWciLCJpbmplY3RlZENzcyIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJjc3NSZXNvdXJjZSIsImFkZFN0eWxlVGFnIiwiY2xlYXJQYWdlUmVzb3VyY2VzIiwicmVzb3VyY2UiLCJkaXNwb3NlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJlbGVtZW50IiwicmVtb3ZlIiwic2V0Q29udGVudCIsImNzc1RlbXBsYXRlIiwic3ZnVGVtcGxhdGUiLCJwdXBwZXRlZXJFeHBvcnQiLCJpc1NWRyIsInNpemUiLCJzdmdFbGVtZW50IiwicXVlcnlTZWxlY3RvciIsImNoYXJ0SGVpZ2h0IiwiYmFzZVZhbCIsImNoYXJ0V2lkdGgiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ4IiwieSIsIl9nZXRDbGlwUmVnaW9uIiwidmlld3BvcnRIZWlnaHQiLCJhYnMiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsInJlc3VsdCIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJfY3JlYXRlU1ZHIiwiX2NyZWF0ZUltYWdlIiwiX2NyZWF0ZVBERiIsIiRldmFsIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidHJ1bmMiLCJvdXRlckhUTUwiLCJjbGlwIiwicmFjZSIsInNjcmVlbnNob3QiLCJlbmNvZGluZyIsImZ1bGxQYWdlIiwib3B0aW1pemVGb3JTcGVlZCIsImNhcHR1cmVCZXlvbmRWaWV3cG9ydCIsInF1YWxpdHkiLCJvbWl0QmFja2dyb3VuZCIsIl9yZXNvbHZlIiwiZW11bGF0ZU1lZGlhVHlwZSIsInBkZiIsInBvb2xTdGF0cyIsImV4cG9ydHNBdHRlbXB0ZWQiLCJleHBvcnRzUGVyZm9ybWVkIiwiZXhwb3J0c0Ryb3BwZWQiLCJleHBvcnRzRnJvbVN2ZyIsImV4cG9ydHNGcm9tT3B0aW9ucyIsImV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMiLCJleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cyIsInRpbWVTcGVudCIsInRpbWVTcGVudEF2ZXJhZ2UiLCJpbml0UG9vbCIsInBvb2xPcHRpb25zIiwiUG9vbCIsIl9mYWN0b3J5IiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsImNsZWFyU3RhdHVzIiwiX2V2ZW50SWQiLCJpbml0aWFsUmVzb3VyY2VzIiwiYWNxdWlyZSIsInByb21pc2UiLCJyZWxlYXNlIiwia2lsbFBvb2wiLCJ3b3JrZXIiLCJ1c2VkIiwiZGVzdHJveWVkIiwicG9zdFdvcmsiLCJ3b3JrZXJIYW5kbGUiLCJnZXRQb29sSW5mbyIsImFjcXVpcmVDb3VudGVyIiwicmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsImV4cG9ydFRpbWUiLCJnZXRQb29sU3RhdHMiLCJnZXRQb29sSW5mb0pTT04iLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwibnVtRnJlZSIsImFsbENyZWF0ZWQiLCJwZW5kaW5nQWNxdWlyZXMiLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwZW5kaW5nQ3JlYXRlcyIsIm51bVBlbmRpbmdDcmVhdGVzIiwicGVuZGluZ1ZhbGlkYXRpb25zIiwibnVtUGVuZGluZ1ZhbGlkYXRpb25zIiwicGVuZGluZ0Rlc3Ryb3lzIiwiYWJzb2x1dGVBbGwiLCJjcmVhdGUiLCJ1dWlkIiwicmFuZG9tIiwic3RhcnREYXRlIiwidmFsaWRhdGUiLCJtYWluRnJhbWUiLCJkZXRhY2hlZCIsInJlbW92ZUFsbExpc3RlbmVycyIsInNhbml0aXplIiwiSlNET00iLCJET01QdXJpZnkiLCJBRERfVEFHUyIsInNpbmdsZUV4cG9ydCIsInN0YXJ0RXhwb3J0IiwiZGF0YSIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiYmF0Y2hSZXN1bHRzIiwiYWxsU2V0dGxlZCIsInJlYXNvbiIsImV4cG9ydGluZ09wdGlvbnMiLCJlbmRDYWxsYmFjayIsImZpbGVDb250ZW50IiwiX2V4cG9ydEZyb21TdmciLCJfZXhwb3J0RnJvbU9wdGlvbnMiLCJnZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJzZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJpbnB1dFRvRXhwb3J0IiwiX3ByZXBhcmVFeHBvcnQiLCJfaGFuZGxlQ3VzdG9tTG9naWMiLCJfaGFuZGxlR2xvYmFsQW5kVGhlbWUiLCJfZmluZENoYXJ0U2l6ZSIsIm9wdGlvbnNDaGFydCIsIm9wdGlvbnNFeHBvcnRpbmciLCJnbG9iYWxPcHRpb25zQ2hhcnQiLCJnbG9iYWxPcHRpb25zRXhwb3J0aW5nIiwidGhlbWVPcHRpb25zQ2hhcnQiLCJ0aGVtZU9wdGlvbnNFeHBvcnRpbmciLCJzb3VyY2VIZWlnaHQiLCJzb3VyY2VXaWR0aCIsInBhcmFtIiwiX2hhbmRsZVJlc291cmNlcyIsImFsbG93ZWRQcm9wcyIsImhhbmRsZWRSZXNvdXJjZXMiLCJjb3JyZWN0UmVzb3VyY2VzIiwicHJvcE5hbWUiLCJvcHRpb25zTmFtZSIsInRpbWVySWRzIiwiYWRkVGltZXIiLCJjbGVhckFsbFRpbWVycyIsImNsZWFySW50ZXJ2YWwiLCJjbGVhclRpbWVvdXQiLCJsb2dFcnJvck1pZGRsZXdhcmUiLCJyZXF1ZXN0IiwibmV4dCIsInJldHVybkVycm9yTWlkZGxld2FyZSIsInN0YXR1cyIsImpzb24iLCJlcnJvck1pZGRsZXdhcmUiLCJhcHAiLCJ1c2UiLCJyYXRlTGltaXRpbmdNaWRkbGV3YXJlIiwicmF0ZUxpbWl0aW5nT3B0aW9ucyIsInJhdGVPcHRpb25zIiwibGltaXRlciIsInJhdGVMaW1pdCIsIndpbmRvd01zIiwibGltaXQiLCJkZWxheU1zIiwiaGFuZGxlciIsImZvcm1hdCIsInNlbmQiLCJkZWZhdWx0Iiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwiY29udGVudFR5cGVNaWRkbGV3YXJlIiwiY29udGVudFR5cGUiLCJoZWFkZXJzIiwicmVxdWVzdEJvZHlNaWRkbGV3YXJlIiwiY29ubmVjdGlvbiIsInJlbW90ZUFkZHJlc3MiLCJ2YWxpZGF0ZWRPcHRpb25zIiwicGFyYW1zIiwiZmlsZW5hbWUiLCJ2YWxpZGF0aW9uTWlkZGxld2FyZSIsInBvc3QiLCJyZXZlcnNlZE1pbWUiLCJwbmciLCJqcGVnIiwiZ2lmIiwicmVxdWVzdEV4cG9ydCIsInJlcXVlc3RDb3VudGVyIiwiY29ubmVjdGlvbkFib3J0ZWQiLCJzb2NrZXQiLCJoYWRFcnJvcnMiLCJoZWFkZXIiLCJhdHRhY2htZW50IiwiZXhwb3J0Um91dGVzIiwic2VydmVyU3RhcnRUaW1lIiwicGFja2FnZUZpbGUiLCJzdWNjZXNzUmF0ZXMiLCJyZWNvcmRJbnRlcnZhbCIsIndpbmRvd1NpemUiLCJfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSIsImEiLCJiIiwiX3N0YXJ0U3VjY2Vzc1JhdGUiLCJzZXRJbnRlcnZhbCIsInN0YXRzIiwic3VjY2Vzc1JhdGlvIiwiaGVhbHRoUm91dGVzIiwicGVyaW9kIiwibW92aW5nQXZlcmFnZSIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJzZXJ2ZXJWZXJzaW9uIiwiaGlnaGNoYXJ0c1ZlcnNpb24iLCJhdmVyYWdlRXhwb3J0VGltZSIsImF0dGVtcHRlZEV4cG9ydHMiLCJwZXJmb3JtZWRFeHBvcnRzIiwiZmFpbGVkRXhwb3J0cyIsInN1Y2Vzc1JhdGlvIiwidG9GaXhlZCIsInN2Z0V4cG9ydHMiLCJqc29uRXhwb3J0cyIsInN2Z0V4cG9ydHNBdHRlbXB0cyIsImpzb25FeHBvcnRzQXR0ZW1wdHMiLCJ1aVJvdXRlcyIsInNlbmRGaWxlIiwiYWNjZXB0UmFuZ2VzIiwidmVyc2lvbkNoYW5nZVJvdXRlcyIsImFkbWluVG9rZW4iLCJ0b2tlbiIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJPcHRpb25zIiwidXBsb2FkTGltaXRCeXRlcyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRTaXplIiwiZGlzYWJsZSIsImNvcnMiLCJtZXRob2RzIiwic2V0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsInN0YXRpYyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyIsImxpc3RlbiIsImNlcnQiLCJyZWFkRmlsZSIsImh0dHBzU2VydmVyIiwiY2xvc2VTZXJ2ZXJzIiwiZGVsZXRlIiwiZ2V0U2VydmVycyIsImdldEV4cHJlc3MiLCJnZXRBcHAiLCJlbmFibGVSYXRlTGltaXRpbmciLCJtaWRkbGV3YXJlcyIsInNodXRkb3duQ2xlYW5VcCIsImV4aXRDb2RlIiwiZXhpdCIsImluaXRFeHBvcnQiLCJpbml0T3B0aW9ucyIsIl9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyIsImNvZGUiXSwibWFwcGluZ3MiOiJ3bkJBMkJPLE1BQU1BLFlBQVlDLElBQWFBLGNBQUMsSUFBSUMsSUFBSSxPQUFRLG9CQUFBQyxTQUFBQyxRQUFBLE9BQUFDLGNBQUFDLFlBQUFDLEtBQUFDLHdCQUFBLFdBQUFBLHVCQUFBQyxRQUFBQyxlQUFBRix1QkFBQUcsS0FBQSxJQUFBVCxJQUFBLFlBQUFDLFNBQUFTLFNBQUFMLE9BK0JoRCxTQUFTTSxTQUFTQyxHQUV2QixHQUFlLE9BQVhBLEdBQXFDLGlCQUFYQSxFQUM1QixPQUFPQSxFQUlULE1BQU1DLEVBQWFDLE1BQU1DLFFBQVFILEdBQVUsR0FBSyxHQUdoRCxJQUFLLE1BQU1JLEtBQU9KLEVBQ1pLLE9BQU9DLFVBQVVDLGVBQWVDLEtBQUtSLEVBQVFJLEtBQy9DSCxFQUFX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 diff --git a/dist/index.esm.js b/dist/index.esm.js index 4d06b6f0..b0acab16 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -1,2 +1,2 @@ -import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import{readFile}from"fs/promises";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?e:join(__dirname,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env);class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 globalOptions=_initGlobalOptions(defaultConfig),instanceOptions=deepCopy(globalOptions);function getOptions(e=!0){return e?instanceOptions:globalOptions}function setGlobalOptions(e={},t=[]){let o={},r={};return t&&Array.isArray(t)&&t.length&&(o=_loadConfigFile(t,globalOptions.customLogic),r=_pairArgumentValue(nestedProps,t)),_updateGlobalOptions(defaultConfig,globalOptions,o,r,e),globalOptions}function updateOptions(e,t=!1){return t&&(Object.keys(instanceOptions).forEach((e=>{delete instanceOptions[e]})),mergeOptions(instanceOptions,deepCopy(globalOptions))),mergeOptions(instanceOptions,e),instanceOptions}function mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initGlobalOptions(e){const t={};for(const[o,r]of Object.entries(e))if(Object.prototype.hasOwnProperty.call(r,"value")){const e=envs[r.envLink];t[o]=null!=e?e:r.value}else t[o]=_initGlobalOptions(r);return t}function _updateGlobalOptions(e,t,o,r,n){Object.keys(e).forEach((i=>{const s=e[i],a=o&&o[i],l=r&&r[i],c=n&&n[i];if(void 0===s.value)_updateGlobalOptions(s,t[i],a,l,c);else{null!=a&&(t[i]=a);const e=envs[s.envLink];s.envLink in envs&&null!=e&&(t[i]=e),null!=l&&(t[i]=l),null!=c&&(t[i]=c)}}))}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _loadConfigFile(e,t){const o=e.findIndex((e=>"loadConfig"===e.replace(/-/g,""))),r=o>-1&&e[o+1];if(r&&t.allowFileResources)try{return isAllowedConfig(readFileSync(getAbsolutePath(r),"utf8"),!1,t.allowCodeExecution)}catch(e){logWithStack(2,e,`[config] Unable to load the configuration from the ${r} file.`)}return{}}function _pairArgumentValue(e,t){const o={};for(let r=0;r{if(i.length-1===s){const i=t[++r];i||log(2,`[config] Missing value for the CLI '--${n}' argument. Using the default value.`),e[o]=i||null}else void 0===e[o]&&(e[o]={});return e[o]}),o)}return o}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}const cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=getOptions();t.highcharts.version=e,await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:join(__dirname,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.",400);const o=mergeOptions(deepCopy(getOptions()),{export:e.export,customLogic:e.customLogic}),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e={}){const t=updateOptions(e,!0);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,setGlobalOptions:setGlobalOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; +import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import{readFile}from"fs/promises";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?e:join(__dirname,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(){return deepCopy(globalOptions)}function updateOptions(e,t=!1){return _mergeOptions(t?deepCopy(globalOptions):globalOptions,e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:join(__dirname,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; //# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index a9644ea5..f896f5e7 100644 --- a/dist/index.esm.js.map +++ b/dist/index.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/errors/ExportError.js","../lib/config.js","../lib/fetch.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? path : join(__dirname, path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initGlobalOptions(defaultConfig);\r\n\r\n// An object for the instance options, created each time the `initExport` occurs\r\nconst instanceOptions = deepCopy(globalOptions);\r\n\r\n/**\r\n * Retrieves a reference to the options object. Depending on the `getInstance`\r\n * parameter, it returns either the global options or the instance-specific\r\n * options object.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getInstance=true] - Optional parameter that decides whether\r\n * to return the instance-specific options (when `true`) or the global options\r\n * (when `false`). The default value is `true`.\r\n *\r\n * @returns {Object} A reference to either the global options\r\n * or the instance-specific options, based on the `getInstance` parameter.\r\n */\r\nexport function getOptions(getInstance = true) {\r\n return getInstance ? instanceOptions : globalOptions;\r\n}\r\n\r\n/**\r\n * Sets the global options of the export server, keeping the principle\r\n * of the options load priority from all available sources. It accepts optional\r\n * `customOptions` object and `cliArgs` array with arguments from the CLI. These\r\n * options will be validated and applied if provided.\r\n *\r\n * The priority order of setting values is:\r\n *\r\n * 1. Options from the `lib/schemas/config.js` file (default values).\r\n * 2. Options from a custom JSON file (loaded by the `loadConfig` option).\r\n * 3. Options from the environment variables (the `.env` file).\r\n * 4. Options from the command line interface (CLI).\r\n * 5. Options from the first parameter (the `customOptions` is by default\r\n * an empty object).\r\n *\r\n * @function setGlobalOptions\r\n *\r\n * @param {Object} [customOptions={}] - Optional custom options for additional\r\n * configuration. The default value is an empty object.\r\n * @param {Array.} [cliArgs=[]] - Optional command line arguments\r\n * for additional configuration. The default value is an empty array.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from all available sources.\r\n */\r\nexport function setGlobalOptions(customOptions = {}, cliArgs = []) {\r\n // Object for options loaded via the `loadConfig` option\r\n let configOptions = {};\r\n\r\n // Object for options from the CLI\r\n let cliOptions = {};\r\n\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `loadConfig`\r\n configOptions = _loadConfigFile(cliArgs, globalOptions.customLogic);\r\n\r\n // Get options from the CLI\r\n cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n }\r\n\r\n // Update values of the global options with values from each source possible\r\n _updateGlobalOptions(\r\n defaultConfig,\r\n globalOptions,\r\n configOptions,\r\n cliOptions,\r\n customOptions\r\n );\r\n\r\n // Return updated global options\r\n return globalOptions;\r\n}\r\n\r\n/**\r\n * Updates the instance options with additional options. It optionally allows\r\n * to reinitialize the instance options with the values of a current global\r\n * options object.\r\n *\r\n * @param {Object} updateOptions - The update options to merge into the instance\r\n * options.\r\n * @param {boolean} [newInstance=false] - A flag to indicate whether to init\r\n * options for a new instance. If `true`, the existing instance options will\r\n * be cleared and reinitialized based on the global options.\r\n *\r\n * @returns {Object} - The updated instance options.\r\n */\r\nexport function updateOptions(updateOptions, newInstance = false) {\r\n // Check if options need to be created for a new instance\r\n if (newInstance) {\r\n // Get rid of the old instance options\r\n Object.keys(instanceOptions).forEach((key) => {\r\n delete instanceOptions[key];\r\n });\r\n\r\n // Init the new instance options based on the global options\r\n mergeOptions(instanceOptions, deepCopy(globalOptions));\r\n }\r\n\r\n // Merge additional options to the instance options\r\n mergeOptions(instanceOptions, updateOptions);\r\n\r\n // Return the reference to the instance options object\r\n return instanceOptions;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns global options object based on provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * @function _initGlobalOptions\r\n *\r\n * @param {Object} config - The configuration object to be used for initializing\r\n * options.\r\n *\r\n * @returns {Object} Initialized options object.\r\n */\r\nfunction _initGlobalOptions(config) {\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // If a value from environment variables exists, it takes precedence\r\n const envVal = envs[item.envLink];\r\n if (envVal !== undefined && envVal !== null) {\r\n options[name] = envVal;\r\n } else {\r\n options[name] = item.value;\r\n }\r\n } else {\r\n options[name] = _initGlobalOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Updates global options object with values from various sources, following\r\n * a specific prioritization order. The function checks for values in the order\r\n * of precedence: the `loadConfig` configuration options, environment variables,\r\n * custom options, and CLI options.\r\n *\r\n * @function _updateGlobalOptions\r\n *\r\n * @param {Object} config - The configuration object, which includes the initial\r\n * settings and metadata for each option. This object is used to determine\r\n * the structure and default values for the options.\r\n * @param {Object} options - The global options object that will be updated\r\n * with values from other sources.\r\n * @param {Object} configOpt - The configuration options object, loaded with\r\n * the `loadConfig` option, which may provide values to override defaults.\r\n * @param {Object} cliOpt - The CLI options object, which may include values\r\n * provided through command-line arguments and may override configuration\r\n * options.\r\n * @param {Object} customOpt - The custom options object, typically containing\r\n * additional and user-defined values, which has the highest precedence among\r\n * options.\r\n */\r\nfunction _updateGlobalOptions(config, options, configOpt, cliOpt, customOpt) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the config entry of a specific option\r\n const entry = config[key];\r\n\r\n // Gather values for the options from every possible source, if exists\r\n const configVal = configOpt && configOpt[key];\r\n const cliVal = cliOpt && cliOpt[key];\r\n const customVal = customOpt && customOpt[key];\r\n\r\n // If the value not found, need to go deeper\r\n if (typeof entry.value === 'undefined') {\r\n _updateGlobalOptions(entry, options[key], configVal, cliVal, customVal);\r\n } else {\r\n // If a value from custom JSON options exists, it takes precedence\r\n if (configVal !== undefined && configVal !== null) {\r\n options[key] = configVal;\r\n }\r\n\r\n // If a value from environment variables exists, it takes precedence\r\n const envVal = envs[entry.envLink];\r\n if (entry.envLink in envs && envVal !== undefined && envVal !== null) {\r\n options[key] = envVal;\r\n }\r\n\r\n // If a value from CLI options exists, it takes precedence\r\n if (cliVal !== undefined && cliVal !== null) {\r\n options[key] = cliVal;\r\n }\r\n\r\n // If a value from user options exists, it takes precedence\r\n if (customVal !== undefined && customVal !== null) {\r\n options[key] = customVal;\r\n }\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `loadConfig` option and the corresponding file path.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs, customLogicOptions) {\r\n // Check if the `loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `loadConfig` is present and has a correct value\r\n if (configFileName && customLogicOptions.allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n customLogicOptions.allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n setGlobalOptions,\r\n updateOptions,\r\n mergeOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Get the reference to the global options to update to the new version\r\n const options = getOptions();\r\n\r\n // Set to the new version\r\n options.highcharts.version = newVersion;\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n 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(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { getOptions, isAllowedConfig, mergeOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n deepCopy,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `exportingOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} exportingOptions - The `exportingOptions` object, which\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(exportingOptions, endCallback) {\r\n try {\r\n // Check if provided options is an object\r\n if (!isObject(exportingOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = mergeOptions(deepCopy(getOptions()), {\r\n export: exportingOptions.export,\r\n customLogic: exportingOptions.customLogic\r\n });\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled\r\n if (rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFile } from 'fs/promises';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 readFile(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = await readFile(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport {\r\n getOptions,\r\n updateOptions,\r\n setGlobalOptions,\r\n mapToNewOptions\r\n} from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} [initOptions={}] - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration. The default value\r\n * is an empty object.\r\n */\r\nexport async function initExport(initOptions = {}) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions, true);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n setGlobalOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","join","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","ExportError","Error","constructor","statusCode","super","this","setStatus","setError","name","_initGlobalOptions","instanceOptions","getOptions","getInstance","setGlobalOptions","customOptions","cliArgs","configOptions","cliOptions","_loadConfigFile","_pairArgumentValue","_updateGlobalOptions","updateOptions","newInstance","mergeOptions","originalOptions","newOptions","entries","mapToNewOptions","oldOptions","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","envVal","configOpt","cliOpt","customOpt","configVal","cliVal","customVal","stringifyFunctions","stringify","replaceAll","customLogicOptions","configIndex","findIndex","arg","configFileName","i","option","async","fetch","requestOptions","Promise","resolve","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","exportingOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","readFile","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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CKA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMkB,cAAgB,CAC3BC,UAAW,CACT9B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFsD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbpG,KAAM,OACNqG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP7D,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbpG,KAAM,SAGVwG,OAAQ,CACN9D,MAAO,8BACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbpG,KAAM,SAGVyG,WAAY,CACV/D,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbpG,KAAM,WAGV0G,UAAW,CACThE,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbpG,KAAM,SAGV2G,YAAa,CACXjE,MAAO,CAAC,aAAc,kBAAmB,iBACzCsD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbpG,KAAM,cACN4G,aAAc,0DAGlBC,cAAe,CACbnE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? path : join(__dirname, path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object.\r\n *\r\n * @function getOptions\r\n *\r\n * @returns {Object} A reference to the global options object.\r\n */\r\nexport function getOptions() {\r\n return deepCopy(globalOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with the provided options.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(\r\n getCopy ? deepCopy(globalOptions) : globalOptions,\r\n newOptions\r\n );\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs, globalOptions.customLogic);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return global options\r\n return globalOptions;\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs, customLogicOptions) {\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && customLogicOptions.allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n customLogicOptions.allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n 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(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `exportingOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} exportingOptions - The `exportingOptions` object, which\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(exportingOptions, endCallback) {\r\n try {\r\n // Check if provided options is an object\r\n if (!isObject(exportingOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: exportingOptions.export,\r\n customLogic: exportingOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFile } from 'fs/promises';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 readFile(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = await readFile(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","join","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","updateOptions","newOptions","getCopy","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","resolve","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","exportingOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","readFile","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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CKA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMkB,cAAgB,CAC3BC,UAAW,CACT9B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFsD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbpG,KAAM,OACNqG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP7D,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbpG,KAAM,SAGVwG,OAAQ,CACN9D,MAAO,8BACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbpG,KAAM,SAGVyG,WAAY,CACV/D,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbpG,KAAM,WAGV0G,UAAW,CACThE,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbpG,KAAM,SAGV2G,YAAa,CACXjE,MAAO,CAAC,aAAc,kBAAmB,iBACzCsD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbpG,KAAM,cACN4G,aAAc,0DAGlBC,cAAe,CACbnE,MAAO,CACL,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,eACA,yBACA,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,YAEFsD,MAAO,CAAC,YACRC,QAAS,4BACTE,YAAa,qCACbC,cAAe,CACbpG,KAAM,cACN4G,aAAc,0DAGlBE,iBAAkB,CAChBpE,MAAO,CAAC,kBACRsD,MAAO,CAAC,YACRC,QAAS,+BACTE,YAAa,wCACbC,cAAe,CACbpG,KAAM,cACN4G,aAAc,0DAGlBG,cAAe,CACbrE,MAAO,CACL,wEACA,kGAEFsD,MAAO,CAAC,YACRC,QAAS,4BACTE,YAAa,qDACbC,cAAe,CACbpG,KAAM,OACNqG,UAAW,OAIjBW,OAAQ,CACNC,OAAQ,CACNvE,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,gBACTE,YACE,+DACFC,cAAe,CACbpG,KAAM,SAGVkH,MAAO,CACLxE,MAAO,KACPsD,MAAO,CAAC,SAAU,SAAU,QAC5BC,QAAS,eACTE,YACE,mEACFC,cAAe,CACbpG,KAAM,SAGVmH,QAAS,CACPzE,MAAO,KACPsD,MAAO,CAAC,SAAU,SAAU,QAC5BC,QAAS,iBACTE,YAAa,+BACbC,cAAe,CACbpG,KAAM,SAGVoH,IAAK,CACH1E,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,aACTE,YAAa,mDACbC,cAAe,CACbpG,KAAM,SAGVqH,MAAO,CACL3E,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,eACTE,YACE,gEACFC,cAAe,CACbpG,KAAM,SAGVC,QAAS,CACPyC,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,iBACTE,YACE,qFACFC,cAAe,CACbpG,KAAM,SAGVA,KAAM,CACJ0C,MAAO,MACPsD,MAAO,CAAC,UACRC,QAAS,cACTE,YAAa,oDACbC,cAAe,CACbpG,KAAM,SACNsH,KAAM,eACNC,QAAS,CAAC,MAAO,OAAQ,MAAO,SAGpC7H,OAAQ,CACNgD,MAAO,QACPsD,MAAO,CAAC,UACRC,QAAS,gBACTE,YACE,uEACFC,cAAe,CACbpG,KAAM,SACNsH,KAAM,iBACNC,QAAS,CAAC,QAAS,aAAc,WAAY,gBAGjDC,IAAK,CACH9E,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,aACTE,YACE,oFACFC,cAAe,CACbpG,KAAM,WAGVyH,WAAY,CACV/E,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,qBACTE,YACE,0EACFC,cAAe,CACbpG,KAAM,WAGV0H,OAAQ,CACNhF,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,gBACTE,YAAa,yDACbC,cAAe,CACbpG,KAAM,WAGV2H,MAAO,CACLjF,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,eACTE,YAAa,wDACbC,cAAe,CACbpG,KAAM,WAGV4H,MAAO,CACLlF,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,eACTE,YACE,gFACFC,cAAe,CACbpG,KAAM,WAGV6H,cAAe,CACbnF,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,kDACbC,cAAe,CACbpG,KAAM,WAGV8H,aAAc,CACZpF,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,uBACTE,YAAa,iDACbC,cAAe,CACbpG,KAAM,WAGV+H,aAAc,CACZrF,MAAO,EACPsD,MAAO,CAAC,UACRC,QAAS,uBACTE,YACE,yEACFC,cAAe,CACbpG,KAAM,SACNgI,IAAK,GACLC,IAAK,IAGTC,cAAe,CACbxF,MAAO,KACPsD,MAAO,CAAC,SAAU,SAAU,QAC5BC,QAAS,wBACTE,YACE,mFACFC,cAAe,CACbpG,KAAM,SAGVmI,aAAc,CACZzF,MAAO,KACPsD,MAAO,CAAC,SAAU,SAAU,QAC5BC,QAAS,uBACTE,YACE,kFACFC,cAAe,CACbpG,KAAM,SAGVoI,qBAAsB,CACpB1F,MAAO,KACPsD,MAAO,CAAC,UACRC,QAAS,+BACTE,YAAa,6CACbC,cAAe,CACbpG,KAAM,YAIZqI,YAAa,CACXC,mBAAoB,CAClB5F,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,oCACTE,YACE,mEACFC,cAAe,CACbpG,KAAM,WAGVkD,mBAAoB,CAClBR,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,oCACTE,YACE,kFACFC,cAAe,CACbpG,KAAM,WAGViD,WAAY,CACVP,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,2BACTE,YACE,uHACFC,cAAe,CACbpG,KAAM,SAGVuI,SAAU,CACR7F,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,wBACTE,YACE,kFACFC,cAAe,CACbpG,KAAM,SAGVwI,UAAW,CACT9F,MAAO,KACPsD,MAAO,CAAC,SAAU,SAAU,QAC5BC,QAAS,yBACTE,YACE,sGACFC,cAAe,CACbpG,KAAM,SAGVyI,WAAY,CACV/F,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,2BACTyC,WAAY,WACZvC,YAAa,+CACbC,cAAe,CACbpG,KAAM,SAGV2I,aAAc,CACZjG,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,6BACTE,YACE,+DACFC,cAAe,CACbpG,KAAM,UAIZ4I,OAAQ,CACNC,OAAQ,CACNnG,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,gBACTC,QAAS,eACTC,YAAa,8BACbC,cAAe,CACbpG,KAAM,WAGV8I,KAAM,CACJpG,MAAO,UACPsD,MAAO,CAAC,UACRC,QAAS,cACTE,YAAa,yBACbC,cAAe,CACbpG,KAAM,SAGV+I,KAAM,CACJrG,MAAO,KACPsD,MAAO,CAAC,UACRC,QAAS,cACTE,YAAa,6BACbC,cAAe,CACbpG,KAAM,WAGVgJ,YAAa,CACXtG,MAAO,EACPsD,MAAO,CAAC,UACRC,QAAS,sBACTE,YAAa,kCACbC,cAAe,CACbpG,KAAM,WAGViJ,aAAc,CACZvG,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,sBACTC,QAAS,qBACTC,YACE,0EACFC,cAAe,CACbpG,KAAM,WAGVkJ,MAAO,CACLJ,KAAM,CACJpG,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,oBACTC,QAAS,YACTC,YAAa,0CACbC,cAAe,CACbpG,KAAM,SAGV+I,KAAM,CACJrG,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,oBACTC,QAAS,YACTC,YAAa,0CACbC,cAAe,CACbpG,KAAM,WAGVmJ,QAAS,CACPzG,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,uBACTC,QAAS,eACTC,YACE,8DACFC,cAAe,CACbpG,KAAM,YAIZoJ,aAAc,CACZP,OAAQ,CACNnG,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,8BACTC,QAAS,qBACTC,YAAa,kDACbC,cAAe,CACbpG,KAAM,WAGVqJ,YAAa,CACX3G,MAAO,GACPsD,MAAO,CAAC,UACRC,QAAS,oCACTyC,WAAY,YACZvC,YAAa,gDACbC,cAAe,CACbpG,KAAM,WAGVsJ,OAAQ,CACN5G,MAAO,EACPsD,MAAO,CAAC,UACRC,QAAS,8BACTE,YAAa,2CACbC,cAAe,CACbpG,KAAM,WAGVuJ,MAAO,CACL7G,MAAO,EACPsD,MAAO,CAAC,UACRC,QAAS,6BACTE,YACE,uEACFC,cAAe,CACbpG,KAAM,WAGVwJ,WAAY,CACV9G,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,mCACTE,YAAa,sDACbC,cAAe,CACbpG,KAAM,WAGVyJ,QAAS,CACP/G,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,gCACTE,YAAa,wDACbC,cAAe,CACbpG,KAAM,SAGV0J,UAAW,CACThH,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,kCACTE,YAAa,wDACbC,cAAe,CACbpG,KAAM,UAIZ2J,IAAK,CACHd,OAAQ,CACNnG,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,oBACTC,QAAS,YACTC,YAAa,mCACbC,cAAe,CACbpG,KAAM,WAGV4J,MAAO,CACLlH,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,mBACTC,QAAS,WACTwC,WAAY,UACZvC,YAAa,gDACbC,cAAe,CACbpG,KAAM,WAGV+I,KAAM,CACJrG,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,kBACTC,QAAS,UACTC,YAAa,0BACbC,cAAe,CACbpG,KAAM,WAGV6J,SAAU,CACRnH,MAAO,KACPsD,MAAO,CAAC,SAAU,QAClBC,QAAS,uBACTC,QAAS,cACTwC,WAAY,UACZvC,YAAa,uCACbC,cAAe,CACbpG,KAAM,WAKd8J,KAAM,CACJC,WAAY,CACVrH,MAAO,EACPsD,MAAO,CAAC,UACRC,QAAS,mBACTE,YAAa,sDACbC,cAAe,CACbpG,KAAM,WAGVgK,WAAY,CACVtH,MAAO,EACPsD,MAAO,CAAC,UACRC,QAAS,mBACTyC,WAAY,UACZvC,YAAa,0CACbC,cAAe,CACbpG,KAAM,WAGViK,UAAW,CACTvH,MAAO,GACPsD,MAAO,CAAC,UACRC,QAAS,kBACTE,YAAa,wDACbC,cAAe,CACbpG,KAAM,WAGVkK,eAAgB,CACdxH,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,uBACTE,YAAa,mDACbC,cAAe,CACbpG,KAAM,WAGVmK,cAAe,CACbzH,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,sBACTE,YAAa,kDACbC,cAAe,CACbpG,KAAM,WAGVoK,eAAgB,CACd1H,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,uBACTE,YAAa,oDACbC,cAAe,CACbpG,KAAM,WAGVqK,YAAa,CACX3H,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,oBACTE,YAAa,wDACbC,cAAe,CACbpG,KAAM,WAGVsK,oBAAqB,CACnB5H,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,6BACTE,YACE,wEACFC,cAAe,CACbpG,KAAM,WAGVuK,eAAgB,CACd7H,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,uBACTE,YACE,+DACFC,cAAe,CACbpG,KAAM,WAGViJ,aAAc,CACZvG,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,oBACTC,QAAS,mBACTC,YAAa,6CACbC,cAAe,CACbpG,KAAM,YAIZwD,QAAS,CACPY,MAAO,CACL1B,MAAO,EACPsD,MAAO,CAAC,UACRC,QAAS,gBACTC,QAAS,WACTC,YAAa,0BACbC,cAAe,CACbpG,KAAM,SACN+C,MAAO,EACPiF,IAAK,EACLC,IAAK,IAGT3C,KAAM,CACJ5C,MAAO,+BACPsD,MAAO,CAAC,UACRC,QAAS,eACTC,QAAS,UACTC,YACE,8DACFC,cAAe,CACbpG,KAAM,SAGVqF,KAAM,CACJ3C,MAAO,MACPsD,MAAO,CAAC,UACRC,QAAS,eACTC,QAAS,UACTC,YAAa,0DACbC,cAAe,CACbpG,KAAM,SAGVyD,UAAW,CACTf,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,qBACTC,QAAS,eACTC,YAAa,sCACbC,cAAe,CACbpG,KAAM,WAGV0D,OAAQ,CACNhB,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,kBACTC,QAAS,YACTC,YAAa,wCACbC,cAAe,CACbpG,KAAM,YAIZwK,GAAI,CACF3B,OAAQ,CACNnG,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,YACTC,QAAS,WACTC,YAAa,mDACbC,cAAe,CACbpG,KAAM,WAGVyK,MAAO,CACL/H,MAAO,IACPsD,MAAO,CAAC,UACRC,QAAS,WACTC,QAAS,UACTC,YAAa,gCACbC,cAAe,CACbpG,KAAM,UAIZ0K,MAAO,CACLC,QAAS,CACPjI,MAAO,aACPsD,MAAO,CAAC,UACRC,QAAS,iBACTE,YAAa,+BACbC,cAAe,CACbpG,KAAM,SAGV4K,qBAAsB,CACpBlI,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,gCACTE,YAAa,iDACbC,cAAe,CACbpG,KAAM,WAGV6K,OAAQ,CACNnI,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,gBACTE,YAAa,+CACbC,cAAe,CACbpG,KAAM,WAGV8K,cAAe,CACbpI,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,wBACTE,YAAa,oDACbC,cAAe,CACbpG,KAAM,WAGV+K,iBAAkB,CAChBrI,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,2BACTE,YAAa,yDACbC,cAAe,CACbpG,KAAM,YAIZgL,MAAO,CACLnC,OAAQ,CACNnG,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,eACTC,QAAS,cACTC,YAAa,4DACbC,cAAe,CACbpG,KAAM,WAGViL,SAAU,CACRvI,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,iBACTE,YACE,6EACFC,cAAe,CACbpG,KAAM,WAGVkL,SAAU,CACRxI,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,iBACTE,YAAa,+CACbC,cAAe,CACbpG,KAAM,WAGVmL,gBAAiB,CACfzI,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,0BACTE,YACE,qEACFC,cAAe,CACbpG,KAAM,WAGVoL,OAAQ,CACN1I,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,eACTE,YACE,kFACFC,cAAe,CACbpG,KAAM,WAGVqL,OAAQ,CACN3I,MAAO,EACPsD,MAAO,CAAC,UACRC,QAAS,gBACTE,YAAa,4DACbC,cAAe,CACbpG,KAAM,WAGVsL,cAAe,CACb5I,MAAO,KACPsD,MAAO,CAAC,UACRC,QAAS,uBACTE,YAAa,0BACbC,cAAe,CACbpG,KAAM,aAODuL,YAAcC,mBAAmB1F,eAGjC2F,cAAgBC,qBAAqB5F,eAoBlD,SAAS0F,mBAAmBG,EAAQJ,EAAc,CAAA,EAAIK,EAAY,IAqBhE,OApBAvM,OAAOwC,KAAK8J,GAAQE,SAASzM,IAE3B,MAAM0M,EAAQH,EAAOvM,QAGM,IAAhB0M,EAAMpJ,MAEf8I,mBAAmBM,EAAOP,EAAa,GAAGK,KAAaxM,MAGvDmM,EAAYO,EAAM5F,SAAW9G,GAAO,GAAGwM,KAAaxM,IAAM2M,UAAU,QAG3CtH,IAArBqH,EAAMpD,aACR6C,EAAYO,EAAMpD,YAAc,GAAGkD,KAAaxM,IAAM2M,UAAU,IAEnE,IAIIR,CACT,CAiBA,SAASG,qBAAqBC,EAAQF,EAAgB,IAkBpD,OAjBApM,OAAOwC,KAAK8J,GAAQE,SAASzM,IAE3B,MAAM0M,EAAQH,EAAOvM,QAGM,IAAhB0M,EAAM9F,MAEf0F,qBAAqBI,EAAOL,GAGxBK,EAAM9F,MAAMlG,SAAS,WACvB2L,EAAcvG,KAAK9F,EAEtB,IAIIqM,CACT,CCrhCAO,OAAOL,SAIP,MAAMM,EAAI,CAGRC,MAAQC,GACNC,EACGC,SACAC,WAAW5J,GACVA,EACGvC,MAAM,KACNoM,KAAK7J,GAAUA,EAAMnB,SACrBiL,QAAQ9J,GAAUyJ,EAAYrM,SAAS4C,OAE3C4J,WAAW5J,GAAWA,EAAMZ,OAASY,OAAQ+B,IAIlDgI,QAAS,IACPL,EACGM,KAAK,CAAC,OAAQ,QAAS,KACvBJ,WAAW5J,GAAqB,KAAVA,EAAyB,SAAVA,OAAmB+B,IAI7DiI,KAAOlM,GACL4L,EACGM,KAAK,IAAIlM,EAAQ,KACjB8L,WAAW5J,GAAqB,KAAVA,EAAeA,OAAQ+B,IAIlD4H,OAAQ,IACND,EACGC,SACA9K,OACAoL,QACEjK,IACE,CAAC,QAAS,YAAa,OAAQ,OAAO5C,SAAS4C,IACtC,KAAVA,IACDA,IAAW,CACVqC,QAAS,mDAAmDrC,SAG/D4J,WAAW5J,GAAqB,KAAVA,EAAeA,OAAQ+B,IAIlDmI,YAAa,IACXR,EACGC,SACA9K,OACAoL,QACEjK,GACW,KAAVA,IAAkBmK,MAAMC,WAAWpK,KAAWoK,WAAWpK,GAAS,IACnEA,IAAW,CACVqC,QAAS,qDAAqDrC,SAGjE4J,WAAW5J,GAAqB,KAAVA,EAAeoK,WAAWpK,QAAS+B,IAI9DsI,eAAgB,IACdX,EACGC,SACA9K,OACAoL,QACEjK,GACW,KAAVA,IAAkBmK,MAAMC,WAAWpK,KAAWoK,WAAWpK,IAAU,IACpEA,IAAW,CACVqC,QAAS,yDAAyDrC,SAGrE4J,WAAW5J,GAAqB,KAAVA,EAAeoK,WAAWpK,QAAS+B,KAGnDuI,OAASZ,EAAEa,OAAO,CAE7BC,eAAgBjB,EAAEI,SAGlBc,mBAAoBf,EACjBC,SACA9K,OACAoL,QACEjK,GAAU,6BAA6BR,KAAKQ,IAAoB,KAAVA,IACtDA,IAAW,CACVqC,QAAS,4FAA4FrC,SAGxG4J,WAAW5J,GAAqB,KAAVA,EAAeA,OAAQ+B,IAChD2I,mBAAoBhB,EACjBC,SACA9K,OACAoL,QACEjK,GACCA,EAAMY,WAAW,aACjBZ,EAAMY,WAAW,YACP,KAAVZ,IACDA,IAAW,CACVqC,QAAS,6FAA6FrC,SAGzG4J,WAAW5J,GAAqB,KAAVA,EAAeA,OAAQ+B,IAChD4I,uBAAwBpB,EAAEQ,UAC1Ba,sBAAuBrB,EAAEI,SACzBkB,uBAAwBtB,EAAEI,SAC1BmB,wBAAyBvB,EAAEC,MAAMpG,cAAcQ,WAAWK,YAAYjE,OACtE+K,0BAA2BxB,EAAEC,MAC3BpG,cAAcQ,WAAWO,cAAcnE,OAEzCgL,6BAA8BzB,EAAEC,MAC9BpG,cAAcQ,WAAWQ,iBAAiBpE,OAE5CiL,0BAA2B1B,EAAEC,MAC3BpG,cAAcQ,WAAWS,cAAcrE,OAIzCkL,cAAe3B,EAAEI,SACjBwB,aAAc5B,EAAEI,SAChByB,eAAgB7B,EAAEI,SAClB0B,WAAY9B,EAAEI,SACd2B,aAAc/B,EAAEI,SAChB4B,eAAgBhC,EAAEI,SAClB6B,YAAajC,EAAES,KAAK,CAAC,OAAQ,MAAO,MAAO,QAC3CyB,cAAelC,EAAES,KAAK,CAAC,QAAS,aAAc,WAAY,eAC1D0B,WAAYnC,EAAEQ,UACd4B,mBAAoBpC,EAAEQ,UACtB6B,cAAerC,EAAEW,cACjB2B,aAActC,EAAEW,cAChB4B,aAAcvC,EAAEW,cAChB6B,sBAAuBxC,EAAEW,cACzB8B,qBAAsBzC,EAAEW,cACxB+B,qBAAsB1C,EAAEW,cACxBgC,sBAAuB3C,EAAEI,SACzBwC,qBAAsB5C,EAAEI,SACxByC,6BAA8B7C,EAAEc,iBAGhCgC,kCAAmC9C,EAAEQ,UACrCuC,kCAAmC/C,EAAEQ,UACrCwC,yBAA0BhD,EAAEI,SAC5B6C,sBAAuBjD,EAAEI,SACzB8C,uBAAwBlD,EAAEI,SAC1B+C,yBAA0BnD,EAAEI,SAC5BgD,2BAA4BpD,EAAEI,SAG9BiD,cAAerD,EAAEQ,UACjB8C,YAAatD,EAAEI,SACfmD,YAAavD,EAAEW,cACf6C,oBAAqBxD,EAAEW,cACvB8C,oBAAqBzD,EAAEQ,UAGvBkD,kBAAmB1D,EAAEI,SACrBuD,kBAAmB3D,EAAEW,cACrBiD,qBAAsB5D,EAAEc,iBAGxB+C,4BAA6B7D,EAAEQ,UAC/BsD,kCAAmC9D,EAAEc,iBACrCiD,4BAA6B/D,EAAEc,iBAC/BkD,2BAA4BhE,EAAEc,iBAC9BmD,iCAAkCjE,EAAEQ,UACpC0D,8BAA+BlE,EAAEI,SACjC+D,gCAAiCnE,EAAEI,SAGnCgE,kBAAmBpE,EAAEQ,UACrB6D,iBAAkBrE,EAAEQ,UACpB8D,gBAAiBtE,EAAEW,cACnB4D,qBAAsBvE,EAAEI,SAGxBoE,iBAAkBxE,EAAEc,iBACpB2D,iBAAkBzE,EAAEc,iBACpB4D,gBAAiB1E,EAAEW,cACnBgE,qBAAsB3E,EAAEc,iBACxB8D,oBAAqB5E,EAAEc,iBACvB+D,qBAAsB7E,EAAEc,iBACxBgE,kBAAmB9E,EAAEc,iBACrBiE,2BAA4B/E,EAAEc,iBAC9BkE,qBAAsBhF,EAAEc,iBACxBmE,kBAAmBjF,EAAEQ,UAGrB0E,cAAe/E,EACZC,SACA9K,OACAoL,QACEjK,GACW,KAAVA,IACEmK,MAAMC,WAAWpK,KACjBoK,WAAWpK,IAAU,GACrBoK,WAAWpK,IAAU,IACxBA,IAAW,CACVqC,QAAS,mGAAmGrC,SAG/G4J,WAAW5J,GAAqB,KAAVA,EAAeoK,WAAWpK,QAAS+B,IAC5D2M,aAAcnF,EAAEI,SAChBgF,aAAcpF,EAAEI,SAChBiF,mBAAoBrF,EAAEQ,UACtB8E,gBAAiBtF,EAAEQ,UAGnB+E,UAAWvF,EAAEQ,UACbgF,SAAUxF,EAAEI,SAGZqF,eAAgBzF,EAAES,KAAK,CAAC,cAAe,aAAc,SACrDiF,8BAA+B1F,EAAEQ,UACjCmF,cAAe3F,EAAEQ,UACjBoF,sBAAuB5F,EAAEQ,UACzBqF,yBAA0B7F,EAAEQ,UAG5BsF,aAAc9F,EAAEQ,UAChBuF,eAAgB/F,EAAEQ,UAClBwF,eAAgBhG,EAAEQ,UAClByF,wBAAyBjG,EAAEQ,UAC3B0F,aAAclG,EAAEQ,UAChB2F,cAAenG,EAAEc,iBACjBsF,qBAAsBpG,EAAEW,gBAGb0F,KAAOtF,OAAOuF,UAAUC,MAAMnQ,QAAQoQ,KCtO7CvK,cAAgBwK,aAAa5M,eAS5B,SAAS6M,aACd,OAAO5T,SAASmJ,cAClB,CAgBO,SAAS0K,cAAcC,EAAYC,GAAU,GAElD,OAAOC,cACLD,EAAU/T,SAASmJ,eAAiBA,cACpC2K,EAEJ,CAyDO,SAASG,gBAAgBC,GAE9B,MAAMJ,EAAa,CAAA,EAGnB,GAAInR,SAASuR,GAEX,IAAK,MAAO7T,EAAKsD,KAAUrD,OAAO6T,QAAQD,GAAa,CAErD,MAAME,EAAkB5H,YAAYnM,GAChCmM,YAAYnM,GAAKe,MAAM,KACvB,GAIJgT,EAAgBC,QACd,CAACC,EAAKC,EAAMC,IACTF,EAAIC,GACHH,EAAgBrR,OAAS,IAAMyR,EAAQ7Q,EAAQ2Q,EAAIC,IAAS,IAChET,EAEH,MAED7O,IACE,EACA,mFAKJ,OAAO6O,CACT,CAoBO,SAASW,gBACd7H,OACAvK,UAAW,EACXqS,gBAAiB,GAEjB,IAEE,IAAK/R,SAASiK,SAA6B,iBAAXA,OAE9B,OAAO,KAIT,MAAM+H,aACc,iBAAX/H,OACH8H,eACEE,KAAK,IAAIhI,WACTiI,KAAKpB,MAAM7G,QACbA,OAGAkI,mBAAqBC,kBACzBJ,aACAD,gBACA,GAIIM,cAAgBN,eAClBG,KAAKpB,MACHsB,kBAAkBJ,aAAcD,gBAAgB,IAChD,CAACO,EAAGtR,QACe,iBAAVA,OAAsBA,MAAMY,WAAW,YAC1CqQ,KAAK,IAAIjR,UACTA,QAERkR,KAAKpB,MAAMqB,oBAGf,OAAOzS,SAAWyS,mBAAqBE,aACxC,CAAC,MAAOnP,GAEP,OAAO,IACR,CACH,CA8FA,SAAS8N,aAAa/G,GAEpB,MAAMxE,EAAU,CAAA,EAGhB,IAAK,MAAO8M,EAAMtS,KAAStC,OAAO6T,QAAQvH,GACpCtM,OAAOC,UAAUC,eAAeC,KAAKmC,EAAM,cAElB8C,IAAvB6N,KAAK3Q,EAAKsE,UAAiD,OAAvBqM,KAAK3Q,EAAKsE,SAEhDkB,EAAQ8M,GAAQ3B,KAAK3Q,EAAKsE,SAG1BkB,EAAQ8M,GAAQtS,EAAKe,MAIvByE,EAAQ8M,GAAQvB,aAAa/Q,GAKjC,OAAOwF,CACT,CAYO,SAAS4L,cAAcmB,EAAiBrB,GAE7C,GAAInR,SAASwS,IAAoBxS,SAASmR,GACxC,IAAK,MAAOzT,EAAKsD,KAAUrD,OAAO6T,QAAQL,GACxCqB,EAAgB9U,GACdsC,SAASgB,KACR+I,cAAc3L,SAASV,SACCqF,IAAzByP,EAAgB9U,GACZ2T,cAAcmB,EAAgB9U,GAAMsD,QAC1B+B,IAAV/B,EACEA,EACAwR,EAAgB9U,IAAQ,KAKpC,OAAO8U,CACT,CAsBO,SAASJ,kBAAkB3M,EAASsM,EAAgBU,GAiCzD,OAAOP,KAAKQ,UAAUjN,GAhCG,CAAC6M,EAAGtR,KAO3B,GALqB,iBAAVA,IACTA,EAAQA,EAAMnB,QAKG,mBAAVmB,GACW,iBAAVA,GACNA,EAAMY,WAAW,aACjBZ,EAAMU,SAAS,KACjB,CAEA,GAAIqQ,EAEF,OAAOU,EAEH,YAAYzR,EAAQ,IAAI2R,WAAW,OAAQ,eAE3C,WAAW3R,EAAQ,IAAI2R,WAAW,OAAQ,cAG9C,MAAM,IAAIC,KAEb,CAGD,OAAO5R,CAAK,IAImC2R,WAC/CF,EAAqB,yBAA2B,qBAChD,GAEJ,CCjYOI,eAAeC,MAAM1V,EAAK2V,EAAiB,IAChD,OAAO,IAAIC,SAAQ,CAACC,EAASC,KAC3BC,mBAAmB/V,GAChBgW,IAAIhW,EAAK2V,GAAiBM,IACzB,IAAIC,EAAe,GAGnBD,EAASE,GAAG,QAASC,IACnBF,GAAgBE,CAAK,IAIvBH,EAASE,GAAG,OAAO,KACZD,GACHJ,EAAO,qCAETG,EAASI,KAAOH,EAChBL,EAAQI,EAAS,GACjB,IAEHE,GAAG,SAAUrQ,IACZgQ,EAAOhQ,EAAM,GACb,GAER,CAwEA,SAASiQ,mBAAmB/V,GAC1B,OAAOA,EAAIwE,WAAW,SAAW8R,MAAQC,IAC3C,CCpHA,MAAMC,oBAAoBhB,MAQxB,WAAAiB,CAAYxQ,EAASyQ,GACnBC,QAEAC,KAAK3Q,QAAUA,EACf2Q,KAAK1Q,aAAeD,EAEhByQ,IACFE,KAAKF,WAAaA,EAErB,CASD,SAAAG,CAAUH,GAGR,OAFAE,KAAKF,WAAaA,EAEXE,IACR,CAUD,QAAAE,CAAShR,GAgBP,OAfA8Q,KAAK9Q,MAAQA,EAETA,EAAMqP,OACRyB,KAAKzB,KAAOrP,EAAMqP,MAGhBrP,EAAM4Q,aACRE,KAAKF,WAAa5Q,EAAM4Q,YAGtB5Q,EAAMK,QACRyQ,KAAK1Q,aAAeJ,EAAMG,QAC1B2Q,KAAKzQ,MAAQL,EAAMK,OAGdyQ,IACR,ECxCH,MAAMG,MAAQ,CACZrP,OAAQ,8BACRsP,eAAgB,CAAE,EAClBC,QAAS,GACTC,UAAW,IAeNzB,eAAe0B,oBACpBC,EACAC,GAEA,IACE,IAAIC,EAGJ,MAAM1P,EAAY2P,eAGZC,EAAevV,KAAK2F,EAAW,iBAC/B6P,EAAaxV,KAAK2F,EAAW,cAOnC,IAJCf,WAAWe,IAAcd,UAAUc,EAAW,CAAE8P,WAAW,KAIvD7Q,WAAW2Q,IAAiBJ,EAAkBzP,WACjDzC,IAAI,EAAG,yDACPoS,QAAuBK,aACrBP,EACAC,EACAI,OAEG,CACL,IAAIG,GAAgB,EAGpB,MAAMC,EAAW/C,KAAKpB,MAAMnP,aAAaiT,GAAe,QAIxD,GAAIK,EAASC,SAAW1X,MAAMC,QAAQwX,EAASC,SAAU,CACvD,MAAMC,EAAY,CAAA,EAClBF,EAASC,QAAQ/K,SAASiL,GAAOD,EAAUC,GAAK,IAChDH,EAASC,QAAUC,CACpB,CAGD,MAAMlQ,YAAEA,EAAWE,cAAEA,EAAaC,iBAAEA,GAClCoP,EACIa,EACJpQ,EAAY7E,OAAS+E,EAAc/E,OAASgF,EAAiBhF,OAK3D6U,EAASpQ,UAAY2P,EAAkB3P,SACzCvC,IACE,EACA,yEAEF0S,GAAgB,GAEhBrX,OAAOwC,KAAK8U,EAASC,SAAW,CAAE,GAAE9U,SAAWiV,GAE/C/S,IACE,EACA,+EAEF0S,GAAgB,GAGhBA,GAAiB7P,GAAiB,IAAI7E,MAAMgV,IAC1C,IAAKL,EAASC,QAAQI,GAKpB,OAJAhT,IACE,EACA,eAAegT,iDAEV,CACR,IAKDN,EACFN,QAAuBK,aACrBP,EACAC,EACAI,IAGFvS,IAAI,EAAG,uDAGP6R,MAAME,QAAU1S,aAAakT,EAAY,QAGzCH,EAAiBO,EAASC,QAG1Bf,MAAMG,UAAYiB,eAAepB,MAAME,SAE1C,OAIKmB,sBAAsBhB,EAAmBE,EAChD,CAAC,MAAOxR,GACP,MAAM,IAAI0Q,YACR,8EACA,KACAM,SAAShR,EACZ,CACH,CASO,SAASuS,uBACd,OAAOtB,MAAMG,SACf,CAWOzB,eAAe6C,wBAAwBC,GAE5C,MAAMlQ,EAAUyL,cAAc,CAC5BtM,WAAY,CACVC,QAAS8Q,WAKPpB,oBAAoB9O,EAAQb,WAAYa,EAAQyB,OAAOM,MAC/D,CAWO,SAAS+N,eAAeK,GAC7B,OAAOA,EACJvL,UAAU,EAAGuL,EAAaC,QAAQ,OAClC1X,QAAQ,KAAM,IACdA,QAAQ,KAAM,IACdA,QAAQ,MAAO,IACf0B,MACL,CAYO,SAASiW,kBAAkBC,GAChC,OAAOA,EAAW5X,QAChB,qEACA,GAEJ,CAoBO,SAASwW,eACd,OAAOnW,gBAAgByS,aAAarM,WAAWI,UACjD,CAuBA6N,eAAemD,uBACbC,EACAlD,EACA2B,EACAwB,GAAmB,GAGfD,EAAOvU,SAAS,SAClBuU,EAASA,EAAO5L,UAAU,EAAG4L,EAAO7V,OAAS,IAE/CkC,IAAI,EAAG,6BAA6B2T,QAGpC,MAAM5C,QAAiBP,MAAM,GAAGmD,OAAalD,GAG7C,GAA4B,MAAxBM,EAASS,YAA8C,iBAAjBT,EAASI,KAAkB,CACnE,GAAIiB,EAAgB,CAElBA,EADmBoB,kBAAkBG,IACR,CAC9B,CACD,OAAO5C,EAASI,IACjB,CAGD,GAAIyC,EACF,MAAM,IAAItC,YACR,+BAA+BqC,2EAAgF5C,EAASS,eACxH,KACAI,SAASb,GAEX/Q,IACE,EACA,+BAA+B2T,6DAGrC,CAiBApD,eAAe2C,sBAAsBhB,EAAmBE,EAAiB,IACvE,MAAMyB,EAAc,CAClBtR,QAAS2P,EAAkB3P,QAC3BqQ,QAASR,GAIXP,MAAMC,eAAiB+B,EAEvB7T,IAAI,EAAG,mCACP,IACE8T,cACE/W,KAAKsV,eAAgB,iBACrBzC,KAAKQ,UAAUyD,GACf,OAEH,CAAC,MAAOjT,GACP,MAAM,IAAI0Q,YACR,4CACA,KACAM,SAAShR,EACZ,CACH,CAuBA2P,eAAewD,cACbpR,EACAE,EACAE,EACAoP,EACAC,GAGA,IAAI4B,EACJ,MAAMC,EAAY9B,EAAmBrN,KAC/BoP,EAAY/B,EAAmBpN,KAGrC,GAAIkP,GAAaC,EACf,IACEF,EAAa,IAAIG,gBAAgB,CAC/BrP,KAAMmP,EACNlP,KAAMmP,GAET,CAAC,MAAOtT,GACP,MAAM,IAAI0Q,YACR,0CACA,KACAM,SAAShR,EACZ,CAIH,MAAM6P,EAAiBuD,EACnB,CACEI,MAAOJ,EACP7O,QAASgN,EAAmBhN,SAE9B,GAEEkP,EAAmB,IACpB1R,EAAY4F,KAAKoL,GAClBD,uBAAuB,GAAGC,IAAUlD,EAAgB2B,GAAgB,QAEnEvP,EAAc0F,KAAKoL,GACpBD,uBAAuB,GAAGC,IAAUlD,EAAgB2B,QAEnDrP,EAAcwF,KAAKoL,GACpBD,uBAAuB,GAAGC,IAAUlD,MAKxC,aAD6BC,QAAQ4D,IAAID,IACnBtX,KAAK,MAC7B,CAoBAwT,eAAekC,aAAaP,EAAmBC,EAAoBI,GAEjE,MAAMP,EAC0B,WAA9BE,EAAkB3P,QACd,KACA,GAAG2P,EAAkB3P,UAGrBC,EAAS0P,EAAkB1P,QAAUqP,MAAMrP,OAEjD,IACE,MAAM4P,EAAiB,CAAA,EAuCvB,OArCApS,IACE,EACA,iDAAiDgS,GAAa,aAGhEH,MAAME,cAAgBgC,cACpB,IACK7B,EAAkBvP,YAAY4F,KAAKgM,GACpCvC,EAAY,GAAGxP,KAAUwP,KAAauC,IAAM,GAAG/R,KAAU+R,OAG7D,IACKrC,EAAkBrP,cAAc0F,KAAKuK,GAChC,QAANA,EACId,EACE,GAAGxP,UAAewP,aAAqBc,IACvC,GAAGtQ,kBAAuBsQ,IAC5Bd,EACE,GAAGxP,KAAUwP,aAAqBc,IAClC,GAAGtQ,aAAkBsQ,SAE1BZ,EAAkBpP,iBAAiByF,KAAKiM,GACzCxC,EACI,GAAGxP,WAAgBwP,gBAAwBwC,IAC3C,GAAGhS,sBAA2BgS,OAGtCtC,EAAkBnP,cAClBoP,EACAC,GAIFP,MAAMG,UAAYiB,eAAepB,MAAME,SAGvC+B,cAAcvB,EAAYV,MAAME,SACzBK,CACR,CAAC,MAAOxR,GACP,MAAM,IAAI0Q,YACR,uDACA,KACAM,SAAShR,EACZ,CACH,CCpdO,SAAS6T,kBACdC,WAAWC,WAAa,WACtB,MAAO,CAAEC,SAAU,EACvB,CACA,CAcOrE,eAAesE,YAAYC,EAAeC,GAE/C,MAAMpG,WAAEA,EAAUqG,WAAEA,EAAUC,MAAEA,EAAKC,KAAEA,GAASR,WAIhDA,WAAWS,cAAgBF,GAAM,EAAO,CAAE,EAAEtG,KAG5CrJ,OAAO8P,kBAAmB,EAC1BF,EAAKR,WAAWW,MAAM/Z,UAAW,QAAQ,SAAUga,EAASC,EAAaC,KAEvED,EAAcN,EAAMM,EAAa,CAC/BE,UAAW,CACTC,SAAS,GAEXC,YAAa,CACXC,OAAQ,CACNC,MAAO,CACLH,SAAS,KAOfI,QAAS,CAAE,KAGAF,QAAU,IAAI/N,SAAQ,SAAU+N,GAC3CA,EAAOG,WAAY,CACzB,IAGSzQ,OAAO0Q,qBACV1Q,OAAO0Q,mBAAqBtB,WAAWuB,SAASvE,KAAM,UAAU,KAC9DpM,OAAO8P,kBAAmB,CAAI,KAIlCE,EAAQ9U,MAAMkR,KAAM,CAAC6D,EAAaC,GACtC,IAEEN,EAAKR,WAAWwB,OAAO5a,UAAW,QAAQ,SAAUga,EAASa,EAAOhT,GAClEmS,EAAQ9U,MAAMkR,KAAM,CAACyE,EAAOhT,GAChC,IAGE,MAAMiT,EAAoB,CACxBD,MAAO,CAELJ,WAAW,EAEXrS,OAAQoR,EAAcpR,OACtBC,MAAOmR,EAAcnR,OAEvB8R,UAAW,CAETC,SAAS,IAKPH,EAAc,IAAIc,SAAS,UAAUvB,EAAc5R,QAArC,GAGdiB,EAAe,IAAIkS,SAAS,UAAUvB,EAAc3Q,eAArC,GAGfD,EAAgB,IAAImS,SAAS,UAAUvB,EAAc5Q,gBAArC,GAGhBoS,EAAerB,GACnB,EACA9Q,EACAoR,EAEAa,GAIIG,EAAgBxB,EAAmBxQ,SACrC,IAAI8R,SAAS,UAAUtB,EAAmBxQ,WAA1C,GACA,KAGAwQ,EAAmB9V,YACrB,IAAIoX,SAAS,UAAWtB,EAAmB9V,WAA3C,CAAuDsW,GAIrDrR,GACF8Q,EAAW9Q,GAIbwQ,WAAWI,EAAcpZ,QAAQ,YAAa4a,EAAcC,GAG5D,MAAMC,EAAiB7H,IAGvB,IAAK,MAAMW,KAAQkH,EACmB,mBAAzBA,EAAelH,WACjBkH,EAAelH,GAK1B0F,EAAWN,WAAWS,eAGtBT,WAAWS,cAAgB,EAC7B,CC5HA,MAAMsB,SAAWpX,aACftC,KAAKpC,UAAW,YAAa,iBAC7B,QAIF,IAAI+b,QAAU,KAmCPnG,eAAeoG,cAAcC,GAElC,MAAM5P,MAAEA,EAAKN,MAAEA,GAAUiI,cAGjB9J,OAAQgS,KAAiBC,GAAiB9P,EAG5C+P,EAAgB,CACpB9P,UAAUP,EAAMK,kBAAmB,QACnCiQ,YAAa,MACb/W,KAAM2W,GAAiB,GACvBK,cAAc,EACdC,eAAe,EACfC,cAAc,EACdC,oBAAoB,EACpBC,gBAAiB,QACbR,GAAgBC,GAItB,IAAKJ,QAAS,CAEZ,IAAIY,EAAW,EAEf,MAAMC,EAAOhH,UACX,IACEvQ,IACE,EACA,yDAAyDsX,OAI3DZ,cAAgB3U,UAAUyV,OAAOT,EAClC,CAAC,MAAOnW,GAQP,GAPAD,aACE,EACAC,EACA,oDAIE0W,EAAW,IAOb,MAAM1W,EANNZ,IAAI,EAAG,sCAAsCsX,uBAGvC,IAAI5G,SAASK,GAAa0G,WAAW1G,EAAU,aAC/CwG,GAIT,GAGH,UACQA,IAGyB,UAA3BR,EAAc9P,UAChBjH,IAAI,EAAG,6CAIL6W,GACF7W,IAAI,EAAG,4CAEV,CAAC,MAAOY,GACP,MAAM,IAAI0Q,YACR,gEACA,KACAM,SAAShR,EACZ,CAED,IAAK8V,QACH,MAAM,IAAIpF,YAAY,2CAA4C,IAErE,CAGD,OAAOoF,OACT,CAQOnG,eAAemH,eAEhBhB,SAAWA,QAAQiB,iBACfjB,QAAQkB,QAEhBlB,QAAU,KACV1W,IAAI,EAAG,gCACT,CAgBOuQ,eAAesH,QAAQC,GAE5B,IAAKpB,UAAYA,QAAQiB,UACvB,MAAM,IAAIrG,YAAY,0CAA2C,KAgBnE,GAZAwG,EAAaC,WAAarB,QAAQmB,gBAG5BC,EAAaC,KAAKC,iBAAgB,SAGlCC,gBAAgBH,EAAaC,MAGnCG,eAAeJ,EAAaC,OAGvBD,EAAaC,MAAQD,EAAaC,KAAKI,WAC1C,MAAM,IAAI7G,YAAY,2CAA4C,IAEtE,CAkBOf,eAAe6H,UAAUN,EAAcO,GAAY,GACxD,IACE,GAAIP,EAAaC,OAASD,EAAaC,KAAKI,WAgB1C,OAfIE,SAEIP,EAAaC,KAAKO,KAAK,cAAe,CAC1CC,UAAW,2BAIPN,gBAAgBH,EAAaC,aAG7BD,EAAaC,KAAKS,UAAS,KAC/BC,SAASC,KAAKC,UACZ,4DAA4D,KAG3D,CAEV,CAAC,MAAO/X,GACPD,aACE,EACAC,EACA,yBAAyBkX,EAAac,mDAIxCd,EAAae,UAAYlK,aAAa7I,KAAKG,UAAY,CACxD,CACD,OAAO,CACT,CAiBOsK,eAAeuI,iBAAiBf,EAAMhD,GAE3C,MAAMgE,EAAoB,GAGpBvU,EAAYuQ,EAAmBvQ,UACrC,GAAIA,EAAW,CACb,MAAMwU,EAAa,GAUnB,GAPIxU,EAAUyU,IACZD,EAAW9X,KAAK,CACdgY,QAAS1U,EAAUyU,KAKnBzU,EAAU2U,MACZ,IAAK,MAAM7X,KAAQkD,EAAU2U,MAAO,CAClC,MAAMC,GAAW9X,EAAKhC,WAAW,QAGjC0Z,EAAW9X,KACTkY,EACI,CACEF,QAAS7Z,aAAanD,gBAAgBoF,GAAO,SAE/C,CACExG,IAAKwG,GAGd,CAGH,IAAK,MAAM+X,KAAcL,EACvB,IACED,EAAkB7X,WAAW6W,EAAKuB,aAAaD,GAChD,CAAC,MAAOzY,GACPD,aAAa,EAAGC,EAAO,8CACxB,CAEHoY,EAAWlb,OAAS,EAGpB,MAAMyb,EAAc,GACpB,GAAI/U,EAAUgV,IAAK,CACjB,IAAIC,EAAajV,EAAUgV,IAAIE,MAAM,uBACrC,GAAID,EAEF,IAAK,IAAIE,KAAiBF,EACpBE,IACFA,EAAgBA,EACb9d,QAAQ,OAAQ,IAChBA,QAAQ,UAAW,IACnBA,QAAQ,KAAM,IACdA,QAAQ,KAAM,IACdA,QAAQ,IAAK,IACbA,QAAQ,MAAO,IACf0B,OAGCoc,EAAcra,WAAW,QAC3Bia,EAAYrY,KAAK,CACfpG,IAAK6e,IAEE5E,EAAmB7V,oBAC5Bqa,EAAYrY,KAAK,CACfrE,KAAME,KAAKpC,UAAWgf,MAQhCJ,EAAYrY,KAAK,CACfgY,QAAS1U,EAAUgV,IAAI3d,QAAQ,sBAAuB,KAAO,MAG/D,IAAK,MAAM+d,KAAeL,EACxB,IACER,EAAkB7X,WAAW6W,EAAK8B,YAAYD,GAC/C,CAAC,MAAOhZ,GACPD,aACE,EACAC,EACA,+CAEH,CAEH2Y,EAAYzb,OAAS,CACtB,CACF,CACD,OAAOib,CACT,CAeOxI,eAAeuJ,mBAAmB/B,EAAMgB,GAC7C,IACE,IAAK,MAAMgB,KAAYhB,QACfgB,EAASC,gBAIXjC,EAAKS,UAAS,KAElB,GAA0B,oBAAf9D,WAA4B,CAErC,MAAMuF,EAAYvF,WAAWwF,OAG7B,GAAIhf,MAAMC,QAAQ8e,IAAcA,EAAUnc,OAExC,IAAK,MAAMqc,KAAYF,EACrBE,GAAYA,EAASC,UAErB1F,WAAWwF,OAAO9d,OAGvB,CAGD,SAAUie,GAAmB5B,SAAS6B,qBAAqB,WAErD,IAAMC,GAAkB9B,SAAS6B,qBAAqB,aAElDE,GAAiB/B,SAAS6B,qBAAqB,QAGzD,IAAK,MAAMG,IAAW,IACjBJ,KACAE,KACAC,GAEHC,EAAQC,QACT,GAEJ,CAAC,MAAO9Z,GACPD,aAAa,EAAGC,EAAO,8CACxB,CACH,CAYA2P,eAAe0H,gBAAgBF,SAEvBA,EAAK4C,WAAWlE,SAAU,CAAE8B,UAAW,2BAGvCR,EAAKuB,aAAa,CAAEzc,KAAME,KAAKsV,eAAgB,sBAG/C0F,EAAKS,SAAS/D,gBACtB,CAWA,SAASyD,eAAeH,GAEtB,MAAM/Q,MAAEA,GAAU2H,aAGlBoJ,EAAK9G,GAAG,aAAaV,UAGfwH,EAAKI,UAER,IAICnR,EAAMnC,QAAUmC,EAAMG,iBACxB4Q,EAAK9G,GAAG,WAAYlQ,IAClBR,QAAQP,IAAI,WAAWe,EAAQoQ,SAAS,GAG9C,CC5cA,IAAAyJ,YAAe,IAAM,yXCINC,YAACzX,GAAQ,8LAQlBwX,8EAIExX,wCCaDmN,eAAeuK,gBAAgB/C,EAAMjD,EAAeC,GAEzD,MAAMgE,EAAoB,GAE1B,IACE,IAAIgC,GAAQ,EAGZ,GAAIjG,EAAc1R,IAAK,CAIrB,GAHApD,IAAI,EAAG,mCAGoB,QAAvB8U,EAAc9Y,KAChB,OAAO8Y,EAAc1R,IAIvB2X,GAAQ,QAGFhD,EAAK4C,WAAWE,YAAY/F,EAAc1R,KAAM,CACpDmV,UAAW,oBAEnB,MACMvY,IAAI,EAAG,2CAGD+X,EAAKS,SAAS3D,YAAaC,EAAeC,GAMlDgE,EAAkB7X,cACN4X,iBAAiBf,EAAMhD,IAInC,MAAMiG,EAAOD,QACHhD,EAAKS,UAAU5U,IACnB,MAAMqX,EAAaxC,SAASyC,cAC1B,sCAIIC,EAAcF,EAAWvX,OAAO0X,QAAQ1c,MAAQkF,EAChDyX,EAAaJ,EAAWtX,MAAMyX,QAAQ1c,MAAQkF,EAUpD,OANA6U,SAASC,KAAK4C,MAAMC,KAAO3X,EAI3B6U,SAASC,KAAK4C,MAAME,OAAS,MAEtB,CACLL,cACAE,aACD,GACAvS,WAAWgM,EAAclR,cACtBmU,EAAKS,UAAS,KAElB,MAAM2C,YAAEA,EAAWE,WAAEA,GAAe/V,OAAOoP,WAAWwF,OAAO,GAO7D,OAFAzB,SAASC,KAAK4C,MAAMC,KAAO,EAEpB,CACLJ,cACAE,aACD,KAIDI,EAAEA,EAACC,EAAEA,SAAYC,eAAe5D,GAGhC6D,EAAiB/c,KAAKgd,IAC1Bhd,KAAKid,KAAKd,EAAKG,aAAerG,EAAcpR,SAIxCqY,EAAgBld,KAAKgd,IACzBhd,KAAKid,KAAKd,EAAKK,YAAcvG,EAAcnR,QAU7C,IAAIqY,EAEJ,aARMjE,EAAKkE,YAAY,CACrBvY,OAAQkY,EACRjY,MAAOoY,EACPG,kBAAmBnB,EAAQ,EAAIjS,WAAWgM,EAAclR,SAKlDkR,EAAc9Y,MACpB,IAAK,MACHggB,QAAeG,WAAWpE,GAC1B,MACF,IAAK,MACL,IAAK,OACHiE,QAAeI,aACbrE,EACAjD,EAAc9Y,KACd,CACE2H,MAAOoY,EACPrY,OAAQkY,EACRH,IACAC,KAEF5G,EAAc1Q,sBAEhB,MACF,IAAK,MACH4X,QAAeK,WACbtE,EACA6D,EACAG,EACAjH,EAAc1Q,sBAEhB,MACF,QACE,MAAM,IAAIkN,YACR,uCAAuCwD,EAAc9Y,QACrD,KAMN,aADM8d,mBAAmB/B,EAAMgB,GACxBiD,CACR,CAAC,MAAOpb,GAEP,aADMkZ,mBAAmB/B,EAAMgB,GACxBnY,CACR,CACH,CAcA2P,eAAeoL,eAAe5D,GAC5B,OAAOA,EAAKuE,MAAM,oBAAqB7B,IACrC,MAAMgB,EAAEA,EAACC,EAAEA,EAAC/X,MAAEA,EAAKD,OAAEA,GAAW+W,EAAQ8B,wBACxC,MAAO,CACLd,IACAC,IACA/X,QACAD,OAAQ7E,KAAK2d,MAAM9Y,EAAS,EAAIA,EAAS,KAC1C,GAEL,CAaA6M,eAAe4L,WAAWpE,GACxB,OAAOA,EAAKuE,MACV,gCACC7B,GAAYA,EAAQgC,WAEzB,CAkBAlM,eAAe6L,aAAarE,EAAM/b,EAAM0gB,EAAMtY,GAC5C,OAAOsM,QAAQiM,KAAK,CAClB5E,EAAK6E,WAAW,CACd5gB,OACA0gB,OACAG,SAAU,SACVC,UAAU,EACVC,kBAAkB,EAClBC,uBAAuB,KACV,QAAThhB,EAAiB,CAAEihB,QAAS,IAAO,CAAA,EAEvCC,eAAwB,OAARlhB,IAElB,IAAI0U,SAAQ,CAACyM,EAAUvM,IACrB6G,YACE,IAAM7G,EAAO,IAAIU,YAAY,wBAAyB,OACtDlN,GAAwB,SAIhC,CAiBAmM,eAAe8L,WAAWtE,EAAMrU,EAAQC,EAAOS,GAE7C,aADM2T,EAAKqF,iBAAiB,UACrBrF,EAAKsF,IAAI,CAEd3Z,OAAQA,EAAS,EACjBC,QACAkZ,SAAU,SACV1X,QAASf,GAAwB,MAErC,CCnQA,IAAI0B,KAAO,KAGX,MAAMwX,UAAY,CAChBC,iBAAkB,EAClBC,iBAAkB,EAClBC,eAAgB,EAChBC,eAAgB,EAChBC,mBAAoB,EACpBC,uBAAwB,EACxBC,2BAA4B,EAC5BC,UAAW,EACXC,iBAAkB,GAqBbxN,eAAeyN,SAASC,EAAarH,SAEpCD,cAAcC,GAEpB,IAME,GALA5W,IACE,EACA,8CAA8Cie,EAAYlY,mBAAmBkY,EAAYjY,eAGvFF,KAKF,YAJA9F,IACE,EACA,yEAMAie,EAAYlY,WAAakY,EAAYjY,aACvCiY,EAAYlY,WAAakY,EAAYjY,YAIvCF,KAAO,IAAIoY,KAAK,IAEXC,SAASF,GACZja,IAAKia,EAAYlY,WACjB9B,IAAKga,EAAYjY,WACjBoY,qBAAsBH,EAAY/X,eAClCmY,oBAAqBJ,EAAY9X,cACjCmY,qBAAsBL,EAAY7X,eAClCmY,kBAAmBN,EAAY5X,YAC/BmY,0BAA2BP,EAAY3X,oBACvCmY,mBAAoBR,EAAY1X,eAChCmY,sBAAsB,IAIxB5Y,KAAKmL,GAAG,WAAWV,MAAOwJ,IAExB,MAAM4E,QAAoBvG,UAAU2B,GAAU,GAC9C/Z,IACE,EACA,yBAAyB+Z,EAASnB,gDAAgD+F,KACnF,IAGH7Y,KAAKmL,GAAG,kBAAkB,CAAC2N,EAAU7E,KACnC/Z,IACE,EACA,yBAAyB+Z,EAASnB,0CAEpCmB,EAAShC,KAAO,IAAI,IAGtB,MAAM8G,EAAmB,GAEzB,IAAK,IAAIrK,EAAI,EAAGA,EAAIyJ,EAAYlY,WAAYyO,IAC1C,IACE,MAAMuF,QAAiBjU,KAAKgZ,UAAUC,QACtCF,EAAiB3d,KAAK6Y,EACvB,CAAC,MAAOnZ,GACPD,aAAa,EAAGC,EAAO,+CACxB,CAIHie,EAAiBhX,SAASkS,IACxBjU,KAAKkZ,QAAQjF,EAAS,IAGxB/Z,IACE,EACA,4BAA2B6e,EAAiB/gB,OAAS,SAAS+gB,EAAiB/gB,oCAAsC,KAExH,CAAC,MAAO8C,GACP,MAAM,IAAI0Q,YACR,6DACA,KACAM,SAAShR,EACZ,CACH,CAYO2P,eAAe0O,WAIpB,GAHAjf,IAAI,EAAG,6DAGH8F,KAAM,CAER,IAAK,MAAMoZ,KAAUpZ,KAAKqZ,KACxBrZ,KAAKkZ,QAAQE,EAAOnF,UAIjBjU,KAAKsZ,kBACFtZ,KAAKsU,UACXpa,IAAI,EAAG,4CAET8F,KAAO,IACR,OAGK4R,cACR,CAmBOnH,eAAe8O,SAASlc,GAC7B,IAAImc,EAEJ,IAYE,GAXAtf,IAAI,EAAG,gDAGLsd,UAAUC,iBAGRpa,EAAQ2C,KAAKb,cACfsa,eAIGzZ,KACH,MAAM,IAAIwL,YACR,uDACA,KAKJ,MAAMkO,EAAiBrhB,cAGvB,IACE6B,IAAI,EAAG,qCAGPsf,QAAqBxZ,KAAKgZ,UAAUC,QAGhC5b,EAAQyB,OAAOK,cACjBjF,IACE,EACA,gBAAemD,EAAQsc,UAAY,YAAYtc,EAAQsc,gBAAkB,IACzE,kCAAkCD,SAGvC,CAAC,MAAO5e,GACP,MAAM,IAAI0Q,YACR,UACEnO,EAAQsc,UAAY,YAAYtc,EAAQsc,gBAAkB,0DACJD,SACxD,KACA5N,SAAShR,EACZ,CAGD,GAFAZ,IAAI,EAAG,qCAEFsf,EAAavH,KAGhB,MADAuH,EAAazG,UAAY1V,EAAQ2C,KAAKG,UAAY,EAC5C,IAAIqL,YACR,mEACA,KAKJ,MAAMoO,EAAYliB,iBAElBwC,IACE,EACA,yBAAyBsf,EAAa1G,2CAIxC,MAAM+G,EAAgBxhB,cAGhB6d,QAAelB,gBACnBwE,EAAavH,KACb5U,EAAQH,OACRG,EAAQkB,aAIV,GAAI2X,aAAkB1L,MAmBpB,KANuB,0BAAnB0L,EAAOjb,UAETue,EAAazG,UAAY1V,EAAQ2C,KAAKG,UAAY,EAClDqZ,EAAavH,KAAO,MAIJ,iBAAhBiE,EAAO/L,MACY,0BAAnB+L,EAAOjb,QAED,IAAIuQ,YACR,UACEnO,EAAQsc,UAAY,YAAYtc,EAAQsc,gBAAkB,mHAE5D7N,SAASoK,GAEL,IAAI1K,YACR,UACEnO,EAAQsc,UAAY,YAAYtc,EAAQsc,gBAAkB,sCACxBE,UACpC/N,SAASoK,GAKX7Y,EAAQyB,OAAOK,cACjBjF,IACE,EACA,gBAAemD,EAAQsc,UAAY,YAAYtc,EAAQsc,gBAAkB,IACzE,sCAAsCE,UAK1C7Z,KAAKkZ,QAAQM,GAIb,MACMM,EADUpiB,iBACakiB,EAS7B,OAPApC,UAAUQ,WAAa8B,EACvBtC,UAAUS,iBACRT,UAAUQ,YAAcR,UAAUE,iBAEpCxd,IAAI,EAAG,4BAA4B4f,QAG5B,CACL5D,SACA7Y,UAEH,CAAC,MAAOvC,GAOP,OANE0c,UAAUG,eAER6B,GACFxZ,KAAKkZ,QAAQM,GAGT1e,CACP,CACH,CAqBO,SAASif,eACd,OAAOvC,SACT,CAUO,SAASwC,kBACd,MAAO,CACL9b,IAAK8B,KAAK9B,IACVC,IAAK6B,KAAK7B,IACVkb,KAAMrZ,KAAKia,UACXC,UAAWla,KAAKma,UAChBC,WAAYpa,KAAKia,UAAYja,KAAKma,UAClCE,gBAAiBra,KAAKsa,qBACtBC,eAAgBva,KAAKwa,oBACrBC,mBAAoBza,KAAK0a,wBACzBC,gBAAiB3a,KAAK2a,gBAAgB3iB,OACtC4iB,YACE5a,KAAKia,UACLja,KAAKma,UACLna,KAAKsa,qBACLta,KAAKwa,oBACLxa,KAAK0a,wBACL1a,KAAK2a,gBAAgB3iB,OAE3B,CASO,SAASyhB,cACd,MAAMvb,IACJA,EAAGC,IACHA,EAAGkb,KACHA,EAAIa,UACJA,EAASE,WACTA,EAAUC,gBACVA,EAAeE,eACfA,EAAcE,mBACdA,EAAkBE,gBAClBA,EAAeC,YACfA,GACEZ,kBAEJ9f,IAAI,EAAG,2DAA2DgE,MAClEhE,IAAI,EAAG,2DAA2DiE,MAClEjE,IAAI,EAAG,wCAAwCmf,MAC/Cnf,IAAI,EAAG,wCAAwCggB,MAC/ChgB,IACE,EACA,+DAA+DkgB,MAEjElgB,IACE,EACA,0DAA0DmgB,MAE5DngB,IACE,EACA,yDAAyDqgB,MAE3DrgB,IACE,EACA,2DAA2DugB,MAE7DvgB,IACE,EACA,2DAA2DygB,MAE7DzgB,IAAI,EAAG,uCAAuC0gB,KAChD,CAWA,SAASvC,SAASF,GAChB,MAAO,CAcL0C,OAAQpQ,UAEN,MAAMuH,EAAe,CACnBc,GAAIgI,KAEJ/H,UAAWha,KAAKE,MAAMF,KAAKgiB,UAAY5C,EAAYhY,UAAY,KAGjE,IAEE,MAAM6a,EAAYtjB,iBAclB,aAXMqa,QAAQC,GAGd9X,IACE,EACA,yBAAyB8X,EAAac,6CACpCpb,iBAAmBsjB,QAKhBhJ,CACR,CAAC,MAAOlX,GAKP,MAJAZ,IACE,EACA,yBAAyB8X,EAAac,qDAElChY,CACP,GAgBHmgB,SAAUxQ,MAAOuH,GAiBVA,EAAaC,KASdD,EAAaC,KAAKI,YACpBnY,IACE,EACA,yBAAyB8X,EAAac,yDAEjC,GAILd,EAAaC,KAAKiJ,YAAYC,UAChCjhB,IACE,EACA,yBAAyB8X,EAAac,wDAEjC,KAKPqF,EAAYhY,aACV6R,EAAae,UAAYoF,EAAYhY,aAEvCjG,IACE,EACA,yBAAyB8X,EAAac,yCAAyCqF,EAAYhY,yCAEtF,IAlCPjG,IACE,EACA,yBAAyB8X,EAAac,sDAEjC,GA8CXwB,QAAS7J,MAAOuH,IAMd,GALA9X,IACE,EACA,yBAAyB8X,EAAac,8BAGpCd,EAAaC,OAASD,EAAaC,KAAKI,WAC1C,IAEEL,EAAaC,KAAKmJ,mBAAmB,aACrCpJ,EAAaC,KAAKmJ,mBAAmB,WACrCpJ,EAAaC,KAAKmJ,mBAAmB,uBAG/BpJ,EAAaC,KAAKH,OACzB,CAAC,MAAOhX,GAKP,MAJAZ,IACE,EACA,yBAAyB8X,EAAac,mDAElChY,CACP,CACF,EAGP,CCxkBO,SAASugB,SAASlkB,GAEvB,MAAMqI,EAAS,IAAI8b,MAAM,IAAI9b,OAM7B,OAHe+b,UAAU/b,GAGX6b,SAASlkB,EAAO,CAAEqkB,SAAU,CAAC,kBAC7C,CCDA,IAAIhd,oBAAqB,EAqBlBiM,eAAegR,aAAape,GAEjC,IAAIA,IAAWA,EAAQH,OAwCrB,MAAM,IAAIsO,YACR,kKACA,WAxCIkQ,YACJ,CAAExe,OAAQG,EAAQH,OAAQqB,YAAalB,EAAQkB,cAC/CkM,MAAO3P,EAAO6gB,KAEZ,GAAI7gB,EACF,MAAMA,EAIR,MAAM4C,IAAEA,EAAGvH,QAAEA,EAAOD,KAAEA,GAASylB,EAAKte,QAAQH,OAG5C,IACMQ,EAEFsQ,cACE,GAAG7X,EAAQE,MAAM,KAAKC,SAAW,cACjCY,UAAUykB,EAAKzF,OAAQhgB,IAIzB8X,cACE7X,GAAW,SAASD,IACX,QAATA,EAAiBkB,OAAOC,KAAKskB,EAAKzF,OAAQ,UAAYyF,EAAKzF,OAGhE,CAAC,MAAOpb,GACP,MAAM,IAAI0Q,YACR,sCACA,KACAM,SAAShR,EACZ,OAGKqe,UAAU,GASxB,CAsBO1O,eAAemR,YAAYve,GAEhC,KAAIA,GAAWA,EAAQH,QAAUG,EAAQH,OAAOK,OA4E9C,MAAM,IAAIiO,YACR,+GACA,KA9EmD,CAErD,MAAMqQ,EAAiB,GAGvB,IAAK,IAAIC,KAAQze,EAAQH,OAAOK,MAAMlH,MAAM,MAAQ,GAClDylB,EAAOA,EAAKzlB,MAAM,KACE,IAAhBylB,EAAK9jB,OACP6jB,EAAezgB,KACbsgB,YACE,CACExe,OAAQ,IACHG,EAAQH,OACXC,OAAQ2e,EAAK,GACb3lB,QAAS2lB,EAAK,IAEhBvd,YAAalB,EAAQkB,cAEvB,CAACzD,EAAO6gB,KAEN,GAAI7gB,EACF,MAAMA,EAIR,MAAM4C,IAAEA,EAAGvH,QAAEA,EAAOD,KAAEA,GAASylB,EAAKte,QAAQH,OAG5C,IACMQ,EAEFsQ,cACE,GAAG7X,EAAQE,MAAM,KAAKC,SAAW,cACjCY,UAAUykB,EAAKzF,OAAQhgB,IAIzB8X,cACE7X,EACS,QAATD,EACIkB,OAAOC,KAAKskB,EAAKzF,OAAQ,UACzByF,EAAKzF,OAGd,CAAC,MAAOpb,GACP,MAAM,IAAI0Q,YACR,sCACA,KACAM,SAAShR,EACZ,MAKPZ,IAAI,EAAG,uDAKX,MAAM6hB,QAAqBnR,QAAQoR,WAAWH,SAGxC1C,WAGN4C,EAAaha,SAAQ,CAACmU,EAAQzM,KAExByM,EAAO+F,QACTphB,aACE,EACAqb,EAAO+F,OACP,+BAA+BxS,EAAQ,sCAE1C,GAEP,CAMA,CAoCOgB,eAAeiR,YAAYQ,EAAkBC,GAClD,IAEE,IAAKvkB,SAASskB,GACZ,MAAM,IAAI1Q,YACR,qFACA,KAKJ,MAAMnO,EAAUyL,cACd,CACE5L,OAAQgf,EAAiBhf,OACzBqB,YAAa2d,EAAiB3d,cAEhC,GAIIyQ,EAAgB3R,EAAQH,OAM9B,GAHAhD,IAAI,EAAG,2CAGsB,OAAzB8U,EAAc7R,OAAiB,CAGjC,IAAIif,EAFJliB,IAAI,EAAG,mDAGP,IAEEkiB,EAAc7iB,aACZnD,gBAAgB4Y,EAAc7R,QAC9B,OAEH,CAAC,MAAOrC,GACP,MAAM,IAAI0Q,YACR,mDACA,KACAM,SAAShR,EACZ,CAGD,GAAIkU,EAAc7R,OAAO7D,SAAS,QAEhC0V,EAAc1R,IAAM8e,MACf,KAAIpN,EAAc7R,OAAO7D,SAAS,SAIvC,MAAM,IAAIkS,YACR,kDACA,KAJFwD,EAAc5R,MAAQgf,CAMvB,CACF,CAGD,GAA0B,OAAtBpN,EAAc1R,IAAc,CAC9BpD,IAAI,EAAG,qDAGL6f,eAAejC,uBAGjB,MAAM5B,QAAemG,eACnBhB,SAASrM,EAAc1R,KACvBD,GAOF,QAHE0c,eAAenC,eAGVuE,EAAY,KAAMjG,EAC1B,CAGD,GAA4B,OAAxBlH,EAAc5R,OAA4C,OAA1B4R,EAAc3R,QAAkB,CAClEnD,IAAI,EAAG,sDAGL6f,eAAehC,2BAGjB,MAAM7B,QAAeoG,mBACnBtN,EAAc5R,OAAS4R,EAAc3R,QACrCA,GAOF,QAHE0c,eAAelC,mBAGVsE,EAAY,KAAMjG,EAC1B,CAGD,OAAOiG,EACL,IAAI3Q,YACF,gJACA,KAGL,CAAC,MAAO1Q,GACP,OAAOqhB,EAAYrhB,EACpB,CACH,CASO,SAASyhB,wBACd,OAAO/d,kBACT,CAUO,SAASge,sBAAsB5jB,GACpC4F,mBAAqB5F,CACvB,CAkBA6R,eAAe4R,eAAeI,EAAepf,GAE3C,GAC2B,iBAAlBof,IACNA,EAAchP,QAAQ,SAAW,GAAKgP,EAAchP,QAAQ,UAAY,GAYzE,OAVAvT,IAAI,EAAG,iCAGPmD,EAAQH,OAAOI,IAAMmf,EAGrBpf,EAAQH,OAAOE,MAAQ,KACvBC,EAAQH,OAAOG,QAAU,KAGlBqf,eAAerf,GAEtB,MAAM,IAAImO,YAAY,mCAAoC,IAE9D,CAkBAf,eAAe6R,mBAAmBG,EAAepf,GAC/CnD,IAAI,EAAG,uCAGP,MAAM6P,EAAqBL,gBACzB+S,GACA,EACApf,EAAQkB,YAAYC,oBAItB,GACyB,OAAvBuL,GAC8B,iBAAvBA,IACNA,EAAmBvQ,WAAW,OAC9BuQ,EAAmBzQ,SAAS,KAE7B,MAAM,IAAIkS,YACR,oPACA,KAWJ,OANAnO,EAAQH,OAAOE,MAAQ2M,EAGvB1M,EAAQH,OAAOI,IAAM,KAGdof,eAAerf,EACxB,CAcAoN,eAAeiS,eAAerf,GAC5B,MAAQH,OAAQ8R,EAAezQ,YAAa0Q,GAAuB5R,EAkCnE,OA/BA2R,EAAc9Y,KAAOK,QAAQyY,EAAc9Y,KAAM8Y,EAAc7Y,SAG/D6Y,EAAc7Y,QAAUF,WAAW+Y,EAAc9Y,KAAM8Y,EAAc7Y,SAGrE6Y,EAAcpZ,OAASD,UAAUqZ,EAAcpZ,QAG/CsE,IACE,EACA,+BAA+B+U,EAAmBzQ,mBAAqB,UAAY,iBAIrFme,mBAAmB1N,EAAoBA,EAAmBzQ,oBAG1Doe,sBACE5N,EACAC,EAAmB7V,mBACnB6V,EAAmBzQ,oBAIrBnB,EAAQH,OAAS,IACZ8R,KACA6N,eAAe7N,IAIbuK,SAASlc,EAClB,CAqBA,SAASwf,eAAe7N,GAEtB,MAAQqB,MAAOyM,EAAcnN,UAAWoN,GACtC/N,EAAc3R,SAAWqM,gBAAgBsF,EAAc5R,SAAU,GAG3DiT,MAAO2M,EAAoBrN,UAAWsN,GAC5CvT,gBAAgBsF,EAAc5Q,iBAAkB,GAG1CiS,MAAO6M,EAAmBvN,UAAWwN,GAC3CzT,gBAAgBsF,EAAc3Q,gBAAiB,EAM3CP,EAAQnF,YACZI,KAAKoF,IACH,GACApF,KAAKmF,IACH8Q,EAAclR,OACZif,GAAkBjf,OAClBmf,GAAwBnf,OACxBqf,GAAuBrf,OACvBkR,EAAc/Q,cACd,EACF,IAGJ,GA4BIiX,EAAO,CAAEtX,OAvBboR,EAAcpR,QACdmf,GAAkBK,cAClBN,GAAclf,QACdqf,GAAwBG,cACxBJ,GAAoBpf,QACpBuf,GAAuBC,cACvBF,GAAmBtf,QACnBoR,EAAcjR,eACd,IAeqBF,MAXrBmR,EAAcnR,OACdkf,GAAkBM,aAClBP,GAAcjf,OACdof,GAAwBI,aACxBL,GAAoBnf,OACpBsf,GAAuBE,aACvBH,GAAmBrf,OACnBmR,EAAchR,cACd,IAG4BF,SAG9B,IAAK,IAAKwf,EAAO1kB,KAAUrD,OAAO6T,QAAQ8L,GACxCA,EAAKoI,GACc,iBAAV1kB,GAAsBA,EAAM7C,QAAQ,SAAU,IAAM6C,EAI/D,OAAOsc,CACT,CAkBA,SAASyH,mBAAmB1N,EAAoBzQ,GAE9C,GAAIA,EAAoB,CAEtB,GAA4C,iBAAjCyQ,EAAmBvQ,UAE5BuQ,EAAmBvQ,UAAY6e,iBAC7BtO,EAAmBvQ,UACnBuQ,EAAmB7V,oBACnB,QAEG,IAAK6V,EAAmBvQ,UAC7B,IAEEuQ,EAAmBvQ,UAAY6e,iBAC7BhkB,aAAanD,gBAAgB,kBAAmB,QAChD6Y,EAAmB7V,oBACnB,EAEH,CAAC,MAAO0B,GACPZ,IAAI,EAAG,4DACR,CAIH,IAEE+U,EAAmB9V,WAAaD,WAC9B+V,EAAmB9V,WACnB8V,EAAmB7V,mBAEtB,CAAC,MAAO0B,GACPD,aAAa,EAAGC,EAAO,8CAGvBmU,EAAmB9V,WAAa,IACjC,CAGD,IAEE8V,EAAmBxQ,SAAWvF,WAC5B+V,EAAmBxQ,SACnBwQ,EAAmB7V,oBACnB,EAEH,CAAC,MAAO0B,GACPD,aAAa,EAAGC,EAAO,4CAGvBmU,EAAmBxQ,SAAW,IAC/B,CAGG,CAAC,UAAM9D,GAAW3E,SAASiZ,EAAmB9V,aAChDe,IAAI,EAAG,uDAIL,CAAC,UAAMS,GAAW3E,SAASiZ,EAAmBxQ,WAChDvE,IAAI,EAAG,qDAIL,CAAC,UAAMS,GAAW3E,SAASiZ,EAAmBvQ,YAChDxE,IAAI,EAAG,qDAEb,MAII,GACE+U,EAAmBxQ,UACnBwQ,EAAmBvQ,WACnBuQ,EAAmB9V,WAQnB,MALA8V,EAAmBxQ,SAAW,KAC9BwQ,EAAmBvQ,UAAY,KAC/BuQ,EAAmB9V,WAAa,KAG1B,IAAIqS,YACR,oGACA,IAIR,CAkBA,SAAS+R,iBACP7e,EAAY,KACZtF,EACAoF,GAGA,MAAMgf,EAAe,CAAC,KAAM,MAAO,SAEnC,IAAIC,EAAmB/e,EACnBgf,GAAmB,EAGvB,GAAItkB,GAAsBsF,EAAUpF,SAAS,SAC3C,IACEmkB,EAAmB/T,gBACjBnQ,aAAanD,gBAAgBsI,GAAY,SACzC,EACAF,EAER,CAAM,MACA,OAAO,IACR,MAGDif,EAAmB/T,gBAAgBhL,GAAW,EAAOF,GAGjDif,IAAqBrkB,UAChBqkB,EAAiBpK,MAK5B,IAAK,MAAMsK,KAAYF,EAChBD,EAAaxnB,SAAS2nB,GAEfD,IACVA,GAAmB,UAFZD,EAAiBE,GAO5B,OAAKD,GAKDD,EAAiBpK,QACnBoK,EAAiBpK,MAAQoK,EAAiBpK,MAAM5Q,KAAK5K,GAASA,EAAKJ,WAC9DgmB,EAAiBpK,OAASoK,EAAiBpK,MAAMrb,QAAU,WACvDylB,EAAiBpK,OAKrBoK,GAZE,IAaX,CAoBA,SAASb,sBACP5N,EACA5V,EACAoF,GAGA,CAAC,gBAAiB,gBAAgBuD,SAAS6b,IACzC,IAEM5O,EAAc4O,KAGdxkB,GACsC,iBAA/B4V,EAAc4O,IACrB5O,EAAc4O,GAAatkB,SAAS,SAGpC0V,EAAc4O,GAAelU,gBAC3BnQ,aAAanD,gBAAgB4Y,EAAc4O,IAAe,SAC1D,EACApf,GAIFwQ,EAAc4O,GAAelU,gBAC3BsF,EAAc4O,IACd,EACApf,GAIP,CAAC,MAAO1D,GACPD,aACE,EACAC,EACA,iBAAiB8iB,yBAInB5O,EAAc4O,GAAe,IAC9B,KAIC,CAAC,UAAMjjB,GAAW3E,SAASgZ,EAAc5Q,gBAC3ClE,IAAI,EAAG,0DAIL,CAAC,UAAMS,GAAW3E,SAASgZ,EAAc3Q,eAC3CnE,IAAI,EAAG,wDAEX,CCl0BA,MAAM2jB,SAAW,GASV,SAASC,SAAShL,GACvB+K,SAASziB,KAAK0X,EAChB,CAQO,SAASiL,iBACd7jB,IAAI,EAAG,2DACP,IAAK,MAAM4Y,KAAM+K,SACfG,cAAclL,GACdmL,aAAanL,EAEjB,CCfA,SAASoL,mBAAmBpjB,EAAOqjB,EAASlT,EAAUmT,GAUpD,OARAvjB,aAAa,EAAGC,GAGmB,gBAA/B+N,aAAajI,MAAMC,gBACd/F,EAAMK,MAIRijB,EAAKtjB,EACd,CAYA,SAASujB,sBAAsBvjB,EAAOqjB,EAASlT,EAAUmT,GAEvD,MAAMnjB,QAAEA,EAAOE,MAAEA,GAAUL,EAGrB4Q,EAAa5Q,EAAM4Q,YAAc,IAGvCT,EAASqT,OAAO5S,GAAY6S,KAAK,CAAE7S,aAAYzQ,UAASE,SAC1D,CAOe,SAASqjB,gBAAgBC,GAEtCA,EAAIC,IAAIR,oBAGRO,EAAIC,IAAIL,sBACV,CC5Ce,SAASM,uBAAuBF,EAAKG,GAClD,IAEE,GAAIH,GAAOG,EAAoB7f,OAAQ,CACrC,MAAM9D,EACJ,yEAGI4jB,EAAc,CAClBrf,OAAQof,EAAoBpf,QAAU,EACtCD,YAAaqf,EAAoBrf,aAAe,GAChDE,MAAOmf,EAAoBnf,OAAS,EACpCC,WAAYkf,EAAoBlf,aAAc,EAC9CC,QAASif,EAAoBjf,SAAW,KACxCC,UAAWgf,EAAoBhf,WAAa,MAI1Cif,EAAYnf,YACd+e,EAAI1f,OAAO,eAIb,MAAM+f,EAAUC,UAAU,CAExBC,SAA+B,GAArBH,EAAYrf,OAAc,IAEpCyf,MAAOJ,EAAYtf,YAEnB2f,QAASL,EAAYpf,MACrB0f,QAAS,CAAChB,EAASlT,KACjBA,EAASmU,OAAO,CACdb,KAAM,KACJtT,EAASqT,OAAO,KAAKe,KAAK,CAAEpkB,WAAU,EAExCqkB,QAAS,KACPrU,EAASqT,OAAO,KAAKe,KAAKpkB,EAAQ,GAEpC,EAEJskB,KAAOpB,GAGqB,OAAxBU,EAAYlf,SACc,OAA1Bkf,EAAYjf,WACZue,EAAQqB,MAAMlqB,MAAQupB,EAAYlf,SAClCwe,EAAQqB,MAAMC,eAAiBZ,EAAYjf,YAE3C1F,IAAI,EAAG,2CACA,KAObukB,EAAIC,IAAII,GAER5kB,IACE,EACA,8CAA8C2kB,EAAYtf,4BAA4Bsf,EAAYrf,8CAA8Cqf,EAAYnf,cAE/J,CACF,CAAC,MAAO5E,GACP,MAAM,IAAI0Q,YACR,yEACA,KACAM,SAAShR,EACZ,CACH,CCzDA,SAAS4kB,sBAAsBvB,EAASlT,EAAUmT,GAChD,IAEE,MAAMuB,EAAcxB,EAAQyB,QAAQ,iBAAmB,GAGvD,IACGD,EAAY3pB,SAAS,sBACrB2pB,EAAY3pB,SAAS,uCACrB2pB,EAAY3pB,SAAS,uBAEtB,MAAM,IAAIwV,YACR,iHACA,KAKJ,OAAO4S,GACR,CAAC,MAAOtjB,GACP,OAAOsjB,EAAKtjB,EACb,CACH,CAmBA,SAAS+kB,sBAAsB1B,EAASlT,EAAUmT,GAChD,IAEE,MAAMxL,EAAOuL,EAAQvL,KAGf+G,EAAYmB,KAGlB,IAAKlI,GAAQ9a,cAAc8a,GAQzB,MAPA1Y,IACE,EACA,yBAAyByf,yBACvBwE,EAAQyB,QAAQ,oBAAsBzB,EAAQ2B,WAAWC,2DAIvD,IAAIvU,YACR,yBAAyBmO,8JACzB,KAKJ,MAAMnb,EAAqB+d,wBAGrBnf,EAAQsM,gBAEZkJ,EAAKxV,OAASwV,EAAKvV,SAAWuV,EAAKzV,QAAUyV,EAAK+I,MAElD,EAEAnd,GAIF,GAAc,OAAVpB,IAAmBwV,EAAKtV,IAQ1B,MAPApD,IACE,EACA,yBAAyByf,yBACvBwE,EAAQyB,QAAQ,oBAAsBzB,EAAQ2B,WAAWC,2FACmBjW,KAAKQ,UAAUsI,OAGzF,IAAIpH,YACR,YAAYmO,sRACZ,KAKJ,GAAI/G,EAAKtV,KAAOrF,uBAAuB2a,EAAKtV,KAC1C,MAAM,IAAIkO,YACR,YAAYmO,iMACZ,KA0CJ,OArCAwE,EAAQ6B,iBAAmB,CAEzBrG,YACAzc,OAAQ,CACNE,QACAE,IAAKsV,EAAKtV,IACVnH,QACEyc,EAAKzc,SACL,GAAGgoB,EAAQ8B,OAAOC,UAAY,WAAWtN,EAAK1c,MAAQ,QACxDA,KAAM0c,EAAK1c,KACXN,OAAQgd,EAAKhd,OACb8H,IAAKkV,EAAKlV,IACVC,WAAYiV,EAAKjV,WACjBC,OAAQgV,EAAKhV,OACbC,MAAO+U,EAAK/U,MACZC,MAAO8U,EAAK9U,MACZM,cAAesL,gBACbkJ,EAAKxU,eACL,EACAI,GAEFH,aAAcqL,gBACZkJ,EAAKvU,cACL,EACAG,IAGJD,YAAa,CACXC,qBACApF,oBAAoB,EACpBD,WAAYyZ,EAAKzZ,WACjBsF,SAAUmU,EAAKnU,SACfC,UAAWgL,gBAAgBkJ,EAAKlU,WAAW,EAAMF,KAK9C4f,GACR,CAAC,MAAOtjB,GACP,OAAOsjB,EAAKtjB,EACb,CACH,CAOe,SAASqlB,qBAAqB1B,GAE3CA,EAAI2B,KAAK,CAAC,IAAK,cAAeV,uBAG9BjB,EAAI2B,KAAK,CAAC,IAAK,cAAeP,sBAChC,CC7KA,MAAMQ,aAAe,CACnBC,IAAK,YACLC,KAAM,aACNC,IAAK,YACLjJ,IAAK,kBACLja,IAAK,iBAgBPmN,eAAegW,cAActC,EAASlT,EAAUmT,GAC9C,IAEE,MAAMsC,EAAiBroB,cAGvB,IAAIsoB,GAAoB,EACxBxC,EAAQyC,OAAOzV,GAAG,SAAU0V,IACtBA,IACFF,GAAoB,EACrB,IAIH,MAAMhW,EAAiBwT,EAAQ6B,iBAGzBrG,EAAYhP,EAAegP,UAGjCzf,IAAI,EAAG,qBAAqByf,4CAGtB+B,YAAY/Q,GAAgB,CAAC7P,EAAO6gB,KAKxC,GAHAwC,EAAQyC,OAAOxF,mBAAmB,SAG9BuF,EACFzmB,IACE,EACA,qBAAqByf,mFAHzB,CASA,GAAI7e,EACF,MAAMA,EAIR,IAAK6gB,IAASA,EAAKzF,OASjB,MARAhc,IACE,EACA,qBAAqByf,qBACnBwE,EAAQyB,QAAQ,oBAChBzB,EAAQ2B,WAAWC,mDACiBpE,EAAKzF,WAGvC,IAAI1K,YACR,qBAAqBmO,yGACrB,KAKJ,GAAIgC,EAAKzF,OAAQ,CACfhc,IACE,EACA,qBAAqByf,yCAAiD+G,UAIxE,MAAMxqB,KAAEA,EAAIwH,IAAEA,EAAGC,WAAEA,EAAUxH,QAAEA,GAAYwlB,EAAKte,QAAQH,OAGxD,OAAIQ,EACKuN,EAASoU,KAAKnoB,UAAUykB,EAAKzF,OAAQhgB,KAI9C+U,EAAS6V,OAAO,eAAgBT,aAAanqB,IAAS,aAGjDyH,GACHsN,EAAS8V,WAAW5qB,GAIN,QAATD,EACH+U,EAASoU,KAAK1D,EAAKzF,QACnBjL,EAASoU,KAAKjoB,OAAOC,KAAKskB,EAAKzF,OAAQ,WAC5C,CAlDA,CAkDA,GAEJ,CAAC,MAAOpb,GACP,OAAOsjB,EAAKtjB,EACb,CACH,CASe,SAASkmB,aAAavC,GAKnCA,EAAI2B,KAAK,IAAKK,eAMdhC,EAAI2B,KAAK,aAAcK,cACzB,CCpIA,MAAMQ,gBAAkB,IAAIzpB,KAGtB0pB,YAAcpX,KAAKpB,MACvBnP,aAAatC,KAAKpC,UAAW,gBAAiB,SAI1CssB,aAAe,GAGfC,eAAiB,IAGjBC,WAAa,GAUnB,SAASC,0BACP,OAAOH,aAAa7X,QAAO,CAACiY,EAAGC,IAAMD,EAAIC,GAAG,GAAKL,aAAanpB,MAChE,CAUA,SAASypB,oBACP,OAAOC,aAAY,KACjB,MAAMC,EAAQ5H,eACR6H,EACuB,IAA3BD,EAAMlK,iBACF,EACCkK,EAAMjK,iBAAmBiK,EAAMlK,iBAAoB,IAE1D0J,aAAa/lB,KAAKwmB,GACdT,aAAanpB,OAASqpB,YACxBF,aAAa7qB,OACd,GACA8qB,eACL,CASe,SAASS,aAAapD,GAGnCX,SAAS2D,qBAKThD,EAAIzT,IAAI,WAAW,CAACmT,EAASlT,EAAUmT,KACrC,IACElkB,IAAI,EAAG,qCAEP,MAAMynB,EAAQ5H,eACR+H,EAASX,aAAanpB,OACtB+pB,EAAgBT,0BAGtBrW,EAASoU,KAAK,CAEZf,OAAQ,KACR0D,SAAUf,gBACVgB,OAAQ,GAAGlpB,KAAKmpB,OAAOxqB,iBAAmBupB,gBAAgBtpB,WAAa,IAAO,cAG9EwqB,cAAejB,YAAYzkB,QAC3B2lB,kBAAmB/U,uBAGnBgV,kBAAmBV,EAAM1J,iBACzBqK,iBAAkBX,EAAMlK,iBACxB8K,iBAAkBZ,EAAMjK,iBACxB8K,cAAeb,EAAMhK,eACrB8K,YAAcd,EAAMjK,iBAAmBiK,EAAMlK,iBAAoB,IAGjEzX,KAAMga,kBAGN8H,SACAC,gBACA9mB,QACE8H,MAAMgf,KAAmBZ,aAAanpB,OAClC,oEACA,QAAQ8pB,mCAAwCC,EAAcW,QAAQ,OAG5EC,WAAYhB,EAAM/J,eAClBgL,YAAajB,EAAM9J,mBACnBgL,mBAAoBlB,EAAM7J,uBAC1BgL,oBAAqBnB,EAAM5J,4BAE9B,CAAC,MAAOjd,GACP,OAAOsjB,EAAKtjB,EACb,IAEL,CC9Ge,SAASioB,SAAStE,GAI/BA,EAAIzT,IAAInC,aAAanI,GAAGC,OAAS,KAAK,CAACwd,EAASlT,EAAUmT,KACxD,IACElkB,IAAI,EAAG,qCAEP+Q,EAAS+X,SAAS/rB,KAAKpC,UAAW,SAAU,cAAe,CACzDouB,cAAc,GAEjB,CAAC,MAAOnoB,GACP,OAAOsjB,EAAKtjB,EACb,IAEL,CCfe,SAASooB,oBAAoBzE,GAK1CA,EAAI2B,KAAK,+BAA+B3V,MAAO0T,EAASlT,EAAUmT,KAChE,IACElkB,IAAI,EAAG,0CAGP,MAAMipB,EAAa3a,KAAK/E,uBAGxB,IAAK0f,IAAeA,EAAWnrB,OAC7B,MAAM,IAAIwT,YACR,iHACA,KAKJ,MAAM4X,EAAQjF,EAAQnT,IAAI,WAG1B,IAAKoY,GAASA,IAAUD,EACtB,MAAM,IAAI3X,YACR,2EACA,KAKJ,IAAI+B,EAAa4Q,EAAQ8B,OAAO1S,WAChC,IAAIA,EAmBF,MAAM,IAAI/B,YAAY,qCAAsC,KAlB5D,UAEQ8B,wBAAwBC,EAC/B,CAAC,MAAOzS,GACP,MAAM,IAAI0Q,YACR,6BAA6B1Q,EAAMG,UACnC,KACA6Q,SAAShR,EACZ,CAGDmQ,EAASqT,OAAO,KAAKe,KAAK,CACxB3T,WAAY,IACZ0W,kBAAmB/U,uBACnBpS,QAAS,+CAA+CsS,MAM7D,CAAC,MAAOzS,GACP,OAAOsjB,EAAKtjB,EACb,IAEL,CC1CA,MAAMuoB,cAAgB,IAAIC,IAGpB7E,IAAM8E,UAsBL9Y,eAAe+Y,YAAYC,GAChC,IAEE,MAAMpmB,EAAUyL,cAAc,CAC5BhK,OAAQ2kB,IAOV,KAHAA,EAAgBpmB,EAAQyB,QAGLC,SAAW0f,IAC5B,MAAM,IAAIjT,YACR,mFACA,KAMJ,MAAMkY,EAA+C,KAA5BD,EAAcvkB,YAAqB,KAGtDykB,EAAUC,OAAOC,gBAGjBC,EAASF,OAAO,CACpBD,UACAI,OAAQ,CACNC,UAAWN,KA2Cf,GAtCAjF,IAAIwF,QAAQ,gBAGZxF,IAAIC,IACFwF,KAAK,CACHC,QAAS,CAAC,OAAQ,MAAO,cAM7B1F,IAAIC,KAAI,CAACP,EAASlT,EAAUmT,KAC1BnT,EAASmZ,IAAI,gBAAiB,QAC9BhG,GAAM,IAIRK,IAAIC,IACF6E,QAAQhF,KAAK,CACXU,MAAOyE,KAKXjF,IAAIC,IACF6E,QAAQc,WAAW,CACjBC,UAAU,EACVrF,MAAOyE,KAKXjF,IAAIC,IAAIoF,EAAOS,QAGf9F,IAAIC,IAAI6E,QAAQiB,OAAOvtB,KAAKpC,UAAW,aAGlC4uB,EAAc5jB,IAAIC,MAAO,CAE5B,MAAM2kB,EAAalZ,KAAKmZ,aAAajG,KAGrCkG,2BAA2BF,GAG3BA,EAAWG,OAAOnB,EAAcxkB,KAAMwkB,EAAczkB,MAAM,KAExDqkB,cAAce,IAAIX,EAAcxkB,KAAMwlB,GAEtCvqB,IACE,EACA,mCAAmCupB,EAAczkB,QAAQykB,EAAcxkB,QACxE,GAEJ,CAGD,GAAIwkB,EAAc5jB,IAAId,OAAQ,CAE5B,IAAIzJ,EAAKuvB,EAET,IAEEvvB,QAAYwvB,SACV7tB,KAAKb,gBAAgBqtB,EAAc5jB,IAAIE,UAAW,cAClD,QAIF8kB,QAAaC,SACX7tB,KAAKb,gBAAgBqtB,EAAc5jB,IAAIE,UAAW,cAClD,OAEH,CAAC,MAAOjF,GACPZ,IACE,EACA,qDAAqDupB,EAAc5jB,IAAIE,sDAE1E,CAED,GAAIzK,GAAOuvB,EAAM,CAEf,MAAME,EAAczZ,MAAMoZ,aAAa,CAAEpvB,MAAKuvB,QAAQpG,KAGtDkG,2BAA2BI,GAG3BA,EAAYH,OAAOnB,EAAc5jB,IAAIZ,KAAMwkB,EAAczkB,MAAM,KAE7DqkB,cAAce,IAAIX,EAAc5jB,IAAIZ,KAAM8lB,GAE1C7qB,IACE,EACA,oCAAoCupB,EAAczkB,QAAQykB,EAAc5jB,IAAIZ,QAC7E,GAEJ,CACF,CAGD0f,uBAAuBF,IAAKgF,EAAcnkB,cAG1C6gB,qBAAqB1B,KAGrBuC,aAAavC,KACboD,aAAapD,KACbsE,SAAStE,KACTyE,oBAAoBzE,KAGpBD,gBAAgBC,IACjB,CAAC,MAAO3jB,GACP,MAAM,IAAI0Q,YACR,qDACA,KACAM,SAAShR,EACZ,CACH,CAOO,SAASkqB,eAEd,GAAI3B,cAAcnO,KAAO,EAAG,CAC1Bhb,IAAI,EAAG,iCAGP,IAAK,MAAO+E,EAAMH,KAAWukB,cAC3BvkB,EAAOgT,OAAM,KACXuR,cAAc4B,OAAOhmB,GACrB/E,IAAI,EAAG,mCAAmC+E,KAAQ,GAGvD,CACH,CASO,SAASimB,aACd,OAAO7B,aACT,CASO,SAAS8B,aACd,OAAO5B,OACT,CASO,SAAS6B,SACd,OAAO3G,GACT,CAYO,SAAS4G,mBAAmBzG,GAEjC,MAAMvhB,EAAUyL,cAAc,CAC5BhK,OAAQ,CACNQ,aAAcsf,KAKlBD,uBAAuBF,IAAKphB,EAAQyB,OAAO8f,oBAC7C,CAUO,SAASF,IAAI3nB,KAASuuB,GAC3B7G,IAAIC,IAAI3nB,KAASuuB,EACnB,CAUO,SAASta,IAAIjU,KAASuuB,GAC3B7G,IAAIzT,IAAIjU,KAASuuB,EACnB,CAUO,SAASlF,KAAKrpB,KAASuuB,GAC5B7G,IAAI2B,KAAKrpB,KAASuuB,EACpB,CASA,SAASX,2BAA2B7lB,GAClCA,EAAOqM,GAAG,eAAe,CAACrQ,EAAO8lB,KAC/B/lB,aACE,EACAC,EACA,0BAA0BA,EAAMG,+BAElC2lB,EAAOtM,SAAS,IAGlBxV,EAAOqM,GAAG,SAAUrQ,IAClBD,aAAa,EAAGC,EAAO,0BAA0BA,EAAMG,UAAU,IAGnE6D,EAAOqM,GAAG,cAAeyV,IACvBA,EAAOzV,GAAG,SAAUrQ,IAClBD,aAAa,EAAGC,EAAO,0BAA0BA,EAAMG,UAAU,GACjE,GAEN,CAEA,IAAe6D,OAAA,CACb0kB,wBACAwB,0BACAE,sBACAC,sBACAC,cACAC,sCACA3G,QACA1T,QACAoV,WCvVK3V,eAAe8a,gBAAgBC,EAAW,SAEzC5a,QAAQoR,WAAW,CAEvB+B,iBAGAiH,eAGA7L,aAIF5gB,QAAQktB,KAAKD,EACf,CCSO/a,eAAeib,WAAWC,GAE/B,MAAMtoB,EAAUyL,cAAc6c,GAG9BnJ,sBAAsBnf,EAAQkB,YAAYC,oBAG1CnD,YAAYgC,EAAQ3D,SAGhB2D,EAAQuD,MAAME,sBAChB8kB,oCAIIzZ,oBAAoB9O,EAAQb,WAAYa,EAAQyB,OAAOM,aAGvD8Y,SAAS7a,EAAQ2C,KAAM3C,EAAQpB,UAAU9B,KACjD,CASA,SAASyrB,8BACP1rB,IAAI,EAAG,sDAGP3B,QAAQ4S,GAAG,QAAS0a,IAClB3rB,IAAI,EAAG,sCAAsC2rB,KAAQ,IAIvDttB,QAAQ4S,GAAG,UAAUV,MAAON,EAAM0b,KAChC3rB,IAAI,EAAG,iBAAiBiQ,sBAAyB0b,YAC3CN,iBAAiB,IAIzBhtB,QAAQ4S,GAAG,WAAWV,MAAON,EAAM0b,KACjC3rB,IAAI,EAAG,iBAAiBiQ,sBAAyB0b,YAC3CN,iBAAiB,IAIzBhtB,QAAQ4S,GAAG,UAAUV,MAAON,EAAM0b,KAChC3rB,IAAI,EAAG,iBAAiBiQ,sBAAyB0b,YAC3CN,iBAAiB,IAIzBhtB,QAAQ4S,GAAG,qBAAqBV,MAAO3P,EAAOqP,KAC5CtP,aAAa,EAAGC,EAAO,iBAAiBqP,kBAClCob,gBAAgB,EAAE,GAE5B,CAEA,IAAe9b,MAAA,IAEV3K,OAGH+J,sBACAC,4BACAI,gCAGAwc,sBACAjK,0BACAG,wBACAF,wBAGAvC,kBACAoM,gCAGArrB,QACAW,0BACAY,YAAa,SAAUnB,GASrBmB,YAPgBqN,cAAc,CAC5BpP,QAAS,CACPY,WAKgBZ,QAAQY,MAC7B,EACDoB,qBAAsB,SAAU/B,GAS9B+B,qBAPgBoN,cAAc,CAC5BpP,QAAS,CACPC,eAKyBD,QAAQC,UACtC,EACDgC,kBAAmB,SAAUJ,EAAMC,EAAM5B,GAEvC,MAAMyD,EAAUyL,cAAc,CAC5BpP,QAAS,CACP6B,OACAC,OACA5B,YAKJ+B,kBACE0B,EAAQ3D,QAAQ6B,KAChB8B,EAAQ3D,QAAQ8B,KAChB6B,EAAQ3D,QAAQE,OAEnB"} \ No newline at end of file From 289e5e6ec7d8d120bd6299d3172da5025bfa3e16 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Wed, 22 Jan 2025 23:14:44 +0100 Subject: [PATCH 082/102] Small optimizations. --- README.md | 8 +++++--- dist/index.cjs | 4 ++-- dist/index.esm.js | 2 +- dist/index.esm.js.map | 2 +- lib/browser.js | 4 ++-- lib/cache.js | 2 +- lib/chart.js | 18 +++++++++--------- lib/config.js | 39 ++++++++++++++++++++++----------------- lib/prompt.js | 5 ++--- lib/server/server.js | 6 +++--- 10 files changed, 48 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index fa93afe7..c09c9f53 100644 --- a/README.md +++ b/README.md @@ -753,11 +753,13 @@ This package supports both CommonJS and ES modules. - `@param {string} path` - The path to which the middleware(s) should be applied. - `@param {...Function} middlewares` - The middleware function(s) to be applied. -- `function getOptions()`: Retrieves a copy of the global options object. +- `function getOptions(getCopy = true)`: Retrieves a copy of the global options object or a reference to the global options object, based on the `getCopy` flag. - - `@returns {Object}` A reference to the global options object. + - `@param {boolean} [getCopy=true]` - Specifies whether to return a copied object of the global options (`true`) or a reference to the global options object (`false`). The default value is `false`. -- `function updateOptions(newOptions, getCopy = false)`: Updates the global options with the provided options. + - `@returns {Object}` A copy of the global options object, or a reference to the global options object. + +- `function updateOptions(newOptions, getCopy = false)`: Updates a copy of the global options object or a reference to the global options object, based on the `getCopy` flag. - `@param {Object} newOptions` - An object containing the new options to be merged into the global options. - `@param {boolean} [getCopy=false]` - Determines whether to merge the new options into a copy of the global options object (`true`) or directly into the global options object (`false`). The default value is `false`. diff --git a/dist/index.cjs b/dist/index.cjs index 4aa6cbd8..c86a4b9b 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),promises=require("fs/promises"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?e:path.join(__dirname$1,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(){return deepCopy(globalOptions)}function updateOptions(e,t=!1){return _mergeOptions(t?deepCopy(globalOptions):globalOptions,e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:path.join(__dirname$1,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await promises.readFile(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIGpvaW4gfSBmcm9tICdwYXRoJztcclxuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCc7XHJcblxyXG5jb25zdCBNQVhfQkFDS09GRl9BVFRFTVBUUyA9IDY7XHJcblxyXG4vLyBUaGUgZGlyZWN0b3J5IHBhdGhcclxuZXhwb3J0IGNvbnN0IF9fZGlybmFtZSA9IGZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLi4vLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbmQgc3RhbmRhcmRpemVzIHRleHQgYnkgcmVwbGFjaW5nIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2VcclxuICogY2hhcmFjdGVycyB3aXRoIGEgc2luZ2xlIHNwYWNlIGFuZCB0cmltbWluZyBhbnkgbGVhZGluZyBvciB0cmFpbGluZ1xyXG4gKiB3aGl0ZXNwYWNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJUZXh0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IC0gVGhlIGlucHV0IHRleHQgdG8gYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtSZWdFeHB9IFtydWxlPS9cXHNcXHMrL2ddIC0gVGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBydWxlIHRvIG1hdGNoXHJcbiAqIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgdGhlICcvXFxzXFxzKy9nJyBSZWdFeHAuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcmVwbGFjZXI9JyAnXSAtIFRoZSBzdHJpbmcgdXNlZCB0byByZXBsYWNlIG11bHRpcGxlXHJcbiAqIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgdGhlICcgJyBzdHJpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjbGVhcmVkIGFuZCBzdGFuZGFyZGl6ZWQgdGV4dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhclRleHQodGV4dCwgcnVsZSA9IC9cXHNcXHMrL2csIHJlcGxhY2VyID0gJyAnKSB7XHJcbiAgcmV0dXJuIHRleHQucmVwbGFjZUFsbChydWxlLCByZXBsYWNlcikudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIGRlZXAgY29weSBvZiB0aGUgZ2l2ZW4gb2JqZWN0IG9yIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZGVlcENvcHlcclxuICpcclxuICogQHBhcmFtIHsoT2JqZWN0fEFycmF5KX0gb2JqQXJyIC0gVGhlIG9iamVjdCBvciBhcnJheSB0byBiZSBkZWVwbHkgY29waWVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxBcnJheSl9IFRoZSBkZWVwIGNvcHkgb2YgdGhlIHByb3ZpZGVkIG9iamVjdCBvciBhcnJheS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBkZWVwQ29weShvYmpBcnIpIHtcclxuICAvLyBJZiB0aGUgYG9iakFycmAgaXMgbnVsbCBvciBub3Qgb2YgdGhlIGBvYmplY3RgIHR5cGUsIHJldHVybiBpdFxyXG4gIGlmIChvYmpBcnIgPT09IG51bGwgfHwgdHlwZW9mIG9iakFyciAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmpBcnI7XHJcbiAgfVxyXG5cclxuICAvLyBQcmVwYXJlIGVpdGhlciBhIG5ldyBhcnJheSBvciBhIG5ldyBvYmplY3RcclxuICBjb25zdCBvYmpBcnJDb3B5ID0gQXJyYXkuaXNBcnJheShvYmpBcnIpID8gW10gOiB7fTtcclxuXHJcbiAgLy8gUmVjdXJzaXZlbHkgY29weSBlYWNoIHByb3BlcnR5XHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqQXJyKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iakFyciwga2V5KSkge1xyXG4gICAgICBvYmpBcnJDb3B5W2tleV0gPSBkZWVwQ29weShvYmpBcnJba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNvcGllZCBvYmplY3RcclxuICByZXR1cm4gb2JqQXJyQ29weTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEltcGxlbWVudHMgYW4gZXhwb25lbnRpYWwgYmFja29mZiBzdHJhdGVneSBmb3IgcmV0cnlpbmcgYSBmdW5jdGlvbiB1bnRpbFxyXG4gKiBhIGNlcnRhaW4gbnVtYmVyIG9mIGF0dGVtcHRzIGFyZSByZWFjaGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGV4cEJhY2tvZmZcclxuICpcclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gLSBUaGUgZnVuY3Rpb24gdG8gYmUgcmV0cmllZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IFthdHRlbXB0PTBdIC0gVGhlIGN1cnJlbnQgYXR0ZW1wdCBudW1iZXIuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGAwYC5cclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0XHJcbiAqIG9mIHRoZSBmdW5jdGlvbiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwQmFja29mZihmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlcGVhdCwgdGhyb3cgYW4gZXJyb3JcclxuICAgIGlmICgrK2F0dGVtcHQgPj0gTUFYX0JBQ0tPRkZfQVRURU1QVFMpIHtcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxyXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc3BvbnNlKSA9PiBzZXRUaW1lb3V0KHJlc3BvbnNlLCBkZWxheUluTXMpKTtcclxuXHJcbiAgICAvLy8gVE8gRE86IENvcnJlY3RcclxuICAgIC8vIC8vIEluZm9ybWF0aW9uIGFib3V0IHRoZSByZXNvdXJjZSB0aW1lb3V0XHJcbiAgICAvLyBsb2coXHJcbiAgICAvLyAgIDMsXHJcbiAgICAvLyAgIGBbdXRpbHNdIFdhaXRlZCAke2RlbGF5SW5Nc31tcyB1bnRpbCBuZXh0IGNhbGwgZm9yIHRoZSByZXNvdXJjZSBvZiBJRDogJHthcmdzWzBdfS5gXHJcbiAgICAvLyApO1xyXG5cclxuICAgIC8vIFRyeSBhZ2FpblxyXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkanVzdHMgdGhlIGNvbnN0cnVjdG9yIG5hbWUgYnkgdHJhbnNmb3JtaW5nIGFuZCBub3JtYWxpemluZyBpdCBiYXNlZFxyXG4gKiBvbiBjb21tb24gY2hhcnQgdHlwZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhDb25zdHJcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnN0ciAtIFRoZSBvcmlnaW5hbCBjb25zdHJ1Y3RvciBuYW1lIHRvIGJlIGZpeGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGNvbnN0cnVjdG9yIG5hbWUsIG9yICdjaGFydCcgaWYgdGhlIGlucHV0XHJcbiAqIGlzIG5vdCByZWNvZ25pemVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeENvbnN0cihjb25zdHIpIHtcclxuICB0cnkge1xyXG4gICAgLy8gRml4IHRoZSBjb25zdHJ1Y3RvciBieSBsb3dlcmluZyBjYXNpbmdcclxuICAgIGNvbnN0IGZpeGVkQ29uc3RyID0gYCR7Y29uc3RyLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnY2hhcnQnLCAnJyl9Q2hhcnRgO1xyXG5cclxuICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgcmVzdWx0IGlzIGp1c3QgJ0NoYXJ0J1xyXG4gICAgaWYgKGZpeGVkQ29uc3RyID09PSAnQ2hhcnQnKSB7XHJcbiAgICAgIGZpeGVkQ29uc3RyLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IsIG90aGVyd2lzZSBkZWZhdWx0IHRvICdjaGFydCdcclxuICAgIHJldHVybiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddLmluY2x1ZGVzKFxyXG4gICAgICBmaXhlZENvbnN0clxyXG4gICAgKVxyXG4gICAgICA/IGZpeGVkQ29uc3RyXHJcbiAgICAgIDogJ2NoYXJ0JztcclxuICB9IGNhdGNoIHtcclxuICAgIC8vIERlZmF1bHQgdG8gJ2NoYXJ0JyBpbiBjYXNlIG9mIGFueSBlcnJvclxyXG4gICAgcmV0dXJuICdjaGFydCc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIG91dGZpbGUgYmFzZWQgb24gcHJvdmlkZWQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeE91dGZpbGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBvdXRmaWxlIC0gVGhlIGZpbGUgcGF0aCBvciBuYW1lLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIG91dGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4T3V0ZmlsZSh0eXBlLCBvdXRmaWxlKSB7XHJcbiAgLy8gR2V0IHRoZSBmaWxlIG5hbWUgZnJvbSB0aGUgYG91dGZpbGVgIG9wdGlvblxyXG4gIGNvbnN0IGZpbGVOYW1lID0gZ2V0QWJzb2x1dGVQYXRoKG91dGZpbGUgfHwgJ2NoYXJ0JylcclxuICAgIC5zcGxpdCgnLicpXHJcbiAgICAuc2hpZnQoKTtcclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCBvdXRmaWxlXHJcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke3R5cGV9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhUeXBlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW291dGZpbGU9bnVsbF0gLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGBudWxsYC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNvcnJlY3RlZCBleHBvcnQgdHlwZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhUeXBlKHR5cGUsIG91dGZpbGUgPSBudWxsKSB7XHJcbiAgLy8gTUlNRSB0eXBlc1xyXG4gIGNvbnN0IG1pbWVUeXBlcyA9IHtcclxuICAgICdpbWFnZS9wbmcnOiAncG5nJyxcclxuICAgICdpbWFnZS9qcGVnJzogJ2pwZWcnLFxyXG4gICAgJ2FwcGxpY2F0aW9uL3BkZic6ICdwZGYnLFxyXG4gICAgJ2ltYWdlL3N2Zyt4bWwnOiAnc3ZnJ1xyXG4gIH07XHJcblxyXG4gIC8vIEdldCBmb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IE9iamVjdC52YWx1ZXMobWltZVR5cGVzKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdHlwZSBhbmQgb3V0ZmlsZSdzIGV4dGVuc2lvbnMgYXJlIHRoZSBzYW1lXHJcbiAgaWYgKG91dGZpbGUpIHtcclxuICAgIGNvbnN0IG91dFR5cGUgPSBvdXRmaWxlLnNwbGl0KCcuJykucG9wKCk7XHJcblxyXG4gICAgLy8gU3VwcG9ydCB0aGUgSlBHIHR5cGVcclxuICAgIGlmIChvdXRUeXBlID09PSAnanBnJykge1xyXG4gICAgICB0eXBlID0gJ2pwZWcnO1xyXG4gICAgfSBlbHNlIGlmIChmb3JtYXRzLmluY2x1ZGVzKG91dFR5cGUpICYmIHR5cGUgIT09IG91dFR5cGUpIHtcclxuICAgICAgdHlwZSA9IG91dFR5cGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IHR5cGVcclxuICByZXR1cm4gbWltZVR5cGVzW3R5cGVdIHx8IGZvcm1hdHMuZmluZCgodCkgPT4gdCA9PT0gdHlwZSkgfHwgJ3BuZyc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIHBhdGggaXMgcmVsYXRpdmUgb3IgYWJzb2x1dGUgYW5kIHJldHVybnMgdGhlIGNvcnJlY3RlZCxcclxuICogYWJzb2x1dGUgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzQWJzb2x1dGVQYXRoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gYmUgY2hlY2tlZCBvbi5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGguXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWJzb2x1dGVQYXRoKHBhdGgpIHtcclxuICByZXR1cm4gaXNBYnNvbHV0ZShwYXRoKSA/IHBhdGggOiBqb2luKF9fZGlybmFtZSwgcGF0aCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBpbnB1dCBkYXRhIHRvIGEgQmFzZTY0IHN0cmluZyBiYXNlZCBvbiB0aGUgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRCYXNlNjRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IC0gVGhlIGlucHV0IHRvIGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmb3JtYXQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQmFzZTY0IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QmFzZTY0KGlucHV0LCB0eXBlKSB7XHJcbiAgLy8gRm9yIHBkZiBhbmQgc3ZnIHR5cGVzIHRoZSBpbnB1dCBtdXN0IGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmcm9tIGEgYnVmZmVyXHJcbiAgaWYgKHR5cGUgPT09ICdwZGYnIHx8IHR5cGUgPT0gJ3N2ZycpIHtcclxuICAgIHJldHVybiBCdWZmZXIuZnJvbShpbnB1dCwgJ3V0ZjgnKS50b1N0cmluZygnYmFzZTY0Jyk7XHJcbiAgfVxyXG5cclxuICAvLyBGb3IgcG5nIGFuZCBqcGVnIGlucHV0IGlzIGFscmVhZHkgYSBCYXNlNjQgc3RyaW5nXHJcbiAgcmV0dXJuIGlucHV0O1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyBzdHJpbmdpZmllZCBkYXRlIHdpdGhvdXQgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGUoKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgR01UIHRleHQgaW5mb3JtYXRpb25cclxuICByZXR1cm4gbmV3IERhdGUoKS50b1N0cmluZygpLnNwbGl0KCcoJylbMF0udHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgc3RvcmVkIHRpbWUgdmFsdWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVRpbWVcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXROZXdEYXRlVGltZSgpIHtcclxuICByZXR1cm4gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIGl0ZW0gaXMgYW4gb2JqZWN0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNPYmplY3RcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIGl0ZW0gdG8gYmUgY2hlY2tlZC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIGl0ZW0gaXMgYW4gb2JqZWN0LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3QoaXRlbSkge1xyXG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaXRlbSkgPT09ICdbb2JqZWN0IE9iamVjdF0nO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdEVtcHR5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGVtcHR5LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3RFbXB0eShpdGVtKSB7XHJcbiAgcmV0dXJuIChcclxuICAgIHR5cGVvZiBpdGVtID09PSAnb2JqZWN0JyAmJlxyXG4gICAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICAgIGl0ZW0gIT09IG51bGwgJiZcclxuICAgIE9iamVjdC5rZXlzKGl0ZW0pLmxlbmd0aCA9PT0gMFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCBpbiB0aGUgZ2l2ZW4gc3RyaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaXRlbSAtIFRoZSBzdHJpbmcgdG8gYmUgY2hlY2tlZCBmb3IgYSBwcml2YXRlIElQIHJhbmdlIFVSTC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoaXRlbSkge1xyXG4gIGNvbnN0IHJlZ2V4UGF0dGVybnMgPSBbXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/bG9jYWxob3N0XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTI3XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xNzJcXC4oMVs2LTldfDJbMC05XXwzWzAtMV0pXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTkyXFwuMTY4XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi9cclxuICBdO1xyXG5cclxuICByZXR1cm4gcmVnZXhQYXR0ZXJucy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3QoaXRlbSkpO1xyXG59XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIGVsYXBzZWQgdGltZSB1c2luZyB0aGUgTm9kZS5qcyBgcHJvY2Vzcy5ocnRpbWUoKWAgbWV0aG9kLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbWVhc3VyZVRpbWVcclxuICpcclxuICogQHJldHVybnMge0Z1bmN0aW9ufSBBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZWxhcHNlZCB0aW1lIGluIG1pbGxpc2Vjb25kcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBtZWFzdXJlVGltZSgpIHtcclxuICBjb25zdCBzdGFydCA9IHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO1xyXG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xyXG59XHJcblxyXG4vKipcclxuICogUm91bmRzIGEgbnVtYmVyIHRvIHRoZSBzcGVjaWZpZWQgcHJlY2lzaW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcm91bmROdW1iZXJcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IHZhbHVlIC0gVGhlIG51bWJlciB0byBiZSByb3VuZGVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcHJlY2lzaW9uIC0gVGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyB0byByb3VuZCB0by5cclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gVGhlIHJvdW5kZWQgbnVtYmVyLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHJvdW5kTnVtYmVyKHZhbHVlLCBwcmVjaXNpb24gPSAxKSB7XHJcbiAgY29uc3QgbXVsdGlwbGllciA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24gfHwgMCk7XHJcbiAgcmV0dXJuIE1hdGgucm91bmQoK3ZhbHVlICogbXVsdGlwbGllcikgLyBtdWx0aXBsaWVyO1xyXG59XHJcblxyXG4vKipcclxuICogQ29udmVydHMgYSB2YWx1ZSB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB0b0Jvb2xlYW5cclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUaGUgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQgdmFsdWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdG9Cb29sZWFuKGl0ZW0pIHtcclxuICByZXR1cm4gWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBXcmFwcyBjdXN0b20gY29kZSB0byBleGVjdXRlIGl0IHNhZmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIHdyYXBBcm91bmRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tIGNvZGUgdG8gYmUgd3JhcHBlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBGbGFnIHRvIGFsbG93IGxvYWRpbmcgY29kZSBmcm9tIGEgZmlsZS5cclxuICogQHBhcmFtIHtib29sZWFufSBbaXNDYWxsYmFjaz1mYWxzZV0gLSBGbGFnIHRoYXQgaW5kaWNhdGVzIHRoZSByZXR1cm5lZCBjb2RlXHJcbiAqIG11c3QgYmUgaW4gYSBjYWxsYmFjayBmb3JtYXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoc3RyaW5nfG51bGwpfSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBudWxsIGlmIHdyYXBwaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHdyYXBBcm91bmQoY3VzdG9tQ29kZSwgYWxsb3dGaWxlUmVzb3VyY2VzLCBpc0NhbGxiYWNrID0gZmFsc2UpIHtcclxuICBpZiAoY3VzdG9tQ29kZSAmJiB0eXBlb2YgY3VzdG9tQ29kZSA9PT0gJ3N0cmluZycpIHtcclxuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcclxuXHJcbiAgICBpZiAoY3VzdG9tQ29kZS5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgICAgLy8gTG9hZCBhIGZpbGUgaWYgdGhlIGZpbGUgcmVzb3VyY2VzIGFyZSBhbGxvd2VkXHJcbiAgICAgIHJldHVybiBhbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgICA/IHdyYXBBcm91bmQoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY3VzdG9tQ29kZSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgICAgaXNDYWxsYmFja1xyXG4gICAgICAgICAgKVxyXG4gICAgICAgIDogbnVsbDtcclxuICAgIH0gZWxzZSBpZiAoXHJcbiAgICAgICFpc0NhbGxiYWNrICYmXHJcbiAgICAgIChjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24gKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFRyZWF0IGEgZnVuY3Rpb24gYXMgYSBzZWxmLWludm9raW5nIGV4cHJlc3Npb25cclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gT3IgcmV0dXJuIGFzIGEgc3RyaW5naWZpZWQgY29kZVxyXG4gICAgcmV0dXJuIGN1c3RvbUNvZGUucmVwbGFjZSgvOyQvLCAnJyk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgX19kaXJuYW1lLFxyXG4gIGNsZWFyVGV4dCxcclxuICBkZWVwQ29weSxcclxuICBleHBCYWNrb2ZmLFxyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBnZXROZXdEYXRlLFxyXG4gIGdldE5ld0RhdGVUaW1lLFxyXG4gIGlzT2JqZWN0LFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBtZWFzdXJlVGltZSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQSBtb2R1bGUgZm9yIG1hbmFnaW5nIGxvZ2dpbmcgZnVuY3Rpb25hbGl0eSB3aXRoIGN1c3RvbWl6YWJsZVxyXG4gKiBsb2cgbGV2ZWxzLCBjb25zb2xlIGFuZCBmaWxlIGxvZ2dpbmcgb3B0aW9ucywgYW5kIGVycm9yIGhhbmRsaW5nIHN1cHBvcnQuXHJcbiAqIFRoZSBtb2R1bGUgYWxzbyBlbnN1cmVzIHRoYXQgZmlsZS1iYXNlZCBsb2dzIGFyZSBzdG9yZWQgaW4gYSBzdHJ1Y3R1cmVkXHJcbiAqIGRpcmVjdG9yeSwgY3JlYXRpbmcgdGhlIG5lY2Vzc2FyeSBwYXRocyBhdXRvbWF0aWNhbGx5IGlmIHRoZXkgZG8gbm90IGV4aXN0LlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWJzb2x1dGVQYXRoLCBnZXROZXdEYXRlIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG4vLyBUaGUgYXZhaWxhYmxlIGNvbG9yc1xyXG5jb25zdCBjb2xvcnMgPSBbJ3JlZCcsICd5ZWxsb3cnLCAnYmx1ZScsICdncmF5JywgJ2dyZWVuJ107XHJcblxyXG4vLyBUaGUgZGVmYXVsdCBsb2dnaW5nIGNvbmZpZ1xyXG5jb25zdCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBGdWxsIHBhdGggdG8gdGhlIGxvZyBmaWxlXHJcbiAgcGF0aFRvTG9nOiAnJyxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdXHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhIG1lc3NhZ2Ugd2l0aCBhIHNwZWNpZmllZCBsb2cgbGV2ZWwuIEFjY2VwdHMgYSB2YXJpYWJsZSBudW1iZXJcclxuICogb2YgYXJndW1lbnRzLiBUaGUgYXJndW1lbnRzIGFmdGVyIHRoZSBgbGV2ZWxgIGFyZSBwYXNzZWQgdG8gYGNvbnNvbGUubG9nYFxyXG4gKiBhbmQvb3IgdXNlZCB0byBjb25zdHJ1Y3QgYW5kIGFwcGVuZCBtZXNzYWdlcyB0byBhIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Li4udW5rbm93bn0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZ1xyXG4gKiBsZXZlbCBhbmQgdGhlIHJlbWFpbmluZyBhcmUgc3RyaW5ncyB1c2VkIHRvIGJ1aWxkIHRoZSBsb2cgbWVzc2FnZS5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZyguLi5hcmdzKSB7XHJcbiAgY29uc3QgW25ld0xldmVsLCAuLi50ZXh0c10gPSBhcmdzO1xyXG5cclxuICAvLyBDdXJyZW50IGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgbGV2ZWxzRGVzYywgbGV2ZWwgfSA9IGxvZ2dpbmc7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZSBvciBpcyBpdCBhIGJlbmNobWFyayBsb2dcclxuICBpZiAoXHJcbiAgICBuZXdMZXZlbCAhPT0gNSAmJlxyXG4gICAgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aClcclxuICApIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtnZXROZXdEYXRlKCl9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgYW4gZXJyb3IgbWVzc2FnZSBhbG9uZyB3aXRoIGl0cyBzdGFjayB0cmFjZS4gT3B0aW9uYWxseSwgYSBjdXN0b20gbWVzc2FnZVxyXG4gKiBjYW4gYmUgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dXaXRoU3RhY2tcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IG5ld0xldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHN0YWNrIHRyYWNlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGluY2x1ZGVkXHJcbiAqIGluIHRoZSBsb2cgYWxvbmdzaWRlIHRoZSBlcnJvci5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZ1dpdGhTdGFjayhuZXdMZXZlbCwgZXJyb3IsIGN1c3RvbU1lc3NhZ2UpIHtcclxuICAvLyBHZXQgdGhlIG1haW4gbWVzc2FnZVxyXG4gIGNvbnN0IG1haW5NZXNzYWdlID0gY3VzdG9tTWVzc2FnZSB8fCAoZXJyb3IgJiYgZXJyb3IubWVzc2FnZSkgfHwgJyc7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlXHJcbiAgaWYgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aCkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIEFkZCB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9IGVycm9yICYmIGVycm9yLnN0YWNrO1xyXG5cclxuICAvLyBDb21iaW5lIGN1c3RvbSBtZXNzYWdlIG9yIGVycm9yIG1lc3NhZ2Ugd2l0aCBlcnJvciBzdGFjayBtZXNzYWdlLCBpZiBleGlzdHNcclxuICBjb25zdCB0ZXh0cyA9IFttYWluTWVzc2FnZV07XHJcbiAgaWYgKHN0YWNrTWVzc2FnZSkge1xyXG4gICAgdGV4dHMucHVzaCgnXFxuJywgc3RhY2tNZXNzYWdlKTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgdGV4dHMuc2hpZnQoKVtjb2xvcnNbbmV3TGV2ZWwgLSAxXV0sXHJcbiAgICAgICAgLi4udGV4dHNcclxuICAgICAgXSlcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgbG9nZ2luZyBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaW5pdExvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGxvZ2dpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGxvZ2dpbmdgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaW5pdExvZ2dpbmcobG9nZ2luZ09wdGlvbnMpIHtcclxuICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBgbG9nZ2luZ09wdGlvbnNgIG9iamVjdFxyXG4gIGNvbnN0IHsgbGV2ZWwsIGRlc3QsIGZpbGUsIHRvQ29uc29sZSwgdG9GaWxlIH0gPSBsb2dnaW5nT3B0aW9ucztcclxuXHJcbiAgLy8gUmVzZXQgZmxhZ3MgdG8gdGhlIGRlZmF1bHQgdmFsdWVzXHJcbiAgbG9nZ2luZy5wYXRoQ3JlYXRlZCA9IGZhbHNlO1xyXG4gIGxvZ2dpbmcucGF0aFRvTG9nID0gJyc7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nZ2luZyBsZXZlbFxyXG4gIHNldExvZ0xldmVsKGxldmVsKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb25zb2xlIGxvZ2dpbmdcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpO1xyXG5cclxuICAvLyBTZXQgdGhlIGZpbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKGAwYCA9IG5vIGxvZ2dpbmcsXHJcbiAqIGAxYCA9IGVycm9yLCBgMmAgPSB3YXJuaW5nLCBgM2AgPSBub3RpY2UsIGA0YCA9IHZlcmJvc2UsIG9yIGA1YCA9IGJlbmNobWFyaykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRMb2dMZXZlbFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgbG9nIGxldmVsIHRvIGJlIHNldC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRMb2dMZXZlbChsZXZlbCkge1xyXG4gIGlmIChcclxuICAgIE51bWJlci5pc0ludGVnZXIobGV2ZWwpICYmXHJcbiAgICBsZXZlbCA+PSAwICYmXHJcbiAgICBsZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoXHJcbiAgKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYGxldmVsYCBvcHRpb25cclxuICAgIGxvZ2dpbmcubGV2ZWwgPSBsZXZlbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGNvbnNvbGUgbG9nZ2luZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUNvbnNvbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9Db25zb2xlIC0gVGhlIGZsYWcgZm9yIHNldHRpbmcgdGhlIGxvZ2dpbmcgdG8gdGhlIGNvbnNvbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmcodG9Db25zb2xlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0NvbnNvbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9Db25zb2xlID0gISF0b0NvbnNvbGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlIG5hbWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVGaWxlTG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZGVzdCAtIFRoZSBkZXN0aW5hdGlvbiBwYXRoIHdoZXJlIHRoZSBsb2cgZmlsZSBzaG91bGRcclxuICogYmUgc2F2ZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlIC0gVGhlIG5hbWUgb2YgdGhlIGxvZyBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvRmlsZSAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9nZ2luZyBzaG91bGRcclxuICogYmUgZGlyZWN0ZWQgdG8gYSBmaWxlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgdG9GaWxlYCBvcHRpb25cclxuICBsb2dnaW5nLnRvRmlsZSA9ICEhdG9GaWxlO1xyXG5cclxuICAvLyBTZXQgdGhlIGBkZXN0YCBhbmQgYGZpbGVgIG9wdGlvbnMgb25seSBpZiB0aGUgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWRcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIGxvZ2dpbmcuZGVzdCA9IGRlc3QgfHwgJyc7XHJcbiAgICBsb2dnaW5nLmZpbGUgPSBmaWxlIHx8ICcnO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgdGhlIHByb3ZpZGVkIHRleHRzIHRvIGEgZmlsZSwgaWYgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWQuIEl0IGNyZWF0ZXNcclxuICogdGhlIG5lY2Vzc2FyeSBkaXJlY3Rvcnkgc3RydWN0dXJlIGlmIG5vdCBhbHJlYWR5IGNyZWF0ZWQgYW5kIGFwcGVuZHNcclxuICogdGhlIGNvbnRlbnQsIGluY2x1ZGluZyBhbiBvcHRpb25hbCBwcmVmaXgsIHRvIHRoZSBzcGVjaWZpZWQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9nVG9GaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHRleHRzIC0gQW4gYXJyYXkgb2YgdGV4dHMgdG8gYmUgbG9nZ2VkLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4IC0gQW4gb3B0aW9uYWwgcHJlZml4IHRvIGJlIGFkZGVkIHRvIGVhY2ggbG9nIGVudHJ5LlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KSB7XHJcbiAgaWYgKCFsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAvLyBDcmVhdGUgaWYgZG9lcyBub3QgZXhpc3RcclxuICAgICFleGlzdHNTeW5jKGdldEFic29sdXRlUGF0aChsb2dnaW5nLmRlc3QpKSAmJlxyXG4gICAgICBta2RpclN5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpO1xyXG5cclxuICAgIC8vIENyZWF0ZSB0aGUgZnVsbCBwYXRoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyA9IGdldEFic29sdXRlUGF0aChqb2luKGxvZ2dpbmcuZGVzdCwgbG9nZ2luZy5maWxlKSk7XHJcblxyXG4gICAgLy8gV2Ugbm93IGFzc3VtZSB0aGUgcGF0aCBpcyBhdmFpbGFibGUsIGUuZy4gaXQncyB0aGUgcmVzcG9uc2liaWxpdHlcclxuICAgIC8vIG9mIHRoZSB1c2VyIHRvIGNyZWF0ZSB0aGUgcGF0aCB3aXRoIHRoZSBjb3JyZWN0IGFjY2VzcyByaWdodHMuXHJcbiAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICB9XHJcblxyXG4gIC8vIEFkZCB0aGUgY29udGVudCB0byBhIGZpbGVcclxuICBhcHBlbmRGaWxlKFxyXG4gICAgbG9nZ2luZy5wYXRoVG9Mb2csXHJcbiAgICBbcHJlZml4XS5jb25jYXQodGV4dHMpLmpvaW4oJyAnKSArICdcXG4nLFxyXG4gICAgKGVycm9yKSA9PiB7XHJcbiAgICAgIGlmIChlcnJvciAmJiBsb2dnaW5nLnRvRmlsZSAmJiBsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmcsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmdcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvbmZpZ3VyYXRpb24gbWFuYWdlbWVudCBtb2R1bGUgZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuXHJcbiAqIFByb3ZpZGVzIGRlZmF1bHQgY29uZmlndXJhdGlvbnMgdGhhdCBzdXBwb3J0IGVudmlyb25tZW50IHZhcmlhYmxlcywgQ0xJXHJcbiAqIGFyZ3VtZW50cywgYW5kIGludGVyYWN0aXZlIHByb21wdHMgZm9yIGN1c3RvbWl6YXRpb24gb2Ygb3B0aW9ucyBhbmQgZmVhdHVyZXMuXHJcbiAqIEFkZGl0aW9uYWxseSwgaXQgbWFwcyBsZWdhY3kgb3B0aW9ucyB0byBtb2Rlcm4gc3RydWN0dXJlcywgZ2VuZXJhdGVzIG5lc3RlZFxyXG4gKiBhcmd1bWVudCBtYXBwaW5ncywgYW5kIGRpc3BsYXlzIENMSSB1c2FnZSBpbmZvcm1hdGlvbi5cclxuICovXHJcblxyXG4vKipcclxuICogVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYWxsIGF2YWlsYWJsZSBvcHRpb25zLCBvcmdhbml6ZWRcclxuICogYnkgc2VjdGlvbnMuXHJcbiAqXHJcbiAqIFRoaXMgb2JqZWN0IGluY2x1ZGVzOlxyXG4gKiAtIERlZmF1bHQgdmFsdWVzIGZvciBlYWNoIG9wdGlvblxyXG4gKiAtIERhdGEgdHlwZXMgZm9yIHZhbGlkYXRpb25cclxuICogLSBOYW1lcyBvZiBjb3JyZXNwb25kaW5nIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiAtIERlc2NyaXB0aW9ucyBvZiBlYWNoIHByb3BlcnR5XHJcbiAqIC0gSW5mb3JtYXRpb24gdXNlZCBmb3IgcHJvbXB0cyBpbiBpbnRlcmFjdGl2ZSBjb25maWd1cmF0aW9uXHJcbiAqIC0gW09wdGlvbmFsXSBDb3JyZXNwb25kaW5nIENMSSBhcmd1bWVudCBuYW1lcyBmb3IgQ0xJIHVzYWdlXHJcbiAqIC0gW09wdGlvbmFsXSBMZWdhY3kgbmFtZXMgZnJvbSB0aGUgcHJldmlvdXMgUGhhbnRvbUpTLWJhc2VkIHNlcnZlclxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZmF1bHRDb25maWcgPSB7XHJcbiAgcHVwcGV0ZWVyOiB7XHJcbiAgICBhcmdzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJy0tYWxsb3ctcnVubmluZy1pbnNlY3VyZS1jb250ZW50JyxcclxuICAgICAgICAnLS1hc2gtbm8tbnVkZ2VzJyxcclxuICAgICAgICAnLS1hdXRvcGxheS1wb2xpY3k9dXNlci1nZXN0dXJlLXJlcXVpcmVkJyxcclxuICAgICAgICAnLS1ibG9jay1uZXctd2ViLWNvbnRlbnRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWFjY2VsZXJhdGVkLTJkLWNhbnZhcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLW5ldHdvcmtpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1icmVha3BhZCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jaGVja2VyLWltYWdpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC1leHRlbnNpb25zLXdpdGgtYmFja2dyb3VuZC1wYWdlcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtdXBkYXRlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRlZmF1bHQtYXBwcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZXYtc2htLXVzYWdlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRvbWFpbi1yZWxpYWJpbGl0eScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1leHRlbnNpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWZlYXR1cmVzPUNhbGN1bGF0ZU5hdGl2ZVdpbk9jY2x1c2lvbixJbnRlcmVzdEZlZWRDb250ZW50U3VnZ2VzdGlvbnMsV2ViT1RQJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1sb2dnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXBvcHVwLWJsb2NraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1yZW5kZXJlci1iYWNrZ3JvdW5kaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlYXJjaC1lbmdpbmUtY2hvaWNlLXNjcmVlbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNpdGUtaXNvbGF0aW9uLXRyaWFscycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zcGVlY2gtYXBpJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXN5bmMnLFxyXG4gICAgICAgICctLWVuYWJsZS11bnNhZmUtd2ViZ3B1JyxcclxuICAgICAgICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcclxuICAgICAgICAnLS1oaWRlLXNjcm9sbGJhcnMnLFxyXG4gICAgICAgICctLW1ldHJpY3MtcmVjb3JkaW5nLW9ubHknLFxyXG4gICAgICAgICctLW11dGUtYXVkaW8nLFxyXG4gICAgICAgICctLW5vLWRlZmF1bHQtYnJvd3Nlci1jaGVjaycsXHJcbiAgICAgICAgJy0tbm8tZmlyc3QtcnVuJyxcclxuICAgICAgICAnLS1uby1waW5ncycsXHJcbiAgICAgICAgJy0tbm8tc2FuZGJveCcsXHJcbiAgICAgICAgJy0tbm8tc3RhcnR1cC13aW5kb3cnLFxyXG4gICAgICAgICctLW5vLXp5Z290ZScsXHJcbiAgICAgICAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxyXG4gICAgICAgICctLXByb2Nlc3MtcGVyLXRhYicsXHJcbiAgICAgICAgJy0tdXNlLW1vY2sta2V5Y2hhaW4nXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdQVVBQRVRFRVJfQVJHUycsXHJcbiAgICAgIGNsaU5hbWU6ICdwdXBwZXRlZXJBcmdzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBcnJheSBvZiBQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgICBzZXBhcmF0b3I6ICc7J1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBoaWdoY2hhcnRzOiB7XHJcbiAgICB2ZXJzaW9uOiB7XHJcbiAgICAgIHZhbHVlOiAnbGF0ZXN0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgdmVyc2lvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNkblVybDoge1xyXG4gICAgICB2YWx1ZTogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmb3JjZUZldGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmxhZyB0byByZWZldGNoIHNjcmlwdHMgYWZ0ZXIgZWFjaCBzZXJ2ZXIgcmVydW4nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhY2hlUGF0aDoge1xyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DQUNIRV9QQVRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEaXJlY3RvcnkgcGF0aCBmb3IgY2FjaGVkIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2hpZ2hjaGFydHMnLCAnaGlnaGNoYXJ0cy1tb3JlJywgJ2hpZ2hjaGFydHMtM2QnXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbW9kdWxlU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdzdG9jaycsXHJcbiAgICAgICAgJ21hcCcsXHJcbiAgICAgICAgJ2dhbnR0JyxcclxuICAgICAgICAnZXhwb3J0aW5nJyxcclxuICAgICAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgICAgICdhY2Nlc3NpYmlsaXR5JyxcclxuICAgICAgICAvLyAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgICAgICdib29zdCcsXHJcbiAgICAgICAgJ2RhdGEnLFxyXG4gICAgICAgICdkYXRhLXRvb2xzJyxcclxuICAgICAgICAnZHJhZ2dhYmxlLXBvaW50cycsXHJcbiAgICAgICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAgICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICAgICAnaGVhdG1hcCcsXHJcbiAgICAgICAgJ3RpbGVtYXAnLFxyXG4gICAgICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAgICAgJ3RpbWVsaW5lJyxcclxuICAgICAgICAndHJlZW1hcCcsXHJcbiAgICAgICAgJ3RyZWVncmFwaCcsXHJcbiAgICAgICAgJ2l0ZW0tc2VyaWVzJyxcclxuICAgICAgICAnZHJpbGxkb3duJyxcclxuICAgICAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAgICAgJ2J1bGxldCcsXHJcbiAgICAgICAgJ2Z1bm5lbCcsXHJcbiAgICAgICAgJ2Z1bm5lbDNkJyxcclxuICAgICAgICAnZ2VvaGVhdG1hcCcsXHJcbiAgICAgICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAgICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAgICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzZXJpZXMtb24tcG9pbnQnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgLy8gJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaScsXHJcbiAgICAgICAgJ2Zsb3dtYXAnLFxyXG4gICAgICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAgICAgJ25hdmlnYXRvcicsXHJcbiAgICAgICAgJ3RleHRwYXRoJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBtb2R1bGUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogWydpbmRpY2F0b3JzLWFsbCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBpbmRpY2F0b3Igc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQuanMvMi4zMC4xL21vbWVudC5taW4uanMnLFxyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQtdGltZXpvbmUvMC41LjQ1L21vbWVudC10aW1lem9uZS13aXRoLWRhdGEubWluLmpzJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DVVNUT01fU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBjdXN0b20gc2NyaXB0cyBvciBkZXBlbmRlbmNpZXMgdG8gZmV0Y2gnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGV4cG9ydDoge1xyXG4gICAgaW5maWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0lORklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnB1dCBmaWxlbmFtZSB3aXRoIHR5cGUsIGZvcm1hdHRlZCBjb3JyZWN0bHkgYXMgSlNPTiBvciBTVkcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbnN0cjoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5TVFInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3ZlcnJpZGVzIHRoZSBgaW5maWxlYCB3aXRoIEpTT04sIHN0cmluZ2lmaWVkIEpTT04sIG9yIFNWRyBpbnB1dCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09QVElPTlMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FsaWFzIGZvciB0aGUgYGluc3RyYCBvcHRpb24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzdmc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU1ZHJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTVkcgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCB0byByZW5kZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CQVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdCYXRjaCBqb2Igc3RyaW5nIHdpdGggaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09VVEZJTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3V0cHV0IGZpbGVuYW1lIHdpdGggdHlwZS4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcgYW5kIGlnbm9yZXMgYHR5cGVgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIGV4cG9ydCBmb3JtYXQuIENhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBwbmcnLFxyXG4gICAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0NPTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDaGFydCBjb25zdHJ1Y3Rvci4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgICBoaW50OiAnRGVmYXVsdDogY2hhcnQnLFxyXG4gICAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGI2NDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CNjQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gdGhlIGNoYXJ0IHNob3VsZCBiZSByZWNlaXZlZCBpbiBCYXNlNjQgZm9ybWF0IGluc3RlYWQgb2YgYmluYXJ5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBub0Rvd25sb2FkOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX05PX0RPV05MT0FEJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZXRoZXIgb3Igbm90IHRvIGluY2x1ZGUgb3IgZXhjbHVkZSBhdHRhY2htZW50IGhlYWRlcnMgaW4gdGhlIHJlc3BvbnNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSEVJR0hUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1NDQUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0RlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgbWluOiAwLjEsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9HTE9CQUxfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggZ2xvYmFsIG9wdGlvbnMgZm9yIEhpZ2hjaGFydHMuc2V0T3B0aW9ucycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVEhFTUVfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHdlYnBhZ2UgcmVuZGVyaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgZHVyaW5nIGV4cG9ydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FsbG93cyBvciBkaXNhbGxvd3MgaW5qZWN0aW9uIG9mIGZpbGVzeXN0ZW0gcmVzb3VyY2VzIChkaXNhYmxlZCBpbiBzZXJ2ZXIgbW9kZSknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DQUxMQkFDSycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIENhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgLmpzIGZpbGVuYW1lJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZXMgYXMgSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgZmlsZW5hbWUsIGNvbnRhaW5pbmcgZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSB3aXRoIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdQcm9tcHQtYmFzZWQgb3B0aW9uIHNldHRpbmcsIHNhdmVkIHRvIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVTZXJ2ZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0cyB0aGUgc2VydmVyIHdoZW4gdHJ1ZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaG9zdDoge1xyXG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIb3N0bmFtZSBvZiB0aGUgc2VydmVyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUG9ydCBudW1iZXIgZm9yIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHVwbG9hZExpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiAzLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9VUExPQURfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVxdWVzdCBib2R5IHNpemUgaW4gTUInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAnc2VydmVyQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Rpc3BsYXlzIG9yIG5vdCBhY3Rpb24gZHVyYXRpb25zIGluIG1pbGxpc2Vjb25kcyBkdXJpbmcgc2VydmVyIHJlcXVlc3RzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwcm94eToge1xyXG4gICAgICBob3N0OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eUhvc3QnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnSG9zdCBvZiB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB3aW5kb3c6IHtcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RpbWUgd2luZG93IGluIG1pbnV0ZXMgZm9yIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnRGVsYXkgZHVyYXRpb24gYmV0d2VlbiBzdWNjZXNzaXZlIHJlcXVlc3RzIGJlZm9yZSByZWFjaGluZyB0aGUgbGltaXQnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcEtleToge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0tleSB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwVG9rZW5gJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBUb2tlbjoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVG9rZW4gdG8gYnlwYXNzIHRoZSByYXRlIGxpbWl0ZXIsIHVzZWQgd2l0aCBgc2tpcEtleWAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdGb3JjZXMgdGhlIHNlcnZlciB0byB1c2UgSFRUUFMgb25seSB3aGVuIHRydWUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IGZvciB0aGUgU1NMIHNlcnZlcicsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xDZXJ0UGF0aCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWluaW11bSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICB3b3JrTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDQwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTnVtYmVyIG9mIHRhc2tzIGEgd29ya2VyIGNhbiBoYW5kbGUgYmVmb3JlIHJlc3RhcnRpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpZGxlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMzAwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGlkbGUgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAyMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGJlZm9yZSByZXRyeWluZyByZXNvdXJjZSBjcmVhdGlvbiBvbiBmYWlsdXJlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgdG8gY2hlY2sgYW5kIGRlc3Ryb3kgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1Nob3dzIHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdMb2dnaW5nIHZlcmJvc2l0eSBsZXZlbCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICByb3VuZDogMCxcclxuICAgICAgICBtaW46IDAsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnTG9nIGZpbGUgbmFtZS4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCB0byBiZSBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZXN0OiB7XHJcbiAgICAgIHZhbHVlOiAnbG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFJlcXVpcmVzIGBsb2dUb0ZpbGVgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBjb25zb2xlIGxvZ2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvRmlsZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICB1aToge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVVpJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGVuZHBvaW50IHJvdXRlIGZvciB0aGUgVUknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBOb2RlLmpzIGVudmlyb25tZW50IHR5cGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0Rpc3BsYXkgb3Igc2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoYXJkUmVzZXRQYWdlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfSEFSRF9SRVNFVF9QQUdFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaGV0aGVyIG9yIG5vdCB0byByZXNldCB0aGUgcGFnZSBjb250ZW50IGVudGlyZWx5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBicm93c2VyU2hlbGxNb2RlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHNldCB0aGUgYnJvd3NlciB0byBydW4gaW4gc2hlbGwgbW9kZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBkZWJ1Zzoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZURlYnVnJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSB1bmRlcmx5aW5nIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlYWRsZXNzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBoZWFkbGVzcyBtb2RlIGR1cmluZyBkZWJ1Z2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRldnRvb2xzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgRGV2VG9vbHMgaW4gaGVhZGZ1bCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGxpc3RlbmluZyB0byBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGR1bXBpbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RVTVBJTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdSZWRpcmVjdHMgb3Igbm90IGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gcHJvY2Vzcy5zdGRvdXQgYW5kIHByb2Nlc3Muc3RkZXJyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzbG93TW86IHtcclxuICAgICAgdmFsdWU6IDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVsYXlzIFB1cHBldGVlciBvcGVyYXRpb25zIGJ5IHRoZSBzcGVjaWZpZWQgbWlsbGlzZWNvbmRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IHVzZWQgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLy8gUHJvcGVydGllcyBuZXN0aW5nIGxldmVsIG9mIGFsbCBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRQcm9wcyA9IF9jcmVhdGVOZXN0ZWRQcm9wcyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIFByb3BlcnRpZXMgbmFtZXMgdGhhdCBzaG91bGQgbm90IGJlIHJlY3Vyc2l2ZWx5IG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IF9jcmVhdGVBYnNvbHV0ZVByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdlbmVyYXRlcyBhIG1hcHBpbmcgb2YgbmVzdGVkIGFyZ3VtZW50IGNoYWlucyBmcm9tIGEgbmVzdGVkXHJcbiAqIGNvbmZpZyBvYmplY3QuIFRoaXMgZnVuY3Rpb24gdHJhdmVyc2VzIGEgbmVzdGVkIG9iamVjdCBhbmQgY3JlYXRlcyBhIG1hcHBpbmdcclxuICogd2hlcmUgZWFjaCBrZXkgaXMgYW4gYXJndW1lbnQgbmFtZSAoZWl0aGVyIGZyb20gYGNsaU5hbWVgLCBgbGVnYWN5TmFtZWAsXHJcbiAqIG9yIHRoZSBvcmlnaW5hbCBrZXkpIGFuZCBlYWNoIHZhbHVlIGlzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY2hhaW5cclxuICogb2YgbmVzdGVkIHByb3BlcnRpZXMgbGVhZGluZyB0byB0aGF0IGFyZ3VtZW50LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbbmVzdGVkUHJvcHM9e31dIC0gVGhlIGFjY3VtdWxhdG9yIG9iamVjdCBmb3Igc3RvcmluZ1xyXG4gKiB0aGUgcmVzdWx0aW5nIGFyZ3VtZW50cyBjaGFpbnMuICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcHJvcENoYWluPScnXSAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzLFxyXG4gKiB1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbi4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gb2JqZWN0IG1hcHBpbmcgYXJndW1lbnQgbmFtZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZ1xyXG4gKiBuZXN0ZWQgcHJvcGVydHkgY2hhaW5zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzKGNvbmZpZywgbmVzdGVkUHJvcHMgPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgX2NyZWF0ZU5lc3RlZFByb3BzKGVudHJ5LCBuZXN0ZWRQcm9wcywgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBuZXN0ZWRQcm9wc1tlbnRyeS5jbGlOYW1lIHx8IGtleV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgaWYgKGVudHJ5LmxlZ2FjeU5hbWUgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2tleX1gLnN1YnN0cmluZygxKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIG9iamVjdCB3aXRoIG5lc3RlZCBhcmd1bWVudCBjaGFpbnNcclxuICByZXR1cm4gbmVzdGVkUHJvcHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSBnYXRoZXJzIHRoZSBuYW1lcyBvZiBwcm9wZXJ0aWVzIGZyb20gYSBjb25maWd1cmF0aW9uIG9iamVjdCB0aGF0XHJcbiAqIGNhbiBiZSB0cmVhdGVkIGFzIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZXNlIHByb3BlcnRpZXMgaGF2ZSB2YWx1ZXMgdGhhdFxyXG4gKiBhcmUgb2JqZWN0cyBhbmQgZG8gbm90IGNvbnRhaW4gZnVydGhlciBuZXN0ZWQgZGVwdGggd2hlbiBtZXJnaW5nIGFuIG9iamVjdFxyXG4gKiBjb250YWluaW5nIHRoZXNlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbYWJzb2x1dGVQcm9wcz1bXV0gLSBBbiBhcnJheSB0byBjb2xsZWN0IHRoZSBuYW1lc1xyXG4gKiBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxzdHJpbmc+fSBBbiBhcnJheSBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZVxyXG4gKiBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZUFic29sdXRlUHJvcHMoY29uZmlnLCBhYnNvbHV0ZVByb3BzID0gW10pIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudHlwZXMgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzXHJcbiAgICAgIF9jcmVhdGVBYnNvbHV0ZVByb3BzKGVudHJ5LCBhYnNvbHV0ZVByb3BzKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIHRoZSBvcHRpb24gY2FuIGJlIGFuIG9iamVjdCwgc2F2ZSBpdHMgdHlwZSBpbiB0aGUgYXJyYXlcclxuICAgICAgaWYgKGVudHJ5LnR5cGVzLmluY2x1ZGVzKCdPYmplY3QnKSkge1xyXG4gICAgICAgIGFic29sdXRlUHJvcHMucHVzaChrZXkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgYXJyYXkgd2l0aCB0aGUgbmFtZXMgb2YgYWJzb2x1dGUgcHJvcGVydGllc1xyXG4gIHJldHVybiBhYnNvbHV0ZVByb3BzO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRQcm9wcyxcclxuICBhYnNvbHV0ZVByb3BzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZFxyXG4gKiB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBgZW52c2AuIFdlIHNob3VsZCBub3QgdXNlIHRoZSBgcHJvY2Vzcy5lbnZgXHJcbiAqIGRpcmVjdGx5IGluIHRoZSBhcHBsaWNhdGlvbiBhcyB0aGVzZSB3b3VsZCBub3QgYmUgcGFyc2VkIHByb3Blcmx5LlxyXG4gKlxyXG4gKiBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSBwYXJzZWQgYW5kIHZhbGlkYXRlZCBvbmx5IG9uY2Ugd2hlblxyXG4gKiB0aGUgYXBwbGljYXRpb24gc3RhcnRzLiBXZSBzaG91bGQgd3JpdGUgYSBjdXN0b20gdmFsaWRhdG9yIG9yIGEgdHJhbnNmb3JtZXJcclxuICogZm9yIGVhY2ggb2YgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gcHVwcGV0ZWVyXHJcbiAgUFVQUEVURUVSX0FSR1M6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIGhpZ2hjaGFydHNcclxuICBISUdIQ0hBUlRTX1ZFUlNJT046IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PiAvXihsYXRlc3R8XFxkKyhcXC5cXGQrKXswLDJ9KSQvLnRlc3QodmFsdWUpIHx8IHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vJykgfHxcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcclxuICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgSElHSENIQVJUU19DRE5fVVJMLiBJdCBzaG91bGQgc3RhcnQgd2l0aCBodHRwOi8vIG9yIGh0dHBzOi8vLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTOiB2LmFycmF5KGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jb3JlU2NyaXB0cy52YWx1ZSksXHJcbiAgSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5tb2R1bGVTY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9JTkZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0lOU1RSOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9TVkc6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0JBVENIOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PVVRGSUxFOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9UWVBFOiB2LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLFxyXG4gIEVYUE9SVF9DT05TVFI6IHYuZW51bShbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddKSxcclxuICBFWFBPUlRfQjY0OiB2LmJvb2xlYW4oKSxcclxuICBFWFBPUlRfTk9fRE9XTkxPQUQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0dMT0JBTF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9USEVNRV9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19DVVNUT01fQ09ERTogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ0FMTEJBQ0s6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX1JFU09VUkNFUzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfTE9BRF9DT05GSUc6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUc6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlclxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1VQTE9BRF9MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgTE9HR0lOR19UT19GSUxFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gdWlcclxuICBVSV9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PREVfRU5WOiB2LmVudW0oWydkZXZlbG9wbWVudCcsICdwcm9kdWN0aW9uJywgJ3Rlc3QnXSksXHJcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0hBUkRfUkVTRVRfUEFHRTogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgTWFuYWdlcyBjb25maWd1cmF0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGJ5IGxvYWRpbmdcclxuICogYW5kIG1lcmdpbmcgb3B0aW9ucyBmcm9tIG11bHRpcGxlIHNvdXJjZXMsIHN1Y2ggYXMgZGVmYXVsdCBzZXR0aW5ncyxcclxuICogZW52aXJvbm1lbnQgdmFyaWFibGVzLCB1c2VyLXByb3ZpZGVkIG9wdGlvbnMsIGFuZCBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKiBFbnN1cmVzIHRoZSBnbG9iYWwgb3B0aW9ucyBhcmUgdXAtdG8tZGF0ZSB3aXRoIHRoZSBoaWdoZXN0IHByaW9yaXR5IHZhbHVlcy5cclxuICogUHJvdmlkZXMgZnVuY3Rpb25zIGZvciBhY2Nlc3NpbmcgYW5kIHVwZGF0aW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZGVlcENvcHksIGdldEFic29sdXRlUGF0aCwgaXNPYmplY3QgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCB7IGFic29sdXRlUHJvcHMsIG5lc3RlZFByb3BzLCBkZWZhdWx0Q29uZmlnIH0gZnJvbSAnLi9zY2hlbWFzL2NvbmZpZy5qcyc7XHJcblxyXG4vLyBTZXRzIHRoZSBnbG9iYWwgb3B0aW9ucyB3aXRoIGluaXRpYWwgdmFsdWVzIGZyb20gdGhlIGRlZmF1bHQgY29uZmlnXHJcbmNvbnN0IGdsb2JhbE9wdGlvbnMgPSBfaW5pdE9wdGlvbnMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoKSB7XHJcbiAgcmV0dXJuIGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIHVwZGF0ZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgbmV3IG9wdGlvbnMgdG8gYmVcclxuICogbWVyZ2VkIGludG8gdGhlIGdsb2JhbCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PWZhbHNlXSAtIERldGVybWluZXMgd2hldGhlciB0byBtZXJnZSB0aGUgbmV3XHJcbiAqIG9wdGlvbnMgaW50byBhIGNvcHkgb2YgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYHRydWVgKSBvciBkaXJlY3RseSBpbnRvXHJcbiAqIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3QgKGBmYWxzZWApLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBvcHRpb25zIG9iamVjdCwgZWl0aGVyIHRoZSBtb2RpZmllZCBnbG9iYWxcclxuICogb3B0aW9ucyBvciBhIG1vZGlmaWVkIGNvcHksIGJhc2VkIG9uIHRoZSB2YWx1ZSBvZiBgZ2V0Q29weWAuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdXBkYXRlT3B0aW9ucyhuZXdPcHRpb25zLCBnZXRDb3B5ID0gZmFsc2UpIHtcclxuICAvLyBNZXJnZSBuZXcgb3B0aW9ucyB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb3IgaXRzIGNvcHkgYW5kIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgcmV0dXJuIF9tZXJnZU9wdGlvbnMoXHJcbiAgICBnZXRDb3B5ID8gZGVlcENvcHkoZ2xvYmFsT3B0aW9ucykgOiBnbG9iYWxPcHRpb25zLFxyXG4gICAgbmV3T3B0aW9uc1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHZhbHVlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEksIGtlZXBpbmdcclxuICogdGhlIHByaW5jaXBsZSBvZiBvcHRpb25zIGxvYWQgcHJpb3JpdHkuIFRoaXMgZnVuY3Rpb24gYWNjZXB0cyBhIGBjbGlBcmdzYFxyXG4gKiBhcnJheSBjb250YWluaW5nIGFyZ3VtZW50cyBmcm9tIHRoZSBDTEksIHdoaWNoIHdpbGwgYmUgdmFsaWRhdGVkIGFuZCBhcHBsaWVkXHJcbiAqIGlmIHByb3ZpZGVkLlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSBhIGN1c3RvbSBKU09OIGZpbGUgKGxvYWRlZCBieSB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uKS5cclxuICogMi4gVmFsdWVzIGZyb20gdGhlIGNvbW1hbmQgbGluZSBpbnRlcmZhY2UgKENMSSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRDbGlPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBBbiBhcnJheSBvZiBjb21tYW5kIGxpbmUgYXJndW1lbnRzIHVzZWRcclxuICogZm9yIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIHVwZGF0ZWQgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCByZWZsZWN0aW5nIHRoZSBtZXJnZWRcclxuICogY29uZmlndXJhdGlvbiBmcm9tIHNvdXJjZXMgcHJvdmlkZWQgdGhyb3VnaCB0aGUgQ0xJLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldENsaU9wdGlvbnMoY2xpQXJncykge1xyXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcclxuICBpZiAoY2xpQXJncyAmJiBBcnJheS5pc0FycmF5KGNsaUFyZ3MpICYmIGNsaUFyZ3MubGVuZ3RoKSB7XHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBjdXN0b20gSlNPTiBsb2FkZWQgdmlhIHRoZSBgLS1sb2FkQ29uZmlnYFxyXG4gICAgY29uc3QgY29uZmlnT3B0aW9ucyA9IF9sb2FkQ29uZmlnRmlsZShjbGlBcmdzLCBnbG9iYWxPcHRpb25zLmN1c3RvbUxvZ2ljKTtcclxuXHJcbiAgICAvLyBVcGRhdGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCB0aGUgdmFsdWVzIGZyb20gdGhlIGBjb25maWdPcHRpb25zYFxyXG4gICAgdXBkYXRlT3B0aW9ucyhjb25maWdPcHRpb25zKTtcclxuXHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBDTElcclxuICAgIGNvbnN0IGNsaU9wdGlvbnMgPSBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNsaU9wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGdsb2JhbCBvcHRpb25zXHJcbiAgcmV0dXJuIGdsb2JhbE9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNYXBzIG9sZC1zdHJ1Y3R1cmVkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyAoUGhhbnRvbUpTKSB0byBhIG5ldyBmb3JtYXRcclxuICogKFB1cHBldGVlcikuIFRoaXMgZnVuY3Rpb24gY29udmVydHMgZmxhdCwgb2xkLXN0cnVjdHVyZWQgb3B0aW9ucyBpbnRvXHJcbiAqIGEgbmV3LCBuZXN0ZWQgY29uZmlndXJhdGlvbiBmb3JtYXQgYmFzZWQgb24gYSBwcmVkZWZpbmVkIG1hcHBpbmdcclxuICogKGBuZXN0ZWRQcm9wc2ApLiBUaGUgbmV3IGZvcm1hdCBpcyB1c2VkIGZvciBQdXBwZXRlZXIsIHdoaWxlIHRoZSBvbGQgZm9ybWF0XHJcbiAqIHdhcyB1c2VkIGZvciBQaGFudG9tSlMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtYXBUb05ld09wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9sZE9wdGlvbnMgLSBUaGUgb2xkLCBmbGF0IGNvbmZpZ3VyYXRpb24gb3B0aW9uc1xyXG4gKiB0byBiZSBjb252ZXJ0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgbmV3IG9iamVjdCBjb250YWluaW5nIG9wdGlvbnMgc3RydWN0dXJlZCBhY2NvcmRpbmdcclxuICogdG8gdGhlIG1hcHBpbmcgZGVmaW5lZCBpbiBgbmVzdGVkUHJvcHNgIG9yIGFuIGVtcHR5IG9iamVjdCBpZiB0aGUgcHJvdmlkZWRcclxuICogYG9sZE9wdGlvbnNgIGlzIG5vdCBhIGNvcnJlY3Qgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9ucyhvbGRPcHRpb25zKSB7XHJcbiAgLy8gQW4gb2JqZWN0IGZvciB0aGUgbmV3IHN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gIGNvbnN0IG5ld09wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgdmFsdWUgaXMgYSBjb3JyZWN0IG9iamVjdFxyXG4gIGlmIChpc09iamVjdChvbGRPcHRpb25zKSkge1xyXG4gICAgLy8gSXRlcmF0ZSBvdmVyIGVhY2gga2V5LXZhbHVlIHBhaXIgaW4gdGhlIG9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnNcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG9sZE9wdGlvbnMpKSB7XHJcbiAgICAgIC8vIElmIHRoZXJlIGlzIGEgbmVzdGVkIG1hcHBpbmcsIHNwbGl0IGl0IGludG8gYSBwcm9wZXJ0aWVzIGNoYWluXHJcbiAgICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW2tleV1cclxuICAgICAgICA/IG5lc3RlZFByb3BzW2tleV0uc3BsaXQoJy4nKVxyXG4gICAgICAgIDogW107XHJcblxyXG4gICAgICAvLyBJZiBpdCBpcyB0aGUgbGFzdCBwcm9wZXJ0eSBpbiB0aGUgY2hhaW4sIGFzc2lnbiB0aGUgdmFsdWUsIG90aGVyd2lzZSxcclxuICAgICAgLy8gY3JlYXRlIG9yIHJldXNlIHRoZSBuZXN0ZWQgb2JqZWN0XHJcbiAgICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoXHJcbiAgICAgICAgKG9iaiwgcHJvcCwgaW5kZXgpID0+XHJcbiAgICAgICAgICAob2JqW3Byb3BdID1cclxuICAgICAgICAgICAgcHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4ID8gdmFsdWUgOiBvYmpbcHJvcF0gfHwge30pLFxyXG4gICAgICAgIG5ld09wdGlvbnNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICAnW2NvbmZpZ10gTm8gY29ycmVjdCBvYmplY3Qgd2l0aCBvcHRpb25zIHdhcyBwcm92aWRlZC4gUmV0dXJuaW5nIGFuIGVtcHR5IGFycmF5LidcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIG5ldywgc3RydWN0dXJlZCBvcHRpb25zIG9iamVjdFxyXG4gIHJldHVybiBuZXdPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogVmFsaWRhdGVzLCBwYXJzZXMsIGFuZCBjaGVja3MgaWYgdGhlIHByb3ZpZGVkIGNvbmZpZyBpcyBhbGxvd2VkIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnXHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gY29uZmlnIC0gVGhlIGNvbmZpZyB0byBiZSB2YWxpZGF0ZWQgYW5kIHBhcnNlZCBhcyBhIHNldFxyXG4gKiBvZiBvcHRpb25zLiBNdXN0IGJlIGVpdGhlciBhbiBvYmplY3Qgb3IgYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3RvU3RyaW5nPWZhbHNlXSAtIFdoZXRoZXIgdG8gcmV0dXJuIGEgc3RyaW5naWZpZWQgdmVyc2lvblxyXG4gKiBvZiB0aGUgcGFyc2VkIGNvbmZpZy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICogQHBhcmFtIHtib29sZWFufSBbYWxsb3dGdW5jdGlvbnM9ZmFsc2VdIC0gV2hldGhlciB0byBhbGxvdyBmdW5jdGlvbnNcclxuICogaW4gdGhlIHBhcnNlZCBjb25maWcuIElmIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkLiBPdGhlcndpc2UsIHdoZW5cclxuICogYSBmdW5jdGlvbiBpcyBmb3VuZCwgbnVsbCBpcyByZXR1cm5lZC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8c3RyaW5nfG51bGwpfSBSZXR1cm5zIGEgcGFyc2VkIHNldCBvZiBvcHRpb25zIG9iamVjdCxcclxuICogYSBzdHJpbmdpZmllZCBzZXQgb2Ygb3B0aW9ucyBvYmplY3QgaWYgdGhlIGB0b1N0cmluZ2AgaXMgdHJ1ZSwgYW5kIG51bGxcclxuICogaWYgdGhlIGNvbmZpZyBpcyBub3QgYSB2YWxpZCBzZXQgb2Ygb3B0aW9ucyBvciBwYXJzaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzQWxsb3dlZENvbmZpZyhcclxuICBjb25maWcsXHJcbiAgdG9TdHJpbmcgPSBmYWxzZSxcclxuICBhbGxvd0Z1bmN0aW9ucyA9IGZhbHNlXHJcbikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBBY2NlcHQgb25seSBvYmplY3RzIGFuZCBzdHJpbmdzXHJcbiAgICBpZiAoIWlzT2JqZWN0KGNvbmZpZykgJiYgdHlwZW9mIGNvbmZpZyAhPT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gUmV0dXJuIG51bGwgaWYgYW55IG90aGVyIHR5cGVcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBvYmplY3QgcmVwcmVzZW50YXRpb24gb2YgdGhlIG9yaWdpbmFsIGNvbmZpZ1xyXG4gICAgY29uc3Qgb2JqZWN0Q29uZmlnID1cclxuICAgICAgdHlwZW9mIGNvbmZpZyA9PT0gJ3N0cmluZydcclxuICAgICAgICA/IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IGV2YWwoYCgke2NvbmZpZ30pYClcclxuICAgICAgICAgIDogSlNPTi5wYXJzZShjb25maWcpXHJcbiAgICAgICAgOiBjb25maWc7XHJcblxyXG4gICAgLy8gUHJlc2VydmUgb3IgcmVtb3ZlIHBvdGVudGlhbCBmdW5jdGlvbnMgYmFzZWQgb24gdGhlIGBhbGxvd0Z1bmN0aW9uc2AgZmxhZ1xyXG4gICAgY29uc3Qgc3RyaW5naWZpZWRPcHRpb25zID0gX29wdGlvbnNTdHJpbmdpZnkoXHJcbiAgICAgIG9iamVjdENvbmZpZyxcclxuICAgICAgYWxsb3dGdW5jdGlvbnMsXHJcbiAgICAgIGZhbHNlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFBhcnNlIHRoZSBjb25maWcgdG8gY2hlY2sgaWYgaXQgaXMgdmFsaWQgc2V0IG9mIG9wdGlvbnNcclxuICAgIGNvbnN0IHBhcnNlZE9wdGlvbnMgPSBhbGxvd0Z1bmN0aW9uc1xyXG4gICAgICA/IEpTT04ucGFyc2UoXHJcbiAgICAgICAgICBfb3B0aW9uc1N0cmluZ2lmeShvYmplY3RDb25maWcsIGFsbG93RnVuY3Rpb25zLCB0cnVlKSxcclxuICAgICAgICAgIChfLCB2YWx1ZSkgPT5cclxuICAgICAgICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJiB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpXHJcbiAgICAgICAgICAgICAgPyBldmFsKGAoJHt2YWx1ZX0pYClcclxuICAgICAgICAgICAgICA6IHZhbHVlXHJcbiAgICAgICAgKVxyXG4gICAgICA6IEpTT04ucGFyc2Uoc3RyaW5naWZpZWRPcHRpb25zKTtcclxuXHJcbiAgICAvLyBSZXR1cm4gc3RyaW5naWZpZWQgb3Igb2JqZWN0IG9wdGlvbnMgYmFzZWQgb24gdGhlIGB0b1N0cmluZ2AgZmxhZ1xyXG4gICAgcmV0dXJuIHRvU3RyaW5nID8gc3RyaW5naWZpZWRPcHRpb25zIDogcGFyc2VkT3B0aW9ucztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gUmV0dXJuIG51bGwgaWYgcGFyc2luZyBmYWlsc1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbywgdmVyc2lvbiwgYW5kIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludExpY2Vuc2VcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludExpY2Vuc2UoKSB7XHJcbiAgLy8gUHJpbnQgdGhlIGxvZ28gYW5kIHZlcnNpb24gaW5mb3JtYXRpb25cclxuICBwcmludFZlcnNpb24oKTtcclxuXHJcbiAgLy8gUHJpbnQgdGhlIGxpY2Vuc2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdUaGlzIHNvZnR3YXJlIHJlcXVpcmVzIGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGZvciBjb21tZXJjaWFsIHVzZS5cXG4nXHJcbiAgICAgIC55ZWxsb3csXHJcbiAgICAnXFxuRm9yIGEgZnVsbCBsaXN0IG9mIENMSSBvcHRpb25zLCB0eXBlOicsXHJcbiAgICAnXFxuaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyIC0taGVscFxcbicuZ3JlZW4sXHJcbiAgICAnXFxuSWYgeW91IGRvIG5vdCBoYXZlIGEgbGljZW5zZSwgb25lIGNhbiBiZSBvYnRhaW5lZCBoZXJlOicsXHJcbiAgICAnXFxuaHR0cHM6Ly9zaG9wLmhpZ2hzb2Z0LmNvbS9cXG4nLmdyZWVuLFxyXG4gICAgJ1xcblRvIGN1c3RvbWl6ZSB5b3VyIGluc3RhbGxhdGlvbiwgcGxlYXNlIHJlZmVyIHRvIHRoZSBSRUFETUUgZmlsZSBhdDonLFxyXG4gICAgJ1xcbmh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWVcXG4nLmdyZWVuXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByaW50cyB1c2FnZSBpbmZvcm1hdGlvbiBmb3IgQ0xJIGFyZ3VtZW50cywgZGlzcGxheWluZyBhdmFpbGFibGUgb3B0aW9uc1xyXG4gKiBhbmQgdGhlaXIgZGVzY3JpcHRpb25zLiBJdCBjYW4gbGlzdCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5IGlmIGNhdGVnb3JpZXNcclxuICogY29udGFpbiBuZXN0ZWQgb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50VXNhZ2VcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFVzYWdlKCkge1xyXG4gIC8vIERpc3BsYXkgUkVBRE1FIGFuZCBnZW5lcmFsIHVzYWdlIGluZm9ybWF0aW9uXHJcbiAgY29uc29sZS5sb2coXHJcbiAgICAnXFxuVXNhZ2Ugb2YgQ0xJIGFyZ3VtZW50czonLmJvbGQsXHJcbiAgICAnXFxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nLFxyXG4gICAgYFxcbkZvciBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uLCB2aXNpdCB0aGUgUkVBRE1FIGZpbGUgYXQ6ICR7J2h0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWUnLmdyZWVufS5cXG5gXHJcbiAgKTtcclxuXHJcbiAgLy8gSXRlcmF0ZSB0aHJvdWdoIGVhY2ggY2F0ZWdvcnkgaW4gdGhlIGBkZWZhdWx0Q29uZmlnYCBhbmQgZGlzcGxheSB1c2FnZSBpbmZvXHJcbiAgT2JqZWN0LmtleXMoZGVmYXVsdENvbmZpZykuZm9yRWFjaCgoY2F0ZWdvcnkpID0+IHtcclxuICAgIGNvbnNvbGUubG9nKGAke2NhdGVnb3J5LnRvVXBwZXJDYXNlKCl9YC5ib2xkLnJlZCk7XHJcbiAgICBfY3ljbGVDYXRlZ29yaWVzKGRlZmF1bHRDb25maWdbY2F0ZWdvcnldKTtcclxuICAgIGNvbnNvbGUubG9nKCcnKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByaW50cyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGxvZ28gb3IgdGV4dCB3aXRoIHRoZSB2ZXJzaW9uXHJcbiAqIGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW25vTG9nbz1mYWxzZV0gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24sIHdpdGhvdXQgdGhlIGxvZ28uIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRWZXJzaW9uKG5vTG9nbyA9IGZhbHNlKSB7XHJcbiAgLy8gR2V0IHBhY2thZ2UgdmVyc2lvbiBlaXRoZXIgZnJvbSBgLmVudmAgb3IgZnJvbSBgcGFja2FnZS5qc29uYFxyXG4gIGNvbnN0IHBhY2thZ2VWZXJzaW9uID0gSlNPTi5wYXJzZShcclxuICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ3BhY2thZ2UuanNvbicpLCAndXRmOCcpXHJcbiAgKS52ZXJzaW9uO1xyXG5cclxuICAvLyBQcmludCB0ZXh0IG9ubHlcclxuICBpZiAobm9Mb2dvKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHYke3BhY2thZ2VWZXJzaW9ufWApO1xyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBQcmludCB0aGUgbG9nb1xyXG4gICAgY29uc29sZS5sb2coXHJcbiAgICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ21zZycsICdzdGFydHVwLm1zZycpLCAndXRmOCcpLnRvU3RyaW5nKClcclxuICAgICAgICAuYm9sZC55ZWxsb3csXHJcbiAgICAgIGB2JHtwYWNrYWdlVmVyc2lvbn1cXG5gLmJvbGRcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgYW5kIHJldHVybnMgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCBiYXNlZCBvbiB0aGUgcHJvdmlkZWRcclxuICogY29uZmlndXJhdGlvbiwgc2V0dGluZyB2YWx1ZXMgZnJvbSBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogVGhlIHByaW9yaXR5IG9yZGVyIGZvciBzZXR0aW5nIHZhbHVlcyBpczpcclxuICpcclxuICogMS4gVmFsdWVzIGZyb20gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSAoZGVmYXVsdHMpLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgKHNwZWNpZmllZCBpbiB0aGUgYC5lbnZgIGZpbGUpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2luaXRPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgdXNlZCBmb3IgaW5pdGlhbGl6aW5nXHJcbiAqIHRoZSBnbG9iYWwgb3B0aW9ucy4gSXQgc2hvdWxkIGluY2x1ZGUgbmVzdGVkIHByb3BlcnRpZXMgd2l0aCBhIGB2YWx1ZWBcclxuICogYW5kIGFuIGBlbnZMaW5rYCBmb3IgbGlua2luZyB0byBlbnZpcm9ubWVudCB2YXJpYWJsZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBpbml0aWFsaXplZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHBvcHVsYXRlZCB3aXRoXHJcbiAqIHZhbHVlcyBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5XHJcbiAqIG9yZGVyLlxyXG4gKi9cclxuZnVuY3Rpb24gX2luaXRPcHRpb25zKGNvbmZpZykge1xyXG4gIC8vIEluaXQgdGhlIG9iamVjdCBmb3Igb3B0aW9uc1xyXG4gIGNvbnN0IG9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gU3RhcnQgaW5pdGlhbGl6aW5nIHRoZSBgb3B0aW9uc2Agb2JqZWN0IHJlY3Vyc2l2ZWx5XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoY29uZmlnKSkge1xyXG4gICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChpdGVtLCAndmFsdWUnKSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGNvcnJlY3QgdmFsdWUgYmFzZWQgb24gdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5IG9yZGVyXHJcbiAgICAgIGlmIChlbnZzW2l0ZW0uZW52TGlua10gIT09IHVuZGVmaW5lZCAmJiBlbnZzW2l0ZW0uZW52TGlua10gIT09IG51bGwpIHtcclxuICAgICAgICAvLyBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIHZhbHVlXHJcbiAgICAgICAgb3B0aW9uc1tuYW1lXSA9IGVudnNbaXRlbS5lbnZMaW5rXTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaGUgdmFsdWUgZnJvbSB0aGUgY29uZmlnIGZpbGVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gaXRlbS52YWx1ZTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIGEgc2VjdGlvbiBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICBvcHRpb25zW25hbWVdID0gX2luaXRPcHRpb25zKGl0ZW0pO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBjcmVhdGVkIGBvcHRpb25zYCBvYmplY3RcclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGNvbnNpZGVyaW5nIGFic29sdXRlIHByb3BlcnRpZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcmlnaW5hbE9wdGlvbnMgLSBPcmlnaW5hbCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBuZXdPcHRpb25zIC0gTmV3IGNvbmZpZ3VyYXRpb24gb3B0aW9ucyB0byBiZSBtZXJnZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IE1lcmdlZCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX21lcmdlT3B0aW9ucyhvcmlnaW5hbE9wdGlvbnMsIG5ld09wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgYG9yaWdpbmFsT3B0aW9uc2AgYW5kIGBuZXdPcHRpb25zYCBhcmUgY29ycmVjdCBvYmplY3RzXHJcbiAgaWYgKGlzT2JqZWN0KG9yaWdpbmFsT3B0aW9ucykgJiYgaXNPYmplY3QobmV3T3B0aW9ucykpIHtcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICAgIG9yaWdpbmFsT3B0aW9uc1trZXldID1cclxuICAgICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXHJcbiAgICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgPyBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9uc1trZXldLCB2YWx1ZSlcclxuICAgICAgICAgIDogdmFsdWUgIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICAgIDogb3JpZ2luYWxPcHRpb25zW2tleV0gfHwgbnVsbDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb3JpZ2luYWwgKG1vZGlmaWVkIG9yIG5vdCkgb3B0aW9uc1xyXG4gIHJldHVybiBvcmlnaW5hbE9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmdcclxuICogd2l0aCB0aGUgb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy4gSW4gb3JkZXIgZm9yIGEgZnVuY3Rpb25cclxuICogdG8gYmUgcHJlc2VydmVkLCBpdCBuZWVkcyB0byBmb2xsb3cgdGhlIGZvcm1hdCBgZnVuY3Rpb24gKC4uLikgey4uLn1gLlxyXG4gKiBTdWNoIGEgZnVuY3Rpb24gY2FuIGFsc28gYmUgc3RyaW5naWZpZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWRcclxuICogaW4gdGhlIG91dHB1dC4gT3RoZXJ3aXNlIGFuIGVycm9yIGlzIHRocm93bi5cclxuICogQHBhcmFtIHtib29sZWFufSBzdHJpbmdpZnlGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBzYXZlZFxyXG4gKiBhcyBzdHJpbmdzLiBUaGUgYGFsbG93RnVuY3Rpb25zYCBtdXN0IGJlIHNldCB0byB0cnVlIGFzIHdlbGwgZm9yIHRoaXMgdG8gdGFrZVxyXG4gKiBhbiBlZmZlY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgd2hlbiBmdW5jdGlvbnMgYXJlIG5vdCBhbGxvd2VkIGJ1dCBhcmVcclxuICogZm91bmQgaW4gcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX29wdGlvbnNTdHJpbmdpZnkob3B0aW9ucywgYWxsb3dGdW5jdGlvbnMsIHN0cmluZ2lmeUZ1bmN0aW9ucykge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAoXywgdmFsdWUpID0+IHtcclxuICAgIC8vIFRyaW0gc3RyaW5nIHZhbHVlc1xyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgdmFsdWUgaXMgYSBmdW5jdGlvbiBvciBzdHJpbmdpZmllZCBmdW5jdGlvblxyXG4gICAgaWYgKFxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgfHxcclxuICAgICAgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpICYmXHJcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBJZiBhbGxvd0Z1bmN0aW9ucyBpcyBzZXQgdG8gdHJ1ZSwgcHJlc2VydmUgZnVuY3Rpb25zXHJcbiAgICAgIGlmIChhbGxvd0Z1bmN0aW9ucykge1xyXG4gICAgICAgIC8vIEJhc2VkIG9uIHRoZSBgc3RyaW5naWZ5RnVuY3Rpb25zYCBvcHRpb25zLCBzZXQgZnVuY3Rpb24gdmFsdWVzXHJcbiAgICAgICAgcmV0dXJuIHN0cmluZ2lmeUZ1bmN0aW9uc1xyXG4gICAgICAgICAgPyAvLyBBcyBzdHJpbmdpZmllZCBmdW5jdGlvbnNcclxuICAgICAgICAgICAgYFwiRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOXCJgXHJcbiAgICAgICAgICA6IC8vIEFzIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOYDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaHJvdyBhbiBlcnJvciBvdGhlcndpc2VcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgc2ltcGx5IHJldHVybiB0aGUgdmFsdWVcclxuICAgIHJldHVybiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIHN0cmluZ2lmeUZ1bmN0aW9ucyA/IC9cXFxcXCJFWFBfRlVOfEVYUF9GVU5cXFxcXCIvZyA6IC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHByb3ZpZGVkIHZpYVxyXG4gKiB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGluIHRoZSBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2xvYWRDb25maWdGaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIHRvIHNlYXJjaFxyXG4gKiBmb3IgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgZmlsZSBwYXRoLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGxvYWRlZCBmcm9tIHRoZSBzcGVjaWZpZWRcclxuICogZmlsZSwgb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBmaWxlIGlzIG5vdCBmb3VuZCwgaW52YWxpZCwgb3IgYW4gZXJyb3JcclxuICogb2NjdXJzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiB2YWx1ZVxyXG4gIGNvbnN0IGNvbmZpZ0ZpbGVOYW1lID0gY29uZmlnSW5kZXggPiAtMSAmJiBjbGlBcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lICYmIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIExvYWQgYW4gb3B0aW9uYWwgY3VzdG9tIEpTT04gY29uZmlnIGZpbGVcclxuICAgICAgcmV0dXJuIGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGNvbmZpZ0ZpbGVOYW1lKSwgJ3V0ZjgnKSxcclxuICAgICAgICBmYWxzZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgMixcclxuICAgICAgICBlcnJvcixcclxuICAgICAgICBgW2NvbmZpZ10gVW5hYmxlIHRvIGxvYWQgdGhlIGNvbmZpZ3VyYXRpb24gZnJvbSB0aGUgJHtjb25maWdGaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBObyBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gcmV0dXJuXHJcbiAgcmV0dXJuIHt9O1xyXG59XHJcblxyXG4vKipcclxuICogUGFyc2VzIGNvbW1hbmQtbGluZSBhcmd1bWVudHMgYW5kIHBhaXJzIGVhY2ggYXJndW1lbnQgd2l0aCBpdHMgY29ycmVzcG9uZGluZ1xyXG4gKiBvcHRpb24gaW4gdGhlIGNvbmZpZ3VyYXRpb24uIFRoZSB2YWx1ZXMgYXJlIHN0cnVjdHVyZWQgaW50byBhIG5lc3RlZCBvcHRpb25zXHJcbiAqIG9iamVjdCwgYmFzZWQgb24gcHJlZGVmaW5lZCBtYXBwaW5ncy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBuZXN0ZWRQcm9wcyAtIEFuIGFycmF5IG9mIG5lc3RpbmcgbGV2ZWwgZm9yIGFsbFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjbGlBcmdzIC0gQW4gYXJyYXkgb2YgY29tbWFuZC1saW5lIGFyZ3VtZW50c1xyXG4gKiBjb250YWluaW5nIG9wdGlvbnMgYW5kIHRoZWlyIGFzc29jaWF0ZWQgdmFsdWVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0IHdoZXJlIGVhY2ggb3B0aW9uIGZyb21cclxuICogdGhlIGNvbW1hbmQtbGluZSBpcyBwYWlyZWQgd2l0aCBpdHMgdmFsdWUsIHN0cnVjdHVyZWQgaW50byBuZXN0ZWQgb2JqZWN0c1xyXG4gKiBhcyBkZWZpbmVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX3BhaXJBcmd1bWVudFZhbHVlKG5lc3RlZFByb3BzLCBjbGlBcmdzKSB7XHJcbiAgLy8gQW4gZW1wdHkgb2JqZWN0IHRvIGNvbGxlY3QgYW5kIHN0cnVjdHVyaXplIGRhdGEgZnJvbSB0aGUgYXJnc1xyXG4gIGNvbnN0IGNsaU9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gQ3ljbGUgdGhyb3VnaCBhbGwgQ0xJIGFyZ3MgYW5kIGZpbHRlciB0aGVtXHJcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBjbGlBcmdzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICBjb25zdCBvcHRpb24gPSBjbGlBcmdzW2ldLnJlcGxhY2UoLy0vZywgJycpO1xyXG5cclxuICAgIC8vIEZpbmQgdGhlIHJpZ2h0IHBsYWNlIGZvciBwcm9wZXJ0eSdzIHZhbHVlXHJcbiAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRQcm9wc1tvcHRpb25dXHJcbiAgICAgID8gbmVzdGVkUHJvcHNbb3B0aW9uXS5zcGxpdCgnLicpXHJcbiAgICAgIDogW107XHJcblxyXG4gICAgLy8gQ3JlYXRlIG9wdGlvbnMgb2JqZWN0IHdpdGggdmFsdWVzIGZyb20gQ0xJIGZvciBsYXRlciBwYXJzaW5nIGFuZCBtZXJnaW5nXHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XHJcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcclxuICAgICAgICBjb25zdCB2YWx1ZSA9IGNsaUFyZ3NbKytpXTtcclxuICAgICAgICBpZiAoIXZhbHVlKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGBbY29uZmlnXSBNaXNzaW5nIHZhbHVlIGZvciB0aGUgQ0xJICctLSR7b3B0aW9ufScgYXJndW1lbnQuIFVzaW5nIHRoZSBkZWZhdWx0IHZhbHVlLmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIG9ialtwcm9wXSA9IHZhbHVlIHx8IG51bGw7XHJcbiAgICAgIH0gZWxzZSBpZiAob2JqW3Byb3BdID09PSB1bmRlZmluZWQpIHtcclxuICAgICAgICBvYmpbcHJvcF0gPSB7fTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xyXG4gICAgfSwgY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcGFyc2VkIENMSSBvcHRpb25zXHJcbiAgcmV0dXJuIGNsaU9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSB0cmF2ZXJzZXMgdGhlIG9wdGlvbnMgb2JqZWN0IHRvIHByaW50IHRoZSB1c2FnZSBpbmZvcm1hdGlvblxyXG4gKiBmb3IgZWFjaCBvcHRpb24gY2F0ZWdvcnkgYW5kIGluZGl2aWR1YWwgb3B0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2N5Y2xlQ2F0ZWdvcmllc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIENMSSBvcHRpb25zLiBJdCBtYXlcclxuICogaW5jbHVkZSBuZXN0ZWQgY2F0ZWdvcmllcyBhbmQgaW5kaXZpZHVhbCBvcHRpb25zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb25zKSB7XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgb3B0aW9uXSBvZiBPYmplY3QuZW50cmllcyhvcHRpb25zKSkge1xyXG4gICAgLy8gSWYgdGhlIGN1cnJlbnQgZW50cnkgaXMgYSBjYXRlZ29yeSBhbmQgbm90IGEgbGVhZiBvcHRpb24sIHJlY3Vyc2UgaW50byBpdFxyXG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob3B0aW9uLCAndmFsdWUnKSkge1xyXG4gICAgICBfY3ljbGVDYXRlZ29yaWVzKG9wdGlvbik7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBQcmVwYXJlIGRlc2NyaXB0aW9uXHJcbiAgICAgIGNvbnN0IGRlc2NOYW1lID0gYCAtLSR7b3B0aW9uLmNsaU5hbWUgfHwgbmFtZX1gO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB2YWx1ZVxyXG4gICAgICBsZXQgb3B0aW9uVmFsdWUgPSBvcHRpb24udmFsdWU7XHJcblxyXG4gICAgICAvLyBQcmVwYXJlIHZhbHVlIGZvciBvcHRpb24gdGhhdCBpcyBub3QgbnVsbCBhbmQgaXMgYXJyYXkgb2Ygc3RyaW5nc1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmdbXScpKSB7XHJcbiAgICAgICAgb3B0aW9uVmFsdWUgPVxyXG4gICAgICAgICAgJ1snICsgb3B0aW9uVmFsdWUubWFwKChpdGVtKSA9PiBgJyR7aXRlbX0nYCkuam9pbignLCAnKSArICddJztcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGEgc3RyaW5nXHJcbiAgICAgIGlmIChvcHRpb25WYWx1ZSAhPT0gbnVsbCAmJiBvcHRpb24udHlwZXMuaW5jbHVkZXMoJ3N0cmluZycpKSB7XHJcbiAgICAgICAgb3B0aW9uVmFsdWUgPSBgJyR7b3B0aW9uVmFsdWV9J2A7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERpc3BsYXkgY29ycmVjdGx5IGFsaWduZWQgbWVzc2FnZXNcclxuICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgZGVzY05hbWUuZ3JlZW4sXHJcbiAgICAgICAgYCR7KCc8JyArIG9wdGlvbi50eXBlcy5qb2luKCd8JykgKyAnPicpLnllbGxvd31gLFxyXG4gICAgICAgIGAke1N0cmluZyhvcHRpb25WYWx1ZSkuYm9sZH1gLmJsdWUsXHJcbiAgICAgICAgYC0gJHtvcHRpb24uZGVzY3JpcHRpb259LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHVwZGF0ZU9wdGlvbnMsXHJcbiAgc2V0Q2xpT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcbiAgaXNBbGxvd2VkQ29uZmlnLFxyXG4gIHByaW50TGljZW5zZSxcclxuICBwcmludFVzYWdlLFxyXG4gIHByaW50VmVyc2lvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgSFRUUCB1dGlsaXR5IG1vZHVsZSBmb3IgZmV0Y2hpbmcgYW5kIHBvc3RpbmcgZGF0YS4gU3VwcG9ydHMgYm90aFxyXG4gKiBIVFRQIGFuZCBIVFRQUyBwcm90b2NvbHMsIHByb3ZpZGluZyBtZXRob2RzIHRvIG1ha2UgR0VUIGFuZCBQT1NUIHJlcXVlc3RzXHJcbiAqIHdpdGggY3VzdG9taXphYmxlIG9wdGlvbnMuIEluY2x1ZGVzIHByb3RvY29sIGRldGVybWluYXRpb24gYmFzZWQgb24gVVJMXHJcbiAqIGFuZCBhdWdtZW50cyByZXNwb25zZSBvYmplY3RzIHdpdGggYSAndGV4dCcgcHJvcGVydHkgZm9yIGVhc2llciBkYXRhIGFjY2Vzcy5cclxuICovXHJcblxyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGRhdGEgZnJvbSB0aGUgc3BlY2lmaWVkIFVSTCB1c2luZyBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBmZXRjaFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBmZXRjaCBkYXRhIGZyb20uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZmV0Y2godXJsLCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpXHJcbiAgICAgIC5nZXQodXJsLCByZXF1ZXN0T3B0aW9ucywgKHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2VEYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFyZXNwb25zZURhdGEpIHtcclxuICAgICAgICAgICAgcmVqZWN0KCdOb3RoaW5nIHdhcyBmZXRjaGVkIGZyb20gdGhlIFVSTC4nKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogU2VuZHMgYSBQT1NUIHJlcXVlc3QgdG8gdGhlIHNwZWNpZmllZCBVUkwgd2l0aCB0aGUgcHJvdmlkZWQgSlNPTiBib2R5IHVzaW5nXHJcbiAqIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHBvc3RcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gc2VuZCB0aGUgUE9TVCByZXF1ZXN0IHRvLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2JvZHk9e31dIC0gVGhlIEpTT04gYm9keSB0byBpbmNsdWRlIGluIHRoZSBQT1NUIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtyZXF1ZXN0T3B0aW9ucz17fV0gLSBPcHRpb25zIGZvciB0aGUgSFRUUC9IVFRQUyByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBIVFRQL0hUVFBTIHJlc3BvbnNlXHJcbiAqIG9iamVjdCB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0KHVybCwgYm9keSA9IHt9LCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIGNvbnN0IGRhdGEgPSBKU09OLnN0cmluZ2lmeShib2R5KTtcclxuXHJcbiAgICAvLyBTZXQgZGVmYXVsdCBoZWFkZXJzIGFuZCBtZXJnZSB3aXRoIHJlcXVlc3RPcHRpb25zXHJcbiAgICBjb25zdCBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihcclxuICAgICAge1xyXG4gICAgICAgIG1ldGhvZDogJ1BPU1QnLFxyXG4gICAgICAgIGhlYWRlcnM6IHtcclxuICAgICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXHJcbiAgICAgICAgICAnQ29udGVudC1MZW5ndGgnOiBkYXRhLmxlbmd0aFxyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcmVxdWVzdE9wdGlvbnNcclxuICAgICk7XHJcblxyXG4gICAgY29uc3QgcmVxdWVzdCA9IF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpXHJcbiAgICAgIC5yZXF1ZXN0KHVybCwgb3B0aW9ucywgKHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2VEYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWRcclxuICAgICAgICByZXNwb25zZS5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgLy8gV3JpdGUgdGhlIHJlcXVlc3QgYm9keSBhbmQgZW5kIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LndyaXRlKGRhdGEpO1xyXG4gICAgcmVxdWVzdC5lbmQoKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgdGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBVUkwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZGV0ZXJtaW5lIHRoZSBwcm90b2NvbC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIChodHRwIG9yIGh0dHBzKS5cclxuICovXHJcbmZ1bmN0aW9uIF9nZXRQcm90b2NvbE1vZHVsZSh1cmwpIHtcclxuICByZXR1cm4gdXJsLnN0YXJ0c1dpdGgoJ2h0dHBzJykgPyBodHRwcyA6IGh0dHA7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBmZXRjaCxcclxuICBwb3N0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEEgY3VzdG9tIGVycm9yIGNsYXNzIGZvciBoYW5kbGluZyBleHBvcnQtcmVsYXRlZCBlcnJvcnMuIEV4dGVuZHMgdGhlIG5hdGl2ZVxyXG4gKiBgRXJyb3JgIGNsYXNzIHRvIGluY2x1ZGUgYWRkaXRpb25hbCBwcm9wZXJ0aWVzIGxpa2Ugc3RhdHVzIGNvZGUgYW5kIHN0YWNrXHJcbiAqIHRyYWNlIGRldGFpbHMuXHJcbiAqL1xyXG5jbGFzcyBFeHBvcnRFcnJvciBleHRlbmRzIEVycm9yIHtcclxuICAvKipcclxuICAgKiBDcmVhdGVzIGFuIGluc3RhbmNlIG9mIHRoZSBgRXhwb3J0RXJyb3JgLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtzdHJpbmd9IG1lc3NhZ2UgLSBUaGUgZXJyb3IgbWVzc2FnZSB0byBiZSBkaXNwbGF5ZWQuXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBPcHRpb25hbCBIVFRQIHN0YXR1cyBjb2RlIGFzc29jaWF0ZWRcclxuICAgKiB3aXRoIHRoZSBlcnJvciAoZS5nLiwgNDAwLCA1MDApLlxyXG4gICAqL1xyXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UsIHN0YXR1c0NvZGUpIHtcclxuICAgIHN1cGVyKCk7XHJcblxyXG4gICAgdGhpcy5tZXNzYWdlID0gbWVzc2FnZTtcclxuICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gbWVzc2FnZTtcclxuXHJcbiAgICBpZiAoc3RhdHVzQ29kZSkge1xyXG4gICAgICB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXNDb2RlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0cyBvciB1cGRhdGVzIHRoZSBIVFRQIHN0YXR1cyBjb2RlIGZvciB0aGUgZXJyb3IuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzQ29kZSAtIFRoZSBIVFRQIHN0YXR1cyBjb2RlIHRvIGFzc2lnbiB0byB0aGUgZXJyb3IuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyB7RXhwb3J0RXJyb3J9IFRoZSB1cGRhdGVkIGluc3RhbmNlIG9mIHRoZSBgRXhwb3J0RXJyb3JgIGNsYXNzLlxyXG4gICAqL1xyXG4gIHNldFN0YXR1cyhzdGF0dXNDb2RlKSB7XHJcbiAgICB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXNDb2RlO1xyXG5cclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0cyBhZGRpdGlvbmFsIGVycm9yIGRldGFpbHMgYmFzZWQgb24gYW4gZXhpc3RpbmcgZXJyb3Igb2JqZWN0LlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBBbiBlcnJvciBvYmplY3QgY29udGFpbmluZyBkZXRhaWxzIHRvIHBvcHVsYXRlXHJcbiAgICogdGhlIGBFeHBvcnRFcnJvcmAgaW5zdGFuY2UuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyB7RXhwb3J0RXJyb3J9IFRoZSB1cGRhdGVkIGluc3RhbmNlIG9mIHRoZSBgRXhwb3J0RXJyb3JgIGNsYXNzLlxyXG4gICAqL1xyXG4gIHNldEVycm9yKGVycm9yKSB7XHJcbiAgICB0aGlzLmVycm9yID0gZXJyb3I7XHJcblxyXG4gICAgaWYgKGVycm9yLm5hbWUpIHtcclxuICAgICAgdGhpcy5uYW1lID0gZXJyb3IubmFtZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhdHVzQ29kZSkge1xyXG4gICAgICB0aGlzLnN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChlcnJvci5zdGFjaykge1xyXG4gICAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XHJcbiAgICAgIHRoaXMuc3RhY2sgPSBlcnJvci5zdGFjaztcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IEV4cG9ydEVycm9yO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhlIGNhY2hlIG1hbmFnZXIgaXMgcmVzcG9uc2libGUgZm9yIGhhbmRsaW5nIGFuZCBtYW5hZ2luZ1xyXG4gKiB0aGUgSGlnaGNoYXJ0cyBsaWJyYXJ5IGFsb25nIHdpdGggaXRzIGRlcGVuZGVuY2llcy4gSXQgZW5zdXJlcyB0aGF0IHRoZXNlXHJcbiAqIHJlc291cmNlcyBhcmUgc3RvcmVkIGFuZCByZXRyaWV2ZWQgZWZmaWNpZW50bHkgdG8gb3B0aW1pemUgcGVyZm9ybWFuY2VcclxuICogYW5kIHJlZHVjZSByZWR1bmRhbnQgbmV0d29yayByZXF1ZXN0cy4gVGhlIGNhY2hlIGlzIHN0b3JlZCBpbiB0aGUgYC5jYWNoZWBcclxuICogZGlyZWN0b3J5IGJ5IGRlZmF1bHQsIHdoaWNoIHNlcnZlcyBhcyBhIGRlZGljYXRlZCBmb2xkZXIgZm9yIGtlZXBpbmcgY2FjaGVkXHJcbiAqIGZpbGVzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGV4aXN0c1N5bmMsIG1rZGlyU3luYywgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBIdHRwc1Byb3h5QWdlbnQgfSBmcm9tICdodHRwcy1wcm94eS1hZ2VudCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBmZXRjaCB9IGZyb20gJy4vZmV0Y2guanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGluaXRpYWwgY2FjaGUgdGVtcGxhdGVcclxuY29uc3QgY2FjaGUgPSB7XHJcbiAgY2RuVXJsOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICBhY3RpdmVNYW5pZmVzdDoge30sXHJcbiAgc291cmNlczogJycsXHJcbiAgaGNWZXJzaW9uOiAnJ1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyB0aGUgY2FjaGUgZm9yIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLCB1cGRhdGVzIHRoZSBjYWNoZSBpZiBuZWVkZWQsXHJcbiAqIGFuZCBsb2FkcyB0aGUgc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjaGVja0FuZFVwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgc2VydmVyLnByb3h5YCBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGUoXHJcbiAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgc2VydmVyUHJveHlPcHRpb25zXHJcbikge1xyXG4gIHRyeSB7XHJcbiAgICBsZXQgZmV0Y2hlZE1vZHVsZXM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjYWNoZSBwYXRoXHJcbiAgICBjb25zdCBjYWNoZVBhdGggPSBnZXRDYWNoZVBhdGgoKTtcclxuXHJcbiAgICAvLyBQcmVwYXJlIHBhdGhzIHRvIG1hbmlmZXN0IGFuZCBzb3VyY2VzIGZyb20gdGhlIGNhY2hlIGZvbGRlclxyXG4gICAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgICBjb25zdCBzb3VyY2VQYXRoID0gam9pbihjYWNoZVBhdGgsICdzb3VyY2VzLmpzJyk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBjYWNoZSBkZXN0aW5hdGlvbiBpZiBpdCBkb2Vzbid0IGV4aXN0IGFscmVhZHlcclxuICAgICFleGlzdHNTeW5jKGNhY2hlUGF0aCkgJiYgbWtkaXJTeW5jKGNhY2hlUGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XHJcblxyXG4gICAgLy8gRmV0Y2ggYWxsIHRoZSBzY3JpcHRzIGVpdGhlciBpZiB0aGUgYG1hbmlmZXN0Lmpzb25gIGRvZXMgbm90IGV4aXN0XHJcbiAgICAvLyBvciBpZiB0aGUgYGZvcmNlRmV0Y2hgIG9wdGlvbiBpcyBlbmFibGVkXHJcbiAgICBpZiAoIWV4aXN0c1N5bmMobWFuaWZlc3RQYXRoKSB8fCBoaWdoY2hhcnRzT3B0aW9ucy5mb3JjZUZldGNoKSB7XHJcbiAgICAgIGxvZygzLCAnW2NhY2hlXSBGZXRjaGluZyBhbmQgY2FjaGluZyBIaWdoY2hhcnRzIGRlcGVuZGVuY2llcy4nKTtcclxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCBfdXBkYXRlQ2FjaGUoXHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgIHNvdXJjZVBhdGhcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XHJcblxyXG4gICAgICAvLyBSZWFkIHRoZSBtYW5pZmVzdCBKU09OXHJcbiAgICAgIGNvbnN0IG1hbmlmZXN0ID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMobWFuaWZlc3RQYXRoKSwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBtb2R1bGVzIGlzIGFuIGFycmF5LCBpZiBzbywgd2UgcmV3cml0ZSBpdCB0byBhIG1hcCB0byBtYWtlXHJcbiAgICAgIC8vIGl0IGVhc2llciB0byByZXNvbHZlIG1vZHVsZXMuXHJcbiAgICAgIGlmIChtYW5pZmVzdC5tb2R1bGVzICYmIEFycmF5LmlzQXJyYXkobWFuaWZlc3QubW9kdWxlcykpIHtcclxuICAgICAgICBjb25zdCBtb2R1bGVNYXAgPSB7fTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzLmZvckVhY2goKG0pID0+IChtb2R1bGVNYXBbbV0gPSAxKSk7XHJcbiAgICAgICAgbWFuaWZlc3QubW9kdWxlcyA9IG1vZHVsZU1hcDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSBhY3R1YWwgbnVtYmVyIG9mIHNjcmlwdHMgdG8gYmUgZmV0Y2hlZFxyXG4gICAgICBjb25zdCB7IGNvcmVTY3JpcHRzLCBtb2R1bGVTY3JpcHRzLCBpbmRpY2F0b3JTY3JpcHRzIH0gPVxyXG4gICAgICAgIGhpZ2hjaGFydHNPcHRpb25zO1xyXG4gICAgICBjb25zdCBudW1iZXJPZk1vZHVsZXMgPVxyXG4gICAgICAgIGNvcmVTY3JpcHRzLmxlbmd0aCArIG1vZHVsZVNjcmlwdHMubGVuZ3RoICsgaW5kaWNhdG9yU2NyaXB0cy5sZW5ndGg7XHJcblxyXG4gICAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgaGlnaGNoYXJ0cyBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAgIC8vIElmIHRoZXJlIGFyZSBjaGFuZ2VzLCBmZXRjaCByZXF1ZXN0ZWQgbW9kdWxlcyBhbmQgcHJvZHVjdHMsXHJcbiAgICAgIC8vIGFuZCBiYWtlIHRoZW0gaW50byBhIGdpYW50IGJsb2IuIFNhdmUgdGhlIGJsb2IuXHJcbiAgICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgICdbY2FjaGVdIEEgSGlnaGNoYXJ0cyB2ZXJzaW9uIG1pc21hdGNoIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC4nXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgICAgfSBlbHNlIGlmIChcclxuICAgICAgICBPYmplY3Qua2V5cyhtYW5pZmVzdC5tb2R1bGVzIHx8IHt9KS5sZW5ndGggIT09IG51bWJlck9mTW9kdWxlc1xyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gVGhlIGNhY2hlIGFuZCB0aGUgcmVxdWVzdGVkIG1vZHVsZXMgZG8gbm90IG1hdGNoLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENoZWNrIGVhY2ggbW9kdWxlLCBpZiBhbnl0aGluZyBpcyBtaXNzaW5nIHJlZmV0Y2ggZXZlcnl0aGluZ1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSAobW9kdWxlU2NyaXB0cyB8fCBbXSkuc29tZSgobW9kdWxlTmFtZSkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFtYW5pZmVzdC5tb2R1bGVzW21vZHVsZU5hbWVdKSB7XHJcbiAgICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICAgIGBbY2FjaGVdIFRoZSAke21vZHVsZU5hbWV9IGlzIG1pc3NpbmcgaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLmBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFVwZGF0ZSBjYWNoZSBpZiBuZWVkZWRcclxuICAgICAgaWYgKHJlcXVlc3RVcGRhdGUpIHtcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICAgIGhpZ2hjaGFydHNPcHRpb25zLFxyXG4gICAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDMsICdbY2FjaGVdIERlcGVuZGVuY3kgY2FjaGUgaXMgdXAgdG8gZGF0ZSwgcHJvY2VlZGluZy4nKTtcclxuXHJcbiAgICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xyXG4gICAgICAgIGNhY2hlLnNvdXJjZXMgPSByZWFkRmlsZVN5bmMoc291cmNlUGF0aCwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IGN1cnJlbnQgbW9kdWxlcyBtYXBcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XHJcblxyXG4gICAgICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICAgICAgY2FjaGUuaGNWZXJzaW9uID0gZXh0cmFjdFZlcnNpb24oY2FjaGUuc291cmNlcyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcclxuICAgIC8vIGluIGEgc2xpZ2h0bHkgZGlmZmVyZW50IGZvcm1hdFxyXG4gICAgYXdhaXQgX3NhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHNPcHRpb25zLCBmZXRjaGVkTW9kdWxlcyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gQ291bGQgbm90IGNvbmZpZ3VyZSBjYWNoZSBhbmQgY3JlYXRlIG9yIHVwZGF0ZSB0aGUgY29uZmlnIG1hbmlmZXN0LicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgdmVyc2lvbiBvZiBIaWdoY2hhcnRzIGZyb20gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0SGlnaGNoYXJ0c1ZlcnNpb25cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNhY2hlZCBIaWdoY2hhcnRzIHZlcnNpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSB7XHJcbiAgcmV0dXJuIGNhY2hlLmhjVmVyc2lvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBpbiB0aGUgYXBwbGllZCBjb25maWd1cmF0aW9uIGFuZCBjaGVja3NcclxuICogdGhlIGNhY2hlIGZvciB0aGUgbmV3IHZlcnNpb24uXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IG5ld1ZlcnNpb24gLSBUaGUgbmV3IEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uKG5ld1ZlcnNpb24pIHtcclxuICAvLyBVcGRhdGUgdG8gdGhlIG5ldyB2ZXJzaW9uXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgaGlnaGNoYXJ0czoge1xyXG4gICAgICB2ZXJzaW9uOiBuZXdWZXJzaW9uXHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIENoZWNrIGlmIGNhY2hlIG5lZWRzIHRvIGJlIHVwZGF0ZWRcclxuICBhd2FpdCBjaGVja0FuZFVwZGF0ZUNhY2hlKG9wdGlvbnMuaGlnaGNoYXJ0cywgb3B0aW9ucy5zZXJ2ZXIucHJveHkpO1xyXG59XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgSGlnaGNoYXJ0cyB2ZXJzaW9uIGZyb20gdGhlIGNhY2hlJ3Mgc291cmNlcyBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHRyYWN0VmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2FjaGVTb3VyY2VzIC0gVGhlIGNhY2hlIHNvdXJjZXMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgZXh0cmFjdGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0VmVyc2lvbihjYWNoZVNvdXJjZXMpIHtcclxuICByZXR1cm4gY2FjaGVTb3VyY2VzXHJcbiAgICAuc3Vic3RyaW5nKDAsIGNhY2hlU291cmNlcy5pbmRleE9mKCcqLycpKVxyXG4gICAgLnJlcGxhY2UoJy8qJywgJycpXHJcbiAgICAucmVwbGFjZSgnKi8nLCAnJylcclxuICAgIC5yZXBsYWNlKC9cXG4vZywgJycpXHJcbiAgICAudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgdGhlIEhpZ2hjaGFydHMgbW9kdWxlIG5hbWUgYmFzZWQgb24gdGhlIHNjcmlwdFBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0UGF0aCAtIFRoZSBwYXRoIG9mIHRoZSBzY3JpcHQgZnJvbSB3aGljaCB0aGUgbW9kdWxlXHJcbiAqIG5hbWUgd2lsbCBiZSBleHRyYWN0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgbW9kdWxlIG5hbWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0UGF0aCkge1xyXG4gIHJldHVybiBzY3JpcHRQYXRoLnJlcGxhY2UoXHJcbiAgICAvKC4qKVxcL3woLiopbW9kdWxlc1xcL3xzdG9ja1xcLyguKilpbmRpY2F0b3JzXFwvfG1hcHNcXC8oLiopbW9kdWxlc1xcLy9naSxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBjYWNoZSBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY2FjaGUgb2JqZWN0IGNvbnRhaW5pbmcgdmFyaW91cyBjYWNoZWQgZGF0YS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZSgpIHtcclxuICByZXR1cm4gY2FjaGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBjYWNoZSBwYXRoIGZvciBIaWdoY2hhcnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBhYnNvbHV0ZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnkgZm9yIEhpZ2hjaGFydHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoKCkge1xyXG4gIHJldHVybiBnZXRBYnNvbHV0ZVBhdGgoZ2V0T3B0aW9ucygpLmhpZ2hjaGFydHMuY2FjaGVQYXRoLCAndXRmOCcpOyAvLyAjNTYyXHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGEgc2luZ2xlIHNjcmlwdCBhbmQgdXBkYXRlcyB0aGUgYGZldGNoZWRNb2R1bGVzYCBhY2NvcmRpbmdseS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHQgLSBBIHBhdGggdG8gc2NyaXB0IHRvIGdldC5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gQWRkaXRpb25hbCBvcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnRcclxuICogdG8gdXNlIGZvciBhIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtzaG91bGRUaHJvd0Vycm9yPWZhbHNlXSAtIEEgZmxhZyB0byBpbmRpY2F0ZSBpZiB0aGUgZXJyb3JcclxuICogc2hvdWxkIGJlIHRocm93bi4gVGhpcyBzaG91bGQgYmUgdXNlZCBvbmx5IGZvciB0aGUgY29yZSBzY3JpcHRzLiBUaGUgZGVmYXVsdFxyXG4gKiB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgdGV4dCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgZmV0Y2hlZCBzY3JpcHQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhIHByb2JsZW1cclxuICogd2l0aCBmZXRjaGluZyB0aGUgc2NyaXB0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChcclxuICBzY3JpcHQsXHJcbiAgcmVxdWVzdE9wdGlvbnMsXHJcbiAgZmV0Y2hlZE1vZHVsZXMsXHJcbiAgc2hvdWxkVGhyb3dFcnJvciA9IGZhbHNlXHJcbikge1xyXG4gIC8vIEdldCByaWQgb2YgdGhlIC5qcyBmcm9tIHRoZSBjdXN0b20gc3RyaW5nc1xyXG4gIGlmIChzY3JpcHQuZW5kc1dpdGgoJy5qcycpKSB7XHJcbiAgICBzY3JpcHQgPSBzY3JpcHQuc3Vic3RyaW5nKDAsIHNjcmlwdC5sZW5ndGggLSAzKTtcclxuICB9XHJcbiAgbG9nKDQsIGBbY2FjaGVdIEZldGNoaW5nIHNjcmlwdCAtICR7c2NyaXB0fS5qc2ApO1xyXG5cclxuICAvLyBGZXRjaCB0aGUgc2NyaXB0XHJcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHtzY3JpcHR9LmpzYCwgcmVxdWVzdE9wdGlvbnMpO1xyXG5cclxuICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAgaWYgKHJlc3BvbnNlLnN0YXR1c0NvZGUgPT09IDIwMCAmJiB0eXBlb2YgcmVzcG9uc2UudGV4dCA9PSAnc3RyaW5nJykge1xyXG4gICAgaWYgKGZldGNoZWRNb2R1bGVzKSB7XHJcbiAgICAgIGNvbnN0IG1vZHVsZU5hbWUgPSBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHQpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlc1ttb2R1bGVOYW1lXSA9IDE7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gcmVzcG9uc2UudGV4dDtcclxuICB9XHJcblxyXG4gIC8vIEJhc2VkIG9uIHRoZSBgc2hvdWxkVGhyb3dFcnJvcmAgZmxhZywgZGVjaWRlIGhvdyB0byBzZXJ2ZSBlcnJvciBtZXNzYWdlXHJcbiAgaWYgKHNob3VsZFRocm93RXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbiAoc3RhdHVzIGNvZGU6ICR7cmVzcG9uc2Uuc3RhdHVzQ29kZX0pLmAsXHJcbiAgICAgIDQwNFxyXG4gICAgKS5zZXRFcnJvcihyZXNwb25zZSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxvZyhcclxuICAgICAgMixcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNhdmVzIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCBmZXRjaGVkIG1vZHVsZXMgdG8gdGhlIGNhY2hlIG1hbmlmZXN0XHJcbiAqIGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NhdmVDb25maWdUb01hbmlmZXN0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2ZldGNoZWRNb2R1bGVzPXt9XSAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzID0ge30pIHtcclxuICBjb25zdCBuZXdNYW5pZmVzdCA9IHtcclxuICAgIHZlcnNpb246IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24sXHJcbiAgICBtb2R1bGVzOiBmZXRjaGVkTW9kdWxlc1xyXG4gIH07XHJcblxyXG4gIC8vIFVwZGF0ZSBjYWNoZSBvYmplY3Qgd2l0aCB0aGUgY3VycmVudCBtb2R1bGVzXHJcbiAgY2FjaGUuYWN0aXZlTWFuaWZlc3QgPSBuZXdNYW5pZmVzdDtcclxuXHJcbiAgbG9nKDMsICdbY2FjaGVdIFdyaXRpbmcgYSBuZXcgbWFuaWZlc3QuJyk7XHJcbiAgdHJ5IHtcclxuICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgIGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdtYW5pZmVzdC5qc29uJyksXHJcbiAgICAgIEpTT04uc3RyaW5naWZ5KG5ld01hbmlmZXN0KSxcclxuICAgICAgJ3V0ZjgnXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIEVycm9yIHdyaXRpbmcgdGhlIGNhY2hlIG1hbmlmZXN0LicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBIaWdoY2hhcnRzIGBzY3JpcHRzYCBhbmQgYGN1c3RvbVNjcmlwdHNgIGZyb20gdGhlIGdpdmVuIENETnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2ZldGNoU2NyaXB0c1xyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjb3JlU2NyaXB0cyAtIEhpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBtb2R1bGVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBtb2R1bGVzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjdXN0b21TY3JpcHRzIC0gQ3VzdG9tIHNjcmlwdCBwYXRocyB0byBmZXRjaCAoZnVsbFxyXG4gKiBVUkxzKS5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZmV0Y2hlZCBzY3JpcHRzXHJcbiAqIGNvbnRlbnQgam9pbmVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIHdoaWxlXHJcbiAqIHNldHRpbmcgYW4gSFRUUCBBZ2VudCBmb3IgcHJveHkuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzKFxyXG4gIGNvcmVTY3JpcHRzLFxyXG4gIG1vZHVsZVNjcmlwdHMsXHJcbiAgY3VzdG9tU2NyaXB0cyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgZmV0Y2hlZE1vZHVsZXNcclxuKSB7XHJcbiAgLy8gQ29uZmlndXJlIHByb3h5IGlmIGV4aXN0c1xyXG4gIGxldCBwcm94eUFnZW50O1xyXG4gIGNvbnN0IHByb3h5SG9zdCA9IHNlcnZlclByb3h5T3B0aW9ucy5ob3N0O1xyXG4gIGNvbnN0IHByb3h5UG9ydCA9IHNlcnZlclByb3h5T3B0aW9ucy5wb3J0O1xyXG5cclxuICAvLyBUcnkgdG8gY3JlYXRlIGEgUHJveHkgQWdlbnRcclxuICBpZiAocHJveHlIb3N0ICYmIHByb3h5UG9ydCkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgcHJveHlBZ2VudCA9IG5ldyBIdHRwc1Byb3h5QWdlbnQoe1xyXG4gICAgICAgIGhvc3Q6IHByb3h5SG9zdCxcclxuICAgICAgICBwb3J0OiBwcm94eVBvcnRcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tjYWNoZV0gQ291bGQgbm90IGNyZWF0ZSBhIFByb3h5IEFnZW50LicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gSWYgZXhpc3RzLCBhZGQgcHJveHkgYWdlbnQgdG8gcmVxdWVzdCBvcHRpb25zXHJcbiAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSBwcm94eUFnZW50XHJcbiAgICA/IHtcclxuICAgICAgICBhZ2VudDogcHJveHlBZ2VudCxcclxuICAgICAgICB0aW1lb3V0OiBzZXJ2ZXJQcm94eU9wdGlvbnMudGltZW91dFxyXG4gICAgICB9XHJcbiAgICA6IHt9O1xyXG5cclxuICBjb25zdCBhbGxGZXRjaFByb21pc2VzID0gW1xyXG4gICAgLi4uY29yZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcywgdHJ1ZSlcclxuICAgICksXHJcbiAgICAuLi5tb2R1bGVTY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMpXHJcbiAgICApLFxyXG4gICAgLi4uY3VzdG9tU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMpXHJcbiAgICApXHJcbiAgXTtcclxuXHJcbiAgY29uc3QgZmV0Y2hlZFNjcmlwdHMgPSBhd2FpdCBQcm9taXNlLmFsbChhbGxGZXRjaFByb21pc2VzKTtcclxuICByZXR1cm4gZmV0Y2hlZFNjcmlwdHMuam9pbignO1xcbicpO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgbG9jYWwgY2FjaGUgd2l0aCBIaWdoY2hhcnRzIHNjcmlwdHMgYW5kIHRoZWlyIHZlcnNpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF91cGRhdGVDYWNoZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaGlnaGNoYXJ0c09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgaGlnaGNoYXJ0c2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzb3VyY2VQYXRoIC0gVGhlIHBhdGggdG8gdGhlIHNvdXJjZSBmaWxlIGluIHRoZSBjYWNoZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0IHJlcHJlc2VudGluZ1xyXG4gKiB0aGUgZmV0Y2hlZCBtb2R1bGVzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gaXNzdWUgdXBkYXRpbmdcclxuICogdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfdXBkYXRlQ2FjaGUoaGlnaGNoYXJ0c09wdGlvbnMsIHNlcnZlclByb3h5T3B0aW9ucywgc291cmNlUGF0aCkge1xyXG4gIC8vIEdldCBIaWdoY2hhcnRzIHZlcnNpb24gZm9yIHNjcmlwdHNcclxuICBjb25zdCBoY1ZlcnNpb24gPVxyXG4gICAgaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbiA9PT0gJ2xhdGVzdCdcclxuICAgICAgPyBudWxsXHJcbiAgICAgIDogYCR7aGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbn1gO1xyXG5cclxuICAvLyBHZXQgdGhlIENETiB1cmwgZm9yIHNjcmlwdHNcclxuICBjb25zdCBjZG5VcmwgPSBoaWdoY2hhcnRzT3B0aW9ucy5jZG5VcmwgfHwgY2FjaGUuY2RuVXJsO1xyXG5cclxuICB0cnkge1xyXG4gICAgY29uc3QgZmV0Y2hlZE1vZHVsZXMgPSB7fTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbY2FjaGVdIFVwZGF0aW5nIGNhY2hlIHZlcnNpb24gdG8gSGlnaGNoYXJ0czogJHtoY1ZlcnNpb24gfHwgJ2xhdGVzdCd9LmBcclxuICAgICk7XHJcblxyXG4gICAgY2FjaGUuc291cmNlcyA9IGF3YWl0IF9mZXRjaFNjcmlwdHMoXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5jb3JlU2NyaXB0cy5tYXAoKGMpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb24gPyBgJHtjZG5Vcmx9LyR7aGNWZXJzaW9ufS8ke2N9YCA6IGAke2NkblVybH0vJHtjfWBcclxuICAgICAgICApXHJcbiAgICAgIF0sXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5tb2R1bGVTY3JpcHRzLm1hcCgobSkgPT5cclxuICAgICAgICAgIG0gPT09ICdtYXAnXHJcbiAgICAgICAgICAgID8gaGNWZXJzaW9uXHJcbiAgICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L21hcHMvJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbWFwcy9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgIDogaGNWZXJzaW9uXHJcbiAgICAgICAgICAgICAgPyBgJHtjZG5Vcmx9LyR7aGNWZXJzaW9ufS9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgICAgOiBgJHtjZG5Vcmx9L21vZHVsZXMvJHttfWBcclxuICAgICAgICApLFxyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmluZGljYXRvclNjcmlwdHMubWFwKChpKSA9PlxyXG4gICAgICAgICAgaGNWZXJzaW9uXHJcbiAgICAgICAgICAgID8gYCR7Y2RuVXJsfS9zdG9jay8ke2hjVmVyc2lvbn0vaW5kaWNhdG9ycy8ke2l9YFxyXG4gICAgICAgICAgICA6IGAke2NkblVybH0vc3RvY2svaW5kaWNhdG9ycy8ke2l9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgaGlnaGNoYXJ0c09wdGlvbnMuY3VzdG9tU2NyaXB0cyxcclxuICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICBmZXRjaGVkTW9kdWxlc1xyXG4gICAgKTtcclxuXHJcbiAgICAvLyBFeHRyYWN0IGFuZCBzYXZlIHZlcnNpb24gb2YgY3VycmVudGx5IHVzZWQgSGlnaGNoYXJ0c1xyXG4gICAgY2FjaGUuaGNWZXJzaW9uID0gZXh0cmFjdFZlcnNpb24oY2FjaGUuc291cmNlcyk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgZmV0Y2hlZCBtb2R1bGVzIGludG8gY2FjaGVzJyBzb3VyY2UgSlNPTlxyXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcclxuICAgIHJldHVybiBmZXRjaGVkTW9kdWxlcztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBVbmFibGUgdG8gdXBkYXRlIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgY2hlY2tBbmRVcGRhdGVDYWNoZSxcclxuICBnZXRIaWdoY2hhcnRzVmVyc2lvbixcclxuICB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbixcclxuICBleHRyYWN0VmVyc2lvbixcclxuICBleHRyYWN0TW9kdWxlTmFtZSxcclxuICBnZXRDYWNoZSxcclxuICBnZXRDYWNoZVBhdGhcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1ldGhvZHMgZm9yIGluaXRpYWxpemluZyBIaWdoY2hhcnRzIHdpdGggY3VzdG9taXplZFxyXG4gKiBhbmltYXRpb24gc2V0dGluZ3MgYW5kIHRyaWdnZXJpbmcgdGhlIGNyZWF0aW9uIG9mIEhpZ2hjaGFydHMgY2hhcnRzIHdpdGhcclxuICogZXhwb3J0LXNwZWNpZmljIGNvbmZpZ3VyYXRpb25zIGluIHRoZSBwYWdlIGNvbnRleHQuIFN1cHBvcnRzIGR5bmFtaWMgb3B0aW9uXHJcbiAqIG1lcmdpbmcsIGN1c3RvbSBsb2dpYyBpbmplY3Rpb24sIGFuZCBjb250cm9sIG92ZXIgcmVuZGVyaW5nIGJlaGF2aW9ycy4gVXNlZFxyXG4gKiBieSB0aGUgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqL1xyXG5cclxuLyogZXNsaW50LWRpc2FibGUgbm8tdW5kZWYgKi9cclxuXHJcbi8qKlxyXG4gKiBTZXR0aW5nIHRoZSBgSGlnaGNoYXJ0cy5hbmltT2JqZWN0YCBmdW5jdGlvbi4gQ2FsbGVkIHdoZW4gaW5pdGluZyB0aGUgcGFnZS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0c1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0cygpIHtcclxuICBIaWdoY2hhcnRzLmFuaW1PYmplY3QgPSBmdW5jdGlvbiAoKSB7XHJcbiAgICByZXR1cm4geyBkdXJhdGlvbjogMCB9O1xyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIHRoZSBhY3R1YWwgSGlnaGNoYXJ0cyBjaGFydCBvbiBhIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY3JlYXRlQ2hhcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUNoYXJ0KGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEdldCByZXF1aXJlZCBmdW5jdGlvbnNcclxuICBjb25zdCB7IGdldE9wdGlvbnMsIHNldE9wdGlvbnMsIG1lcmdlLCB3cmFwIH0gPSBIaWdoY2hhcnRzO1xyXG5cclxuICAvLyBDcmVhdGUgYSBzZXBhcmF0ZSBvYmplY3QgZm9yIGEgcG90ZW50aWFsIGBzZXRPcHRpb25zYCB1c2FnZXMgaW4gb3JkZXJcclxuICAvLyB0byBwcmV2ZW50IGZyb20gcG9sbHV0aW5nIG90aGVyIGV4cG9ydHMgdGhhdCBjYW4gaGFwcGVuIG9uIHRoZSBzYW1lIHBhZ2VcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSBtZXJnZShmYWxzZSwge30sIGdldE9wdGlvbnMoKSk7XHJcblxyXG4gIC8vIE5PVEU6IElzIHRoaXMgdXNlZCBmb3IgYW55dGhpbmcgdXNlZnVsP1xyXG4gIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gZmFsc2U7XHJcbiAgd3JhcChIaWdoY2hhcnRzLkNoYXJ0LnByb3RvdHlwZSwgJ2luaXQnLCBmdW5jdGlvbiAocHJvY2VlZCwgdXNlck9wdGlvbnMsIGNiKSB7XHJcbiAgICAvLyBPdmVycmlkZSB0aGUgYHVzZXJPcHRpb25zYCB3aXRoIGltYWdlIGZyaWVuZGx5IG9wdGlvbnNcclxuICAgIHVzZXJPcHRpb25zID0gbWVyZ2UodXNlck9wdGlvbnMsIHtcclxuICAgICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgfSxcclxuICAgICAgcGxvdE9wdGlvbnM6IHtcclxuICAgICAgICBzZXJpZXM6IHtcclxuICAgICAgICAgIGxhYmVsOiB7XHJcbiAgICAgICAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICAvKiBFeHBlY3RzIHRvb2x0aXAgaW4gdGhlIGB1c2VyT3B0aW9uc2Agd2hlbiBgZm9yRXhwb3J0YCBpcyB0cnVlLlxyXG4gICAgICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL2hpZ2hjaGFydHMvYmxvYi8zYWQ0MzBhMzUzYjgwNTZiOWU3NjRhYTRlNWNkNjgyOGFhNDc5ZGIyL2pzL3BhcnRzL0NoYXJ0LmpzI0wyNDFcclxuICAgICAgICAqL1xyXG4gICAgICB0b29sdGlwOiB7fVxyXG4gICAgfSk7XHJcblxyXG4gICAgKHVzZXJPcHRpb25zLnNlcmllcyB8fCBbXSkuZm9yRWFjaChmdW5jdGlvbiAoc2VyaWVzKSB7XHJcbiAgICAgIHNlcmllcy5hbmltYXRpb24gPSBmYWxzZTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEFkZCBmbGFnIHRvIGtub3cgaWYgY2hhcnQgcmVuZGVyIGhhcyBiZWVuIGNhbGxlZC5cclxuICAgIGlmICghd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlcikge1xyXG4gICAgICB3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyID0gSGlnaGNoYXJ0cy5hZGRFdmVudCh0aGlzLCAncmVuZGVyJywgKCkgPT4ge1xyXG4gICAgICAgIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gdHJ1ZTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbdXNlck9wdGlvbnMsIGNiXSk7XHJcbiAgfSk7XHJcblxyXG4gIHdyYXAoSGlnaGNoYXJ0cy5TZXJpZXMucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCBjaGFydCwgb3B0aW9ucykge1xyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbY2hhcnQsIG9wdGlvbnNdKTtcclxuICB9KTtcclxuXHJcbiAgLy8gU29tZSBtYW5kYXRvcnkgYWRkaXRpb25hbCBgY2hhcnRgIGFuZCBgZXhwb3J0aW5nYCBvcHRpb25zXHJcbiAgY29uc3QgYWRkaXRpb25hbE9wdGlvbnMgPSB7XHJcbiAgICBjaGFydDoge1xyXG4gICAgICAvLyBCeSBkZWZhdWx0IGFuaW1hdGlvbiBpcyBkaXNhYmxlZFxyXG4gICAgICBhbmltYXRpb246IGZhbHNlLFxyXG4gICAgICAvLyBHZXQgdGhlIHJpZ2h0IHNpemUgdmFsdWVzXHJcbiAgICAgIGhlaWdodDogZXhwb3J0T3B0aW9ucy5oZWlnaHQsXHJcbiAgICAgIHdpZHRoOiBleHBvcnRPcHRpb25zLndpZHRoXHJcbiAgICB9LFxyXG4gICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgIC8vIE5vIG5lZWQgZm9yIHRoZSBleHBvcnRpbmcgYnV0dG9uXHJcbiAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IHRoZSBpbnB1dCB0byBleHBvcnQgZnJvbSB0aGUgYGluc3RyYCBvcHRpb25cclxuICBjb25zdCB1c2VyT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy5pbnN0cn1gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvblxyXG4gIGNvbnN0IHRoZW1lT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy5nbG9iYWxPcHRpb25zfWApKCk7XHJcblxyXG4gIC8vIE1lcmdlIHRoZSBmb2xsb3dpbmcgb3B0aW9ucyBvYmplY3RzIHRvIGNyZWF0ZSBmaW5hbCBvcHRpb25zXHJcbiAgY29uc3QgZmluYWxPcHRpb25zID0gbWVyZ2UoXHJcbiAgICBmYWxzZSxcclxuICAgIHRoZW1lT3B0aW9ucyxcclxuICAgIHVzZXJPcHRpb25zLFxyXG4gICAgLy8gUGxhY2VkIGl0IGhlcmUgaW5zdGVhZCBpbiB0aGUgaW5pdCBiZWNhdXNlIG9mIHRoZSBzaXplIGlzc3Vlc1xyXG4gICAgYWRkaXRpb25hbE9wdGlvbnNcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gIGNvbnN0IGZpbmFsQ2FsbGJhY2sgPSBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2tcclxuICAgID8gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2t9YCkoKVxyXG4gICAgOiBudWxsO1xyXG5cclxuICAvLyBUcmlnZ2VyIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSB7XHJcbiAgICBuZXcgRnVuY3Rpb24oJ29wdGlvbnMnLCBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkodXNlck9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBnbG9iYWwgb3B0aW9ucyBpZiBleGlzdFxyXG4gIGlmIChnbG9iYWxPcHRpb25zKSB7XHJcbiAgICBzZXRPcHRpb25zKGdsb2JhbE9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsbCB0aGUgY2hhcnQgY3JlYXRpb25cclxuICBIaWdoY2hhcnRzW2V4cG9ydE9wdGlvbnMuY29uc3RyXSgnY29udGFpbmVyJywgZmluYWxPcHRpb25zLCBmaW5hbENhbGxiYWNrKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBjdXJyZW50IGdsb2JhbCBvcHRpb25zXHJcbiAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIENsZWFyIGl0IGp1c3QgaW4gY2FzZSAoZS5nLiB0aGUgYHNldE9wdGlvbnNgIHdhcyB1c2VkIGluIHRoZSBgY3VzdG9tQ29kZWApXHJcbiAgZm9yIChjb25zdCBwcm9wIGluIGRlZmF1bHRPcHRpb25zKSB7XHJcbiAgICBpZiAodHlwZW9mIGRlZmF1bHRPcHRpb25zW3Byb3BdICE9PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgIGRlbGV0ZSBkZWZhdWx0T3B0aW9uc1twcm9wXTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgZGVmYXVsdCBvcHRpb25zIGJhY2tcclxuICBzZXRPcHRpb25zKEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaik7XHJcblxyXG4gIC8vIEVtcHR5IHRoZSBjdXN0b20gZ2xvYmFsIG9wdGlvbnMgb2JqZWN0XHJcbiAgSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqID0ge307XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzZXR1cEhpZ2hjaGFydHMsXHJcbiAgY3JlYXRlQ2hhcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgbWFuYWdpbmcgUHVwcGV0ZWVyIGJyb3dzZXJcclxuICogaW5zdGFuY2UsIGNyZWF0aW5nIGFuZCBjbGVhcmluZyBwYWdlcywgaW5qZWN0aW5nIGN1c3RvbSByZXNvdXJjZXMsXHJcbiAqIGFuZCBzZXR0aW5nIHVwIEhpZ2hjaGFydHMgZm9yIHNlcnZlci1zaWRlIHJlbmRlcmluZy4gVGhlIG1vZHVsZSBlbnN1cmVzXHJcbiAqIHRoYXQgcmVzb3VyY2VzIGFyZSBjb3JyZWN0bHkgbWFuYWdlZCBhbmQgY2FuIGhhbmRsZSBmYWlsdXJlcyBkdXJpbmdcclxuICogb3BlcmF0aW9ucyBsaWtlIGxhdW5jaGluZyB0aGUgYnJvd3NlciBvciBjcmVhdGluZyBuZXcgcGFnZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgcHVwcGV0ZWVyIGZyb20gJ3B1cHBldGVlcic7XHJcblxyXG5pbXBvcnQgeyBnZXRDYWNoZVBhdGggfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgc2V0dXBIaWdoY2hhcnRzIH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgcGFnZXNcclxuY29uc3QgdGVtcGxhdGUgPSByZWFkRmlsZVN5bmMoXHJcbiAgam9pbihfX2Rpcm5hbWUsICd0ZW1wbGF0ZXMnLCAndGVtcGxhdGUuaHRtbCcpLFxyXG4gICd1dGY4J1xyXG4pO1xyXG5cclxuLy8gVG8gc2F2ZSB0aGUgYnJvd3NlclxyXG5sZXQgYnJvd3NlciA9IG51bGw7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBleGlzdGluZyBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJyb3dzZXJcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEJyb3dzZXIoKSB7XHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBObyB2YWxpZCBicm93c2VyIGhhcyBiZWVuIGNyZWF0ZWQuJywgNTAwKTtcclxuICB9XHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVCcm93c2VyXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyXHJcbiAqIGxhdW5jaC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyXHJcbiAqIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBtYXggcmV0cmllcyB0byBvcGVuXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhcmUgcmVhY2hlZCwgb3IgaWYgbm8gYnJvd3NlciBpbnN0YW5jZSBpcyBmb3VuZCBhZnRlclxyXG4gKiByZXRyaWVzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncykge1xyXG4gIC8vIEdldCBgZGVidWdgIGFuZCBgb3RoZXJgIG9wdGlvbnNcclxuICBjb25zdCB7IGRlYnVnLCBvdGhlciB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZW5hYmxlOiBlbmFibGVkRGVidWcsIC4uLmRlYnVnT3B0aW9ucyB9ID0gZGVidWc7XHJcblxyXG4gIC8vIExhdW5jaCBvcHRpb25zIGZvciB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGNvbnN0IGxhdW5jaE9wdGlvbnMgPSB7XHJcbiAgICBoZWFkbGVzczogb3RoZXIuYnJvd3NlclNoZWxsTW9kZSA/ICdzaGVsbCcgOiB0cnVlLFxyXG4gICAgdXNlckRhdGFEaXI6ICd0bXAnLFxyXG4gICAgYXJnczogcHVwcGV0ZWVyQXJncyB8fCBbXSxcclxuICAgIGhhbmRsZVNJR0lOVDogZmFsc2UsXHJcbiAgICBoYW5kbGVTSUdURVJNOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR0hVUDogZmFsc2UsXHJcbiAgICB3YWl0Rm9ySW5pdGlhbFBhZ2U6IGZhbHNlLFxyXG4gICAgZGVmYXVsdFZpZXdwb3J0OiBudWxsLFxyXG4gICAgLi4uKGVuYWJsZWREZWJ1ZyAmJiBkZWJ1Z09wdGlvbnMpXHJcbiAgfTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlclxyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgLy8gQSBjb3VudGVyIGZvciB0aGUgYnJvd3NlcidzIGxhdW5jaCByZXRyaWVzXHJcbiAgICBsZXQgdHJ5Q291bnQgPSAwO1xyXG5cclxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbYnJvd3Nlcl0gQXR0ZW1wdGluZyB0byBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIExhdW5jaCB0aGUgYnJvd3NlclxyXG4gICAgICAgIGJyb3dzZXIgPSBhd2FpdCBwdXBwZXRlZXIubGF1bmNoKGxhdW5jaE9wdGlvbnMpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuXHJcbiAgICAgICAgICAvLyBXYWl0IGZvciBhIDQgc2Vjb25kcyBiZWZvcmUgdHJ5aW5nIGFnYWluXHJcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIDQwMDApKTtcclxuICAgICAgICAgIGF3YWl0IG9wZW4oKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIGF3YWl0IG9wZW4oKTtcclxuXHJcbiAgICAgIC8vIFNoZWxsIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChsYXVuY2hPcHRpb25zLmhlYWRsZXNzID09PSAnc2hlbGwnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBzaGVsbCBtb2RlLmApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEZWJ1ZyBtb2RlIGluZm9ybVxyXG4gICAgICBpZiAoZW5hYmxlZERlYnVnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1ticm93c2VyXSBNYXhpbXVtIHJldHJpZXMgdG8gb3BlbiBhIGJyb3dzZXIgaW5zdGFuY2UgcmVhY2hlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJywgNTAwKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGJyb3dzZXIgaW5zdGFuY2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyB0aGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgaWYgaXQgaXMgY29ubmVjdGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsb3NlQnJvd3NlclxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsb3NlQnJvd3NlcigpIHtcclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciB3aGVuIGNvbm5lY3RlZFxyXG4gIGlmIChicm93c2VyICYmIGJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgfVxyXG4gIGJyb3dzZXIgPSBudWxsO1xyXG4gIGxvZyg0LCAnW2Jyb3dzZXJdIENsb3NlZCB0aGUgYnJvd3Nlci4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBuZXcgUHVwcGV0ZWVyIHBhZ2Ugd2l0aGluIGFuIGV4aXN0aW5nIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqIFRoZSBmdW5jdGlvbiBjcmVhdGVzIGEgbmV3IHBhZ2UsIGRpc2FibGVzIGNhY2hpbmcsIHNldHMgY29udGVudCB1c2luZ1xyXG4gKiB0aGUgYF9zZXRQYWdlQ29udGVudCgpYCwgYW5kIHJldHVybnMgdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gbmV3UGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBgaWRgLFxyXG4gKiBgd29ya0NvdW50YCwgYW5kIGBwYWdlYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG5vIHZhbGlkIGJyb3dzZXJcclxuICogaGFzIGJlZW4gY29ubmVjdGVkIG9yIGlmIGEgcGFnZSBpcyBpbnZhbGlkIG9yIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBuZXdQYWdlKHBvb2xSZXNvdXJjZSkge1xyXG4gIC8vIEVycm9yIGluIGNhc2Ugb2Ygbm8gY29ubmVjdGVkIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIgfHwgIWJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFticm93c2VyXSBCcm93c2VyIGlzIG5vdCB5ZXQgY29ubmVjdGVkLmAsIDUwMCk7XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBwYWdlXHJcbiAgcG9vbFJlc291cmNlLnBhZ2UgPSBhd2FpdCBicm93c2VyLm5ld1BhZ2UoKTtcclxuXHJcbiAgLy8gRGlzYWJsZSBjYWNoZVxyXG4gIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IF9zZXRQYWdlQ29udGVudChwb29sUmVzb3VyY2UucGFnZSk7XHJcblxyXG4gIC8vIFNldCBwYWdlIGV2ZW50c1xyXG4gIF9zZXRQYWdlRXZlbnRzKHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIHBhZ2UgaXMgY29ycmVjdGx5IGNyZWF0ZWRcclxuICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlIHx8IHBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIFRoZSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLicsIDQwMCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLiBMb2dzXHJcbiAqIHRocm93biBlcnJvciBpZiBjbGVhcmluZyBvZiBhIHBhZ2UncyBjb250ZW50IGZhaWxzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBwYWdlIGFuZCBpZC5cclxuICogQHBhcmFtIHtib29sZWFufSBbaGFyZFJlc2V0PWZhbHNlXSAtIEEgZmxhZyBpbmRpY2F0aW5nIHRoZSB0eXBlIG9mIGNsZWFyaW5nXHJcbiAqIHRvIGJlIHBlcmZvcm1lZC4gSWYgdHJ1ZSwgbmF2aWdhdGVzIHRvIGBhYm91dDpibGFua2AgYW5kIHJlc2V0cyBjb250ZW50XHJcbiAqIGFuZCBzY3JpcHRzLiBJZiBmYWxzZSwgY2xlYXJzIHRoZSBib2R5IGNvbnRlbnQgYnkgc2V0dGluZyBhIHByZWRlZmluZWQgSFRNTFxyXG4gKiBzdHJ1Y3R1cmUuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0cnVlIHdoZW4gcGFnZVxyXG4gKiBpcyBjb3JyZWN0bHkgY2xlYXJlZCBhbmQgZmFsc2Ugd2hlbiBpdCBpcyBub3QuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlKHBvb2xSZXNvdXJjZSwgaGFyZFJlc2V0ID0gZmFsc2UpIHtcclxuICB0cnkge1xyXG4gICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIGlmIChoYXJkUmVzZXQpIHtcclxuICAgICAgICAvLyBOYXZpZ2F0ZSB0byBgYWJvdXQ6YmxhbmtgXHJcbiAgICAgICAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2UuZ290bygnYWJvdXQ6YmxhbmsnLCB7XHJcbiAgICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBTZXQgdGhlIGNvbnRlbnQgYW5kIGFuZCBzY3JpcHRzIGFnYWluXHJcbiAgICAgICAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBDbGVhciBib2R5IGNvbnRlbnRcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LmlubmVySFRNTCA9XHJcbiAgICAgICAgICAgICc8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+PGRpdiBpZD1cImNvbnRhaW5lclwiPjwvZGl2PjwvZGl2Pic7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgMixcclxuICAgICAgZXJyb3IsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIENvbnRlbnQgb2YgdGhlIHBhZ2UgY291bGQgbm90IGJlIGNsZWFyZWQuYFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgcG9vbFJlc291cmNlLndvcmtDb3VudCA9IGdldE9wdGlvbnMoKS5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgfVxyXG4gIHJldHVybiBmYWxzZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgY3VzdG9tIEpTIGFuZCBDU1MgcmVzb3VyY2VzIHRvIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHJlc291cmNlcyB3aWxsXHJcbiAqIGJlIGFkZGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxBcnJheS48T2JqZWN0Pj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIGFycmF5XHJcbiAqIG9mIGluamVjdGVkIHJlc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheVxyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIC8vIFVzZSByZXNvdXJjZXNcclxuICBjb25zdCByZXNvdXJjZXMgPSBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzO1xyXG4gIGlmIChyZXNvdXJjZXMpIHtcclxuICAgIGNvbnN0IGluamVjdGVkSnMgPSBbXTtcclxuXHJcbiAgICAvLyBMb2FkIGN1c3RvbSBKUyBjb2RlXHJcbiAgICBpZiAocmVzb3VyY2VzLmpzKSB7XHJcbiAgICAgIGluamVjdGVkSnMucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmpzXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExvYWQgc2NyaXB0cyBmcm9tIGFsbCBjdXN0b20gZmlsZXNcclxuICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICAgIGNvbnN0IGlzTG9jYWwgPSAhZmlsZS5zdGFydHNXaXRoKCdodHRwJykgPyB0cnVlIDogZmFsc2U7XHJcblxyXG4gICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBzY3JpcHQgZnJvbSByZXNvdXJjZXMnIGZpbGVzXHJcbiAgICAgICAgaW5qZWN0ZWRKcy5wdXNoKFxyXG4gICAgICAgICAgaXNMb2NhbFxyXG4gICAgICAgICAgICA/IHtcclxuICAgICAgICAgICAgICAgIGNvbnRlbnQ6IHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoZmlsZSksICd1dGY4JylcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIDoge1xyXG4gICAgICAgICAgICAgICAgdXJsOiBmaWxlXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBmb3IgKGNvbnN0IGpzUmVzb3VyY2Ugb2YgaW5qZWN0ZWRKcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoanNSZXNvdXJjZSkpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFticm93c2VyXSBUaGUgSlMgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgaW5qZWN0ZWRKcy5sZW5ndGggPSAwO1xyXG5cclxuICAgIC8vIExvYWQgQ1NTXHJcbiAgICBjb25zdCBpbmplY3RlZENzcyA9IFtdO1xyXG4gICAgaWYgKHJlc291cmNlcy5jc3MpIHtcclxuICAgICAgbGV0IGNzc0ltcG9ydHMgPSByZXNvdXJjZXMuY3NzLm1hdGNoKC9AaW1wb3J0XFxzKihbXjtdKik7L2cpO1xyXG4gICAgICBpZiAoY3NzSW1wb3J0cykge1xyXG4gICAgICAgIC8vIEhhbmRsZSBjc3Mgc2VjdGlvblxyXG4gICAgICAgIGZvciAobGV0IGNzc0ltcG9ydFBhdGggb2YgY3NzSW1wb3J0cykge1xyXG4gICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGgpIHtcclxuICAgICAgICAgICAgY3NzSW1wb3J0UGF0aCA9IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICAucmVwbGFjZSgndXJsKCcsICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCdAaW1wb3J0JywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoL1wiL2csICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC8nL2csICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC87LywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoL1xcKS9nLCAnJylcclxuICAgICAgICAgICAgICAudHJpbSgpO1xyXG5cclxuICAgICAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIGNzcyBmcm9tIHJlc291cmNlc1xyXG4gICAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aC5zdGFydHNXaXRoKCdodHRwJykpIHtcclxuICAgICAgICAgICAgICBpbmplY3RlZENzcy5wdXNoKHtcclxuICAgICAgICAgICAgICAgIHVybDogY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9IGVsc2UgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgICAgICAgICBpbmplY3RlZENzcy5wdXNoKHtcclxuICAgICAgICAgICAgICAgIHBhdGg6IGpvaW4oX19kaXJuYW1lLCBjc3NJbXBvcnRQYXRoKVxyXG4gICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBUaGUgcmVzdCBvZiB0aGUgQ1NTIHNlY3Rpb24gd2lsbCBiZSBjb250ZW50IGJ5IG5vd1xyXG4gICAgICBpbmplY3RlZENzcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuY3NzLnJlcGxhY2UoL0BpbXBvcnRcXHMqKFteO10qKTsvZywgJycpIHx8ICcgJ1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIGZvciAoY29uc3QgY3NzUmVzb3VyY2Ugb2YgaW5qZWN0ZWRDc3MpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFN0eWxlVGFnKGNzc1Jlc291cmNlKSk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgICAgIGBbYnJvd3Nlcl0gVGhlIENTUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICAgIGluamVjdGVkQ3NzLmxlbmd0aCA9IDA7XHJcbiAgICB9XHJcbiAgfVxyXG4gIHJldHVybiBpbmplY3RlZFJlc291cmNlcztcclxufVxyXG5cclxuLyoqXHJcbiAqIENsZWFycyBvdXQgYWxsIHN0YXRlIHNldCBvbiB0aGUgcGFnZSB3aXRoIGBhZGRTY3JpcHRUYWdgIGFuZCBgYWRkU3R5bGVUYWdgLlxyXG4gKiBSZW1vdmVzIGluamVjdGVkIHJlc291cmNlcyBhbmQgcmVzZXRzIENTUyBhbmQgc2NyaXB0IHRhZ3Mgb24gdGhlIHBhZ2UuXHJcbiAqIEFkZGl0aW9uYWxseSwgaXQgZGVzdHJveXMgcHJldmlvdXNseSBleGlzdGluZyBjaGFydHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCBmcm9tIHdoaWNoIHJlc291cmNlcyB3aWxsXHJcbiAqIGJlIGNsZWFyZWQuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPE9iamVjdD59IGluamVjdGVkUmVzb3VyY2VzIC0gQXJyYXkgb2YgaW5qZWN0ZWQgcmVzb3VyY2VzXHJcbiAqIHRvIGJlIGNsZWFyZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgdHJ5IHtcclxuICAgIGZvciAoY29uc3QgcmVzb3VyY2Ugb2YgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICAgICAgYXdhaXQgcmVzb3VyY2UuZGlzcG9zZSgpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0cyBhZnRlciBleHBvcnQgaXMgZG9uZSBhbmQgcmVzZXQgYWxsIENTUyBhbmQgc2NyaXB0IHRhZ3NcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAvLyBXZSBhcmUgbm90IGd1YXJhbnRlZWQgdGhhdCBIaWdoY2hhcnRzIGlzIGxvYWRlZCwgd2hlbiBkb2luZyBTVkcgZXhwb3J0c1xyXG4gICAgICBpZiAodHlwZW9mIEhpZ2hjaGFydHMgIT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgY29uc3Qgb2xkQ2hhcnRzID0gSGlnaGNoYXJ0cy5jaGFydHM7XHJcblxyXG4gICAgICAgIC8vIENoZWNrIGluIGFueSBhbHJlYWR5IGV4aXN0aW5nIGNoYXJ0c1xyXG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KG9sZENoYXJ0cykgJiYgb2xkQ2hhcnRzLmxlbmd0aCkge1xyXG4gICAgICAgICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzXHJcbiAgICAgICAgICBmb3IgKGNvbnN0IG9sZENoYXJ0IG9mIG9sZENoYXJ0cykge1xyXG4gICAgICAgICAgICBvbGRDaGFydCAmJiBvbGRDaGFydC5kZXN0cm95KCk7XHJcbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgICBIaWdoY2hhcnRzLmNoYXJ0cy5zaGlmdCgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5zY3JpcHRzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3NjcmlwdCcpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWywgLi4uc3R5bGVzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3N0eWxlJyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4ubGlua3NUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnbGluaycpO1xyXG5cclxuICAgICAgLy8gUmVtb3ZlIHRhZ3NcclxuICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIFtcclxuICAgICAgICAuLi5zY3JpcHRzVG9SZW1vdmUsXHJcbiAgICAgICAgLi4uc3R5bGVzVG9SZW1vdmUsXHJcbiAgICAgICAgLi4ubGlua3NUb1JlbW92ZVxyXG4gICAgICBdKSB7XHJcbiAgICAgICAgZWxlbWVudC5yZW1vdmUoKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFticm93c2VyXSBDb3VsZCBub3QgY2xlYXIgcGFnZSdzIHJlc291cmNlcy5gKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb250ZW50IGZvciBhIFB1cHBldGVlciBQYWdlIHVzaW5nIGEgcHJlZGVmaW5lZCB0ZW1wbGF0ZVxyXG4gKiBhbmQgYWRkaXRpb25hbCBzY3JpcHRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9zZXRQYWdlQ29udGVudFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggdGhlIGNvbnRlbnRcclxuICogaXMgYmVpbmcgc2V0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3NldFBhZ2VDb250ZW50KHBhZ2UpIHtcclxuICAvLyBTZXQgdGhlIGluaXRpYWwgcGFnZSBjb250ZW50XHJcbiAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHRlbXBsYXRlLCB7IHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnIH0pO1xyXG5cclxuICAvLyBBZGQgYWxsIHJlZ2lzdGVyZWQgSGlnY2hhcnRzIHNjcmlwdHMsIHF1aXRlIGRlbWFuZGluZ1xyXG4gIGF3YWl0IHBhZ2UuYWRkU2NyaXB0VGFnKHsgcGF0aDogam9pbihnZXRDYWNoZVBhdGgoKSwgJ3NvdXJjZXMuanMnKSB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSBpbml0aWFsIGBhbmltT2JqZWN0YCBmb3IgSGlnaGNoYXJ0c1xyXG4gIGF3YWl0IHBhZ2UuZXZhbHVhdGUoc2V0dXBIaWdoY2hhcnRzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCBldmVudHMgKGxpa2UgYHBhZ2VlcnJvcmAgYW5kIGBjb25zb2xlYCkgZm9yIGEgUHVwcGV0ZWVyIFBhZ2UgaW4gb3JkZXJcclxuICogdG8gY2F0Y2ggYW5kIGRpc3BsYXkgZXJyb3JzIGFuZCBjb25zb2xlIGxvZ3MgZnJvbSB0aGUgd2luZG93IGNvbnRleHQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfc2V0UGFnZUV2ZW50c1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggdGhlIGxpc3RlbmVyc1xyXG4gKiBhcmUgYmVpbmcgc2V0LlxyXG4gKi9cclxuZnVuY3Rpb24gX3NldFBhZ2VFdmVudHMocGFnZSkge1xyXG4gIC8vIEdldCBgZGVidWdgIG9wdGlvbnNcclxuICBjb25zdCB7IGRlYnVnIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIFNldCB0aGUgYHBhZ2VlcnJvcmAgbGlzdGVuZXJcclxuICBwYWdlLm9uKCdwYWdlZXJyb3InLCBhc3luYyAoKSA9PiB7XHJcbiAgICAvLyBJdCB3b3VsZCBzZWVtIGxpa2UgdGhpcyBtYXkgZmlyZSBhdCB0aGUgc2FtZSB0aW1lIG9yIHNob3J0bHkgYmVmb3JlXHJcbiAgICAvLyBhIHBhZ2UgaXMgY2xvc2VkLlxyXG4gICAgaWYgKHBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGNvbnNvbGVgIGxpc3RlbmVyLCBpZiBuZWVkZWRcclxuICBpZiAoZGVidWcuZW5hYmxlICYmIGRlYnVnLmxpc3RlblRvQ29uc29sZSkge1xyXG4gICAgcGFnZS5vbignY29uc29sZScsIChtZXNzYWdlKSA9PiB7XHJcbiAgICAgIGNvbnNvbGUubG9nKGBbZGVidWddICR7bWVzc2FnZS50ZXh0KCl9YCk7XHJcbiAgICB9KTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRCcm93c2VyLFxyXG4gIGNyZWF0ZUJyb3dzZXIsXHJcbiAgY2xvc2VCcm93c2VyLFxyXG4gIG5ld1BhZ2UsXHJcbiAgY2xlYXJQYWdlLFxyXG4gIGFkZFBhZ2VSZXNvdXJjZXMsXHJcbiAgY2xlYXJQYWdlUmVzb3VyY2VzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIFRoZSBDU1MgdG8gYmUgdXNlZCBvbiB0aGUgZXhwb3J0ZWQgcGFnZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIENTUyBjb25maWd1cmF0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKCkgPT4gYFxyXG5cclxuaHRtbCwgYm9keSB7XHJcbiAgbWFyZ2luOiAwO1xyXG4gIHBhZGRpbmc6IDA7XHJcbiAgYm94LXNpemluZzogYm9yZGVyLWJveDtcclxufVxyXG5cclxuI3RhYmxlLWRpdiwgI3NsaWRlcnMsICNkYXRhdGFibGUsICNjb250cm9scywgLmxkLXJvdyB7XHJcbiAgZGlzcGxheTogbm9uZTtcclxuICBoZWlnaHQ6IDA7XHJcbn1cclxuXHJcbiNjaGFydC1jb250YWluZXIge1xyXG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XHJcbiAgbWFyZ2luOiAwO1xyXG4gIG92ZXJmbG93OiBhdXRvO1xyXG4gIGZvbnQtc2l6ZTogMDtcclxufVxyXG5cclxuI2NoYXJ0LWNvbnRhaW5lciA+IGZpZ3VyZSwgZGl2IHtcclxuICBtYXJnaW4tdG9wOiAwICFpbXBvcnRhbnQ7XHJcbiAgbWFyZ2luLWJvdHRvbTogMCAhaW1wb3J0YW50O1xyXG59XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCBjc3NUZW1wbGF0ZSBmcm9tICcuL2Nzcy5qcyc7XHJcblxyXG4vKipcclxuICogVGhlIFNWRyB0ZW1wbGF0ZSB0byB1c2Ugd2hlbiBsb2FkaW5nIFNWRyBjb250ZW50IHRvIGJlIGV4cG9ydGVkLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc3ZnIC0gVGhlIFNWRyBpbnB1dCBjb250ZW50IHRvIGJlIGV4cG9ydGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgU1ZHIHRlbXBsYXRlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKHN2ZykgPT4gYFxyXG48IURPQ1RZUEUgaHRtbD5cclxuPGh0bWwgbGFuZz0nZW4tVVMnPlxyXG4gIDxoZWFkPlxyXG4gICAgPG1ldGEgaHR0cC1lcXVpdj1cIkNvbnRlbnQtVHlwZVwiIGNvbnRlbnQ9XCJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLThcIj5cclxuICAgIDx0aXRsZT5IaWdoY2hhcnRzIEV4cG9ydDwvdGl0bGU+XHJcbiAgPC9oZWFkPlxyXG4gIDxzdHlsZT5cclxuICAgICR7Y3NzVGVtcGxhdGUoKX1cclxuICA8L3N0eWxlPlxyXG4gIDxib2R5PlxyXG4gICAgPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPlxyXG4gICAgICAke3N2Z31cclxuICAgIDwvZGl2PlxyXG4gIDwvYm9keT5cclxuPC9odG1sPlxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIGhhbmRsZXMgY2hhcnQgZXhwb3J0IGZ1bmN0aW9uYWxpdHkgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKiBJdCBzdXBwb3J0cyBleHBvcnRpbmcgY2hhcnRzIGFzIFNWRywgUE5HLCBKUEVHLCBhbmQgUERGIGZvcm1hdHMuIFRoZSBtb2R1bGVcclxuICogbWFuYWdlcyBwYWdlIHJlc291cmNlcywgc2V0cyB1cCB0aGUgZXhwb3J0IGVudmlyb25tZW50LCBhbmQgcHJvY2Vzc2VzIGNoYXJ0XHJcbiAqIGNvbmZpZ3VyYXRpb25zIG9yIFNWRyBpbnB1dHMgZm9yIHJlbmRlcmluZy4gRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlXHJcbiAqIHVzaW5nIFB1cHBldGVlci5cclxuICovXHJcblxyXG5pbXBvcnQgeyBhZGRQYWdlUmVzb3VyY2VzLCBjbGVhclBhZ2VSZXNvdXJjZXMgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBjcmVhdGVDaGFydCB9IGZyb20gJy4vaGlnaGNoYXJ0cy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBzdmdUZW1wbGF0ZSBmcm9tICcuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L3N2Z0V4cG9ydC5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZSB1c2luZyBQdXBwZXRlZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcHVwcGV0ZWVyRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPChzdHJpbmd8QnVmZmVyfEV4cG9ydEVycm9yKT59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzXHJcbiAqIHRvIHRoZSBleHBvcnRlZCBkYXRhIG9yIHJlamVjdGluZyB3aXRoIGFuIGBFeHBvcnRFcnJvcmAuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBleHBvcnQgdG8gYW4gdW5zdXBwb3J0ZWRcclxuICogb3V0cHV0IGZvcm1hdCBvY2N1cnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHVwcGV0ZWVyRXhwb3J0KHBhZ2UsIGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheSAoYWRkaXRpb25hbCBKUyBhbmQgQ1NTKVxyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsZXQgaXNTVkcgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBEZWNpZGUgb24gdGhlIGV4cG9ydCBtZXRob2RcclxuICAgIGlmIChleHBvcnRPcHRpb25zLnN2Zykge1xyXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBgdHlwZWAgaXMgYWxzbyBTVkcsIHJldHVybiB0aGUgaW5wdXRcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgICAgICByZXR1cm4gZXhwb3J0T3B0aW9ucy5zdmc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIE1hcmsgYXMgU1ZHIGV4cG9ydCBmb3IgdGhlIGxhdGVyIHNpemUgY29ycmVjdGlvbnNcclxuICAgICAgaXNTVkcgPSB0cnVlO1xyXG5cclxuICAgICAgLy8gU1ZHIGV4cG9ydFxyXG4gICAgICBhd2FpdCBwYWdlLnNldENvbnRlbnQoc3ZnVGVtcGxhdGUoZXhwb3J0T3B0aW9ucy5zdmcpLCB7XHJcbiAgICAgICAgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCdcclxuICAgICAgfSk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIEpTT04gY29uZmlnLicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRcclxuICAgICAgYXdhaXQgcGFnZS5ldmFsdWF0ZShjcmVhdGVDaGFydCwgZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBLZWVwcyB0cmFjayBvZiBhbGwgcmVzb3VyY2VzIGFkZGVkIG9uIHRoZSBwYWdlIHdpdGggYWRkWFhYVGFnLiBldGNcclxuICAgIC8vIEl0J3MgVklUQUwgdGhhdCBhbGwgYWRkZWQgcmVzb3VyY2VzIGVuZHMgdXAgaGVyZSBzbyB3ZSBjYW4gY2xlYXIgdGhpbmdzXHJcbiAgICAvLyBvdXQgd2hlbiBkb2luZyBhIG5ldyBleHBvcnQgaW4gdGhlIHNhbWUgcGFnZSFcclxuICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goXHJcbiAgICAgIC4uLihhd2FpdCBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIGN1c3RvbUxvZ2ljT3B0aW9ucykpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgcmVhbCBjaGFydCBzaXplIGFuZCBzZXQgdGhlIHpvb20gYWNjb3JkaW5nbHlcclxuICAgIGNvbnN0IHNpemUgPSBpc1NWR1xyXG4gICAgICA/IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKHNjYWxlKSA9PiB7XHJcbiAgICAgICAgICBjb25zdCBzdmdFbGVtZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcclxuICAgICAgICAgICAgJyNjaGFydC1jb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnXHJcbiAgICAgICAgICApO1xyXG5cclxuICAgICAgICAgIC8vIEdldCB0aGUgdmFsdWVzIGNvcnJlY3RseSBzY2FsZWRcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0SGVpZ2h0ID0gc3ZnRWxlbWVudC5oZWlnaHQuYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xyXG4gICAgICAgICAgY29uc3QgY2hhcnRXaWR0aCA9IHN2Z0VsZW1lbnQud2lkdGguYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xyXG5cclxuICAgICAgICAgIC8vIEluIGNhc2Ugb2YgU1ZHIHRoZSB6b29tIG11c3QgYmUgc2V0IGRpcmVjdGx5IGZvciBib2R5IGFzIHNjYWxlXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IHNjYWxlO1xyXG5cclxuICAgICAgICAgIC8vIFNldCB0aGUgbWFyZ2luIHRvIDBweFxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLm1hcmdpbiA9ICcwcHgnO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0sIHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSkpXHJcbiAgICAgIDogYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGNvbnN0IHsgY2hhcnRIZWlnaHQsIGNoYXJ0V2lkdGggfSA9IHdpbmRvdy5IaWdoY2hhcnRzLmNoYXJ0c1swXTtcclxuXHJcbiAgICAgICAgICAvLyBObyBuZWVkIGZvciBzdWNoIHNjYWxlIG1hbmlwdWxhdGlvbiBpbiBjYXNlIG9mIG90aGVyIHR5cGVzXHJcbiAgICAgICAgICAvLyBvZiBleHBvcnRzLiBSZXNldCB0aGUgem9vbSBmb3Igb3RoZXIgZXhwb3J0cyB0aGFuIHRvIFNWR3NcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gMTtcclxuXHJcbiAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICBjaGFydEhlaWdodCxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aFxyXG4gICAgICAgICAgfTtcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNsaXAgcmVnaW9uIGZvciB0aGUgcGFnZVxyXG4gICAgY29uc3QgeyB4LCB5IH0gPSBhd2FpdCBfZ2V0Q2xpcFJlZ2lvbihwYWdlKTtcclxuXHJcbiAgICAvLyBTZXQgZmluYWwgYGhlaWdodGAgZm9yIHZpZXdwb3J0XHJcbiAgICBjb25zdCB2aWV3cG9ydEhlaWdodCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydEhlaWdodCB8fCBleHBvcnRPcHRpb25zLmhlaWdodClcclxuICAgICk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGB3aWR0aGAgZm9yIHZpZXdwb3J0XHJcbiAgICBjb25zdCB2aWV3cG9ydFdpZHRoID0gTWF0aC5hYnMoXHJcbiAgICAgIE1hdGguY2VpbChzaXplLmNoYXJ0V2lkdGggfHwgZXhwb3J0T3B0aW9ucy53aWR0aClcclxuICAgICk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBmaW5hbCB2aWV3cG9ydCBub3cgdGhhdCB3ZSBoYXZlIHRoZSByZWFsIGhlaWdodFxyXG4gICAgYXdhaXQgcGFnZS5zZXRWaWV3cG9ydCh7XHJcbiAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICBkZXZpY2VTY2FsZUZhY3RvcjogaXNTVkcgPyAxIDogcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKVxyXG4gICAgfSk7XHJcblxyXG4gICAgbGV0IHJlc3VsdDtcclxuICAgIC8vIFJhc3Rlcml6YXRpb24gcHJvY2Vzc1xyXG4gICAgc3dpdGNoIChleHBvcnRPcHRpb25zLnR5cGUpIHtcclxuICAgICAgY2FzZSAnc3ZnJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlU1ZHKHBhZ2UpO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBjYXNlICdwbmcnOlxyXG4gICAgICBjYXNlICdqcGVnJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlSW1hZ2UoXHJcbiAgICAgICAgICBwYWdlLFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy50eXBlLFxyXG4gICAgICAgICAge1xyXG4gICAgICAgICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgICAgeCxcclxuICAgICAgICAgICAgeVxyXG4gICAgICAgICAgfSxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBjYXNlICdwZGYnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVQREYoXHJcbiAgICAgICAgICBwYWdlLFxyXG4gICAgICAgICAgdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtleHBvcnRdIFVuc3VwcG9ydGVkIG91dHB1dCBmb3JtYXQ6ICR7ZXhwb3J0T3B0aW9ucy50eXBlfS5gLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDbGVhciBwcmV2aW91c2x5IGluamVjdGVkIEpTIGFuZCBDU1MgcmVzb3VyY2VzXHJcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIHJlc3VsdDtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiBlcnJvcjtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGNsaXBwaW5nIHJlZ2lvbiBjb29yZGluYXRlcyBvZiB0aGUgc3BlY2lmaWVkIHBhZ2UgZWxlbWVudFxyXG4gKiB3aXRoIHRoZSAnY2hhcnQtY29udGFpbmVyJyBpZC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZ2V0Q2xpcFJlZ2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHhgLCBgeWAsIGB3aWR0aGAsIGFuZCBgaGVpZ2h0YCBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2dldENsaXBSZWdpb24ocGFnZSkge1xyXG4gIHJldHVybiBwYWdlLiRldmFsKCcjY2hhcnQtY29udGFpbmVyJywgKGVsZW1lbnQpID0+IHtcclxuICAgIGNvbnN0IHsgeCwgeSwgd2lkdGgsIGhlaWdodCB9ID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgIHJldHVybiB7XHJcbiAgICAgIHgsXHJcbiAgICAgIHksXHJcbiAgICAgIHdpZHRoLFxyXG4gICAgICBoZWlnaHQ6IE1hdGgudHJ1bmMoaGVpZ2h0ID4gMSA/IGhlaWdodCA6IDUwMClcclxuICAgIH07XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIFNWRyBieSBldmFsdWF0aW5nIHRoZSBgb3V0ZXJIVE1MYCBvZiB0aGUgZmlyc3QgJ3N2ZycgZWxlbWVudFxyXG4gKiBpbnNpZGUgYW4gZWxlbWVudCB3aXRoIHRoZSBpZCAnY29udGFpbmVyJy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlU1ZHXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgU1ZHIHN0cmluZy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9jcmVhdGVTVkcocGFnZSkge1xyXG4gIHJldHVybiBwYWdlLiRldmFsKFxyXG4gICAgJyNjb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnLFxyXG4gICAgKGVsZW1lbnQpID0+IGVsZW1lbnQub3V0ZXJIVE1MXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gaW1hZ2UgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBgc2NyZWVuc2hvdGAgZnVuY3Rpb25hbGl0eSB3aXRoXHJcbiAqIHNwZWNpZmllZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVJbWFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBJbWFnZSB0eXBlLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2xpcCAtIENsaXBwaW5nIHJlZ2lvbiBjb29yZGluYXRlcy5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBpbWFnZSBidWZmZXJcclxuICogb3IgcmVqZWN0aW5nIHdpdGggYW4gYEV4cG9ydEVycm9yYCBmb3IgdGltZW91dC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9jcmVhdGVJbWFnZShwYWdlLCB0eXBlLCBjbGlwLCByYXN0ZXJpemF0aW9uVGltZW91dCkge1xyXG4gIHJldHVybiBQcm9taXNlLnJhY2UoW1xyXG4gICAgcGFnZS5zY3JlZW5zaG90KHtcclxuICAgICAgdHlwZSxcclxuICAgICAgY2xpcCxcclxuICAgICAgZW5jb2Rpbmc6ICdiYXNlNjQnLFxyXG4gICAgICBmdWxsUGFnZTogZmFsc2UsXHJcbiAgICAgIG9wdGltaXplRm9yU3BlZWQ6IHRydWUsXHJcbiAgICAgIGNhcHR1cmVCZXlvbmRWaWV3cG9ydDogdHJ1ZSxcclxuICAgICAgLi4uKHR5cGUgIT09ICdwbmcnID8geyBxdWFsaXR5OiA4MCB9IDoge30pLFxyXG4gICAgICAvLyBBbHdheXMgcmVuZGVyIG9uIGEgdHJhbnNwYXJlbnQgcGFnZSBpZiB0aGUgZXhwZWN0ZWQgdHlwZSBmb3JtYXQgaXMgUE5HXHJcbiAgICAgIG9taXRCYWNrZ3JvdW5kOiB0eXBlID09ICdwbmcnIC8vICM0NDcsICM0NjNcclxuICAgIH0pLFxyXG4gICAgbmV3IFByb21pc2UoKF9yZXNvbHZlLCByZWplY3QpID0+XHJcbiAgICAgIHNldFRpbWVvdXQoXHJcbiAgICAgICAgKCkgPT4gcmVqZWN0KG5ldyBFeHBvcnRFcnJvcignUmFzdGVyaXphdGlvbiB0aW1lb3V0JywgNDA4KSksXHJcbiAgICAgICAgcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxyXG4gICAgICApXHJcbiAgICApXHJcbiAgXSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUERGIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgYHBkZmAgZnVuY3Rpb25hbGl0eSB3aXRoIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVQREZcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHQgLSBQREYgaGVpZ2h0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGggLSBQREYgd2lkdGguXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgUERGIGJ1ZmZlci5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9jcmVhdGVQREYocGFnZSwgaGVpZ2h0LCB3aWR0aCwgcmFzdGVyaXphdGlvblRpbWVvdXQpIHtcclxuICBhd2FpdCBwYWdlLmVtdWxhdGVNZWRpYVR5cGUoJ3NjcmVlbicpO1xyXG4gIHJldHVybiBwYWdlLnBkZih7XHJcbiAgICAvLyBUaGlzIHdpbGwgcmVtb3ZlIGFuIGV4dHJhIGVtcHR5IHBhZ2UgaW4gUERGIGV4cG9ydHNcclxuICAgIGhlaWdodDogaGVpZ2h0ICsgMSxcclxuICAgIHdpZHRoLFxyXG4gICAgZW5jb2Rpbmc6ICdiYXNlNjQnLFxyXG4gICAgdGltZW91dDogcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgcHVwcGV0ZWVyRXhwb3J0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBwcm92aWRlcyBhIHdvcmtlciBwb29sIGltcGxlbWVudGF0aW9uIGZvciBtYW5hZ2luZ1xyXG4gKiB0aGUgYnJvd3NlciBpbnN0YW5jZSBhbmQgcGFnZXMsIHNwZWNpZmljYWxseSBkZXNpZ25lZCBmb3IgdXNlIHdpdGhcclxuICogdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci4gSXQgb3B0aW1pemVzIHJlc291cmNlcyB1c2FnZSBhbmQgcGVyZm9ybWFuY2VcclxuICogYnkgbWFpbnRhaW5pbmcgYSBwb29sIG9mIHdvcmtlcnMgdGhhdCBjYW4gaGFuZGxlIGNvbmN1cnJlbnQgZXhwb3J0IHRhc2tzXHJcbiAqIHVzaW5nIFB1cHBldGVlci5cclxuICovXHJcblxyXG5pbXBvcnQgeyBQb29sIH0gZnJvbSAndGFybic7XHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7IGNyZWF0ZUJyb3dzZXIsIGNsb3NlQnJvd3NlciwgbmV3UGFnZSwgY2xlYXJQYWdlIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgcHVwcGV0ZWVyRXhwb3J0IH0gZnJvbSAnLi9leHBvcnQuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0TmV3RGF0ZVRpbWUsIG1lYXN1cmVUaW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIHBvb2wgaW5zdGFuY2VcclxubGV0IHBvb2wgPSBudWxsO1xyXG5cclxuLy8gUG9vbCBzdGF0aXN0aWNzXHJcbmNvbnN0IHBvb2xTdGF0cyA9IHtcclxuICBleHBvcnRzQXR0ZW1wdGVkOiAwLFxyXG4gIGV4cG9ydHNQZXJmb3JtZWQ6IDAsXHJcbiAgZXhwb3J0c0Ryb3BwZWQ6IDAsXHJcbiAgZXhwb3J0c0Zyb21Tdmc6IDAsXHJcbiAgZXhwb3J0c0Zyb21PcHRpb25zOiAwLFxyXG4gIGV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHM6IDAsXHJcbiAgZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHM6IDAsXHJcbiAgdGltZVNwZW50OiAwLFxyXG4gIHRpbWVTcGVudEF2ZXJhZ2U6IDBcclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyB0aGUgZXhwb3J0IHBvb2wgd2l0aCB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiwgY3JlYXRpbmdcclxuICogYSBicm93c2VyIGluc3RhbmNlIGFuZCBzZXR0aW5nIHVwIHdvcmtlciByZXNvdXJjZXMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdFBvb2xcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyXHJcbiAqIGxhdW5jaC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGVuZGluZyB0aGUgZnVuY3Rpb25cclxuICogZXhlY3V0aW9uIHdoZW4gYW4gYWxyZWFkeSBpbml0aWFsaXplZCBwb29sIG9mIHJlc291cmNlcyBpcyBmb3VuZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGNvdWxkIG5vdCBjcmVhdGUgdGhlIHBvb2xcclxuICogb2Ygd29ya2Vycy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0UG9vbChwb29sT3B0aW9ucywgcHVwcGV0ZWVyQXJncykge1xyXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgcHVwcGV0ZWVyIGFyZ3VtZW50c1xyXG4gIGF3YWl0IGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncyk7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gSW5pdGlhbGl6aW5nIHBvb2wgd2l0aCB3b3JrZXJzOiBtaW4gJHtwb29sT3B0aW9ucy5taW5Xb3JrZXJzfSwgbWF4ICR7cG9vbE9wdGlvbnMubWF4V29ya2Vyc30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBpZiAocG9vbCkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICAnW3Bvb2xdIEFscmVhZHkgaW5pdGlhbGl6ZWQsIHBsZWFzZSBraWxsIGl0IGJlZm9yZSBjcmVhdGluZyBhIG5ldyBvbmUuJ1xyXG4gICAgICApO1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcCBhbiBleWUgb24gYSBjb3JyZWN0IG1pbiBhbmQgbWF4IHdvcmtlcnMgbnVtYmVyXHJcbiAgICBpZiAocG9vbE9wdGlvbnMubWluV29ya2VycyA+IHBvb2xPcHRpb25zLm1heFdvcmtlcnMpIHtcclxuICAgICAgcG9vbE9wdGlvbnMubWluV29ya2VycyA9IHBvb2xPcHRpb25zLm1heFdvcmtlcnM7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgcG9vbCBhbG9uZyB3aXRoIGEgbWluaW1hbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBwb29sID0gbmV3IFBvb2woe1xyXG4gICAgICAvLyBHZXQgdGhlIGBjcmVhdGVgLCBgdmFsaWRhdGVgLCBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9uc1xyXG4gICAgICAuLi5fZmFjdG9yeShwb29sT3B0aW9ucyksXHJcbiAgICAgIG1pbjogcG9vbE9wdGlvbnMubWluV29ya2VycyxcclxuICAgICAgbWF4OiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzLFxyXG4gICAgICBhY3F1aXJlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuYWNxdWlyZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmNyZWF0ZVRpbWVvdXQsXHJcbiAgICAgIGRlc3Ryb3lUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5kZXN0cm95VGltZW91dCxcclxuICAgICAgaWRsZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmlkbGVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVSZXRyeUludGVydmFsLFxyXG4gICAgICByZWFwSW50ZXJ2YWxNaWxsaXM6IHBvb2xPcHRpb25zLnJlYXBlckludGVydmFsLFxyXG4gICAgICBwcm9wYWdhdGVDcmVhdGVFcnJvcjogZmFsc2VcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFNldCBldmVudHNcclxuICAgIHBvb2wub24oJ3JlbGVhc2UnLCBhc3luYyAocmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gQ2xlYXIgcGFnZVxyXG4gICAgICBjb25zdCBjbGVhclN0YXR1cyA9IGF3YWl0IGNsZWFyUGFnZShyZXNvdXJjZSwgZmFsc2UpO1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cmVzb3VyY2UuaWR9XSAtIFJlbGVhc2luZyBhIHdvcmtlci4gQ2xlYXIgcGFnZSBzdGF0dXM6ICR7Y2xlYXJTdGF0dXN9LmBcclxuICAgICAgKTtcclxuICAgIH0pO1xyXG5cclxuICAgIHBvb2wub24oJ2Rlc3Ryb3lTdWNjZXNzJywgKF9ldmVudElkLCByZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cmVzb3VyY2UuaWR9XSAtIERlc3Ryb3llZCBhIHdvcmtlciBzdWNjZXNzZnVsbHkuYFxyXG4gICAgICApO1xyXG4gICAgICByZXNvdXJjZS5wYWdlID0gbnVsbDtcclxuICAgIH0pO1xyXG5cclxuICAgIGNvbnN0IGluaXRpYWxSZXNvdXJjZXMgPSBbXTtcclxuICAgIC8vIENyZWF0ZSBhbiBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcG9vbE9wdGlvbnMubWluV29ya2VyczsgaSsrKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG4gICAgICAgIGluaXRpYWxSZXNvdXJjZXMucHVzaChyZXNvdXJjZSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW3Bvb2xdIENvdWxkIG5vdCBjcmVhdGUgYW4gaW5pdGlhbCByZXNvdXJjZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlcyBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBpbml0aWFsUmVzb3VyY2VzLmZvckVhY2goKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIHBvb2wucmVsZWFzZShyZXNvdXJjZSk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gVGhlIHBvb2wgaXMgcmVhZHkke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RoID8gYCB3aXRoICR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGh9IGluaXRpYWwgcmVzb3VyY2VzIHdhaXRpbmcuYCA6ICcuJ31gXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcG9vbF0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgY3JlYXRlIHRoZSBwb29sIG9mIHdvcmtlcnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBUZXJtaW5hdGVzIGFsbCB3b3JrZXJzIGluIHRoZSBwb29sLCBkZXN0cm95cyB0aGUgcG9vbCwgYW5kIGNsb3NlcyB0aGUgYnJvd3NlclxyXG4gKiBpbnN0YW5jZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBraWxsUG9vbFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSBhbGwgd29ya2VycyBhcmVcclxuICogdGVybWluYXRlZCwgdGhlIHBvb2wgaXMgZGVzdHJveWVkLCBhbmQgdGhlIGJyb3dzZXIgaXMgc3VjY2Vzc2Z1bGx5IGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBraWxsUG9vbCgpIHtcclxuICBsb2coMywgJ1twb29sXSBLaWxsaW5nIHBvb2wgd2l0aCBhbGwgd29ya2VycyBhbmQgY2xvc2luZyBicm93c2VyLicpO1xyXG5cclxuICAvLyBJZiBzdGlsbCBhbGl2ZSwgZGVzdHJveSB0aGUgcG9vbCBvZiBwYWdlcyBiZWZvcmUgY2xvc2luZyBhIGJyb3dzZXJcclxuICBpZiAocG9vbCkge1xyXG4gICAgLy8gRnJlZSB1cCBub3QgcmVsZWFzZWQgd29ya2Vyc1xyXG4gICAgZm9yIChjb25zdCB3b3JrZXIgb2YgcG9vbC51c2VkKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXIucmVzb3VyY2UpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgdGhlIHBvb2wgaWYgaXQgaXMgc3RpbGwgYXZhaWxhYmxlXHJcbiAgICBpZiAoIXBvb2wuZGVzdHJveWVkKSB7XHJcbiAgICAgIGF3YWl0IHBvb2wuZGVzdHJveSgpO1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBEZXN0cm95ZWQgdGhlIHBvb2wgb2YgcmVzb3VyY2VzLicpO1xyXG4gICAgfVxyXG4gICAgcG9vbCA9IG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGF3YWl0IGNsb3NlQnJvd3NlcigpO1xyXG59XHJcblxyXG4vKipcclxuICogUHJvY2Vzc2VzIHRoZSBleHBvcnQgd29yayB1c2luZyBhIHdvcmtlciBmcm9tIHRoZSBwb29sLiBBY3F1aXJlcyBhIHdvcmtlclxyXG4gKiBoYW5kbGUgZnJvbSB0aGUgcG9vbCwgcGVyZm9ybXMgdGhlIGV4cG9ydCB1c2luZyBwdXBwZXRlZXIsIGFuZCByZWxlYXNlc1xyXG4gKiB0aGUgd29ya2VyIGhhbmRsZSBiYWNrIHRvIHRoZSBwb29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHBvc3RXb3JrXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgY29tcGxldGUgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBleHBvcnQgcmVzdWx0XHJcbiAqIGFuZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcG9zdFdvcmsob3B0aW9ucykge1xyXG4gIGxldCB3b3JrZXJIYW5kbGU7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsb2coNCwgJ1twb29sXSBXb3JrIHJlY2VpdmVkLCBzdGFydGluZyB0byBwcm9jZXNzLicpO1xyXG5cclxuICAgIC8vIEFuIGV4cG9ydCBhdHRlbXB0IGNvdW50ZWRcclxuICAgICsrcG9vbFN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQ7XHJcblxyXG4gICAgLy8gRGlzcGxheSB0aGUgcG9vbCBpbmZvcm1hdGlvbiBpZiBuZWVkZWRcclxuICAgIGlmIChvcHRpb25zLnBvb2wuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGdldFBvb2xJbmZvKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaW4gY2FzZSBvZiBsYWNraW5nIHRoZSBwb29sIGluc3RhbmNlXHJcbiAgICBpZiAoIXBvb2wpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbcG9vbF0gV29yayByZWNlaXZlZCwgYnV0IHBvb2wgaGFzIG5vdCBiZWVuIHN0YXJ0ZWQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaGUgYWNxdWlyZSBjb3VudGVyXHJcbiAgICBjb25zdCBhY3F1aXJlQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gVHJ5IHRvIGFjcXVpcmUgdGhlIHdvcmtlciBhbG9uZyB3aXRoIHRoZSBpZCwgd29ya3MgY291bnQgYW5kIHBhZ2VcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgICAvLyBBY3F1aXJlIGEgcG9vbCByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIHBhZ2UgYWNxdWlyZSB0aW1lXHJcbiAgICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICA1LFxyXG4gICAgICAgICAgYFtiZW5jaG1hcmtdICR7b3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnfWAsXHJcbiAgICAgICAgICBgQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZSB0b29rICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgIH1FcnJvciBlbmNvdW50ZXJlZCB3aGVuIGFjcXVpcmluZyBhbiBhdmFpbGFibGUgZW50cnk6ICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuICAgIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmVkIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuXHJcbiAgICBpZiAoIXdvcmtlckhhbmRsZS5wYWdlKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICAgIHdvcmtlckhhbmRsZS53b3JrQ291bnQgPSBvcHRpb25zLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbcG9vbF0gUmVzb2x2ZWQgd29ya2VyIHBhZ2UgaXMgaW52YWxpZDogdGhlIHBvb2wgc2V0dXAgaXMgd29ua3kuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBTYXZlIHRoZSBzdGFydCB0aW1lXHJcbiAgICBjb25zdCB3b3JrU3RhcnQgPSBnZXROZXdEYXRlVGltZSgpO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgNCxcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3dvcmtlckhhbmRsZS5pZH1dIC0gU3RhcnRpbmcgd29yayBvbiB0aGlzIHBvb2wgZW50cnkuYFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTdGFydCBtZWFzdXJpbmcgZXhwb3J0IHRpbWVcclxuICAgIGNvbnN0IGV4cG9ydENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFBlcmZvcm0gYW4gZXhwb3J0IG9uIGEgcHVwcGV0ZWVyIGxldmVsXHJcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwdXBwZXRlZXJFeHBvcnQoXHJcbiAgICAgIHdvcmtlckhhbmRsZS5wYWdlLFxyXG4gICAgICBvcHRpb25zLmV4cG9ydCxcclxuICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiBpdCdzIGFuIGVycm9yXHJcbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgRXJyb3IpIHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSWYgdGhlcmUncyBhIHJhc3Rlcml6YXRpb24gdGltZW91dCwgd2Ugd2FudCBuZWVkIHRvIGZsdXNoIHRoZSBwYWdlLlxyXG4gICAgICAvLyBUaGlzIGlzIGJlY2F1c2UgdGhlIHBhZ2UgbWF5IGJlIGluIGEgc3RhdGUgd2hlcmUgaXQncyB3YWl0aW5nIGZvclxyXG4gICAgICAvLyB0aGUgc2NyZWVuc2hvdCB0byBmaW5pc2ggZXZlbiB0aG91Z2ggdGhlIHRpbWVvdXQgaGFzIG9jY3VyZWQuXHJcbiAgICAgIC8vIFdoaWNoIG9mIGNvdXJzZSBjYXVzZXMgYSBsb3Qgb2YgaXNzdWVzIHdpdGggdGhlIGV2ZW50IHN5c3RlbSxcclxuICAgICAgLy8gYW5kIHBhZ2UgY29uc2lzdGVuY3kuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIE9ubHkgcGFnZS5zY3JlZW5zaG90IHdpbGwgdGhyb3cgdGhpcywgdGltZW91dHMgZm9yIFBERidzIGFyZVxyXG4gICAgICAvLyBoYW5kbGVkIGJ5IHRoZSBwYWdlLnBkZiBmdW5jdGlvbiBpdHNlbGYuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIC4uLnllcywgdGhpcyBpcyB1Z2x5LlxyXG4gICAgICBpZiAocmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnKSB7XHJcbiAgICAgICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UgPSBudWxsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoXHJcbiAgICAgICAgcmVzdWx0Lm5hbWUgPT09ICdUaW1lb3V0RXJyb3InIHx8XHJcbiAgICAgICAgcmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnXHJcbiAgICAgICkge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgICB9UmFzdGVyaXphdGlvbiB0aW1lb3V0OiB5b3VyIGNoYXJ0IG1heSBiZSB0b28gY29tcGxleCBvciBsYXJnZSwgYW5kIGZhaWxlZCB0byByZW5kZXIgd2l0aGluIHRoZSBhbGxvdHRlZCB0aW1lLmBcclxuICAgICAgICApLnNldEVycm9yKHJlc3VsdCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICAgIH1FcnJvciBlbmNvdW50ZXJlZCBkdXJpbmcgZXhwb3J0OiAke2V4cG9ydENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgdGhlIFB1cHBldGVlciBleHBvcnQgdGltZVxyXG4gICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNSxcclxuICAgICAgICBgW2JlbmNobWFya10gJHtvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJyd9YCxcclxuICAgICAgICBgRXhwb3J0aW5nIGEgY2hhcnQgc3VjZXNzZnVsbHkgdG9vayAke2V4cG9ydENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgcmVzb3VyY2UgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcblxyXG4gICAgLy8gVXNlZCBmb3Igc3RhdGlzdGljcyBpbiBhdmVyYWdlVGltZSBhbmQgcHJvY2Vzc2VkV29ya0NvdW50LCB3aGljaFxyXG4gICAgLy8gaW4gdHVybiBpcyB1c2VkIGJ5IHRoZSAvaGVhbHRoIHJvdXRlLlxyXG4gICAgY29uc3Qgd29ya0VuZCA9IGdldE5ld0RhdGVUaW1lKCk7XHJcbiAgICBjb25zdCBleHBvcnRUaW1lID0gd29ya0VuZCAtIHdvcmtTdGFydDtcclxuXHJcbiAgICBwb29sU3RhdHMudGltZVNwZW50ICs9IGV4cG9ydFRpbWU7XHJcbiAgICBwb29sU3RhdHMudGltZVNwZW50QXZlcmFnZSA9XHJcbiAgICAgIHBvb2xTdGF0cy50aW1lU3BlbnQgLyArK3Bvb2xTdGF0cy5leHBvcnRzUGVyZm9ybWVkO1xyXG5cclxuICAgIGxvZyg0LCBgW3Bvb2xdIFdvcmsgY29tcGxldGVkIGluICR7ZXhwb3J0VGltZX1tcy5gKTtcclxuXHJcbiAgICAvLyBPdGhlcndpc2UgcmV0dXJuIHRoZSByZXN1bHRcclxuICAgIHJldHVybiB7XHJcbiAgICAgIHJlc3VsdCxcclxuICAgICAgb3B0aW9uc1xyXG4gICAgfTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgKytwb29sU3RhdHMuZXhwb3J0c0Ryb3BwZWQ7XHJcblxyXG4gICAgaWYgKHdvcmtlckhhbmRsZSkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuICAgIH1cclxuXHJcbiAgICB0aHJvdyBlcnJvcjtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xcclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8bnVsbCl9IFRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UgaWYgaW5pdGlhbGl6ZWQsIG9yIG51bGxcclxuICogaWYgdGhlIHBvb2wgaGFzIG5vdCBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbCgpIHtcclxuICByZXR1cm4gcG9vbDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIHN0YXRpc3RpYyBvZiBhIHBvb2wgaW5zdGFjZSBhYm91dCBleHBvcnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbFN0YXRzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjdXJyZW50IHBvb2wgc3RhdGlzdGljcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sU3RhdHMoKSB7XHJcbiAgcmV0dXJuIHBvb2xTdGF0cztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBwb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LCBpbmNsdWRpbmcgbWluaW11bSBhbmQgbWF4aW11bVxyXG4gKiB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmUgcmVxdWVzdHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sSW5mb0pTT05cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gUG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mb0pTT04oKSB7XHJcbiAgcmV0dXJuIHtcclxuICAgIG1pbjogcG9vbC5taW4sXHJcbiAgICBtYXg6IHBvb2wubWF4LFxyXG4gICAgdXNlZDogcG9vbC5udW1Vc2VkKCksXHJcbiAgICBhdmFpbGFibGU6IHBvb2wubnVtRnJlZSgpLFxyXG4gICAgYWxsQ3JlYXRlZDogcG9vbC5udW1Vc2VkKCkgKyBwb29sLm51bUZyZWUoKSxcclxuICAgIHBlbmRpbmdBY3F1aXJlczogcG9vbC5udW1QZW5kaW5nQWNxdWlyZXMoKSxcclxuICAgIHBlbmRpbmdDcmVhdGVzOiBwb29sLm51bVBlbmRpbmdDcmVhdGVzKCksXHJcbiAgICBwZW5kaW5nVmFsaWRhdGlvbnM6IHBvb2wubnVtUGVuZGluZ1ZhbGlkYXRpb25zKCksXHJcbiAgICBwZW5kaW5nRGVzdHJveXM6IHBvb2wucGVuZGluZ0Rlc3Ryb3lzLmxlbmd0aCxcclxuICAgIGFic29sdXRlQWxsOlxyXG4gICAgICBwb29sLm51bVVzZWQoKSArXHJcbiAgICAgIHBvb2wubnVtRnJlZSgpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nQWNxdWlyZXMoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ0NyZWF0ZXMoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ1ZhbGlkYXRpb25zKCkgK1xyXG4gICAgICBwb29sLnBlbmRpbmdEZXN0cm95cy5sZW5ndGhcclxuICB9O1xyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgcG9vbCwgaW5jbHVkaW5nIHRoZSBtaW5pbXVtXHJcbiAqIGFuZCBtYXhpbXVtIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZVxyXG4gKiByZXF1ZXN0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xJbmZvXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm8oKSB7XHJcbiAgY29uc3Qge1xyXG4gICAgbWluLFxyXG4gICAgbWF4LFxyXG4gICAgdXNlZCxcclxuICAgIGF2YWlsYWJsZSxcclxuICAgIGFsbENyZWF0ZWQsXHJcbiAgICBwZW5kaW5nQWNxdWlyZXMsXHJcbiAgICBwZW5kaW5nQ3JlYXRlcyxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9ucyxcclxuICAgIHBlbmRpbmdEZXN0cm95cyxcclxuICAgIGFic29sdXRlQWxsXHJcbiAgfSA9IGdldFBvb2xJbmZvSlNPTigpO1xyXG5cclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWluaW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttaW59LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtYXhpbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21heH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiB1c2VkIHJlc291cmNlczogJHt1c2VkfS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGZyZWUgcmVzb3VyY2VzOiAke2F2YWlsYWJsZX0uYCk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgY3JlYXRlZCAodXNlZCBhbmQgZnJlZSkgcmVzb3VyY2VzOiAke2FsbENyZWF0ZWR9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgYWNxdWlyZWQ6ICR7cGVuZGluZ0FjcXVpcmVzfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGNyZWF0ZWQ6ICR7cGVuZGluZ0NyZWF0ZXN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgdmFsaWRhdGVkOiAke3BlbmRpbmdWYWxpZGF0aW9uc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBkZXN0cm95ZWQ6ICR7cGVuZGluZ0Rlc3Ryb3lzfS5gXHJcbiAgKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCByZXNvdXJjZXM6ICR7YWJzb2x1dGVBbGx9LmApO1xyXG59XHJcblxyXG4vKipcclxuICogRmFjdG9yeSBmdW5jdGlvbiB0aGF0IHJldHVybnMgYW4gb2JqZWN0IHdpdGggYGNyZWF0ZWAsIGB2YWxpZGF0ZWAsXHJcbiAqIGFuZCBgZGVzdHJveWAgZnVuY3Rpb25zIGZvciB0aGUgcG9vbCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9mYWN0b3J5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2ZhY3RvcnkocG9vbE9wdGlvbnMpIHtcclxuICByZXR1cm4ge1xyXG4gICAgLyoqXHJcbiAgICAgKiBDcmVhdGVzIGEgbmV3IHdvcmtlciBwYWdlIGZvciB0aGUgZXhwb3J0IHBvb2wuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gY3JlYXRlXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0XHJcbiAgICAgKiBjb250YWluaW5nIHRoZSB3b3JrZXIgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCBpbml0aWFsXHJcbiAgICAgKiB3b3JrIGNvdW50LlxyXG4gICAgICpcclxuICAgICAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBlcnJvciBkdXJpbmdcclxuICAgICAqIHRoZSBjcmVhdGlvbiBvZiB0aGUgbmV3IHBhZ2UuXHJcbiAgICAgKi9cclxuICAgIGNyZWF0ZTogYXN5bmMgKCkgPT4ge1xyXG4gICAgICAvLyBJbml0IHRoZSByZXNvdXJjZSB3aXRoIHVuaXF1ZSBpZCBhbmQgd29yayBjb3VudFxyXG4gICAgICBjb25zdCBwb29sUmVzb3VyY2UgPSB7XHJcbiAgICAgICAgaWQ6IHV1aWQoKSxcclxuICAgICAgICAvLyBUcnkgdG8gZGlzdHJpYnV0ZSB0aGUgaW5pdGlhbCB3b3JrIGNvdW50XHJcbiAgICAgICAgd29ya0NvdW50OiBNYXRoLnJvdW5kKE1hdGgucmFuZG9tKCkgKiAocG9vbE9wdGlvbnMud29ya0xpbWl0IC8gMikpXHJcbiAgICAgIH07XHJcblxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIFN0YXJ0IG1lYXN1cmluZyBhIHBhZ2UgY3JlYXRpb24gdGltZVxyXG4gICAgICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IGdldE5ld0RhdGVUaW1lKCk7XHJcblxyXG4gICAgICAgIC8vIENyZWF0ZSBhIG5ldyBwYWdlXHJcbiAgICAgICAgYXdhaXQgbmV3UGFnZShwb29sUmVzb3VyY2UpO1xyXG5cclxuICAgICAgICAvLyBNZWFzdXJlIHRoZSB0aW1lIG9mIGZ1bGwgY3JlYXRpb24gYW5kIGNvbmZpZ3VyYXRpb24gb2YgYSBwYWdlXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFN1Y2Nlc3NmdWxseSBjcmVhdGVkIGEgd29ya2VyLCB0b29rICR7XHJcbiAgICAgICAgICAgIGdldE5ld0RhdGVUaW1lKCkgLSBzdGFydERhdGVcclxuICAgICAgICAgIH1tcy5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0dXJuIHJlYWR5IHBvb2wgcmVzb3VyY2VcclxuICAgICAgICByZXR1cm4gcG9vbFJlc291cmNlO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBFcnJvciBlbmNvdW50ZXJlZCB3aGVuIGNyZWF0aW5nIGEgbmV3IHBhZ2UuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuICAgIH0sXHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBWYWxpZGF0ZXMgYSB3b3JrZXIgcGFnZSBpbiB0aGUgZXhwb3J0IHBvb2wsIGNoZWNraW5nIGlmIGl0IGhhcyBleGNlZWRlZFxyXG4gICAgICogdGhlIHdvcmsgbGltaXQuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gdmFsaWRhdGVcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICAgKiB0aGUgd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0cnVlIGlmIHRoZSB3b3JrZXJcclxuICAgICAqIGlzIHZhbGlkIGFuZCB3aXRoaW4gdGhlIHdvcmsgbGltaXQ7IG90aGVyd2lzZSwgdG8gZmFsc2UuXHJcbiAgICAgKi9cclxuICAgIHZhbGlkYXRlOiBhc3luYyAocG9vbFJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIEluIGNlcnRhaW4gY2FzZXMgYWNxdWlyaW5nIHRocm93cyBhIFRhcmdldENsb3NlRXJyb3IsIHdoaWNoIG1heVxyXG4gICAgICAvLyBiZSBjYXVzZWQgYnkgdHdvIHRoaW5nczpcclxuICAgICAgLy8gLSBUaGUgcGFnZSBpcyBjbG9zZWQgYW5kIGF0dGVtcHRlZCB0byBiZSByZXVzZWQuXHJcbiAgICAgIC8vIC0gTG9zdCBjb250YWN0IHdpdGggdGhlIGJyb3dzZXIuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIFdoYXQgd2UncmUgc2VlaW5nIGluIGxvZ3MgaXMgdGhhdCBzdWNjZXNzaXZlIGV4cG9ydHMgdHlwaWNhbGx5XHJcbiAgICAgIC8vIHN1Y2NlZWRzLCBhbmQgdGhlIHNlcnZlciByZWNvdmVycywgaW5kaWNhdGluZyB0aGF0IGl0J3MgbGlrZWx5XHJcbiAgICAgIC8vIHRoZSBmaXJzdCBjYXNlLiBUaGlzIGlzIGFuIGF0dGVtcHQgYXQgYWxsaWV2YXRpbmcgdGhlIGlzc3VlIGJ5XHJcbiAgICAgIC8vIHNpbXBseSBub3QgdmFsaWRhdGluZyB0aGUgd29ya2VyIGlmIHRoZSBwYWdlIGlzIG51bGwgb3IgY2xvc2VkLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBUaGUgYWN0dWFsIHJlc3VsdCBmcm9tIHdoZW4gdGhpcyBoYXBwZW5lZCwgd2FzIHRoYXQgYSB3b3JrZXIgd291bGRcclxuICAgICAgLy8gYmUgY29tcGxldGVseSBsb2NrZWQsIHN0b3BwaW5nIGl0IGZyb20gYmVpbmcgYWNxdWlyZWQgdW50aWxcclxuICAgICAgLy8gaXRzIHdvcmsgY291bnQgcmVhY2hlZCB0aGUgbGltaXQuXHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHBhZ2VgIGlzIHZhbGlkXHJcbiAgICAgIGlmICghcG9vbFJlc291cmNlLnBhZ2UpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKG5vIHZhbGlkIHBhZ2UgaXMgZm91bmQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBwYWdlYCBpcyBjbG9zZWRcclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKHBhZ2UgaXMgY2xvc2VkIG9yIGludmFsaWQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBtYWluRnJhbWVgIGlzIGRldGFjaGVkXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZS5tYWluRnJhbWUoKS5kZXRhY2hlZCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAocGFnZSdzIGZyYW1lIGlzIGRldGFjaGVkKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgd29ya0xpbWl0YCBpcyBleGNlZWRlZFxyXG4gICAgICBpZiAoXHJcbiAgICAgICAgcG9vbE9wdGlvbnMud29ya0xpbWl0ICYmXHJcbiAgICAgICAgKytwb29sUmVzb3VyY2Uud29ya0NvdW50ID4gcG9vbE9wdGlvbnMud29ya0xpbWl0XHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAoZXhjZWVkZWQgdGhlICR7cG9vbE9wdGlvbnMud29ya0xpbWl0fSB3b3JrcyBwZXIgcmVzb3VyY2UgbGltaXQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVGhlIGBwb29sUmVzb3VyY2VgIGlzIHZhbGlkYXRlZFxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH0sXHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBEZXN0cm95cyBhIHdvcmtlciBlbnRyeSBpbiB0aGUgZXhwb3J0IHBvb2wsIGNsb3NpbmcgaXRzIGFzc29jaWF0ZWQgcGFnZS5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiBkZXN0cm95XHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHBvb2xSZXNvdXJjZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAgICogdGhlIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cclxuICAgICAqL1xyXG4gICAgZGVzdHJveTogYXN5bmMgKHBvb2xSZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBEZXN0cm95aW5nIGEgd29ya2VyLmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZSAmJiAhcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAvLyBSZW1vdmUgYWxsIGF0dGFjaGVkIGV2ZW50IGxpc3RlbmVycyBmcm9tIHRoZSByZXNvdXJjZVxyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdwYWdlZXJyb3InKTtcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygnY29uc29sZScpO1xyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdmcmFtZWRldGFjaGVkJyk7XHJcblxyXG4gICAgICAgICAgLy8gV2UgbmVlZCB0byB3YWl0IGFyb3VuZCBmb3IgdGhpc1xyXG4gICAgICAgICAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2UuY2xvc2UoKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAzLFxyXG4gICAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBQYWdlIGNvdWxkIG5vdCBiZSBjbG9zZWQgdXBvbiBkZXN0cm95aW5nLmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9O1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgaW5pdFBvb2wsXHJcbiAga2lsbFBvb2wsXHJcbiAgcG9zdFdvcmssXHJcbiAgZ2V0UG9vbCxcclxuICBnZXRQb29sU3RhdHMsXHJcbiAgZ2V0UG9vbEluZm8sXHJcbiAgZ2V0UG9vbEluZm9KU09OXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBVc2VkIHRvIHNhbml0aXplIHRoZSBzdHJpbmdzIGNvbWluZyBmcm9tIHRoZSBleHBvcnRpbmcgbW9kdWxlXHJcbiAqIHRvIHByZXZlbnQgWFNTIGF0dGFja3MgKHdpdGggdGhlIERPTVB1cmlmeSBsaWJyYXJ5KS5cclxuICovXHJcblxyXG5pbXBvcnQgRE9NUHVyaWZ5IGZyb20gJ2RvbXB1cmlmeSc7XHJcbmltcG9ydCB7IEpTRE9NIH0gZnJvbSAnanNkb20nO1xyXG5cclxuLyoqXHJcbiAqIFNhbml0aXplcyBhIGdpdmVuIEhUTUwgc3RyaW5nIGJ5IHJlbW92aW5nIDxzY3JpcHQ+IHRhZ3MuIFRoaXMgZnVuY3Rpb24gdXNlc1xyXG4gKiBhIHJlZ3VsYXIgZXhwcmVzc2lvbiB0byBmaW5kIGFuZCByZW1vdmUgYWxsIG9jY3VycmVuY2VzIG9mIDxzY3JpcHQ+PC9zY3JpcHQ+XHJcbiAqIHRhZ3MgYW5kIGFueSBjb250ZW50IHdpdGhpbiB0aGVtLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2FuaXRpemVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IC0gVGhlIEhUTUwgc3RyaW5nIHRvIGJlIHNhbml0aXplZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIHNhbml0aXplZCBIVE1MIHN0cmluZy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzYW5pdGl6ZShpbnB1dCkge1xyXG4gIC8vIEdldCB0aGUgdmlydHVhbCBET01cclxuICBjb25zdCB3aW5kb3cgPSBuZXcgSlNET00oJycpLndpbmRvdztcclxuXHJcbiAgLy8gQ3JlYXRlIGEgcHVyaWZ5aW5nIGluc3RhbmNlXHJcbiAgY29uc3QgcHVyaWZ5ID0gRE9NUHVyaWZ5KHdpbmRvdyk7XHJcblxyXG4gIC8vIFJldHVybiBzYW5pdGl6ZWQgaW5wdXRcclxuICByZXR1cm4gcHVyaWZ5LnNhbml0aXplKGlucHV0LCB7IEFERF9UQUdTOiBbJ2ZvcmVpZ25PYmplY3QnXSB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNhbml0aXplXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBwcm92aWRlcyBmdW5jdGlvbnMgdG8gcHJlcGFyZSBmb3IgdGhlIGV4cG9ydGluZyBjaGFydHNcclxuICogaW50byB2YXJpb3VzIGltYWdlIG91dHB1dCBmb3JtYXRzIHN1Y2ggYXMgSlBFRywgUE5HLCBQREYsIGFuZCBTVkdzLlxyXG4gKiBJdCBzdXBwb3J0cyBzaW5nbGUgYW5kIGJhdGNoIGV4cG9ydCBvcGVyYXRpb25zIGFuZCBhbGxvd3MgY3VzdG9taXphdGlvblxyXG4gKiB0aHJvdWdoIG9wdGlvbnMgcGFzc2VkIGZyb20gY29uZmlndXJhdGlvbnMgb3IgQVBJcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcblxyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcsIHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRQb29sU3RhdHMsIGtpbGxQb29sLCBwb3N0V29yayB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IHNhbml0aXplIH0gZnJvbSAnLi9zYW5pdGl6ZS5qcyc7XHJcbmltcG9ydCB7XHJcbiAgZml4Q29uc3RyLFxyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIGlzT2JqZWN0LFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHdyYXBBcm91bmRcclxufSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgZ2xvYmFsIGZsYWcgZm9yIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uXHJcbmxldCBhbGxvd0NvZGVFeGVjdXRpb24gPSBmYWxzZTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBvcHRpb25zIGFuZCBzYXZlc1xyXG4gKiB0aGUgcmVzdWx0aW5nIGltYWdlIHRvIHRoZSBwcm92aWRlZCBvdXRwdXQgZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzaW5nbGVFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgYG9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3NcclxuICogZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhbiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGVcclxuICogc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gVGhlIG9iamVjdCBtdXN0IGNvbnRhaW4gYXQgbGVhc3Qgb25lXHJcbiAqIG9mIHRoZSBmb2xsb3dpbmcgYGV4cG9ydGAgcHJvcGVydGllczogYGluZmlsZWAsIGBpbnN0cmAsIGBvcHRpb25zYCwgb3IgYHN2Z2BcclxuICogdG8gZ2VuZXJhdGUgYSB2YWxpZCBpbWFnZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIHNpbmdsZSBleHBvcnRcclxuICogcHJvY2VzcyBpcyBjb21wbGV0ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2luZ2xlRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQpIHtcclxuICAgIC8vIFBlcmZvcm0gYW4gZXhwb3J0XHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChcclxuICAgICAgeyBleHBvcnQ6IG9wdGlvbnMuZXhwb3J0LCBjdXN0b21Mb2dpYzogb3B0aW9ucy5jdXN0b21Mb2dpYyB9LFxyXG4gICAgICBhc3luYyAoZXJyb3IsIGRhdGEpID0+IHtcclxuICAgICAgICAvLyBFeGl0IHByb2Nlc3Mgd2hlbiBlcnJvciBleGlzdHNcclxuICAgICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBgYjY0YCwgYG91dGZpbGVgLCBhbmQgYHR5cGVgIGZvciBhIGNoYXJ0XHJcbiAgICAgICAgY29uc3QgeyBiNjQsIG91dGZpbGUsIHR5cGUgfSA9IGRhdGEub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlc3VsdFxyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpZiAoYjY0KSB7XHJcbiAgICAgICAgICAgIC8vIEFzIGEgQmFzZTY0IHN0cmluZyB0byBhIHR4dCBmaWxlXHJcbiAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgYCR7b3V0ZmlsZS5zcGxpdCgnLicpLnNoaWZ0KCkgfHwgJ2NoYXJ0J30udHh0YCxcclxuICAgICAgICAgICAgICBnZXRCYXNlNjQoZGF0YS5yZXN1bHQsIHR5cGUpXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAvLyBBcyBhIGNvcnJlY3QgaW1hZ2UgZm9ybWF0XHJcbiAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgb3V0ZmlsZSB8fCBgY2hhcnQuJHt0eXBlfWAsXHJcbiAgICAgICAgICAgICAgdHlwZSAhPT0gJ3N2ZycgPyBCdWZmZXIuZnJvbShkYXRhLnJlc3VsdCwgJ2Jhc2U2NCcpIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICA1MDBcclxuICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gS2lsbCBwb29sIGFuZCBjbG9zZSBicm93c2VyIGFmdGVyIGZpbmlzaGluZyBzaW5nbGUgZXhwb3J0XHJcbiAgICAgICAgYXdhaXQga2lsbFBvb2woKTtcclxuICAgICAgfVxyXG4gICAgKTtcclxuICB9IGVsc2Uge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NoYXJ0XSBObyBleHBlY3RlZCBgZXhwb3J0YCBvcHRpb25zIHdlcmUgZm91bmQuIFBsZWFzZSBwcm92aWRlIG9uZSBvZiB0aGUgZm9sbG93aW5nIG9wdGlvbnM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuJyxcclxuICAgICAgNDAwXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIGJhdGNoIGV4cG9ydCBwcm9jZXNzIGZvciBtdWx0aXBsZSBjaGFydHMgYmFzZWQgb24gaW5mb3JtYXRpb25cclxuICogcHJvdmlkZWQgaW4gdGhlIGBiYXRjaGAgb3B0aW9uLiBUaGUgYGJhdGNoYCBpcyBhIHN0cmluZyBpbiB0aGUgZm9sbG93aW5nXHJcbiAqIGZvcm1hdDogXCJpbmZpbGUxLmpzb249b3V0ZmlsZTEucG5nO2luZmlsZTIuanNvbj1vdXRmaWxlMi5wbmc7Li4uXCIuIFJlc3VsdHNcclxuICogYXJlIHNhdmVkIHRvIHRoZSBzcGVjaWZpZWQgb3V0cHV0IGZpbGVzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGJhdGNoRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIEl0IG11c3QgY29udGFpbiB0aGUgYGJhdGNoYCBvcHRpb24gZnJvbVxyXG4gKiB0aGUgYGV4cG9ydGAgc2VjdGlvbiB0byBnZW5lcmF0ZSB2YWxpZCBpbWFnZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBiYXRjaCBleHBvcnRcclxuICogcHJvY2Vzc2VzIGFyZSBjb21wbGV0ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIGFueSBvZiB0aGUgYmF0Y2ggZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYmF0Y2hFeHBvcnQob3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBleHBvcnQgbWFrZXMgc2Vuc2VcclxuICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLmV4cG9ydCAmJiBvcHRpb25zLmV4cG9ydC5iYXRjaCkge1xyXG4gICAgLy8gQW4gYXJyYXkgZm9yIGNvbGxlY3RpbmcgYmF0Y2ggZXhwb3J0c1xyXG4gICAgY29uc3QgYmF0Y2hGdW5jdGlvbnMgPSBbXTtcclxuXHJcbiAgICAvLyBTcGxpdCBhbmQgcGFpciB0aGUgYGJhdGNoYCBhcmd1bWVudHNcclxuICAgIGZvciAobGV0IHBhaXIgb2Ygb3B0aW9ucy5leHBvcnQuYmF0Y2guc3BsaXQoJzsnKSB8fCBbXSkge1xyXG4gICAgICBwYWlyID0gcGFpci5zcGxpdCgnPScpO1xyXG4gICAgICBpZiAocGFpci5sZW5ndGggPT09IDIpIHtcclxuICAgICAgICBiYXRjaEZ1bmN0aW9ucy5wdXNoKFxyXG4gICAgICAgICAgc3RhcnRFeHBvcnQoXHJcbiAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICBleHBvcnQ6IHtcclxuICAgICAgICAgICAgICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICAgICAgICAgICAgaW5maWxlOiBwYWlyWzBdLFxyXG4gICAgICAgICAgICAgICAgb3V0ZmlsZTogcGFpclsxXVxyXG4gICAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgICAgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgICAgICAgIG91dGZpbGUsXHJcbiAgICAgICAgICAgICAgICAgICAgdHlwZSAhPT0gJ3N2ZydcclxuICAgICAgICAgICAgICAgICAgICAgID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKVxyXG4gICAgICAgICAgICAgICAgICAgICAgOiBkYXRhLnJlc3VsdFxyXG4gICAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAgICAgICAgICdbY2hhcnRdIEVycm9yIHdoaWxlIHNhdmluZyBhIGNoYXJ0LicsXHJcbiAgICAgICAgICAgICAgICAgIDUwMFxyXG4gICAgICAgICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICApXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMiwgJ1tjaGFydF0gTm8gY29ycmVjdCBwYWlyIGZvdW5kIGZvciB0aGUgYmF0Y2ggZXhwb3J0LicpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQXdhaXQgYWxsIGV4cG9ydHMgYXJlIGRvbmVcclxuICAgIGNvbnN0IGJhdGNoUmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChiYXRjaEZ1bmN0aW9ucyk7XHJcblxyXG4gICAgLy8gS2lsbCBwb29sIGFuZCBjbG9zZSBicm93c2VyIGFmdGVyIGZpbmlzaGluZyBiYXRjaCBleHBvcnRcclxuICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcblxyXG4gICAgLy8gTG9nIGVycm9ycyBpZiBmb3VuZFxyXG4gICAgYmF0Y2hSZXN1bHRzLmZvckVhY2goKHJlc3VsdCwgaW5kZXgpID0+IHtcclxuICAgICAgLy8gTG9nIHRoZSBlcnJvciB3aXRoIHN0YWNrIGFib3V0IHRoZSBzcGVjaWZpYyBiYXRjaCBleHBvcnRcclxuICAgICAgaWYgKHJlc3VsdC5yZWFzb24pIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAxLFxyXG4gICAgICAgICAgcmVzdWx0LnJlYXNvbixcclxuICAgICAgICAgIGBbY2hhcnRdIEJhdGNoIGV4cG9ydCBudW1iZXIgJHtpbmRleCArIDF9IGNvdWxkIG5vdCBiZSBjb3JyZWN0bHkgY29tcGxldGVkLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGVsc2Uge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NoYXJ0XSBObyBleHBlY3RlZCBgZXhwb3J0YCBvcHRpb25zIHdlcmUgZm91bmQuIFBsZWFzZSBwcm92aWRlIHRoZSBgYmF0Y2hgIG9wdGlvbiB0byBnZW5lcmF0ZSB2YWxpZCBpbWFnZXMuJyxcclxuICAgICAgNDAwXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhbiBleHBvcnQgcHJvY2Vzcy4gVGhlIGBleHBvcnRpbmdPcHRpb25zYCBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXRcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgcGFydGlhbFxyXG4gKiBvcHRpb25zIGFyZSBwcm92aWRlZCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICpcclxuICogVGhlIGBlbmRDYWxsYmFja2AgZnVuY3Rpb24gaXMgaW52b2tlZCB1cG9uIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBleHBvcnQsXHJcbiAqIGVpdGhlciBzdWNjZXNzZnVsbHkgb3Igd2l0aCBhbiBlcnJvci4gVGhlIGBlcnJvcmAgb2JqZWN0IGlzIHByb3ZpZGVkXHJcbiAqIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIGNvbnRhaW5pbmdcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5XHJcbiAqIGFuZCB0aGUgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0aW5nT3B0aW9ucyAtIFRoZSBgZXhwb3J0aW5nT3B0aW9uc2Agb2JqZWN0LCB3aGljaFxyXG4gKiBzaG91bGQgaW5jbHVkZSBzZXR0aW5ncyBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuXHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJZiB0aGUgcHJvdmlkZWRcclxuICogb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZW5kQ2FsbGJhY2sgLSBUaGUgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCB1cG9uXHJcbiAqIGZpbmFsaXppbmcgdGhlIGV4cG9ydCBwcm9jZXNzIG9yIHVwb24gZW5jb3VudGVyaW5nIGFuIGVycm9yLiBUaGUgZmlyc3RcclxuICogYXJndW1lbnQgaXMgdGhlIGBlcnJvcmAgb2JqZWN0LCBhbmQgdGhlIHNlY29uZCBhcmd1bWVudCBpcyB0aGUgYGRhdGFgIG9iamVjdCxcclxuICogd2hpY2ggaW5jbHVkZXMgdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgXHJcbiAqIHByb3BlcnR5IGFuZCB0aGUgZnVsbCBzZXQgb2Ygb3B0aW9ucyBpbiB0aGUgYG9wdGlvbnNgIHByb3BlcnR5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gVGhpcyBmdW5jdGlvbiBkb2VzIG5vdCByZXR1cm4gYSB2YWx1ZSBkaXJlY3RseS5cclxuICogSW5zdGVhZCwgaXQgY29tbXVuaWNhdGVzIHJlc3VsdHMgdmlhIHRoZSBgZW5kQ2FsbGJhY2tgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYSBwcm9ibGVtIHdpdGhcclxuICogcHJvY2Vzc2luZyBpbnB1dCBvZiBhbnkgdHlwZS4gVGhlIGVycm9yIGlzIHBhc3NlZCBpbnRvIHRoZSBgZW5kQ2FsbGJhY2tgXHJcbiAqIGZ1bmN0aW9uIGFuZCBwcm9jZXNzZWQgdGhlcmUuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc3RhcnRFeHBvcnQoZXhwb3J0aW5nT3B0aW9ucywgZW5kQ2FsbGJhY2spIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgb3B0aW9ucyBpcyBhbiBvYmplY3RcclxuICAgIGlmICghaXNPYmplY3QoZXhwb3J0aW5nT3B0aW9ucykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgcHJvdmlkZWQgYGV4cG9ydGluZ09wdGlvbnNgLiBOZWVkcyB0byBiZSBhbiBvYmplY3QuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBNZXJnZSBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gdGhlIGNvcHkgb2YgdGhlIGluc3RhbmNlIG9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKFxyXG4gICAgICB7XHJcbiAgICAgICAgZXhwb3J0OiBleHBvcnRpbmdPcHRpb25zLmV4cG9ydCxcclxuICAgICAgICBjdXN0b21Mb2dpYzogZXhwb3J0aW5nT3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICByZXR1cm4gcG9zdFdvcmsob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBmb3IgY2hhcnQgZXhwb3J0cyBiYXNlZFxyXG4gKiBvbiB0aGUgcHJvdmlkZWQgZXhwb3J0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICogMS4gVGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBgc2NhbGVgIGZyb20gdGhlIGBleHBvcnRPcHRpb25zYC5cclxuICogMi4gT3B0aW9ucyBmcm9tIHRoZSBjaGFydCBjb25maWd1cmF0aW9uIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBnbG9iYWwgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgdGhlbWUgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCBzZWN0aW9ucykuXHJcbiAqIDUuIEZhbGxiYWNrIGRlZmF1bHQgdmFsdWVzIChgaGVpZ2h0ID0gNDAwYCwgYHdpZHRoID0gNjAwYCwgYHNjYWxlID0gMWApLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZpbmRDaGFydFNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb2RlIGV4ZWN1dGlvblxyXG4gKiBpcyBub3QgYWxsb3dlZCBidXQgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYXJlIHN0aWxsIHByb3ZpZGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgLy8gSW4gY2FzZSBvZiBhbGxvd2luZyBjb2RlIGV4ZWN1dGlvblxyXG4gIGlmIChhbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIC8vIFByb2Nlc3MgdGhlIGByZXNvdXJjZXNgIG9wdGlvblxyXG4gICAgaWYgKHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID09PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBDdXN0b20gc3RyaW5naWZpZWQgcmVzb3VyY2VzXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gTG9hZCB0aGUgZGVmYXVsdCBvbmVcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoJ3Jlc291cmNlcy5qc29uJyksICd1dGY4JyksXHJcbiAgICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IGByZXNvdXJjZXMuanNvbmAgZmlsZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGN1c3RvbSBjb2RlIGFuZCB3cmFwIGFyb3VuZCBpdCBpbiBhIHNlbGYgaW52b2tpbmcgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1tjaGFydF0gVGhlIGBjdXN0b21Db2RlYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gVHJ5IHRvIGxvYWQgY2FsbGJhY2sgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2ssXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY2FsbGJhY2tgIGNhbm5vdCBiZSBsb2FkZWQuJyk7XHJcblxyXG4gICAgICAvLyBJbiBjYXNlIG9mIGFuIGVycm9yLCBzZXQgdGhlIG9wdGlvbiB3aXRoIG51bGxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGN1c3RvbUNvZGVgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGN1c3RvbUNvZGVgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGNhbGxiYWNrYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgY2FsbGJhY2tgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHJlc291cmNlc2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gSWYgdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIGZsYWcgaXMgc2V0IHRvIGZhbHNlLCB3ZSBzaG91bGQgcmVmdXNlXHJcbiAgICAvLyB0aGUgdXNhZ2Ugb2YgdGhlIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgLCBhbmQgYGN1c3RvbUNvZGVgIG9wdGlvbnMuXHJcbiAgICAvLyBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbCByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LlxyXG4gICAgaWYgKFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFJlc2V0IGFsbCBjdXN0b20gY29kZSBvcHRpb25zXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcblxyXG4gICAgICAvLyBTZW5kIGEgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydCB0aGVzZSBzZXR0aW5nc1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnLCBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gLFxyXG4gICAgICAgIDQwM1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZnJvbSB0aGUgYHJlc291cmNlc2Agb3B0aW9uIGZvciBleHBvcnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFtyZXNvdXJjZXM9bnVsbF0gLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuXHJcbiAqIENhbiBiZSBlaXRoZXIgYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiwgYSBwYXRoIHRvIGEgSlNPTiBmaWxlLFxyXG4gKiBvciBudWxsLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgbnVsbGAuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2FkaW5nXHJcbiAqIHJlc291cmNlcyBmcm9tIGZpbGVzIGlzIGFsbG93ZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGhhbmRsZWQgcmVzb3VyY2VzIG9yIG51bGwgaWYgbm8gdmFsaWQgcmVzb3VyY2VzXHJcbiAqIGFyZSBmb3VuZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgcmVzb3VyY2VzID0gbnVsbCxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIExpc3Qgb2YgYWxsb3dlZCBzZWN0aW9ucyBpbiB0aGUgcmVzb3VyY2VzIEpTT05cclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKHJlc291cmNlcyksICd1dGY4JyksXHJcbiAgICAgICAgZmFsc2UsXHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxyXG4gICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhyZXNvdXJjZXMsIGZhbHNlLCBhbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIGZpbGVzIHNlY3Rpb25cclxuICAgIGlmIChoYW5kbGVkUmVzb3VyY2VzICYmICFhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaWx0ZXIgZnJvbSB1bm5lY2Vzc2FyeSBwcm9wZXJ0aWVzXHJcbiAgZm9yIChjb25zdCBwcm9wTmFtZSBpbiBoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXNbcHJvcE5hbWVdO1xyXG4gICAgfSBlbHNlIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiBhbGxvd2VkIHByb3BlcnRpZXMgaXMgcHJlc2VudFxyXG4gIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgbG9hZGluZyBhbmQgdmFsaWRhdGlvbiBvZiB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYFxyXG4gKiBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIElmIHRoZSBvcHRpb24gaXMgYSBzdHJpbmcgYW5kIHJlZmVyZW5jZXMgYSBKU09OIGZpbGVcclxuICogKHdoZW4gdGhlIGBhbGxvd0ZpbGVSZXNvdXJjZXNgIGlzIHRydWUpLCBpdCByZWFkcyBhbmQgcGFyc2VzIHRoZSBmaWxlLlxyXG4gKiBPdGhlcndpc2UsIGl0IGF0dGVtcHRzIHRvIHBhcnNlIHRoZSBzdHJpbmcgb3Igb2JqZWN0IGFzIEpTT04uIElmIGFueSBlcnJvcnNcclxuICogb2NjdXIgZHVyaW5nIHRoaXMgcHJvY2VzcywgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC4gSWYgdGhlcmUgaXMgYW4gZXJyb3JcclxuICogbG9hZGluZyBvciBwYXJzaW5nIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3IgYHRoZW1lT3B0aW9uc2AsIHRoZSBlcnJvciBpcyBsb2dnZWRcclxuICogYW5kIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBlbmFibGluZyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzcGVjaWZpZWQgRXhwcmVzcyBhcHAuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXRcclxuICogdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCByYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWQgYW5kIHRoZSBhcHAgZXhpc3RzXHJcbiAgICBpZiAoYXBwICYmIHJhdGVMaW1pdGluZ09wdGlvbnMuZW5hYmxlKSB7XHJcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPVxyXG4gICAgICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgICAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICAgICAgd2luZG93OiByYXRlTGltaXRpbmdPcHRpb25zLndpbmRvdyB8fCAxLFxyXG4gICAgICAgIG1heFJlcXVlc3RzOiByYXRlTGltaXRpbmdPcHRpb25zLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgICAgIGRlbGF5OiByYXRlTGltaXRpbmdPcHRpb25zLmRlbGF5IHx8IDAsXHJcbiAgICAgICAgdHJ1c3RQcm94eTogcmF0ZUxpbWl0aW5nT3B0aW9ucy50cnVzdFByb3h5IHx8IGZhbHNlLFxyXG4gICAgICAgIHNraXBLZXk6IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcEtleSB8fCBudWxsLFxyXG4gICAgICAgIHNraXBUb2tlbjogcmF0ZUxpbWl0aW5nT3B0aW9ucy5za2lwVG9rZW4gfHwgbnVsbFxyXG4gICAgICB9O1xyXG5cclxuICAgICAgLy8gU2V0IGlmIGJlaGluZCBhIHByb3h5XHJcbiAgICAgIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICAgICAgYXBwLmVuYWJsZSgndHJ1c3QgcHJveHknKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gICAgICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcclxuICAgICAgICAvLyBUaW1lIGZyYW1lIGZvciB3aGljaCByZXF1ZXN0cyBhcmUgY2hlY2tlZCBhbmQgcmVtZW1iZXJlZFxyXG4gICAgICAgIHdpbmRvd01zOiByYXRlT3B0aW9ucy53aW5kb3cgKiA2MCAqIDEwMDAsXHJcbiAgICAgICAgLy8gTGltaXQgZWFjaCBJUCB0byAxMDAgcmVxdWVzdHMgcGVyIGB3aW5kb3dNc2BcclxuICAgICAgICBsaW1pdDogcmF0ZU9wdGlvbnMubWF4UmVxdWVzdHMsXHJcbiAgICAgICAgLy8gRGlzYWJsZSBkZWxheWluZywgZnVsbCBzcGVlZCB1bnRpbCB0aGUgbWF4IGxpbWl0IGlzIHJlYWNoZWRcclxuICAgICAgICBkZWxheU1zOiByYXRlT3B0aW9ucy5kZWxheSxcclxuICAgICAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlLmZvcm1hdCh7XHJcbiAgICAgICAgICAgIGpzb246ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZSB9KTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgICAgIC8vIEFsbG93IGJ5cGFzc2luZyB0aGUgbGltaXRlciBpZiBhIHZhbGlkIGtleS90b2tlbiBoYXMgYmVlbiBzZW50XHJcbiAgICAgICAgICBpZiAoXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IG51bGwgJiZcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkuYWNjZXNzX3Rva2VuID09PSByYXRlT3B0aW9ucy5za2lwVG9rZW5cclxuICAgICAgICAgICkge1xyXG4gICAgICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcblxyXG4gICAgICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gICAgICBhcHAudXNlKGxpbWl0ZXIpO1xyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtyYXRlIGxpbWl0aW5nXSBFbmFibGVkIHJhdGUgbGltaXRpbmcgd2l0aCAke3JhdGVPcHRpb25zLm1heFJlcXVlc3RzfSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcmF0ZSBsaW1pdGluZ10gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciB2YWxpZGF0aW5nIGluY29taW5nIEhUVFAgcmVxdWVzdHNcclxuICogaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi4gVGhpcyBtb2R1bGUgZW5zdXJlcyB0aGF0IHJlcXVlc3RzIGNvbnRhaW5cclxuICogYXBwcm9wcmlhdGUgY29udGVudCB0eXBlcyBhbmQgdmFsaWQgcmVxdWVzdCBib2RpZXMsIGluY2x1ZGluZyBwcm9wZXIgSlNPTlxyXG4gKiBzdHJ1Y3R1cmVzIGFuZCBjaGFydCBkYXRhIGZvciBleHBvcnRzLiBJdCBjaGVja3MgZm9yIHBvdGVudGlhbCBpc3N1ZXMgc3VjaFxyXG4gKiBhcyBtaXNzaW5nIG9yIG1hbGZvcm1lZCBkYXRhLCBwcml2YXRlIHJhbmdlIFVSTHMgaW4gU1ZHIHBheWxvYWRzLCBhbmQgYWxsb3dzXHJcbiAqIGZvciBmbGV4aWJsZSBvcHRpb25zIHZhbGlkYXRpb24uIFRoZSBtaWRkbGV3YXJlIGxvZ3MgZGV0YWlsZWQgaW5mb3JtYXRpb25cclxuICogYW5kIGhhbmRsZXMgZXJyb3JzIHJlbGF0ZWQgdG8gaW5jb3JyZWN0IHBheWxvYWRzLCBjaGFydCBkYXRhLCBhbmQgcHJpdmF0ZSBVUkxcclxuICogdXNhZ2UuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uIH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpc09iamVjdEVtcHR5LCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgY29udGVudC10eXBlIGhlYWRlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBjb250ZW50LXR5cGVcclxuICogaXMgbm90IGNvcnJlY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSBjb250ZW50IHR5cGUgaGVhZGVyXHJcbiAgICBjb25zdCBjb250ZW50VHlwZSA9IHJlcXVlc3QuaGVhZGVyc1snY29udGVudC10eXBlJ10gfHwgJyc7XHJcblxyXG4gICAgLy8gQWxsb3cgb25seSBKU09OLCBVUkwtZW5jb2RlZCBhbmQgZm9ybSBkYXRhIHdpdGhvdXQgZmlsZXMgdHlwZXMgb2YgZGF0YVxyXG4gICAgaWYgKFxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL2pzb24nKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnbXVsdGlwYXJ0L2Zvcm0tZGF0YScpXHJcbiAgICApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbdmFsaWRhdGlvbl0gQ29udGVudC1UeXBlIG11c3QgYmUgYXBwbGljYXRpb24vanNvbiwgYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkLCBvciBtdWx0aXBhcnQvZm9ybS1kYXRhLicsXHJcbiAgICAgICAgNDE1XHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYHJlcXVlc3RCb2R5TWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIHJlcXVlc3QncyBib2R5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGJvZHkgaXMgbm90IGNvcnJlY3QuXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY2hhcnQgZGF0YSBmcm9tIHRoZSBib2R5XHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaW4gY2FzZSBvZiB0aGUgcHJpdmF0ZSByYW5nZVxyXG4gKiB1cmwgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGJvZHlcclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHV1aWQoKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgZW1wdHkuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGJvZHkgaXMgcmVxdWlyZWQuIFBsZWFzZSBlbnN1cmUgdGhhdCB5b3VyIENvbnRlbnQtVHlwZSBoZWFkZXIgaXMgY29ycmVjdC4gQWNjZXB0ZWQgdHlwZXMgYXJlICdhcHBsaWNhdGlvbi9qc29uJyBhbmQgJ211bHRpcGFydC9mb3JtLWRhdGEnLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uIGZvciB0aGUgc2VydmVyXHJcbiAgICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb24gPSBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKTtcclxuXHJcbiAgICAvLyBGaW5kIGEgY29ycmVjdCBjaGFydCBvcHRpb25zXHJcbiAgICBjb25zdCBpbnN0ciA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgLy8gVXNlIG9uZSBvZiB0aGUgYmVsb3dcclxuICAgICAgYm9keS5pbnN0ciB8fCBib2R5Lm9wdGlvbnMgfHwgYm9keS5pbmZpbGUgfHwgYm9keS5kYXRhLFxyXG4gICAgICAvLyBTdHJpbmdpZnkgb3B0aW9uc1xyXG4gICAgICB0cnVlLFxyXG4gICAgICAvLyBBbGxvdyBvciBkaXNhbGxvdyBmdW5jdGlvbnNcclxuICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRoZXJlIGlzIG5vIGNvcnJlY3QgY2hhcnQgZGF0YVxyXG4gICAgaWYgKGluc3RyID09PSBudWxsICYmICFib2R5LnN2Zykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIG1pc3NpbmcgY29ycmVjdCBjaGFydCBkYXRhIGZvciBleHBvcnQ6ICR7SlNPTi5zdHJpbmdpZnkoYm9keSl9LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGVzdCBvZiB4bGluazpocmVmIGVsZW1lbnRzIGZyb20gcGF5bG9hZCdzIFNWRyBmYWlsc1xyXG4gICAgaWYgKGJvZHkuc3ZnICYmIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoYm9keS5zdmcpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBTVkcgcG90ZW50aWFsbHkgY29udGFpbiBhdCBsZWFzdCBvbmUgZm9yYmlkZGVuIFVSTCBpbiAneGxpbms6aHJlZicgZWxlbWVudC4gUGxlYXNlIHJldmlldyB0aGUgU1ZHIGNvbnRlbnQgYW5kIGVuc3VyZSB0aGF0IGFsbCByZWZlcmVuY2VkIFVSTHMgY29tcGx5IHdpdGggc2VjdXJpdHkgcG9saWNpZXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3Qgb3B0aW9ucyBhbmQgc3RvcmUgcGFyc2VkIHN0cnVjdHVyZSBpbiB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC52YWxpZGF0ZWRPcHRpb25zID0ge1xyXG4gICAgICAvLyBTZXQgdGhlIGNyZWF0ZWQgSUQgYXMgYSBgcmVxdWVzdElkYCBwcm9wZXJ0eSBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICByZXF1ZXN0SWQsXHJcbiAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIHN2ZzogYm9keS5zdmcsXHJcbiAgICAgICAgb3V0ZmlsZTpcclxuICAgICAgICAgIGJvZHkub3V0ZmlsZSB8fFxyXG4gICAgICAgICAgYCR7cmVxdWVzdC5wYXJhbXMuZmlsZW5hbWUgfHwgJ2NoYXJ0J30uJHtib2R5LnR5cGUgfHwgJ3BuZyd9YCxcclxuICAgICAgICB0eXBlOiBib2R5LnR5cGUsXHJcbiAgICAgICAgY29uc3RyOiBib2R5LmNvbnN0cixcclxuICAgICAgICBiNjQ6IGJvZHkuYjY0LFxyXG4gICAgICAgIG5vRG93bmxvYWQ6IGJvZHkubm9Eb3dubG9hZCxcclxuICAgICAgICBoZWlnaHQ6IGJvZHkuaGVpZ2h0LFxyXG4gICAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxyXG4gICAgICAgIHNjYWxlOiBib2R5LnNjYWxlLFxyXG4gICAgICAgIGdsb2JhbE9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkuZ2xvYmFsT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApLFxyXG4gICAgICAgIHRoZW1lT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS50aGVtZU9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKVxyXG4gICAgICB9LFxyXG4gICAgICBjdXN0b21Mb2dpYzoge1xyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvbixcclxuICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IGZhbHNlLFxyXG4gICAgICAgIGN1c3RvbUNvZGU6IGJvZHkuY3VzdG9tQ29kZSxcclxuICAgICAgICBjYWxsYmFjazogYm9keS5jYWxsYmFjayxcclxuICAgICAgICByZXNvdXJjZXM6IGlzQWxsb3dlZENvbmZpZyhib2R5LnJlc291cmNlcywgdHJ1ZSwgYWxsb3dDb2RlRXhlY3V0aW9uKVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIC8vIENhbGwgdGhlIG5leHQgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApIHtcclxuICAvLyBBZGQgY29udGVudCB0eXBlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIGNvbnRlbnRUeXBlTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCByZXF1ZXN0IGJvZHkgcmVxdWVzdCB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCByZXF1ZXN0Qm9keU1pZGRsZXdhcmUpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIHRoZSBleHBvcnQgcm91dGVzIGFuZCBsb2dpYyBmb3IgaGFuZGxpbmcgY2hhcnQgZXhwb3J0XHJcbiAqIHJlcXVlc3RzIGluIGFuIEV4cHJlc3Mgc2VydmVyLiBUaGlzIG1vZHVsZSBwcm9jZXNzZXMgaW5jb21pbmcgcmVxdWVzdHNcclxuICogdG8gZXhwb3J0IGNoYXJ0cyBpbiB2YXJpb3VzIGZvcm1hdHMgKGUuZy4gSlBFRywgUE5HLCBQREYsIFNWRykuIEl0IGludGVncmF0ZXNcclxuICogd2l0aCBIaWdoY2hhcnRzJyBjb3JlIGZ1bmN0aW9uYWxpdGllcyBhbmQgc3VwcG9ydHMgYm90aCBpbW1lZGlhdGUgZG93bmxvYWRcclxuICogcmVzcG9uc2VzIGFuZCBCYXNlNjQtZW5jb2RlZCBjb250ZW50IHJldHVybnMuIFRoZSBjb2RlIGFsc28gZmVhdHVyZXNcclxuICogYmVuY2htYXJraW5nIGZvciBwZXJmb3JtYW5jZSBtb25pdG9yaW5nLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHN0YXJ0RXhwb3J0IH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRCYXNlNjQsIG1lYXN1cmVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBSZXZlcnNlZCBNSU1FIHR5cGVzXHJcbmNvbnN0IHJldmVyc2VkTWltZSA9IHtcclxuICBwbmc6ICdpbWFnZS9wbmcnLFxyXG4gIGpwZWc6ICdpbWFnZS9qcGVnJyxcclxuICBnaWY6ICdpbWFnZS9naWYnLFxyXG4gIHBkZjogJ2FwcGxpY2F0aW9uL3BkZicsXHJcbiAgc3ZnOiAnaW1hZ2Uvc3ZnK3htbCdcclxufTtcclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleHBvcnQgcmVxdWVzdHMgZnJvbSB0aGUgY2xpZW50LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnQocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gU3RhcnQgY291bnRpbmcgdGltZSBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gSW4gY2FzZSB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQsIGZvcmNlIHRvIGFib3J0IGZ1cnRoZXIgYWN0aW9uc1xyXG4gICAgbGV0IGNvbm5lY3Rpb25BYm9ydGVkID0gZmFsc2U7XHJcbiAgICByZXF1ZXN0LnNvY2tldC5vbignY2xvc2UnLCAoaGFkRXJyb3JzKSA9PiB7XHJcbiAgICAgIGlmIChoYWRFcnJvcnMpIHtcclxuICAgICAgICBjb25uZWN0aW9uQWJvcnRlZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgb3B0aW9ucyBwcmV2aW91c2x5IHZhbGlkYXRlZCBpbiB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucztcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3QgaWRcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHJlcXVlc3RPcHRpb25zLnJlcXVlc3RJZDtcclxuXHJcbiAgICAvLyBJbmZvIGFib3V0IGFuIGluY29taW5nIHJlcXVlc3Qgd2l0aCBjb3JyZWN0IGRhdGFcclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3QuYCk7XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChyZXF1ZXN0T3B0aW9ucywgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgIC8vIFJlbW92ZSB0aGUgY2xvc2UgZXZlbnQgZnJvbSB0aGUgc29ja2V0XHJcbiAgICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBjb25uZWN0aW9uIHdhcyBjbG9zZWQsIGRvIG5vdGhpbmdcclxuICAgICAgaWYgKGNvbm5lY3Rpb25BYm9ydGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gYmVmb3JlIHRoZSBjaGFydCBmaW5pc2hlZCBwcm9jZXNzaW5nLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZXJyb3IsIGxvZyBpdCBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCBsb2cgdGhlIG1lc3NhZ2UgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKCFkYXRhIHx8ICFkYXRhLnJlc3VsdCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8XHJcbiAgICAgICAgICAgIHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHJlc3VsdCBpcyAke2RhdGEucmVzdWx0fS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBVbmV4cGVjdGVkIHJldHVybiBvZiB0aGUgZXhwb3J0IHJlc3VsdCBmcm9tIHRoZSBjaGFydCBnZW5lcmF0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciByZXF1ZXN0IGRhdGEuYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFJldHVybiB0aGUgcmVzdWx0IGluIGFuIGFwcHJvcHJpYXRlIGZvcm1hdFxyXG4gICAgICBpZiAoZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3MgdG9vayAke3JlcXVlc3RDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYHR5cGVgLCBgYjY0YCwgYG5vRG93bmxvYWRgLCBhbmQgYG91dGZpbGVgIGZyb20gb3B0aW9uc1xyXG4gICAgICAgIGNvbnN0IHsgdHlwZSwgYjY0LCBub0Rvd25sb2FkLCBvdXRmaWxlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBJZiBvbmx5IEJhc2U2NCBpcyByZXF1aXJlZCwgcmV0dXJuIGl0XHJcbiAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTZXQgY29ycmVjdCBjb250ZW50IHR5cGVcclxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XHJcblxyXG4gICAgICAgIC8vIERlY2lkZSB3aGV0aGVyIHRvIGRvd25sb2FkIG9yIG5vdCBjaGFydCBmaWxlXHJcbiAgICAgICAgaWYgKCFub0Rvd25sb2FkKSB7XHJcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KG91dGZpbGUpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgU1ZHLCByZXR1cm4gcGxhaW4gY29udGVudCwgb3RoZXJ3aXNlIGEgYjY0IHN0cmluZyBmcm9tIGEgYnVmZmVyXHJcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXHJcbiAgICAgICAgICA/IHJlc3BvbnNlLnNlbmQoZGF0YS5yZXN1bHQpXHJcbiAgICAgICAgICA6IHJlc3BvbnNlLnNlbmQoQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYGV4cG9ydGAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZXhwb3J0Um91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gZXhwb3J0Um91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy8nIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyBhdCB0aGUgcm9vdFxyXG4gICAqIGVuZHBvaW50LlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvJywgcmVxdWVzdEV4cG9ydCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy86ZmlsZW5hbWUnIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyB3aXRoXHJcbiAgICogYSBzcGVjaWZpZWQgZmlsZW5hbWUgcGFyYW1ldGVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgcmVxdWVzdEV4cG9ydCk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmVyIGhlYWx0aCBtb25pdG9yaW5nLCBpbmNsdWRpbmdcclxuICogdXB0aW1lLCBzdWNjZXNzIHJhdGVzLCBhbmQgb3RoZXIgc2VydmVyIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBnZXRQb29sSW5mb0pTT04gfSBmcm9tICcuLi8uLi9wb29sLmpzJztcclxuaW1wb3J0IHsgYWRkVGltZXIgfSBmcm9tICcuLi8uLi90aW1lci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0TmV3RGF0ZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vLyBTZXQgdGhlIHN0YXJ0IGRhdGUgb2YgdGhlIHNlcnZlclxyXG5jb25zdCBzZXJ2ZXJTdGFydFRpbWUgPSBuZXcgRGF0ZSgpO1xyXG5cclxuLy8gR2V0IHRoZSBgcGFja2FnZS5qc29uYCBjb250ZW50XHJcbmNvbnN0IHBhY2thZ2VGaWxlID0gSlNPTi5wYXJzZShcclxuICByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICdwYWNrYWdlLmpzb24nKSwgJ3V0ZjgnKVxyXG4pO1xyXG5cclxuLy8gQW4gYXJyYXkgZm9yIHN1Y2Nlc3MgcmF0ZSByYXRpb3NcclxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XHJcblxyXG4vLyBSZWNvcmQgZXZlcnkgbWludXRlXHJcbmNvbnN0IHJlY29yZEludGVydmFsID0gNjAgKiAxMDAwO1xyXG5cclxuLy8gMzAgbWludXRlc1xyXG5jb25zdCB3aW5kb3dTaXplID0gMzA7XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgYHN1Y2Nlc3NSYXRlc2BcclxuICogYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBBIG1vdmluZyBhdmVyYWdlIGZvciBzdWNjZXNzIHJhdGlvIG9mIHRoZSBzZXJ2ZXIgZXhwb3J0cy5cclxuICovXHJcbmZ1bmN0aW9uIF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCkge1xyXG4gIHJldHVybiBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIHRoZSBpbnRlcnZhbCByZXNwb25zaWJsZSBmb3IgY2FsY3VsYXRpbmcgY3VycmVudCBzdWNjZXNzIHJhdGUgcmF0aW9cclxuICogYW5kIGNvbGxlY3RzIHJlY29yZHMgdG8gdGhlIGBzdWNjZXNzUmF0ZXNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3N0YXJ0U3VjY2Vzc1JhdGVcclxuICpcclxuICogQHJldHVybnMge05vZGVKUy5UaW1lb3V0fSBJZCBvZiBhbiBpbnRlcnZhbC5cclxuICovXHJcbmZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlKCkge1xyXG4gIHJldHVybiBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICBjb25zdCBzdGF0cyA9IGdldFBvb2xTdGF0cygpO1xyXG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID1cclxuICAgICAgc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCA9PT0gMFxyXG4gICAgICAgID8gMVxyXG4gICAgICAgIDogKHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQgLyBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkKSAqIDEwMDtcclxuXHJcbiAgICBzdWNjZXNzUmF0ZXMucHVzaChzdWNjZXNzUmF0aW8pO1xyXG4gICAgaWYgKHN1Y2Nlc3NSYXRlcy5sZW5ndGggPiB3aW5kb3dTaXplKSB7XHJcbiAgICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xyXG4gICAgfVxyXG4gIH0sIHJlY29yZEludGVydmFsKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBoZWFsdGhgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGhlYWx0aFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGhlYWx0aFJvdXRlcyhhcHApIHtcclxuICAvLyBTdGFydCBwcm9jZXNzaW5nIHN1Y2Nlc3MgcmF0ZSByYXRpbyBpbnRlcnZhbCBhbmQgc2F2ZSBpdHMgaWQgdG8gdGhlIGFycmF5XHJcbiAgLy8gZm9yIHRoZSBncmFjZWZ1bCBjbGVhcmluZyBvbiBzaHV0ZG93biB3aXRoIGluamVjdGVkIGBhZGRUaW1lcmAgZnVudGlvblxyXG4gIGFkZFRpbWVyKF9zdGFydFN1Y2Nlc3NSYXRlKCkpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy9oZWFsdGgnIC0gQSByb3V0ZSBmb3IgZ2V0dGluZyB0aGUgYmFzaWMgc3RhdHMgb2YgdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KCcvaGVhbHRoJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1toZWFsdGhdIFJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgICAgY29uc3QgbW92aW5nQXZlcmFnZSA9IF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgICAvLyBTZW5kIHRoZSBzZXJ2ZXIncyBzdGF0aXN0aWNzXHJcbiAgICAgIHJlc3BvbnNlLnNlbmQoe1xyXG4gICAgICAgIC8vIFN0YXR1cyBhbmQgdGltZXNcclxuICAgICAgICBzdGF0dXM6ICdPSycsXHJcbiAgICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcclxuICAgICAgICB1cHRpbWU6IGAke01hdGguZmxvb3IoKGdldE5ld0RhdGVUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MCl9IG1pbnV0ZXNgLFxyXG5cclxuICAgICAgICAvLyBWZXJzaW9uc1xyXG4gICAgICAgIHNlcnZlclZlcnNpb246IHBhY2thZ2VGaWxlLnZlcnNpb24sXHJcbiAgICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IGdldEhpZ2hjaGFydHNWZXJzaW9uKCksXHJcblxyXG4gICAgICAgIC8vIEV4cG9ydHNcclxuICAgICAgICBhdmVyYWdlRXhwb3J0VGltZTogc3RhdHMudGltZVNwZW50QXZlcmFnZSxcclxuICAgICAgICBhdHRlbXB0ZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkLFxyXG4gICAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQsXHJcbiAgICAgICAgZmFpbGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0Ryb3BwZWQsXHJcbiAgICAgICAgc3VjZXNzUmF0aW86IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDAsXHJcblxyXG4gICAgICAgIC8vIFBvb2xcclxuICAgICAgICBwb29sOiBnZXRQb29sSW5mb0pTT04oKSxcclxuXHJcbiAgICAgICAgLy8gTW92aW5nIGF2ZXJhZ2VcclxuICAgICAgICBwZXJpb2QsXHJcbiAgICAgICAgbW92aW5nQXZlcmFnZSxcclxuICAgICAgICBtZXNzYWdlOlxyXG4gICAgICAgICAgaXNOYU4obW92aW5nQXZlcmFnZSkgfHwgIXN1Y2Nlc3NSYXRlcy5sZW5ndGhcclxuICAgICAgICAgICAgPyAnVG9vIGVhcmx5IHRvIHJlcG9ydC4gTm8gZXhwb3J0cyBtYWRlIHlldC4gUGxlYXNlIGNoZWNrIGJhY2sgc29vbi4nXHJcbiAgICAgICAgICAgIDogYExhc3QgJHtwZXJpb2R9IG1pbnV0ZXMgaGFkIGEgc3VjY2VzcyByYXRlIG9mICR7bW92aW5nQXZlcmFnZS50b0ZpeGVkKDIpfSUuYCxcclxuXHJcbiAgICAgICAgLy8gU1ZHIGFuZCBKU09OIGV4cG9ydHNcclxuICAgICAgICBzdmdFeHBvcnRzOiBzdGF0cy5leHBvcnRzRnJvbVN2ZyxcclxuICAgICAgICBqc29uRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zLFxyXG4gICAgICAgIHN2Z0V4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyxcclxuICAgICAgICBqc29uRXhwb3J0c0F0dGVtcHRzOiBzdGF0cy5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0c1xyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciBzZXJ2aW5nIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXJcclxuICogd2hlbiBlbmFibGVkLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHVpYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1aVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHVpUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIEdFVCAnLycgLSBBIHJvdXRlIGZvciBhIFVJIHdoZW4gZW5hYmxlZCBvbiB0aGUgZXhwb3J0IHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KGdldE9wdGlvbnMoKS51aS5yb3V0ZSB8fCAnLycsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdWldIFJldHVybmluZyBVSSBmb3IgdGhlIGV4cG9ydC4nKTtcclxuXHJcbiAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwge1xyXG4gICAgICAgIGFjY2VwdFJhbmdlczogZmFsc2VcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3IgdXBkYXRpbmcgdGhlIEhpZ2hjaGFydHMgdmVyc2lvblxyXG4gKiBvbiB0aGUgc2VydmVyLCB3aXRoIGF1dGhlbnRpY2F0aW9uIGFuZCB2YWxpZGF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGdldEhpZ2hjaGFydHNWZXJzaW9uLCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHZlcnNpb25fY2hhbmdlYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nIC0gQSByb3V0ZSBmb3IgY2hhbmdpbmdcclxuICAgKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicsIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdmVyc2lvbl0gQ2hhbmdpbmcgSGlnaGNoYXJ0cyB2ZXJzaW9uLicpO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBkaXJlY3RseSBmcm9tIGVudnNcclxuICAgICAgY29uc3QgYWRtaW5Ub2tlbiA9IGVudnMuSElHSENIQVJUU19BRE1JTl9UT0tFTjtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBleGlzdGVuY2Ugb2YgdGhlIHRva2VuXHJcbiAgICAgIGlmICghYWRtaW5Ub2tlbiB8fCAhYWRtaW5Ub2tlbi5sZW5ndGgpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIFRoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBmcm9tIHRoZSBoYy1hdXRoIGhlYWRlclxyXG4gICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbdmVyc2lvbl0gSW52YWxpZCBvciBtaXNzaW5nIHRva2VuOiBTZXQgdGhlIHRva2VuIGluIHRoZSBoYy1hdXRoIGhlYWRlci4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xyXG4gICAgICBsZXQgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XHJcbiAgICAgIGlmIChuZXdWZXJzaW9uKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFVwZGF0ZSB2ZXJzaW9uXHJcbiAgICAgICAgICBhd2FpdCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICBgW3ZlcnNpb25dIFZlcnNpb24gY2hhbmdlOiAke2Vycm9yLm1lc3NhZ2V9YCxcclxuICAgICAgICAgICAgNDAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFN1Y2Nlc3NcclxuICAgICAgICByZXNwb25zZS5zdGF0dXMoMjAwKS5zZW5kKHtcclxuICAgICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcclxuICAgICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG4gICAgICAgICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIEhpZ2hjaGFydHMgdG8gdmVyc2lvbjogJHtuZXdWZXJzaW9ufS5gXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gTm8gdmVyc2lvbiBzcGVjaWZpZWRcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1t2ZXJzaW9uXSBObyBuZXcgdmVyc2lvbiBzdXBwbGllZC4nLCA0MDApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEEgbW9kdWxlIHRoYXQgc2V0cyB1cCBhbmQgbWFuYWdlcyBIVFRQIGFuZCBIVFRQUyBzZXJ2ZXJzXHJcbiAqIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBoYW5kbGVzIHNlcnZlciBpbml0aWFsaXphdGlvbixcclxuICogY29uZmlndXJhdGlvbiwgZXJyb3IgaGFuZGxpbmcsIG1pZGRsZXdhcmUgc2V0dXAsIHJvdXRlIGRlZmluaXRpb24sIGFuZCByYXRlXHJcbiAqIGxpbWl0aW5nLiBUaGUgbW9kdWxlIGV4cG9ydHMgZnVuY3Rpb25zIHRvIHN0YXJ0LCBzdG9wLCBhbmQgbWFuYWdlIHNlcnZlclxyXG4gKiBpbnN0YW5jZXMsIGFzIHdlbGwgYXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIGRlZmluaW5nIHJvdXRlcyBhbmQgYXR0YWNoaW5nXHJcbiAqIG1pZGRsZXdhcmVzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlIH0gZnJvbSAnZnMvcHJvbWlzZXMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgY29ycyBmcm9tICdjb3JzJztcclxuaW1wb3J0IGV4cHJlc3MgZnJvbSAnZXhwcmVzcyc7XHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5pbXBvcnQgbXVsdGVyIGZyb20gJ211bHRlcic7XHJcblxyXG5pbXBvcnQgeyB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGdldEFic29sdXRlUGF0aCB9IGZyb20gJy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBlcnJvck1pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy9lcnJvci5qcyc7XHJcbmltcG9ydCByYXRlTGltaXRpbmdNaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzJztcclxuaW1wb3J0IHZhbGlkYXRpb25NaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvdmFsaWRhdGlvbi5qcyc7XHJcblxyXG5pbXBvcnQgZXhwb3J0Um91dGVzIGZyb20gJy4vcm91dGVzL2V4cG9ydC5qcyc7XHJcbmltcG9ydCBoZWFsdGhSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvaGVhbHRoLmpzJztcclxuaW1wb3J0IHVpUm91dGVzIGZyb20gJy4vcm91dGVzL3VpLmpzJztcclxuaW1wb3J0IHZlcnNpb25DaGFuZ2VSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvdmVyc2lvbkNoYW5nZS5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIEFycmF5IG9mIGFuIGFjdGl2ZSBzZXJ2ZXJzXHJcbmNvbnN0IGFjdGl2ZVNlcnZlcnMgPSBuZXcgTWFwKCk7XHJcblxyXG4vLyBDcmVhdGUgZXhwcmVzcyBhcHBcclxuY29uc3QgYXBwID0gZXhwcmVzcygpO1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhbiBIVFRQIGFuZC9vciBIVFRQUyBzZXJ2ZXIgYmFzZWQgb24gdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24uXHJcbiAqIFRoZSBgc2VydmVyT3B0aW9uc2Agb2JqZWN0IGNvbnRhaW5zIHNlcnZlci1yZWxhdGVkIHByb3BlcnRpZXMgKHJlZmVyXHJcbiAqIHRvIHRoZSBgc2VydmVyYCBzZWN0aW9uIGluIHRoZSBgLi9saWIvc2NoZW1hcy9jb25maWcuanNgIGZpbGUgZm9yIGRldGFpbHMpLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHN0YXJ0U2VydmVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHNlcnZlcmBcclxuICogb3B0aW9ucy4gVGhpcyBvYmplY3QgbWF5IGluY2x1ZGUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiB0aGUgYHNlcnZlcmBcclxuICogb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgZGVmYXVsdFxyXG4gKiB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBlaXRoZXJcclxuICogbm90IGVuYWJsZWQgb3Igbm8gdmFsaWQgRXhwcmVzcyBhcHAgaXMgZm91bmQsIHNpZ25hbGluZyB0aGUgZW5kIG9mIHRoZVxyXG4gKiBmdW5jdGlvbidzIGV4ZWN1dGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBzZXJ2ZXIgY2Fubm90XHJcbiAqIGJlIGNvbmZpZ3VyZWQgYW5kIHN0YXJ0ZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc3RhcnRTZXJ2ZXIoc2VydmVyT3B0aW9ucykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIHNlcnZlcjogc2VydmVyT3B0aW9uc1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gVXNlIHZhbGlkYXRlZCBvcHRpb25zXHJcbiAgICBzZXJ2ZXJPcHRpb25zID0gb3B0aW9ucy5zZXJ2ZXI7XHJcblxyXG4gICAgLy8gU3RvcCBpZiBub3QgZW5hYmxlZFxyXG4gICAgaWYgKCFzZXJ2ZXJPcHRpb25zLmVuYWJsZSB8fCAhYXBwKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3NlcnZlcl0gU2VydmVyIGNhbm5vdCBiZSBzdGFydGVkIChub3QgZW5hYmxlZCBvciBubyBjb3JyZWN0IEV4cHJlc3MgYXBwIGZvdW5kKS4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRvbyBiaWcgbGltaXRzIGxlYWQgdG8gdGltZW91dHMgaW4gdGhlIGV4cG9ydCBwcm9jZXNzIHdoZW5cclxuICAgIC8vIHRoZSByYXN0ZXJpemF0aW9uIHRpbWVvdXQgaXMgc2V0IHRvbyBsb3dcclxuICAgIGNvbnN0IHVwbG9hZExpbWl0Qnl0ZXMgPSBzZXJ2ZXJPcHRpb25zLnVwbG9hZExpbWl0ICogMTAyNCAqIDEwMjQ7XHJcblxyXG4gICAgLy8gTWVtb3J5IHN0b3JhZ2UgZm9yIG11bHRlciBwYWNrYWdlXHJcbiAgICBjb25zdCBzdG9yYWdlID0gbXVsdGVyLm1lbW9yeVN0b3JhZ2UoKTtcclxuXHJcbiAgICAvLyBFbmFibGUgcGFyc2luZyBvZiBmb3JtIGRhdGEgKGZpbGVzKSB3aXRoIG11bHRlciBwYWNrYWdlXHJcbiAgICBjb25zdCB1cGxvYWQgPSBtdWx0ZXIoe1xyXG4gICAgICBzdG9yYWdlLFxyXG4gICAgICBsaW1pdHM6IHtcclxuICAgICAgICBmaWVsZFNpemU6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gRGlzYWJsZSB0aGUgWC1Qb3dlcmVkLUJ5IGhlYWRlclxyXG4gICAgYXBwLmRpc2FibGUoJ3gtcG93ZXJlZC1ieScpO1xyXG5cclxuICAgIC8vIEVuYWJsZSBDT1JTIHN1cHBvcnRcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGNvcnMoe1xyXG4gICAgICAgIG1ldGhvZHM6IFsnUE9TVCcsICdHRVQnLCAnT1BUSU9OUyddXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldHRpbmcgYSBsb3Qgb2YgYFJhbmdlTm90U2F0aXNmaWFibGVFcnJvcmAgZXhjZXB0aW9ucyAoZXZlbiB0aG91Z2ggdGhpc1xyXG4gICAgLy8gaXMgYSBkZXByZWNhdGVkIG9wdGlvbnMsIGxldCdzIHRyeSB0byBzZXQgaXQgdG8gZmFsc2UpXHJcbiAgICBhcHAudXNlKChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgICByZXNwb25zZS5zZXQoJ0FjY2VwdC1SYW5nZXMnLCAnbm9uZScpO1xyXG4gICAgICBuZXh0KCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBFbmFibGUgYm9keSBwYXJzZXIgZm9yIEpTT04gZGF0YVxyXG4gICAgYXBwLnVzZShcclxuICAgICAgZXhwcmVzcy5qc29uKHtcclxuICAgICAgICBsaW1pdDogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBFbmFibGUgYm9keSBwYXJzZXIgZm9yIFVSTC1lbmNvZGVkIGZvcm0gZGF0YVxyXG4gICAgYXBwLnVzZShcclxuICAgICAgZXhwcmVzcy51cmxlbmNvZGVkKHtcclxuICAgICAgICBleHRlbmRlZDogdHJ1ZSxcclxuICAgICAgICBsaW1pdDogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBVc2Ugb25seSBub24tZmlsZSBtdWx0aXBhcnQgZm9ybSBmaWVsZHNcclxuICAgIGFwcC51c2UodXBsb2FkLm5vbmUoKSk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHN0YXRpYyBmb2xkZXIncyByb3V0ZVxyXG4gICAgYXBwLnVzZShleHByZXNzLnN0YXRpYyhqb2luKF9fZGlybmFtZSwgJ3B1YmxpYycpKSk7XHJcblxyXG4gICAgLy8gTGlzdGVuIEhUVFAgc2VydmVyXHJcbiAgICBpZiAoIXNlcnZlck9wdGlvbnMuc3NsLmZvcmNlKSB7XHJcbiAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQKVxyXG4gICAgICBjb25zdCBodHRwU2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoYXBwKTtcclxuXHJcbiAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAvLyBMaXN0ZW5cclxuICAgICAgaHR0cFNlcnZlci5saXN0ZW4oc2VydmVyT3B0aW9ucy5wb3J0LCBzZXJ2ZXJPcHRpb25zLmhvc3QsICgpID0+IHtcclxuICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUCBzZXJ2ZXJcclxuICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJPcHRpb25zLnBvcnQsIGh0dHBTZXJ2ZXIpO1xyXG5cclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFN0YXJ0ZWQgSFRUUCBzZXJ2ZXIgb24gJHtzZXJ2ZXJPcHRpb25zLmhvc3R9OiR7c2VydmVyT3B0aW9ucy5wb3J0fS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTGlzdGVuIEhUVFBTIHNlcnZlclxyXG4gICAgaWYgKHNlcnZlck9wdGlvbnMuc3NsLmVuYWJsZSkge1xyXG4gICAgICAvLyBTZXQgdXAgYW4gU1NMIHNlcnZlciBhbHNvXHJcbiAgICAgIGxldCBrZXksIGNlcnQ7XHJcblxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIEdldCB0aGUgU1NMIGtleVxyXG4gICAgICAgIGtleSA9IGF3YWl0IHJlYWRGaWxlKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmtleScpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wgY2VydGlmaWNhdGVcclxuICAgICAgICBjZXJ0ID0gYXdhaXQgcmVhZEZpbGUoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIuY3J0JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gVW5hYmxlIHRvIGxvYWQga2V5L2NlcnRpZmljYXRlIGZyb20gdGhlICcke3NlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRofScgcGF0aC4gQ291bGQgbm90IHJ1biBzZWN1cmVkIGxheWVyIHNlcnZlci5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKGtleSAmJiBjZXJ0KSB7XHJcbiAgICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFBTKVxyXG4gICAgICAgIGNvbnN0IGh0dHBzU2VydmVyID0gaHR0cHMuY3JlYXRlU2VydmVyKHsga2V5LCBjZXJ0IH0sIGFwcCk7XHJcblxyXG4gICAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgIC8vIExpc3RlblxyXG4gICAgICAgIGh0dHBzU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBzZXJ2ZXJPcHRpb25zLmhvc3QsICgpID0+IHtcclxuICAgICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQUyBzZXJ2ZXJcclxuICAgICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMuc3NsLnBvcnQsIGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0fS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBzZXJ2ZXJPcHRpb25zLnJhdGVMaW1pdGluZyk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSB2YWxpZGF0aW9uIGhhbmRsZXJcclxuICAgIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHJvdXRlc1xyXG4gICAgZXhwb3J0Um91dGVzKGFwcCk7XHJcbiAgICBoZWFsdGhSb3V0ZXMoYXBwKTtcclxuICAgIHVpUm91dGVzKGFwcCk7XHJcbiAgICB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXHJcbiAgICBlcnJvck1pZGRsZXdhcmUoYXBwKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbG9zZVNlcnZlcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbG9zZVNlcnZlcnMoKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgYXJlIHNlcnZlcnMgd29ya2luZ1xyXG4gIGlmIChhY3RpdmVTZXJ2ZXJzLnNpemUgPiAwKSB7XHJcbiAgICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NpbmcgYWxsIHNlcnZlcnMuYCk7XHJcblxyXG4gICAgLy8gQ2xvc2UgZWFjaCBvbmUgb2Ygc2VydmVyc1xyXG4gICAgZm9yIChjb25zdCBbcG9ydCwgc2VydmVyXSBvZiBhY3RpdmVTZXJ2ZXJzKSB7XHJcbiAgICAgIHNlcnZlci5jbG9zZSgoKSA9PiB7XHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5kZWxldGUocG9ydCk7XHJcbiAgICAgICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zZWQgc2VydmVyIG9uIHBvcnQ6ICR7cG9ydH0uYCk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRTZXJ2ZXJzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtBcnJheS48T2JqZWN0Pn0gU2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VydmVycygpIHtcclxuICByZXR1cm4gYWN0aXZlU2VydmVycztcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEV4cHJlc3NcclxuICpcclxuICogQHJldHVybnMge0V4cHJlc3N9IFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEV4cHJlc3MoKSB7XHJcbiAgcmV0dXJuIGV4cHJlc3M7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QXBwXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXBwKCkge1xyXG4gIHJldHVybiBhcHA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXRcclxuICogb2YgdGhlIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlc1xyXG4gKiB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlUmF0ZUxpbWl0aW5nKHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgc2VydmVyOiB7XHJcbiAgICAgIHJhdGVMaW1pdGluZzogcmF0ZUxpbWl0aW5nT3B0aW9uc1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9uc1xyXG4gIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBvcHRpb25zLnNlcnZlci5yYXRlTGltaXRpbmdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFwcGx5IG1pZGRsZXdhcmUocykgdG8gYSBzcGVjaWZpYyBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXNlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggR0VUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIFBPU1QgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5wb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEF0dGFjaCBlcnJvciBoYW5kbGVycyB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnNcclxuICpcclxuICogQHBhcmFtIHsoaHR0cC5TZXJ2ZXJ8aHR0cHMuU2VydmVyKX0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoc2VydmVyKSB7XHJcbiAgc2VydmVyLm9uKCdjbGllbnRFcnJvcicsIChlcnJvciwgc29ja2V0KSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDEsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9LCBkZXN0cm95aW5nIHNvY2tldC5gXHJcbiAgICApO1xyXG4gICAgc29ja2V0LmRlc3Ryb3koKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignY29ubmVjdGlvbicsIChzb2NrZXQpID0+IHtcclxuICAgIHNvY2tldC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGNsb3NlU2VydmVycyxcclxuICBnZXRTZXJ2ZXJzLFxyXG4gIGdldEV4cHJlc3MsXHJcbiAgZ2V0QXBwLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhhbmRsZXMgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciwgZW5zdXJpbmdcclxuICogcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzIHN1Y2ggYXMgYnJvd3NlciwgcGFnZXMsIHNlcnZlcnMsIGFuZCB0aW1lcnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBjbGVhckFsbFRpbWVycyB9IGZyb20gJy4vdGltZXIuanMnO1xyXG5cclxuaW1wb3J0IHsgY2xvc2VTZXJ2ZXJzIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBjbGVhbnVwIG9wZXJhdGlvbnMgdG8gZW5zdXJlIGEgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIHByb2Nlc3MuXHJcbiAqIFRoaXMgaW5jbHVkZXMgY2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgdGltZW91dHMvaW50ZXJ2YWxzLCBjbG9zaW5nIGFjdGl2ZVxyXG4gKiBzZXJ2ZXJzLCB0ZXJtaW5hdGluZyByZXNvdXJjZXMgKHBhZ2VzKSBvZiB0aGUgcG9vbCwgcG9vbCBpdHNlbGYsIGFuZCBjbG9zaW5nXHJcbiAqIHRoZSBicm93c2VyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbZXhpdENvZGU9MF0gLSBUaGUgZXhpdCBjb2RlIHRvIHVzZSB3aXRoIGBwcm9jZXNzLmV4aXQoKWAuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGAwYC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXAoZXhpdENvZGUgPSAwKSB7XHJcbiAgLy8gQXdhaXQgZnJlZWluZyBhbGwgcmVzb3VyY2VzXHJcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcclxuICAgIC8vIENsZWFyIGFsbCBvbmdvaW5nIGludGVydmFsc1xyXG4gICAgY2xlYXJBbGxUaW1lcnMoKSxcclxuXHJcbiAgICAvLyBHZXQgYXZhaWxhYmxlIHNlcnZlciBpbnN0YW5jZXMgKEhUVFAvSFRUUFMpIGFuZCBjbG9zZSB0aGVtXHJcbiAgICBjbG9zZVNlcnZlcnMoKSxcclxuXHJcbiAgICAvLyBDbG9zZSBhbiBhY3RpdmUgcG9vbCBhbG9uZyB3aXRoIGl0cyB3b3JrZXJzIGFuZCB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gICAga2lsbFBvb2woKVxyXG4gIF0pO1xyXG5cclxuICAvLyBFeGl0IHByb2Nlc3Mgd2l0aCBhIGNvcnJlY3QgY29kZVxyXG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaHV0ZG93bkNsZWFuVXBcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvcmUgbW9kdWxlIGZvciBpbml0aWFsaXppbmcgYW5kIG1hbmFnaW5nIHRoZSBIaWdoY2hhcnRzIEV4cG9ydFxyXG4gKiBTZXJ2ZXIuIFByb3ZpZGVzIGZ1bmN0aW9uYWxpdGllcyBmb3IgY29uZmlndXJpbmcgZXhwb3J0cywgc2V0dGluZyB1cCBzZXJ2ZXJcclxuICogb3BlcmF0aW9ucywgbG9nZ2luZywgc2NyaXB0cyBjYWNoaW5nLCByZXNvdXJjZSBwb29saW5nLCBhbmQgZ3JhY2VmdWwgcHJvY2Vzc1xyXG4gKiBjbGVhbnVwLlxyXG4gKi9cclxuXHJcbmltcG9ydCAnY29sb3JzJztcclxuXHJcbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMsIG1hcFRvTmV3T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHtcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIGluaXRMb2dnaW5nLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG4gIHNldExvZ0xldmVsXHJcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpbml0UG9vbCwga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzaHV0ZG93bkNsZWFuVXAgfSBmcm9tICcuL3Jlc291cmNlUmVsZWFzZS5qcyc7XHJcblxyXG5pbXBvcnQgc2VydmVyIGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwcm9jZXNzLiBUYXNrcyBzdWNoIGFzIGNvbmZpZ3VyaW5nIGxvZ2dpbmcsIGNoZWNraW5nXHJcbiAqIHRoZSBjYWNoZSBhbmQgc291cmNlcywgYW5kIGluaXRpYWxpemluZyB0aGUgcmVzb3VyY2UgcG9vbCBvY2N1ciBkdXJpbmcgdGhpc1xyXG4gKiBzdGFnZS5cclxuICpcclxuICogVGhpcyBmdW5jdGlvbiBtdXN0IGJlIGNhbGxlZCBiZWZvcmUgYXR0ZW1wdGluZyB0byBleHBvcnQgY2hhcnRzIG9yIHNldFxyXG4gKiB1cCBhIHNlcnZlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpbml0T3B0aW9ucyAtIFRoZSBgaW5pdE9wdGlvbnNgIG9iamVjdCwgd2hpY2ggbWF5XHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmdcclxuICogdmFsdWVzIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0RXhwb3J0KGluaXRPcHRpb25zKSB7XHJcbiAgLy8gSW5pdCBhbmQgdXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKGluaXRPcHRpb25zKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBwZXIgZXhwb3J0IG1vZHVsZSBzY29wZVxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gIC8vIEluaXQgdGhlIGxvZ2dpbmdcclxuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAob3B0aW9ucy5vdGhlci5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXHJcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMsIG9wdGlvbnMuc2VydmVyLnByb3h5KTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgcG9vbFxyXG4gIGF3YWl0IGluaXRQb29sKG9wdGlvbnMucG9vbCwgb3B0aW9ucy5wdXBwZXRlZXIuYXJncyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXHJcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nXHJcbiAqIGFuZCAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVyc1xyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCkge1xyXG4gIGxvZygzLCAnW3Byb2Nlc3NdIEF0dGFjaGluZyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdleGl0J1xyXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gUHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0uYCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSU5UJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdURVJNJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICd1bmNhdWdodEV4Y2VwdGlvbidcclxuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGVycm9yLmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDEpO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgLy8gU2VydmVyXHJcbiAgLi4uc2VydmVyLFxyXG5cclxuICAvLyBPcHRpb25zXHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuXHJcbiAgLy8gRXhwb3J0aW5nXHJcbiAgaW5pdEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcblxyXG4gIC8vIFJlbGVhc2VcclxuICBraWxsUG9vbCxcclxuICBzaHV0ZG93bkNsZWFuVXAsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsOiBmdW5jdGlvbiAobGV2ZWwpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGxldmVsXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICBzZXRMb2dMZXZlbChvcHRpb25zLmxvZ2dpbmcubGV2ZWwpO1xyXG4gIH0sXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmc6IGZ1bmN0aW9uICh0b0NvbnNvbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIHRvQ29uc29sZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlQ29uc29sZUxvZ2dpbmcob3B0aW9ucy5sb2dnaW5nLnRvQ29uc29sZSk7XHJcbiAgfSxcclxuICBlbmFibGVGaWxlTG9nZ2luZzogZnVuY3Rpb24gKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBsb2dnaW5nOiB7XHJcbiAgICAgICAgZGVzdCxcclxuICAgICAgICBmaWxlLFxyXG4gICAgICAgIHRvRmlsZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy5kZXN0LFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZmlsZSxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLnRvRmlsZVxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcbiJdLCJuYW1lcyI6WyJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInRhZ05hbWUiLCJ0b1VwcGVyQ2FzZSIsInNyYyIsImJhc2VVUkkiLCJkZWVwQ29weSIsIm9iakFyciIsIm9iakFyckNvcHkiLCJBcnJheSIsImlzQXJyYXkiLCJrZXkiLCJPYmplY3QiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJmaXhDb25zdHIiLCJjb25zdHIiLCJmaXhlZENvbnN0ciIsInRvTG93ZXJDYXNlIiwicmVwbGFjZSIsImluY2x1ZGVzIiwiZml4T3V0ZmlsZSIsInR5cGUiLCJvdXRmaWxlIiwiZ2V0QWJzb2x1dGVQYXRoIiwic3BsaXQiLCJzaGlmdCIsImZpeFR5cGUiLCJtaW1lVHlwZXMiLCJmb3JtYXRzIiwidmFsdWVzIiwib3V0VHlwZSIsInBvcCIsImZpbmQiLCJ0IiwicGF0aCIsImlzQWJzb2x1dGUiLCJqb2luIiwiZ2V0QmFzZTY0IiwiaW5wdXQiLCJCdWZmZXIiLCJmcm9tIiwidG9TdHJpbmciLCJnZXROZXdEYXRlIiwiRGF0ZSIsInRyaW0iLCJnZXROZXdEYXRlVGltZSIsImdldFRpbWUiLCJpc09iamVjdCIsIml0ZW0iLCJpc09iamVjdEVtcHR5Iiwia2V5cyIsImxlbmd0aCIsImlzUHJpdmF0ZVJhbmdlVXJsRm91bmQiLCJzb21lIiwicGF0dGVybiIsInRlc3QiLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwicHJvY2VzcyIsImhydGltZSIsImJpZ2ludCIsIk51bWJlciIsInJvdW5kTnVtYmVyIiwidmFsdWUiLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwiTWF0aCIsInBvdyIsInJvdW5kIiwid3JhcEFyb3VuZCIsImN1c3RvbUNvZGUiLCJhbGxvd0ZpbGVSZXNvdXJjZXMiLCJpc0NhbGxiYWNrIiwiZW5kc1dpdGgiLCJyZWFkRmlsZVN5bmMiLCJzdGFydHNXaXRoIiwiY29sb3JzIiwibG9nZ2luZyIsInRvQ29uc29sZSIsInRvRmlsZSIsInBhdGhDcmVhdGVkIiwicGF0aFRvTG9nIiwibGV2ZWxzRGVzYyIsInRpdGxlIiwiY29sb3IiLCJsb2ciLCJhcmdzIiwibmV3TGV2ZWwiLCJ0ZXh0cyIsImxldmVsIiwicHJlZml4IiwiX2xvZ1RvRmlsZSIsImNvbnNvbGUiLCJhcHBseSIsInVuZGVmaW5lZCIsImNvbmNhdCIsImxvZ1dpdGhTdGFjayIsImVycm9yIiwiY3VzdG9tTWVzc2FnZSIsIm1haW5NZXNzYWdlIiwibWVzc2FnZSIsInN0YWNrTWVzc2FnZSIsInN0YWNrIiwicHVzaCIsImluaXRMb2dnaW5nIiwibG9nZ2luZ09wdGlvbnMiLCJkZXN0IiwiZmlsZSIsInNldExvZ0xldmVsIiwiZW5hYmxlQ29uc29sZUxvZ2dpbmciLCJlbmFibGVGaWxlTG9nZ2luZyIsImlzSW50ZWdlciIsImV4aXN0c1N5bmMiLCJta2RpclN5bmMiLCJhcHBlbmRGaWxlIiwiZGVmYXVsdENvbmZpZyIsInB1cHBldGVlciIsInR5cGVzIiwiZW52TGluayIsImNsaU5hbWUiLCJkZXNjcmlwdGlvbiIsInByb21wdE9wdGlvbnMiLCJzZXBhcmF0b3IiLCJoaWdoY2hhcnRzIiwidmVyc2lvbiIsImNkblVybCIsImZvcmNlRmV0Y2giLCJjYWNoZVBhdGgiLCJjb3JlU2NyaXB0cyIsImluc3RydWN0aW9ucyIsIm1vZHVsZVNjcmlwdHMiLCJpbmRpY2F0b3JTY3JpcHRzIiwiY3VzdG9tU2NyaXB0cyIsImV4cG9ydCIsImluZmlsZSIsImluc3RyIiwib3B0aW9ucyIsInN2ZyIsImJhdGNoIiwiaGludCIsImNob2ljZXMiLCJiNjQiLCJub0Rvd25sb2FkIiwiaGVpZ2h0Iiwid2lkdGgiLCJzY2FsZSIsImRlZmF1bHRIZWlnaHQiLCJkZWZhdWx0V2lkdGgiLCJkZWZhdWx0U2NhbGUiLCJtaW4iLCJtYXgiLCJnbG9iYWxPcHRpb25zIiwidGhlbWVPcHRpb25zIiwicmFzdGVyaXphdGlvblRpbWVvdXQiLCJjdXN0b21Mb2dpYyIsImFsbG93Q29kZUV4ZWN1dGlvbiIsImNhbGxiYWNrIiwicmVzb3VyY2VzIiwibG9hZENvbmZpZyIsImxlZ2FjeU5hbWUiLCJjcmVhdGVDb25maWciLCJzZXJ2ZXIiLCJlbmFibGUiLCJob3N0IiwicG9ydCIsInVwbG9hZExpbWl0IiwiYmVuY2htYXJraW5nIiwicHJveHkiLCJ0aW1lb3V0IiwicmF0ZUxpbWl0aW5nIiwibWF4UmVxdWVzdHMiLCJ3aW5kb3ciLCJkZWxheSIsInRydXN0UHJveHkiLCJza2lwS2V5Iiwic2tpcFRva2VuIiwic3NsIiwiZm9yY2UiLCJjZXJ0UGF0aCIsInBvb2wiLCJtaW5Xb3JrZXJzIiwibWF4V29ya2VycyIsIndvcmtMaW1pdCIsImFjcXVpcmVUaW1lb3V0IiwiY3JlYXRlVGltZW91dCIsImRlc3Ryb3lUaW1lb3V0IiwiaWRsZVRpbWVvdXQiLCJjcmVhdGVSZXRyeUludGVydmFsIiwicmVhcGVySW50ZXJ2YWwiLCJ1aSIsInJvdXRlIiwib3RoZXIiLCJub2RlRW52IiwibGlzdGVuVG9Qcm9jZXNzRXhpdHMiLCJub0xvZ28iLCJoYXJkUmVzZXRQYWdlIiwiYnJvd3NlclNoZWxsTW9kZSIsImRlYnVnIiwiaGVhZGxlc3MiLCJkZXZ0b29scyIsImxpc3RlblRvQ29uc29sZSIsImR1bXBpbyIsInNsb3dNbyIsImRlYnVnZ2luZ1BvcnQiLCJuZXN0ZWRQcm9wcyIsIl9jcmVhdGVOZXN0ZWRQcm9wcyIsImFic29sdXRlUHJvcHMiLCJfY3JlYXRlQWJzb2x1dGVQcm9wcyIsImNvbmZpZyIsInByb3BDaGFpbiIsImZvckVhY2giLCJlbnRyeSIsInN1YnN0cmluZyIsImRvdGVudiIsInYiLCJhcnJheSIsImZpbHRlckFycmF5IiwieiIsInN0cmluZyIsInRyYW5zZm9ybSIsIm1hcCIsImZpbHRlciIsImJvb2xlYW4iLCJlbnVtIiwicmVmaW5lIiwicG9zaXRpdmVOdW0iLCJpc05hTiIsInBhcnNlRmxvYXQiLCJub25OZWdhdGl2ZU51bSIsIkNvbmZpZyIsIm9iamVjdCIsIlBVUFBFVEVFUl9BUkdTIiwiSElHSENIQVJUU19WRVJTSU9OIiwiSElHSENIQVJUU19DRE5fVVJMIiwiSElHSENIQVJUU19GT1JDRV9GRVRDSCIsIkhJR0hDSEFSVFNfQ0FDSEVfUEFUSCIsIkhJR0hDSEFSVFNfQURNSU5fVE9LRU4iLCJISUdIQ0hBUlRTX0NPUkVfU0NSSVBUUyIsIkhJR0hDSEFSVFNfTU9EVUxFX1NDUklQVFMiLCJISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTIiwiSElHSENIQVJUU19DVVNUT01fU0NSSVBUUyIsIkVYUE9SVF9JTkZJTEUiLCJFWFBPUlRfSU5TVFIiLCJFWFBPUlRfT1BUSU9OUyIsIkVYUE9SVF9TVkciLCJFWFBPUlRfQkFUQ0giLCJFWFBPUlRfT1VURklMRSIsIkVYUE9SVF9UWVBFIiwiRVhQT1JUX0NPTlNUUiIsIkVYUE9SVF9CNjQiLCJFWFBPUlRfTk9fRE9XTkxPQUQiLCJFWFBPUlRfSEVJR0hUIiwiRVhQT1JUX1dJRFRIIiwiRVhQT1JUX1NDQUxFIiwiRVhQT1JUX0RFRkFVTFRfSEVJR0hUIiwiRVhQT1JUX0RFRkFVTFRfV0lEVEgiLCJFWFBPUlRfREVGQVVMVF9TQ0FMRSIsIkVYUE9SVF9HTE9CQUxfT1BUSU9OUyIsIkVYUE9SVF9USEVNRV9PUFRJT05TIiwiRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCIsIkNVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTiIsIkNVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUyIsIkNVU1RPTV9MT0dJQ19DVVNUT01fQ09ERSIsIkNVU1RPTV9MT0dJQ19DQUxMQkFDSyIsIkNVU1RPTV9MT0dJQ19SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfTE9BRF9DT05GSUciLCJDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRyIsIlNFUlZFUl9FTkFCTEUiLCJTRVJWRVJfSE9TVCIsIlNFUlZFUl9QT1JUIiwiU0VSVkVSX1VQTE9BRF9MSU1JVCIsIlNFUlZFUl9CRU5DSE1BUktJTkciLCJTRVJWRVJfUFJPWFlfSE9TVCIsIlNFUlZFUl9QUk9YWV9QT1JUIiwiU0VSVkVSX1BST1hZX1RJTUVPVVQiLCJTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUiLCJTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFMiLCJTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1ciLCJTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOIiwiU0VSVkVSX1NTTF9FTkFCTEUiLCJTRVJWRVJfU1NMX0ZPUkNFIiwiU0VSVkVSX1NTTF9QT1JUIiwiU0VSVkVSX1NTTF9DRVJUX1BBVEgiLCJQT09MX01JTl9XT1JLRVJTIiwiUE9PTF9NQVhfV09SS0VSUyIsIlBPT0xfV09SS19MSU1JVCIsIlBPT0xfQUNRVUlSRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfVElNRU9VVCIsIlBPT0xfREVTVFJPWV9USU1FT1VUIiwiUE9PTF9JRExFX1RJTUVPVVQiLCJQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCIsIlBPT0xfUkVBUEVSX0lOVEVSVkFMIiwiUE9PTF9CRU5DSE1BUktJTkciLCJMT0dHSU5HX0xFVkVMIiwiTE9HR0lOR19GSUxFIiwiTE9HR0lOR19ERVNUIiwiTE9HR0lOR19UT19DT05TT0xFIiwiTE9HR0lOR19UT19GSUxFIiwiVUlfRU5BQkxFIiwiVUlfUk9VVEUiLCJPVEhFUl9OT0RFX0VOViIsIk9USEVSX0xJU1RFTl9UT19QUk9DRVNTX0VYSVRTIiwiT1RIRVJfTk9fTE9HTyIsIk9USEVSX0hBUkRfUkVTRVRfUEFHRSIsIk9USEVSX0JST1dTRVJfU0hFTExfTU9ERSIsIkRFQlVHX0VOQUJMRSIsIkRFQlVHX0hFQURMRVNTIiwiREVCVUdfREVWVE9PTFMiLCJERUJVR19MSVNURU5fVE9fQ09OU09MRSIsIkRFQlVHX0RVTVBJTyIsIkRFQlVHX1NMT1dfTU8iLCJERUJVR19ERUJVR0dJTkdfUE9SVCIsImVudnMiLCJwYXJ0aWFsIiwicGFyc2UiLCJlbnYiLCJfaW5pdE9wdGlvbnMiLCJnZXRPcHRpb25zIiwidXBkYXRlT3B0aW9ucyIsIm5ld09wdGlvbnMiLCJnZXRDb3B5IiwiX21lcmdlT3B0aW9ucyIsIm1hcFRvTmV3T3B0aW9ucyIsIm9sZE9wdGlvbnMiLCJlbnRyaWVzIiwicHJvcGVydGllc0NoYWluIiwicmVkdWNlIiwib2JqIiwicHJvcCIsImluZGV4IiwiaXNBbGxvd2VkQ29uZmlnIiwiYWxsb3dGdW5jdGlvbnMiLCJvYmplY3RDb25maWciLCJldmFsIiwiSlNPTiIsInN0cmluZ2lmaWVkT3B0aW9ucyIsIl9vcHRpb25zU3RyaW5naWZ5IiwicGFyc2VkT3B0aW9ucyIsIl8iLCJuYW1lIiwib3JpZ2luYWxPcHRpb25zIiwic3RyaW5naWZ5RnVuY3Rpb25zIiwic3RyaW5naWZ5IiwicmVwbGFjZUFsbCIsIkVycm9yIiwiYXN5bmMiLCJmZXRjaCIsInVybCIsInJlcXVlc3RPcHRpb25zIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJfZ2V0UHJvdG9jb2xNb2R1bGUiLCJnZXQiLCJyZXNwb25zZSIsInJlc3BvbnNlRGF0YSIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiaHR0cHMiLCJodHRwIiwiRXhwb3J0RXJyb3IiLCJjb25zdHJ1Y3RvciIsInN0YXR1c0NvZGUiLCJzdXBlciIsInRoaXMiLCJzZXRTdGF0dXMiLCJzZXRFcnJvciIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsImkiLCJzZXR1cEhpZ2hjaGFydHMiLCJIaWdoY2hhcnRzIiwiYW5pbU9iamVjdCIsImR1cmF0aW9uIiwiY3JlYXRlQ2hhcnQiLCJleHBvcnRPcHRpb25zIiwiY3VzdG9tTG9naWNPcHRpb25zIiwic2V0T3B0aW9ucyIsIm1lcmdlIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJpc1JlbmRlckNvbXBsZXRlIiwiQ2hhcnQiLCJwcm9jZWVkIiwidXNlck9wdGlvbnMiLCJjYiIsImV4cG9ydGluZyIsImVuYWJsZWQiLCJwbG90T3B0aW9ucyIsInNlcmllcyIsImxhYmVsIiwidG9vbHRpcCIsImFuaW1hdGlvbiIsIm9uSGlnaGNoYXJ0c1JlbmRlciIsImFkZEV2ZW50IiwiU2VyaWVzIiwiY2hhcnQiLCJhZGRpdGlvbmFsT3B0aW9ucyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImRlZmF1bHRPcHRpb25zIiwidGVtcGxhdGUiLCJicm93c2VyIiwiY3JlYXRlQnJvd3NlciIsInB1cHBldGVlckFyZ3MiLCJlbmFibGVkRGVidWciLCJkZWJ1Z09wdGlvbnMiLCJsYXVuY2hPcHRpb25zIiwidXNlckRhdGFEaXIiLCJoYW5kbGVTSUdJTlQiLCJoYW5kbGVTSUdURVJNIiwiaGFuZGxlU0lHSFVQIiwid2FpdEZvckluaXRpYWxQYWdlIiwiZGVmYXVsdFZpZXdwb3J0IiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwic2V0VGltZW91dCIsImNsb3NlQnJvd3NlciIsImNvbm5lY3RlZCIsImNsb3NlIiwibmV3UGFnZSIsInBvb2xSZXNvdXJjZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJfc2V0UGFnZUNvbnRlbnQiLCJfc2V0UGFnZUV2ZW50cyIsImlzQ2xvc2VkIiwiY2xlYXJQYWdlIiwiaGFyZFJlc2V0IiwiZ290byIsIndhaXRVbnRpbCIsImV2YWx1YXRlIiwiYm9keSIsImlubmVySFRNTCIsImlkIiwid29ya0NvdW50IiwiYWRkUGFnZVJlc291cmNlcyIsImluamVjdGVkUmVzb3VyY2VzIiwiaW5qZWN0ZWRKcyIsImpzIiwiY29udGVudCIsImZpbGVzIiwiaXNMb2NhbCIsImpzUmVzb3VyY2UiLCJhZGRTY3JpcHRUYWciLCJpbmplY3RlZENzcyIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJjc3NSZXNvdXJjZSIsImFkZFN0eWxlVGFnIiwiY2xlYXJQYWdlUmVzb3VyY2VzIiwicmVzb3VyY2UiLCJkaXNwb3NlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJlbGVtZW50IiwicmVtb3ZlIiwic2V0Q29udGVudCIsImNzc1RlbXBsYXRlIiwic3ZnVGVtcGxhdGUiLCJwdXBwZXRlZXJFeHBvcnQiLCJpc1NWRyIsInNpemUiLCJzdmdFbGVtZW50IiwicXVlcnlTZWxlY3RvciIsImNoYXJ0SGVpZ2h0IiwiYmFzZVZhbCIsImNoYXJ0V2lkdGgiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ4IiwieSIsIl9nZXRDbGlwUmVnaW9uIiwidmlld3BvcnRIZWlnaHQiLCJhYnMiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsInJlc3VsdCIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJfY3JlYXRlU1ZHIiwiX2NyZWF0ZUltYWdlIiwiX2NyZWF0ZVBERiIsIiRldmFsIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidHJ1bmMiLCJvdXRlckhUTUwiLCJjbGlwIiwicmFjZSIsInNjcmVlbnNob3QiLCJlbmNvZGluZyIsImZ1bGxQYWdlIiwib3B0aW1pemVGb3JTcGVlZCIsImNhcHR1cmVCZXlvbmRWaWV3cG9ydCIsInF1YWxpdHkiLCJvbWl0QmFja2dyb3VuZCIsIl9yZXNvbHZlIiwiZW11bGF0ZU1lZGlhVHlwZSIsInBkZiIsInBvb2xTdGF0cyIsImV4cG9ydHNBdHRlbXB0ZWQiLCJleHBvcnRzUGVyZm9ybWVkIiwiZXhwb3J0c0Ryb3BwZWQiLCJleHBvcnRzRnJvbVN2ZyIsImV4cG9ydHNGcm9tT3B0aW9ucyIsImV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMiLCJleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cyIsInRpbWVTcGVudCIsInRpbWVTcGVudEF2ZXJhZ2UiLCJpbml0UG9vbCIsInBvb2xPcHRpb25zIiwiUG9vbCIsIl9mYWN0b3J5IiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsImNsZWFyU3RhdHVzIiwiX2V2ZW50SWQiLCJpbml0aWFsUmVzb3VyY2VzIiwiYWNxdWlyZSIsInByb21pc2UiLCJyZWxlYXNlIiwia2lsbFBvb2wiLCJ3b3JrZXIiLCJ1c2VkIiwiZGVzdHJveWVkIiwicG9zdFdvcmsiLCJ3b3JrZXJIYW5kbGUiLCJnZXRQb29sSW5mbyIsImFjcXVpcmVDb3VudGVyIiwicmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsImV4cG9ydFRpbWUiLCJnZXRQb29sU3RhdHMiLCJnZXRQb29sSW5mb0pTT04iLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwibnVtRnJlZSIsImFsbENyZWF0ZWQiLCJwZW5kaW5nQWNxdWlyZXMiLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwZW5kaW5nQ3JlYXRlcyIsIm51bVBlbmRpbmdDcmVhdGVzIiwicGVuZGluZ1ZhbGlkYXRpb25zIiwibnVtUGVuZGluZ1ZhbGlkYXRpb25zIiwicGVuZGluZ0Rlc3Ryb3lzIiwiYWJzb2x1dGVBbGwiLCJjcmVhdGUiLCJ1dWlkIiwicmFuZG9tIiwic3RhcnREYXRlIiwidmFsaWRhdGUiLCJtYWluRnJhbWUiLCJkZXRhY2hlZCIsInJlbW92ZUFsbExpc3RlbmVycyIsInNhbml0aXplIiwiSlNET00iLCJET01QdXJpZnkiLCJBRERfVEFHUyIsInNpbmdsZUV4cG9ydCIsInN0YXJ0RXhwb3J0IiwiZGF0YSIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiYmF0Y2hSZXN1bHRzIiwiYWxsU2V0dGxlZCIsInJlYXNvbiIsImV4cG9ydGluZ09wdGlvbnMiLCJlbmRDYWxsYmFjayIsImZpbGVDb250ZW50IiwiX2V4cG9ydEZyb21TdmciLCJfZXhwb3J0RnJvbU9wdGlvbnMiLCJnZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJzZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJpbnB1dFRvRXhwb3J0IiwiX3ByZXBhcmVFeHBvcnQiLCJfaGFuZGxlQ3VzdG9tTG9naWMiLCJfaGFuZGxlR2xvYmFsQW5kVGhlbWUiLCJfZmluZENoYXJ0U2l6ZSIsIm9wdGlvbnNDaGFydCIsIm9wdGlvbnNFeHBvcnRpbmciLCJnbG9iYWxPcHRpb25zQ2hhcnQiLCJnbG9iYWxPcHRpb25zRXhwb3J0aW5nIiwidGhlbWVPcHRpb25zQ2hhcnQiLCJ0aGVtZU9wdGlvbnNFeHBvcnRpbmciLCJzb3VyY2VIZWlnaHQiLCJzb3VyY2VXaWR0aCIsInBhcmFtIiwiX2hhbmRsZVJlc291cmNlcyIsImFsbG93ZWRQcm9wcyIsImhhbmRsZWRSZXNvdXJjZXMiLCJjb3JyZWN0UmVzb3VyY2VzIiwicHJvcE5hbWUiLCJvcHRpb25zTmFtZSIsInRpbWVySWRzIiwiYWRkVGltZXIiLCJjbGVhckFsbFRpbWVycyIsImNsZWFySW50ZXJ2YWwiLCJjbGVhclRpbWVvdXQiLCJsb2dFcnJvck1pZGRsZXdhcmUiLCJyZXF1ZXN0IiwibmV4dCIsInJldHVybkVycm9yTWlkZGxld2FyZSIsInN0YXR1cyIsImpzb24iLCJlcnJvck1pZGRsZXdhcmUiLCJhcHAiLCJ1c2UiLCJyYXRlTGltaXRpbmdNaWRkbGV3YXJlIiwicmF0ZUxpbWl0aW5nT3B0aW9ucyIsInJhdGVPcHRpb25zIiwibGltaXRlciIsInJhdGVMaW1pdCIsIndpbmRvd01zIiwibGltaXQiLCJkZWxheU1zIiwiaGFuZGxlciIsImZvcm1hdCIsInNlbmQiLCJkZWZhdWx0Iiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwiY29udGVudFR5cGVNaWRkbGV3YXJlIiwiY29udGVudFR5cGUiLCJoZWFkZXJzIiwicmVxdWVzdEJvZHlNaWRkbGV3YXJlIiwiY29ubmVjdGlvbiIsInJlbW90ZUFkZHJlc3MiLCJ2YWxpZGF0ZWRPcHRpb25zIiwicGFyYW1zIiwiZmlsZW5hbWUiLCJ2YWxpZGF0aW9uTWlkZGxld2FyZSIsInBvc3QiLCJyZXZlcnNlZE1pbWUiLCJwbmciLCJqcGVnIiwiZ2lmIiwicmVxdWVzdEV4cG9ydCIsInJlcXVlc3RDb3VudGVyIiwiY29ubmVjdGlvbkFib3J0ZWQiLCJzb2NrZXQiLCJoYWRFcnJvcnMiLCJoZWFkZXIiLCJhdHRhY2htZW50IiwiZXhwb3J0Um91dGVzIiwic2VydmVyU3RhcnRUaW1lIiwicGFja2FnZUZpbGUiLCJzdWNjZXNzUmF0ZXMiLCJyZWNvcmRJbnRlcnZhbCIsIndpbmRvd1NpemUiLCJfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSIsImEiLCJiIiwiX3N0YXJ0U3VjY2Vzc1JhdGUiLCJzZXRJbnRlcnZhbCIsInN0YXRzIiwic3VjY2Vzc1JhdGlvIiwiaGVhbHRoUm91dGVzIiwicGVyaW9kIiwibW92aW5nQXZlcmFnZSIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJzZXJ2ZXJWZXJzaW9uIiwiaGlnaGNoYXJ0c1ZlcnNpb24iLCJhdmVyYWdlRXhwb3J0VGltZSIsImF0dGVtcHRlZEV4cG9ydHMiLCJwZXJmb3JtZWRFeHBvcnRzIiwiZmFpbGVkRXhwb3J0cyIsInN1Y2Vzc1JhdGlvIiwidG9GaXhlZCIsInN2Z0V4cG9ydHMiLCJqc29uRXhwb3J0cyIsInN2Z0V4cG9ydHNBdHRlbXB0cyIsImpzb25FeHBvcnRzQXR0ZW1wdHMiLCJ1aVJvdXRlcyIsInNlbmRGaWxlIiwiYWNjZXB0UmFuZ2VzIiwidmVyc2lvbkNoYW5nZVJvdXRlcyIsImFkbWluVG9rZW4iLCJ0b2tlbiIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJPcHRpb25zIiwidXBsb2FkTGltaXRCeXRlcyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRTaXplIiwiZGlzYWJsZSIsImNvcnMiLCJtZXRob2RzIiwic2V0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsInN0YXRpYyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyIsImxpc3RlbiIsImNlcnQiLCJyZWFkRmlsZSIsImh0dHBzU2VydmVyIiwiY2xvc2VTZXJ2ZXJzIiwiZGVsZXRlIiwiZ2V0U2VydmVycyIsImdldEV4cHJlc3MiLCJnZXRBcHAiLCJlbmFibGVSYXRlTGltaXRpbmciLCJtaWRkbGV3YXJlcyIsInNodXRkb3duQ2xlYW5VcCIsImV4aXRDb2RlIiwiZXhpdCIsImluaXRFeHBvcnQiLCJpbml0T3B0aW9ucyIsIl9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyIsImNvZGUiXSwibWFwcGluZ3MiOiJ3bkJBMkJPLE1BQU1BLFlBQVlDLElBQWFBLGNBQUMsSUFBSUMsSUFBSSxPQUFRLG9CQUFBQyxTQUFBQyxRQUFBLE9BQUFDLGNBQUFDLFlBQUFDLEtBQUFDLHdCQUFBLFdBQUFBLHVCQUFBQyxRQUFBQyxlQUFBRix1QkFBQUcsS0FBQSxJQUFBVCxJQUFBLFlBQUFDLFNBQUFTLFNBQUFMLE9BK0JoRCxTQUFTTSxTQUFTQyxHQUV2QixHQUFlLE9BQVhBLEdBQXFDLGlCQUFYQSxFQUM1QixPQUFPQSxFQUlULE1BQU1DLEVBQWFDLE1BQU1DLFFBQVFILEdBQVUsR0FBSyxHQUdoRCxJQUFLLE1BQU1JLEtBQU9KLEVBQ1pLLE9BQU9DLFVBQVVDLGVBQWVDLEtBQUtSLEVBQVFJLEtBQy9DSCxFQUFX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 +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?e:path.join(__dirname$1,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIGpvaW4gfSBmcm9tICdwYXRoJztcclxuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCc7XHJcblxyXG5jb25zdCBNQVhfQkFDS09GRl9BVFRFTVBUUyA9IDY7XHJcblxyXG4vLyBUaGUgZGlyZWN0b3J5IHBhdGhcclxuZXhwb3J0IGNvbnN0IF9fZGlybmFtZSA9IGZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLi4vLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbmQgc3RhbmRhcmRpemVzIHRleHQgYnkgcmVwbGFjaW5nIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2VcclxuICogY2hhcmFjdGVycyB3aXRoIGEgc2luZ2xlIHNwYWNlIGFuZCB0cmltbWluZyBhbnkgbGVhZGluZyBvciB0cmFpbGluZ1xyXG4gKiB3aGl0ZXNwYWNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJUZXh0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IC0gVGhlIGlucHV0IHRleHQgdG8gYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtSZWdFeHB9IFtydWxlPS9cXHNcXHMrL2ddIC0gVGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBydWxlIHRvIG1hdGNoXHJcbiAqIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgdGhlICcvXFxzXFxzKy9nJyBSZWdFeHAuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcmVwbGFjZXI9JyAnXSAtIFRoZSBzdHJpbmcgdXNlZCB0byByZXBsYWNlIG11bHRpcGxlXHJcbiAqIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgdGhlICcgJyBzdHJpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjbGVhcmVkIGFuZCBzdGFuZGFyZGl6ZWQgdGV4dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhclRleHQodGV4dCwgcnVsZSA9IC9cXHNcXHMrL2csIHJlcGxhY2VyID0gJyAnKSB7XHJcbiAgcmV0dXJuIHRleHQucmVwbGFjZUFsbChydWxlLCByZXBsYWNlcikudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIGRlZXAgY29weSBvZiB0aGUgZ2l2ZW4gb2JqZWN0IG9yIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZGVlcENvcHlcclxuICpcclxuICogQHBhcmFtIHsoT2JqZWN0fEFycmF5KX0gb2JqQXJyIC0gVGhlIG9iamVjdCBvciBhcnJheSB0byBiZSBkZWVwbHkgY29waWVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxBcnJheSl9IFRoZSBkZWVwIGNvcHkgb2YgdGhlIHByb3ZpZGVkIG9iamVjdCBvciBhcnJheS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBkZWVwQ29weShvYmpBcnIpIHtcclxuICAvLyBJZiB0aGUgYG9iakFycmAgaXMgbnVsbCBvciBub3Qgb2YgdGhlIGBvYmplY3RgIHR5cGUsIHJldHVybiBpdFxyXG4gIGlmIChvYmpBcnIgPT09IG51bGwgfHwgdHlwZW9mIG9iakFyciAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmpBcnI7XHJcbiAgfVxyXG5cclxuICAvLyBQcmVwYXJlIGVpdGhlciBhIG5ldyBhcnJheSBvciBhIG5ldyBvYmplY3RcclxuICBjb25zdCBvYmpBcnJDb3B5ID0gQXJyYXkuaXNBcnJheShvYmpBcnIpID8gW10gOiB7fTtcclxuXHJcbiAgLy8gUmVjdXJzaXZlbHkgY29weSBlYWNoIHByb3BlcnR5XHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqQXJyKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iakFyciwga2V5KSkge1xyXG4gICAgICBvYmpBcnJDb3B5W2tleV0gPSBkZWVwQ29weShvYmpBcnJba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNvcGllZCBvYmplY3RcclxuICByZXR1cm4gb2JqQXJyQ29weTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEltcGxlbWVudHMgYW4gZXhwb25lbnRpYWwgYmFja29mZiBzdHJhdGVneSBmb3IgcmV0cnlpbmcgYSBmdW5jdGlvbiB1bnRpbFxyXG4gKiBhIGNlcnRhaW4gbnVtYmVyIG9mIGF0dGVtcHRzIGFyZSByZWFjaGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGV4cEJhY2tvZmZcclxuICpcclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gLSBUaGUgZnVuY3Rpb24gdG8gYmUgcmV0cmllZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IFthdHRlbXB0PTBdIC0gVGhlIGN1cnJlbnQgYXR0ZW1wdCBudW1iZXIuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGAwYC5cclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0XHJcbiAqIG9mIHRoZSBmdW5jdGlvbiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwQmFja29mZihmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlcGVhdCwgdGhyb3cgYW4gZXJyb3JcclxuICAgIGlmICgrK2F0dGVtcHQgPj0gTUFYX0JBQ0tPRkZfQVRURU1QVFMpIHtcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxyXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc3BvbnNlKSA9PiBzZXRUaW1lb3V0KHJlc3BvbnNlLCBkZWxheUluTXMpKTtcclxuXHJcbiAgICAvLy8gVE8gRE86IENvcnJlY3RcclxuICAgIC8vIC8vIEluZm9ybWF0aW9uIGFib3V0IHRoZSByZXNvdXJjZSB0aW1lb3V0XHJcbiAgICAvLyBsb2coXHJcbiAgICAvLyAgIDMsXHJcbiAgICAvLyAgIGBbdXRpbHNdIFdhaXRlZCAke2RlbGF5SW5Nc31tcyB1bnRpbCBuZXh0IGNhbGwgZm9yIHRoZSByZXNvdXJjZSBvZiBJRDogJHthcmdzWzBdfS5gXHJcbiAgICAvLyApO1xyXG5cclxuICAgIC8vIFRyeSBhZ2FpblxyXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkanVzdHMgdGhlIGNvbnN0cnVjdG9yIG5hbWUgYnkgdHJhbnNmb3JtaW5nIGFuZCBub3JtYWxpemluZyBpdCBiYXNlZFxyXG4gKiBvbiBjb21tb24gY2hhcnQgdHlwZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhDb25zdHJcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnN0ciAtIFRoZSBvcmlnaW5hbCBjb25zdHJ1Y3RvciBuYW1lIHRvIGJlIGZpeGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGNvbnN0cnVjdG9yIG5hbWUsIG9yICdjaGFydCcgaWYgdGhlIGlucHV0XHJcbiAqIGlzIG5vdCByZWNvZ25pemVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeENvbnN0cihjb25zdHIpIHtcclxuICB0cnkge1xyXG4gICAgLy8gRml4IHRoZSBjb25zdHJ1Y3RvciBieSBsb3dlcmluZyBjYXNpbmdcclxuICAgIGNvbnN0IGZpeGVkQ29uc3RyID0gYCR7Y29uc3RyLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnY2hhcnQnLCAnJyl9Q2hhcnRgO1xyXG5cclxuICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgcmVzdWx0IGlzIGp1c3QgJ0NoYXJ0J1xyXG4gICAgaWYgKGZpeGVkQ29uc3RyID09PSAnQ2hhcnQnKSB7XHJcbiAgICAgIGZpeGVkQ29uc3RyLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IsIG90aGVyd2lzZSBkZWZhdWx0IHRvICdjaGFydCdcclxuICAgIHJldHVybiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddLmluY2x1ZGVzKFxyXG4gICAgICBmaXhlZENvbnN0clxyXG4gICAgKVxyXG4gICAgICA/IGZpeGVkQ29uc3RyXHJcbiAgICAgIDogJ2NoYXJ0JztcclxuICB9IGNhdGNoIHtcclxuICAgIC8vIERlZmF1bHQgdG8gJ2NoYXJ0JyBpbiBjYXNlIG9mIGFueSBlcnJvclxyXG4gICAgcmV0dXJuICdjaGFydCc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIG91dGZpbGUgYmFzZWQgb24gcHJvdmlkZWQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeE91dGZpbGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBvdXRmaWxlIC0gVGhlIGZpbGUgcGF0aCBvciBuYW1lLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIG91dGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4T3V0ZmlsZSh0eXBlLCBvdXRmaWxlKSB7XHJcbiAgLy8gR2V0IHRoZSBmaWxlIG5hbWUgZnJvbSB0aGUgYG91dGZpbGVgIG9wdGlvblxyXG4gIGNvbnN0IGZpbGVOYW1lID0gZ2V0QWJzb2x1dGVQYXRoKG91dGZpbGUgfHwgJ2NoYXJ0JylcclxuICAgIC5zcGxpdCgnLicpXHJcbiAgICAuc2hpZnQoKTtcclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCBvdXRmaWxlXHJcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke3R5cGV9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhUeXBlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW291dGZpbGU9bnVsbF0gLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGBudWxsYC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNvcnJlY3RlZCBleHBvcnQgdHlwZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhUeXBlKHR5cGUsIG91dGZpbGUgPSBudWxsKSB7XHJcbiAgLy8gTUlNRSB0eXBlc1xyXG4gIGNvbnN0IG1pbWVUeXBlcyA9IHtcclxuICAgICdpbWFnZS9wbmcnOiAncG5nJyxcclxuICAgICdpbWFnZS9qcGVnJzogJ2pwZWcnLFxyXG4gICAgJ2FwcGxpY2F0aW9uL3BkZic6ICdwZGYnLFxyXG4gICAgJ2ltYWdlL3N2Zyt4bWwnOiAnc3ZnJ1xyXG4gIH07XHJcblxyXG4gIC8vIEdldCBmb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IE9iamVjdC52YWx1ZXMobWltZVR5cGVzKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdHlwZSBhbmQgb3V0ZmlsZSdzIGV4dGVuc2lvbnMgYXJlIHRoZSBzYW1lXHJcbiAgaWYgKG91dGZpbGUpIHtcclxuICAgIGNvbnN0IG91dFR5cGUgPSBvdXRmaWxlLnNwbGl0KCcuJykucG9wKCk7XHJcblxyXG4gICAgLy8gU3VwcG9ydCB0aGUgSlBHIHR5cGVcclxuICAgIGlmIChvdXRUeXBlID09PSAnanBnJykge1xyXG4gICAgICB0eXBlID0gJ2pwZWcnO1xyXG4gICAgfSBlbHNlIGlmIChmb3JtYXRzLmluY2x1ZGVzKG91dFR5cGUpICYmIHR5cGUgIT09IG91dFR5cGUpIHtcclxuICAgICAgdHlwZSA9IG91dFR5cGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IHR5cGVcclxuICByZXR1cm4gbWltZVR5cGVzW3R5cGVdIHx8IGZvcm1hdHMuZmluZCgodCkgPT4gdCA9PT0gdHlwZSkgfHwgJ3BuZyc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIHBhdGggaXMgcmVsYXRpdmUgb3IgYWJzb2x1dGUgYW5kIHJldHVybnMgdGhlIGNvcnJlY3RlZCxcclxuICogYWJzb2x1dGUgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzQWJzb2x1dGVQYXRoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gYmUgY2hlY2tlZCBvbi5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGguXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWJzb2x1dGVQYXRoKHBhdGgpIHtcclxuICByZXR1cm4gaXNBYnNvbHV0ZShwYXRoKSA/IHBhdGggOiBqb2luKF9fZGlybmFtZSwgcGF0aCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBpbnB1dCBkYXRhIHRvIGEgQmFzZTY0IHN0cmluZyBiYXNlZCBvbiB0aGUgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRCYXNlNjRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IC0gVGhlIGlucHV0IHRvIGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmb3JtYXQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQmFzZTY0IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QmFzZTY0KGlucHV0LCB0eXBlKSB7XHJcbiAgLy8gRm9yIHBkZiBhbmQgc3ZnIHR5cGVzIHRoZSBpbnB1dCBtdXN0IGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmcm9tIGEgYnVmZmVyXHJcbiAgaWYgKHR5cGUgPT09ICdwZGYnIHx8IHR5cGUgPT0gJ3N2ZycpIHtcclxuICAgIHJldHVybiBCdWZmZXIuZnJvbShpbnB1dCwgJ3V0ZjgnKS50b1N0cmluZygnYmFzZTY0Jyk7XHJcbiAgfVxyXG5cclxuICAvLyBGb3IgcG5nIGFuZCBqcGVnIGlucHV0IGlzIGFscmVhZHkgYSBCYXNlNjQgc3RyaW5nXHJcbiAgcmV0dXJuIGlucHV0O1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyBzdHJpbmdpZmllZCBkYXRlIHdpdGhvdXQgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGUoKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgR01UIHRleHQgaW5mb3JtYXRpb25cclxuICByZXR1cm4gbmV3IERhdGUoKS50b1N0cmluZygpLnNwbGl0KCcoJylbMF0udHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgc3RvcmVkIHRpbWUgdmFsdWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVRpbWVcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXROZXdEYXRlVGltZSgpIHtcclxuICByZXR1cm4gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIGl0ZW0gaXMgYW4gb2JqZWN0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNPYmplY3RcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIGl0ZW0gdG8gYmUgY2hlY2tlZC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIGl0ZW0gaXMgYW4gb2JqZWN0LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3QoaXRlbSkge1xyXG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaXRlbSkgPT09ICdbb2JqZWN0IE9iamVjdF0nO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdEVtcHR5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGVtcHR5LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3RFbXB0eShpdGVtKSB7XHJcbiAgcmV0dXJuIChcclxuICAgIHR5cGVvZiBpdGVtID09PSAnb2JqZWN0JyAmJlxyXG4gICAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICAgIGl0ZW0gIT09IG51bGwgJiZcclxuICAgIE9iamVjdC5rZXlzKGl0ZW0pLmxlbmd0aCA9PT0gMFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCBpbiB0aGUgZ2l2ZW4gc3RyaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaXRlbSAtIFRoZSBzdHJpbmcgdG8gYmUgY2hlY2tlZCBmb3IgYSBwcml2YXRlIElQIHJhbmdlIFVSTC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoaXRlbSkge1xyXG4gIGNvbnN0IHJlZ2V4UGF0dGVybnMgPSBbXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/bG9jYWxob3N0XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTI3XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xNzJcXC4oMVs2LTldfDJbMC05XXwzWzAtMV0pXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTkyXFwuMTY4XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi9cclxuICBdO1xyXG5cclxuICByZXR1cm4gcmVnZXhQYXR0ZXJucy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3QoaXRlbSkpO1xyXG59XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIGVsYXBzZWQgdGltZSB1c2luZyB0aGUgTm9kZS5qcyBgcHJvY2Vzcy5ocnRpbWUoKWAgbWV0aG9kLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbWVhc3VyZVRpbWVcclxuICpcclxuICogQHJldHVybnMge0Z1bmN0aW9ufSBBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZWxhcHNlZCB0aW1lIGluIG1pbGxpc2Vjb25kcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBtZWFzdXJlVGltZSgpIHtcclxuICBjb25zdCBzdGFydCA9IHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO1xyXG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xyXG59XHJcblxyXG4vKipcclxuICogUm91bmRzIGEgbnVtYmVyIHRvIHRoZSBzcGVjaWZpZWQgcHJlY2lzaW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcm91bmROdW1iZXJcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IHZhbHVlIC0gVGhlIG51bWJlciB0byBiZSByb3VuZGVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcHJlY2lzaW9uIC0gVGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyB0byByb3VuZCB0by5cclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gVGhlIHJvdW5kZWQgbnVtYmVyLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHJvdW5kTnVtYmVyKHZhbHVlLCBwcmVjaXNpb24gPSAxKSB7XHJcbiAgY29uc3QgbXVsdGlwbGllciA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24gfHwgMCk7XHJcbiAgcmV0dXJuIE1hdGgucm91bmQoK3ZhbHVlICogbXVsdGlwbGllcikgLyBtdWx0aXBsaWVyO1xyXG59XHJcblxyXG4vKipcclxuICogQ29udmVydHMgYSB2YWx1ZSB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB0b0Jvb2xlYW5cclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUaGUgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQgdmFsdWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdG9Cb29sZWFuKGl0ZW0pIHtcclxuICByZXR1cm4gWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBXcmFwcyBjdXN0b20gY29kZSB0byBleGVjdXRlIGl0IHNhZmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIHdyYXBBcm91bmRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tIGNvZGUgdG8gYmUgd3JhcHBlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBGbGFnIHRvIGFsbG93IGxvYWRpbmcgY29kZSBmcm9tIGEgZmlsZS5cclxuICogQHBhcmFtIHtib29sZWFufSBbaXNDYWxsYmFjaz1mYWxzZV0gLSBGbGFnIHRoYXQgaW5kaWNhdGVzIHRoZSByZXR1cm5lZCBjb2RlXHJcbiAqIG11c3QgYmUgaW4gYSBjYWxsYmFjayBmb3JtYXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoc3RyaW5nfG51bGwpfSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBudWxsIGlmIHdyYXBwaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHdyYXBBcm91bmQoY3VzdG9tQ29kZSwgYWxsb3dGaWxlUmVzb3VyY2VzLCBpc0NhbGxiYWNrID0gZmFsc2UpIHtcclxuICBpZiAoY3VzdG9tQ29kZSAmJiB0eXBlb2YgY3VzdG9tQ29kZSA9PT0gJ3N0cmluZycpIHtcclxuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcclxuXHJcbiAgICBpZiAoY3VzdG9tQ29kZS5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgICAgLy8gTG9hZCBhIGZpbGUgaWYgdGhlIGZpbGUgcmVzb3VyY2VzIGFyZSBhbGxvd2VkXHJcbiAgICAgIHJldHVybiBhbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgICA/IHdyYXBBcm91bmQoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY3VzdG9tQ29kZSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgICAgaXNDYWxsYmFja1xyXG4gICAgICAgICAgKVxyXG4gICAgICAgIDogbnVsbDtcclxuICAgIH0gZWxzZSBpZiAoXHJcbiAgICAgICFpc0NhbGxiYWNrICYmXHJcbiAgICAgIChjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24gKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFRyZWF0IGEgZnVuY3Rpb24gYXMgYSBzZWxmLWludm9raW5nIGV4cHJlc3Npb25cclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gT3IgcmV0dXJuIGFzIGEgc3RyaW5naWZpZWQgY29kZVxyXG4gICAgcmV0dXJuIGN1c3RvbUNvZGUucmVwbGFjZSgvOyQvLCAnJyk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgX19kaXJuYW1lLFxyXG4gIGNsZWFyVGV4dCxcclxuICBkZWVwQ29weSxcclxuICBleHBCYWNrb2ZmLFxyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBnZXROZXdEYXRlLFxyXG4gIGdldE5ld0RhdGVUaW1lLFxyXG4gIGlzT2JqZWN0LFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBtZWFzdXJlVGltZSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQSBtb2R1bGUgZm9yIG1hbmFnaW5nIGxvZ2dpbmcgZnVuY3Rpb25hbGl0eSB3aXRoIGN1c3RvbWl6YWJsZVxyXG4gKiBsb2cgbGV2ZWxzLCBjb25zb2xlIGFuZCBmaWxlIGxvZ2dpbmcgb3B0aW9ucywgYW5kIGVycm9yIGhhbmRsaW5nIHN1cHBvcnQuXHJcbiAqIFRoZSBtb2R1bGUgYWxzbyBlbnN1cmVzIHRoYXQgZmlsZS1iYXNlZCBsb2dzIGFyZSBzdG9yZWQgaW4gYSBzdHJ1Y3R1cmVkXHJcbiAqIGRpcmVjdG9yeSwgY3JlYXRpbmcgdGhlIG5lY2Vzc2FyeSBwYXRocyBhdXRvbWF0aWNhbGx5IGlmIHRoZXkgZG8gbm90IGV4aXN0LlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWJzb2x1dGVQYXRoLCBnZXROZXdEYXRlIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG4vLyBUaGUgYXZhaWxhYmxlIGNvbG9yc1xyXG5jb25zdCBjb2xvcnMgPSBbJ3JlZCcsICd5ZWxsb3cnLCAnYmx1ZScsICdncmF5JywgJ2dyZWVuJ107XHJcblxyXG4vLyBUaGUgZGVmYXVsdCBsb2dnaW5nIGNvbmZpZ1xyXG5jb25zdCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBGdWxsIHBhdGggdG8gdGhlIGxvZyBmaWxlXHJcbiAgcGF0aFRvTG9nOiAnJyxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdXHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhIG1lc3NhZ2Ugd2l0aCBhIHNwZWNpZmllZCBsb2cgbGV2ZWwuIEFjY2VwdHMgYSB2YXJpYWJsZSBudW1iZXJcclxuICogb2YgYXJndW1lbnRzLiBUaGUgYXJndW1lbnRzIGFmdGVyIHRoZSBgbGV2ZWxgIGFyZSBwYXNzZWQgdG8gYGNvbnNvbGUubG9nYFxyXG4gKiBhbmQvb3IgdXNlZCB0byBjb25zdHJ1Y3QgYW5kIGFwcGVuZCBtZXNzYWdlcyB0byBhIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Li4udW5rbm93bn0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZ1xyXG4gKiBsZXZlbCBhbmQgdGhlIHJlbWFpbmluZyBhcmUgc3RyaW5ncyB1c2VkIHRvIGJ1aWxkIHRoZSBsb2cgbWVzc2FnZS5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZyguLi5hcmdzKSB7XHJcbiAgY29uc3QgW25ld0xldmVsLCAuLi50ZXh0c10gPSBhcmdzO1xyXG5cclxuICAvLyBDdXJyZW50IGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgbGV2ZWxzRGVzYywgbGV2ZWwgfSA9IGxvZ2dpbmc7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZSBvciBpcyBpdCBhIGJlbmNobWFyayBsb2dcclxuICBpZiAoXHJcbiAgICBuZXdMZXZlbCAhPT0gNSAmJlxyXG4gICAgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aClcclxuICApIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtnZXROZXdEYXRlKCl9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgYW4gZXJyb3IgbWVzc2FnZSBhbG9uZyB3aXRoIGl0cyBzdGFjayB0cmFjZS4gT3B0aW9uYWxseSwgYSBjdXN0b20gbWVzc2FnZVxyXG4gKiBjYW4gYmUgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dXaXRoU3RhY2tcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IG5ld0xldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHN0YWNrIHRyYWNlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGluY2x1ZGVkXHJcbiAqIGluIHRoZSBsb2cgYWxvbmdzaWRlIHRoZSBlcnJvci5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZ1dpdGhTdGFjayhuZXdMZXZlbCwgZXJyb3IsIGN1c3RvbU1lc3NhZ2UpIHtcclxuICAvLyBHZXQgdGhlIG1haW4gbWVzc2FnZVxyXG4gIGNvbnN0IG1haW5NZXNzYWdlID0gY3VzdG9tTWVzc2FnZSB8fCAoZXJyb3IgJiYgZXJyb3IubWVzc2FnZSkgfHwgJyc7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlXHJcbiAgaWYgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aCkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIEFkZCB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9IGVycm9yICYmIGVycm9yLnN0YWNrO1xyXG5cclxuICAvLyBDb21iaW5lIGN1c3RvbSBtZXNzYWdlIG9yIGVycm9yIG1lc3NhZ2Ugd2l0aCBlcnJvciBzdGFjayBtZXNzYWdlLCBpZiBleGlzdHNcclxuICBjb25zdCB0ZXh0cyA9IFttYWluTWVzc2FnZV07XHJcbiAgaWYgKHN0YWNrTWVzc2FnZSkge1xyXG4gICAgdGV4dHMucHVzaCgnXFxuJywgc3RhY2tNZXNzYWdlKTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgdGV4dHMuc2hpZnQoKVtjb2xvcnNbbmV3TGV2ZWwgLSAxXV0sXHJcbiAgICAgICAgLi4udGV4dHNcclxuICAgICAgXSlcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgbG9nZ2luZyBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaW5pdExvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGxvZ2dpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGxvZ2dpbmdgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaW5pdExvZ2dpbmcobG9nZ2luZ09wdGlvbnMpIHtcclxuICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBgbG9nZ2luZ09wdGlvbnNgIG9iamVjdFxyXG4gIGNvbnN0IHsgbGV2ZWwsIGRlc3QsIGZpbGUsIHRvQ29uc29sZSwgdG9GaWxlIH0gPSBsb2dnaW5nT3B0aW9ucztcclxuXHJcbiAgLy8gUmVzZXQgZmxhZ3MgdG8gdGhlIGRlZmF1bHQgdmFsdWVzXHJcbiAgbG9nZ2luZy5wYXRoQ3JlYXRlZCA9IGZhbHNlO1xyXG4gIGxvZ2dpbmcucGF0aFRvTG9nID0gJyc7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nZ2luZyBsZXZlbFxyXG4gIHNldExvZ0xldmVsKGxldmVsKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb25zb2xlIGxvZ2dpbmdcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpO1xyXG5cclxuICAvLyBTZXQgdGhlIGZpbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKGAwYCA9IG5vIGxvZ2dpbmcsXHJcbiAqIGAxYCA9IGVycm9yLCBgMmAgPSB3YXJuaW5nLCBgM2AgPSBub3RpY2UsIGA0YCA9IHZlcmJvc2UsIG9yIGA1YCA9IGJlbmNobWFyaykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRMb2dMZXZlbFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgbG9nIGxldmVsIHRvIGJlIHNldC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRMb2dMZXZlbChsZXZlbCkge1xyXG4gIGlmIChcclxuICAgIE51bWJlci5pc0ludGVnZXIobGV2ZWwpICYmXHJcbiAgICBsZXZlbCA+PSAwICYmXHJcbiAgICBsZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoXHJcbiAgKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYGxldmVsYCBvcHRpb25cclxuICAgIGxvZ2dpbmcubGV2ZWwgPSBsZXZlbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGNvbnNvbGUgbG9nZ2luZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUNvbnNvbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9Db25zb2xlIC0gVGhlIGZsYWcgZm9yIHNldHRpbmcgdGhlIGxvZ2dpbmcgdG8gdGhlIGNvbnNvbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmcodG9Db25zb2xlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0NvbnNvbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9Db25zb2xlID0gISF0b0NvbnNvbGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlIG5hbWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVGaWxlTG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZGVzdCAtIFRoZSBkZXN0aW5hdGlvbiBwYXRoIHdoZXJlIHRoZSBsb2cgZmlsZSBzaG91bGRcclxuICogYmUgc2F2ZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlIC0gVGhlIG5hbWUgb2YgdGhlIGxvZyBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvRmlsZSAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9nZ2luZyBzaG91bGRcclxuICogYmUgZGlyZWN0ZWQgdG8gYSBmaWxlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgdG9GaWxlYCBvcHRpb25cclxuICBsb2dnaW5nLnRvRmlsZSA9ICEhdG9GaWxlO1xyXG5cclxuICAvLyBTZXQgdGhlIGBkZXN0YCBhbmQgYGZpbGVgIG9wdGlvbnMgb25seSBpZiB0aGUgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWRcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIGxvZ2dpbmcuZGVzdCA9IGRlc3QgfHwgJyc7XHJcbiAgICBsb2dnaW5nLmZpbGUgPSBmaWxlIHx8ICcnO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgdGhlIHByb3ZpZGVkIHRleHRzIHRvIGEgZmlsZSwgaWYgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWQuIEl0IGNyZWF0ZXNcclxuICogdGhlIG5lY2Vzc2FyeSBkaXJlY3Rvcnkgc3RydWN0dXJlIGlmIG5vdCBhbHJlYWR5IGNyZWF0ZWQgYW5kIGFwcGVuZHNcclxuICogdGhlIGNvbnRlbnQsIGluY2x1ZGluZyBhbiBvcHRpb25hbCBwcmVmaXgsIHRvIHRoZSBzcGVjaWZpZWQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9nVG9GaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHRleHRzIC0gQW4gYXJyYXkgb2YgdGV4dHMgdG8gYmUgbG9nZ2VkLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4IC0gQW4gb3B0aW9uYWwgcHJlZml4IHRvIGJlIGFkZGVkIHRvIGVhY2ggbG9nIGVudHJ5LlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KSB7XHJcbiAgaWYgKCFsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAvLyBDcmVhdGUgaWYgZG9lcyBub3QgZXhpc3RcclxuICAgICFleGlzdHNTeW5jKGdldEFic29sdXRlUGF0aChsb2dnaW5nLmRlc3QpKSAmJlxyXG4gICAgICBta2RpclN5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpO1xyXG5cclxuICAgIC8vIENyZWF0ZSB0aGUgZnVsbCBwYXRoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyA9IGdldEFic29sdXRlUGF0aChqb2luKGxvZ2dpbmcuZGVzdCwgbG9nZ2luZy5maWxlKSk7XHJcblxyXG4gICAgLy8gV2Ugbm93IGFzc3VtZSB0aGUgcGF0aCBpcyBhdmFpbGFibGUsIGUuZy4gaXQncyB0aGUgcmVzcG9uc2liaWxpdHlcclxuICAgIC8vIG9mIHRoZSB1c2VyIHRvIGNyZWF0ZSB0aGUgcGF0aCB3aXRoIHRoZSBjb3JyZWN0IGFjY2VzcyByaWdodHMuXHJcbiAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICB9XHJcblxyXG4gIC8vIEFkZCB0aGUgY29udGVudCB0byBhIGZpbGVcclxuICBhcHBlbmRGaWxlKFxyXG4gICAgbG9nZ2luZy5wYXRoVG9Mb2csXHJcbiAgICBbcHJlZml4XS5jb25jYXQodGV4dHMpLmpvaW4oJyAnKSArICdcXG4nLFxyXG4gICAgKGVycm9yKSA9PiB7XHJcbiAgICAgIGlmIChlcnJvciAmJiBsb2dnaW5nLnRvRmlsZSAmJiBsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmcsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmdcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvbmZpZ3VyYXRpb24gbWFuYWdlbWVudCBtb2R1bGUgZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuXHJcbiAqIFByb3ZpZGVzIGRlZmF1bHQgY29uZmlndXJhdGlvbnMgdGhhdCBzdXBwb3J0IGVudmlyb25tZW50IHZhcmlhYmxlcywgQ0xJXHJcbiAqIGFyZ3VtZW50cywgYW5kIGludGVyYWN0aXZlIHByb21wdHMgZm9yIGN1c3RvbWl6YXRpb24gb2Ygb3B0aW9ucyBhbmQgZmVhdHVyZXMuXHJcbiAqIEFkZGl0aW9uYWxseSwgaXQgbWFwcyBsZWdhY3kgb3B0aW9ucyB0byBtb2Rlcm4gc3RydWN0dXJlcywgZ2VuZXJhdGVzIG5lc3RlZFxyXG4gKiBhcmd1bWVudCBtYXBwaW5ncywgYW5kIGRpc3BsYXlzIENMSSB1c2FnZSBpbmZvcm1hdGlvbi5cclxuICovXHJcblxyXG4vKipcclxuICogVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYWxsIGF2YWlsYWJsZSBvcHRpb25zLCBvcmdhbml6ZWRcclxuICogYnkgc2VjdGlvbnMuXHJcbiAqXHJcbiAqIFRoaXMgb2JqZWN0IGluY2x1ZGVzOlxyXG4gKiAtIERlZmF1bHQgdmFsdWVzIGZvciBlYWNoIG9wdGlvblxyXG4gKiAtIERhdGEgdHlwZXMgZm9yIHZhbGlkYXRpb25cclxuICogLSBOYW1lcyBvZiBjb3JyZXNwb25kaW5nIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiAtIERlc2NyaXB0aW9ucyBvZiBlYWNoIHByb3BlcnR5XHJcbiAqIC0gSW5mb3JtYXRpb24gdXNlZCBmb3IgcHJvbXB0cyBpbiBpbnRlcmFjdGl2ZSBjb25maWd1cmF0aW9uXHJcbiAqIC0gW09wdGlvbmFsXSBDb3JyZXNwb25kaW5nIENMSSBhcmd1bWVudCBuYW1lcyBmb3IgQ0xJIHVzYWdlXHJcbiAqIC0gW09wdGlvbmFsXSBMZWdhY3kgbmFtZXMgZnJvbSB0aGUgcHJldmlvdXMgUGhhbnRvbUpTLWJhc2VkIHNlcnZlclxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZmF1bHRDb25maWcgPSB7XHJcbiAgcHVwcGV0ZWVyOiB7XHJcbiAgICBhcmdzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJy0tYWxsb3ctcnVubmluZy1pbnNlY3VyZS1jb250ZW50JyxcclxuICAgICAgICAnLS1hc2gtbm8tbnVkZ2VzJyxcclxuICAgICAgICAnLS1hdXRvcGxheS1wb2xpY3k9dXNlci1nZXN0dXJlLXJlcXVpcmVkJyxcclxuICAgICAgICAnLS1ibG9jay1uZXctd2ViLWNvbnRlbnRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWFjY2VsZXJhdGVkLTJkLWNhbnZhcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLW5ldHdvcmtpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1icmVha3BhZCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jaGVja2VyLWltYWdpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC1leHRlbnNpb25zLXdpdGgtYmFja2dyb3VuZC1wYWdlcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtdXBkYXRlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRlZmF1bHQtYXBwcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZXYtc2htLXVzYWdlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRvbWFpbi1yZWxpYWJpbGl0eScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1leHRlbnNpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWZlYXR1cmVzPUNhbGN1bGF0ZU5hdGl2ZVdpbk9jY2x1c2lvbixJbnRlcmVzdEZlZWRDb250ZW50U3VnZ2VzdGlvbnMsV2ViT1RQJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1sb2dnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXBvcHVwLWJsb2NraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1yZW5kZXJlci1iYWNrZ3JvdW5kaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlYXJjaC1lbmdpbmUtY2hvaWNlLXNjcmVlbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNpdGUtaXNvbGF0aW9uLXRyaWFscycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zcGVlY2gtYXBpJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXN5bmMnLFxyXG4gICAgICAgICctLWVuYWJsZS11bnNhZmUtd2ViZ3B1JyxcclxuICAgICAgICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcclxuICAgICAgICAnLS1oaWRlLXNjcm9sbGJhcnMnLFxyXG4gICAgICAgICctLW1ldHJpY3MtcmVjb3JkaW5nLW9ubHknLFxyXG4gICAgICAgICctLW11dGUtYXVkaW8nLFxyXG4gICAgICAgICctLW5vLWRlZmF1bHQtYnJvd3Nlci1jaGVjaycsXHJcbiAgICAgICAgJy0tbm8tZmlyc3QtcnVuJyxcclxuICAgICAgICAnLS1uby1waW5ncycsXHJcbiAgICAgICAgJy0tbm8tc2FuZGJveCcsXHJcbiAgICAgICAgJy0tbm8tc3RhcnR1cC13aW5kb3cnLFxyXG4gICAgICAgICctLW5vLXp5Z290ZScsXHJcbiAgICAgICAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxyXG4gICAgICAgICctLXByb2Nlc3MtcGVyLXRhYicsXHJcbiAgICAgICAgJy0tdXNlLW1vY2sta2V5Y2hhaW4nXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdQVVBQRVRFRVJfQVJHUycsXHJcbiAgICAgIGNsaU5hbWU6ICdwdXBwZXRlZXJBcmdzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBcnJheSBvZiBQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgICBzZXBhcmF0b3I6ICc7J1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBoaWdoY2hhcnRzOiB7XHJcbiAgICB2ZXJzaW9uOiB7XHJcbiAgICAgIHZhbHVlOiAnbGF0ZXN0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgdmVyc2lvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNkblVybDoge1xyXG4gICAgICB2YWx1ZTogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmb3JjZUZldGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmxhZyB0byByZWZldGNoIHNjcmlwdHMgYWZ0ZXIgZWFjaCBzZXJ2ZXIgcmVydW4nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhY2hlUGF0aDoge1xyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DQUNIRV9QQVRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEaXJlY3RvcnkgcGF0aCBmb3IgY2FjaGVkIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2hpZ2hjaGFydHMnLCAnaGlnaGNoYXJ0cy1tb3JlJywgJ2hpZ2hjaGFydHMtM2QnXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbW9kdWxlU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdzdG9jaycsXHJcbiAgICAgICAgJ21hcCcsXHJcbiAgICAgICAgJ2dhbnR0JyxcclxuICAgICAgICAnZXhwb3J0aW5nJyxcclxuICAgICAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgICAgICdhY2Nlc3NpYmlsaXR5JyxcclxuICAgICAgICAvLyAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgICAgICdib29zdCcsXHJcbiAgICAgICAgJ2RhdGEnLFxyXG4gICAgICAgICdkYXRhLXRvb2xzJyxcclxuICAgICAgICAnZHJhZ2dhYmxlLXBvaW50cycsXHJcbiAgICAgICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAgICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICAgICAnaGVhdG1hcCcsXHJcbiAgICAgICAgJ3RpbGVtYXAnLFxyXG4gICAgICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAgICAgJ3RpbWVsaW5lJyxcclxuICAgICAgICAndHJlZW1hcCcsXHJcbiAgICAgICAgJ3RyZWVncmFwaCcsXHJcbiAgICAgICAgJ2l0ZW0tc2VyaWVzJyxcclxuICAgICAgICAnZHJpbGxkb3duJyxcclxuICAgICAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAgICAgJ2J1bGxldCcsXHJcbiAgICAgICAgJ2Z1bm5lbCcsXHJcbiAgICAgICAgJ2Z1bm5lbDNkJyxcclxuICAgICAgICAnZ2VvaGVhdG1hcCcsXHJcbiAgICAgICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAgICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAgICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzZXJpZXMtb24tcG9pbnQnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgLy8gJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaScsXHJcbiAgICAgICAgJ2Zsb3dtYXAnLFxyXG4gICAgICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAgICAgJ25hdmlnYXRvcicsXHJcbiAgICAgICAgJ3RleHRwYXRoJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBtb2R1bGUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogWydpbmRpY2F0b3JzLWFsbCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBpbmRpY2F0b3Igc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQuanMvMi4zMC4xL21vbWVudC5taW4uanMnLFxyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQtdGltZXpvbmUvMC41LjQ1L21vbWVudC10aW1lem9uZS13aXRoLWRhdGEubWluLmpzJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DVVNUT01fU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBjdXN0b20gc2NyaXB0cyBvciBkZXBlbmRlbmNpZXMgdG8gZmV0Y2gnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGV4cG9ydDoge1xyXG4gICAgaW5maWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0lORklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnB1dCBmaWxlbmFtZSB3aXRoIHR5cGUsIGZvcm1hdHRlZCBjb3JyZWN0bHkgYXMgSlNPTiBvciBTVkcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbnN0cjoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5TVFInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3ZlcnJpZGVzIHRoZSBgaW5maWxlYCB3aXRoIEpTT04sIHN0cmluZ2lmaWVkIEpTT04sIG9yIFNWRyBpbnB1dCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09QVElPTlMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FsaWFzIGZvciB0aGUgYGluc3RyYCBvcHRpb24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzdmc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU1ZHJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTVkcgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCB0byByZW5kZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CQVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdCYXRjaCBqb2Igc3RyaW5nIHdpdGggaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09VVEZJTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3V0cHV0IGZpbGVuYW1lIHdpdGggdHlwZS4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcgYW5kIGlnbm9yZXMgYHR5cGVgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIGV4cG9ydCBmb3JtYXQuIENhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBwbmcnLFxyXG4gICAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0NPTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDaGFydCBjb25zdHJ1Y3Rvci4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgICBoaW50OiAnRGVmYXVsdDogY2hhcnQnLFxyXG4gICAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGI2NDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CNjQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gdGhlIGNoYXJ0IHNob3VsZCBiZSByZWNlaXZlZCBpbiBCYXNlNjQgZm9ybWF0IGluc3RlYWQgb2YgYmluYXJ5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBub0Rvd25sb2FkOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX05PX0RPV05MT0FEJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZXRoZXIgb3Igbm90IHRvIGluY2x1ZGUgb3IgZXhjbHVkZSBhdHRhY2htZW50IGhlYWRlcnMgaW4gdGhlIHJlc3BvbnNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSEVJR0hUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1NDQUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0RlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgbWluOiAwLjEsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9HTE9CQUxfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggZ2xvYmFsIG9wdGlvbnMgZm9yIEhpZ2hjaGFydHMuc2V0T3B0aW9ucycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVEhFTUVfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHdlYnBhZ2UgcmVuZGVyaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgZHVyaW5nIGV4cG9ydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FsbG93cyBvciBkaXNhbGxvd3MgaW5qZWN0aW9uIG9mIGZpbGVzeXN0ZW0gcmVzb3VyY2VzIChkaXNhYmxlZCBpbiBzZXJ2ZXIgbW9kZSknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DQUxMQkFDSycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIENhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgLmpzIGZpbGVuYW1lJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZXMgYXMgSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgZmlsZW5hbWUsIGNvbnRhaW5pbmcgZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSB3aXRoIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdQcm9tcHQtYmFzZWQgb3B0aW9uIHNldHRpbmcsIHNhdmVkIHRvIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVTZXJ2ZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0cyB0aGUgc2VydmVyIHdoZW4gdHJ1ZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaG9zdDoge1xyXG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIb3N0bmFtZSBvZiB0aGUgc2VydmVyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUG9ydCBudW1iZXIgZm9yIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHVwbG9hZExpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiAzLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9VUExPQURfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVxdWVzdCBib2R5IHNpemUgaW4gTUInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAnc2VydmVyQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Rpc3BsYXlzIG9yIG5vdCBhY3Rpb24gZHVyYXRpb25zIGluIG1pbGxpc2Vjb25kcyBkdXJpbmcgc2VydmVyIHJlcXVlc3RzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwcm94eToge1xyXG4gICAgICBob3N0OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eUhvc3QnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnSG9zdCBvZiB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB3aW5kb3c6IHtcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RpbWUgd2luZG93IGluIG1pbnV0ZXMgZm9yIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnRGVsYXkgZHVyYXRpb24gYmV0d2VlbiBzdWNjZXNzaXZlIHJlcXVlc3RzIGJlZm9yZSByZWFjaGluZyB0aGUgbGltaXQnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcEtleToge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0tleSB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwVG9rZW5gJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBUb2tlbjoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVG9rZW4gdG8gYnlwYXNzIHRoZSByYXRlIGxpbWl0ZXIsIHVzZWQgd2l0aCBgc2tpcEtleWAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdGb3JjZXMgdGhlIHNlcnZlciB0byB1c2UgSFRUUFMgb25seSB3aGVuIHRydWUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IGZvciB0aGUgU1NMIHNlcnZlcicsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xDZXJ0UGF0aCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWluaW11bSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICB3b3JrTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDQwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTnVtYmVyIG9mIHRhc2tzIGEgd29ya2VyIGNhbiBoYW5kbGUgYmVmb3JlIHJlc3RhcnRpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpZGxlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMzAwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGlkbGUgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAyMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGJlZm9yZSByZXRyeWluZyByZXNvdXJjZSBjcmVhdGlvbiBvbiBmYWlsdXJlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgdG8gY2hlY2sgYW5kIGRlc3Ryb3kgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1Nob3dzIHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdMb2dnaW5nIHZlcmJvc2l0eSBsZXZlbCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICByb3VuZDogMCxcclxuICAgICAgICBtaW46IDAsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnTG9nIGZpbGUgbmFtZS4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCB0byBiZSBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZXN0OiB7XHJcbiAgICAgIHZhbHVlOiAnbG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFJlcXVpcmVzIGBsb2dUb0ZpbGVgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBjb25zb2xlIGxvZ2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvRmlsZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICB1aToge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVVpJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGVuZHBvaW50IHJvdXRlIGZvciB0aGUgVUknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBOb2RlLmpzIGVudmlyb25tZW50IHR5cGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0Rpc3BsYXkgb3Igc2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoYXJkUmVzZXRQYWdlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfSEFSRF9SRVNFVF9QQUdFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaGV0aGVyIG9yIG5vdCB0byByZXNldCB0aGUgcGFnZSBjb250ZW50IGVudGlyZWx5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBicm93c2VyU2hlbGxNb2RlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHNldCB0aGUgYnJvd3NlciB0byBydW4gaW4gc2hlbGwgbW9kZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBkZWJ1Zzoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZURlYnVnJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSB1bmRlcmx5aW5nIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlYWRsZXNzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBoZWFkbGVzcyBtb2RlIGR1cmluZyBkZWJ1Z2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRldnRvb2xzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgRGV2VG9vbHMgaW4gaGVhZGZ1bCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGxpc3RlbmluZyB0byBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGR1bXBpbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RVTVBJTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdSZWRpcmVjdHMgb3Igbm90IGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gcHJvY2Vzcy5zdGRvdXQgYW5kIHByb2Nlc3Muc3RkZXJyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzbG93TW86IHtcclxuICAgICAgdmFsdWU6IDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVsYXlzIFB1cHBldGVlciBvcGVyYXRpb25zIGJ5IHRoZSBzcGVjaWZpZWQgbWlsbGlzZWNvbmRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IHVzZWQgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLy8gUHJvcGVydGllcyBuZXN0aW5nIGxldmVsIG9mIGFsbCBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRQcm9wcyA9IF9jcmVhdGVOZXN0ZWRQcm9wcyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIFByb3BlcnRpZXMgbmFtZXMgdGhhdCBzaG91bGQgbm90IGJlIHJlY3Vyc2l2ZWx5IG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IF9jcmVhdGVBYnNvbHV0ZVByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdlbmVyYXRlcyBhIG1hcHBpbmcgb2YgbmVzdGVkIGFyZ3VtZW50IGNoYWlucyBmcm9tIGEgbmVzdGVkXHJcbiAqIGNvbmZpZyBvYmplY3QuIFRoaXMgZnVuY3Rpb24gdHJhdmVyc2VzIGEgbmVzdGVkIG9iamVjdCBhbmQgY3JlYXRlcyBhIG1hcHBpbmdcclxuICogd2hlcmUgZWFjaCBrZXkgaXMgYW4gYXJndW1lbnQgbmFtZSAoZWl0aGVyIGZyb20gYGNsaU5hbWVgLCBgbGVnYWN5TmFtZWAsXHJcbiAqIG9yIHRoZSBvcmlnaW5hbCBrZXkpIGFuZCBlYWNoIHZhbHVlIGlzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY2hhaW5cclxuICogb2YgbmVzdGVkIHByb3BlcnRpZXMgbGVhZGluZyB0byB0aGF0IGFyZ3VtZW50LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbbmVzdGVkUHJvcHM9e31dIC0gVGhlIGFjY3VtdWxhdG9yIG9iamVjdCBmb3Igc3RvcmluZ1xyXG4gKiB0aGUgcmVzdWx0aW5nIGFyZ3VtZW50cyBjaGFpbnMuICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcHJvcENoYWluPScnXSAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzLFxyXG4gKiB1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbi4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gb2JqZWN0IG1hcHBpbmcgYXJndW1lbnQgbmFtZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZ1xyXG4gKiBuZXN0ZWQgcHJvcGVydHkgY2hhaW5zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzKGNvbmZpZywgbmVzdGVkUHJvcHMgPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgX2NyZWF0ZU5lc3RlZFByb3BzKGVudHJ5LCBuZXN0ZWRQcm9wcywgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBuZXN0ZWRQcm9wc1tlbnRyeS5jbGlOYW1lIHx8IGtleV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgaWYgKGVudHJ5LmxlZ2FjeU5hbWUgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2tleX1gLnN1YnN0cmluZygxKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIG9iamVjdCB3aXRoIG5lc3RlZCBhcmd1bWVudCBjaGFpbnNcclxuICByZXR1cm4gbmVzdGVkUHJvcHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSBnYXRoZXJzIHRoZSBuYW1lcyBvZiBwcm9wZXJ0aWVzIGZyb20gYSBjb25maWd1cmF0aW9uIG9iamVjdCB0aGF0XHJcbiAqIGNhbiBiZSB0cmVhdGVkIGFzIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZXNlIHByb3BlcnRpZXMgaGF2ZSB2YWx1ZXMgdGhhdFxyXG4gKiBhcmUgb2JqZWN0cyBhbmQgZG8gbm90IGNvbnRhaW4gZnVydGhlciBuZXN0ZWQgZGVwdGggd2hlbiBtZXJnaW5nIGFuIG9iamVjdFxyXG4gKiBjb250YWluaW5nIHRoZXNlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbYWJzb2x1dGVQcm9wcz1bXV0gLSBBbiBhcnJheSB0byBjb2xsZWN0IHRoZSBuYW1lc1xyXG4gKiBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxzdHJpbmc+fSBBbiBhcnJheSBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZVxyXG4gKiBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZUFic29sdXRlUHJvcHMoY29uZmlnLCBhYnNvbHV0ZVByb3BzID0gW10pIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudHlwZXMgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzXHJcbiAgICAgIF9jcmVhdGVBYnNvbHV0ZVByb3BzKGVudHJ5LCBhYnNvbHV0ZVByb3BzKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIHRoZSBvcHRpb24gY2FuIGJlIGFuIG9iamVjdCwgc2F2ZSBpdHMgdHlwZSBpbiB0aGUgYXJyYXlcclxuICAgICAgaWYgKGVudHJ5LnR5cGVzLmluY2x1ZGVzKCdPYmplY3QnKSkge1xyXG4gICAgICAgIGFic29sdXRlUHJvcHMucHVzaChrZXkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgYXJyYXkgd2l0aCB0aGUgbmFtZXMgb2YgYWJzb2x1dGUgcHJvcGVydGllc1xyXG4gIHJldHVybiBhYnNvbHV0ZVByb3BzO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRQcm9wcyxcclxuICBhYnNvbHV0ZVByb3BzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZFxyXG4gKiB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBgZW52c2AuIFdlIHNob3VsZCBub3QgdXNlIHRoZSBgcHJvY2Vzcy5lbnZgXHJcbiAqIGRpcmVjdGx5IGluIHRoZSBhcHBsaWNhdGlvbiBhcyB0aGVzZSB3b3VsZCBub3QgYmUgcGFyc2VkIHByb3Blcmx5LlxyXG4gKlxyXG4gKiBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSBwYXJzZWQgYW5kIHZhbGlkYXRlZCBvbmx5IG9uY2Ugd2hlblxyXG4gKiB0aGUgYXBwbGljYXRpb24gc3RhcnRzLiBXZSBzaG91bGQgd3JpdGUgYSBjdXN0b20gdmFsaWRhdG9yIG9yIGEgdHJhbnNmb3JtZXJcclxuICogZm9yIGVhY2ggb2YgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gcHVwcGV0ZWVyXHJcbiAgUFVQUEVURUVSX0FSR1M6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIGhpZ2hjaGFydHNcclxuICBISUdIQ0hBUlRTX1ZFUlNJT046IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PiAvXihsYXRlc3R8XFxkKyhcXC5cXGQrKXswLDJ9KSQvLnRlc3QodmFsdWUpIHx8IHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vJykgfHxcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcclxuICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgSElHSENIQVJUU19DRE5fVVJMLiBJdCBzaG91bGQgc3RhcnQgd2l0aCBodHRwOi8vIG9yIGh0dHBzOi8vLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTOiB2LmFycmF5KGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jb3JlU2NyaXB0cy52YWx1ZSksXHJcbiAgSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5tb2R1bGVTY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9JTkZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0lOU1RSOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9TVkc6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0JBVENIOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PVVRGSUxFOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9UWVBFOiB2LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLFxyXG4gIEVYUE9SVF9DT05TVFI6IHYuZW51bShbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddKSxcclxuICBFWFBPUlRfQjY0OiB2LmJvb2xlYW4oKSxcclxuICBFWFBPUlRfTk9fRE9XTkxPQUQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0dMT0JBTF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9USEVNRV9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19DVVNUT01fQ09ERTogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ0FMTEJBQ0s6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX1JFU09VUkNFUzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfTE9BRF9DT05GSUc6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUc6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlclxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1VQTE9BRF9MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgTE9HR0lOR19UT19GSUxFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gdWlcclxuICBVSV9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PREVfRU5WOiB2LmVudW0oWydkZXZlbG9wbWVudCcsICdwcm9kdWN0aW9uJywgJ3Rlc3QnXSksXHJcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0hBUkRfUkVTRVRfUEFHRTogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgTWFuYWdlcyBjb25maWd1cmF0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGJ5IGxvYWRpbmdcclxuICogYW5kIG1lcmdpbmcgb3B0aW9ucyBmcm9tIG11bHRpcGxlIHNvdXJjZXMsIHN1Y2ggYXMgZGVmYXVsdCBzZXR0aW5ncyxcclxuICogZW52aXJvbm1lbnQgdmFyaWFibGVzLCB1c2VyLXByb3ZpZGVkIG9wdGlvbnMsIGFuZCBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKiBFbnN1cmVzIHRoZSBnbG9iYWwgb3B0aW9ucyBhcmUgdXAtdG8tZGF0ZSB3aXRoIHRoZSBoaWdoZXN0IHByaW9yaXR5IHZhbHVlcy5cclxuICogUHJvdmlkZXMgZnVuY3Rpb25zIGZvciBhY2Nlc3NpbmcgYW5kIHVwZGF0aW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZGVlcENvcHksIGdldEFic29sdXRlUGF0aCwgaXNPYmplY3QgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCB7IGFic29sdXRlUHJvcHMsIG5lc3RlZFByb3BzLCBkZWZhdWx0Q29uZmlnIH0gZnJvbSAnLi9zY2hlbWFzL2NvbmZpZy5qcyc7XHJcblxyXG4vLyBTZXRzIHRoZSBnbG9iYWwgb3B0aW9ucyB3aXRoIGluaXRpYWwgdmFsdWVzIGZyb20gdGhlIGRlZmF1bHQgY29uZmlnXHJcbmNvbnN0IGdsb2JhbE9wdGlvbnMgPSBfaW5pdE9wdGlvbnMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IG9yIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWxcclxuICogb3B0aW9ucyBvYmplY3QsIGJhc2VkIG9uIHRoZSBgZ2V0Q29weWAgZmxhZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbZ2V0Q29weT10cnVlXSAtIFNwZWNpZmllcyB3aGV0aGVyIHRvIHJldHVybiBhIGNvcGllZFxyXG4gKiBvYmplY3Qgb2YgdGhlIGdsb2JhbCBvcHRpb25zIChgdHJ1ZWApIG9yIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gKiBvYmplY3QgKGBmYWxzZWApLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBIGNvcHkgb2YgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCwgb3IgYSByZWZlcmVuY2VcclxuICogdG8gdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRPcHRpb25zKGdldENvcHkgPSB0cnVlKSB7XHJcbiAgcmV0dXJuIGdldENvcHkgPyBkZWVwQ29weShnbG9iYWxPcHRpb25zKSA6IGdsb2JhbE9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IG9yIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWxcclxuICogb3B0aW9ucyBvYmplY3QsIGJhc2VkIG9uIHRoZSBgZ2V0Q29weWAgZmxhZy5cclxuICpcclxuICogQGZ1bmN0aW9uIHVwZGF0ZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgbmV3IG9wdGlvbnMgdG8gYmVcclxuICogbWVyZ2VkIGludG8gdGhlIGdsb2JhbCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PWZhbHNlXSAtIERldGVybWluZXMgd2hldGhlciB0byBtZXJnZSB0aGUgbmV3XHJcbiAqIG9wdGlvbnMgaW50byBhIGNvcHkgb2YgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYHRydWVgKSBvciBkaXJlY3RseSBpbnRvXHJcbiAqIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3QgKGBmYWxzZWApLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBvcHRpb25zIG9iamVjdCwgZWl0aGVyIHRoZSBtb2RpZmllZCBnbG9iYWxcclxuICogb3B0aW9ucyBvciBhIG1vZGlmaWVkIGNvcHksIGJhc2VkIG9uIHRoZSB2YWx1ZSBvZiBgZ2V0Q29weWAuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdXBkYXRlT3B0aW9ucyhuZXdPcHRpb25zLCBnZXRDb3B5ID0gZmFsc2UpIHtcclxuICAvLyBNZXJnZSBuZXcgb3B0aW9ucyB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb3IgaXRzIGNvcHkgYW5kIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgcmV0dXJuIF9tZXJnZU9wdGlvbnMoZ2V0T3B0aW9ucyhnZXRDb3B5KSwgbmV3T3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHZhbHVlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEksIGtlZXBpbmdcclxuICogdGhlIHByaW5jaXBsZSBvZiBvcHRpb25zIGxvYWQgcHJpb3JpdHkuIFRoaXMgZnVuY3Rpb24gYWNjZXB0cyBhIGBjbGlBcmdzYFxyXG4gKiBhcnJheSBjb250YWluaW5nIGFyZ3VtZW50cyBmcm9tIHRoZSBDTEksIHdoaWNoIHdpbGwgYmUgdmFsaWRhdGVkIGFuZCBhcHBsaWVkXHJcbiAqIGlmIHByb3ZpZGVkLlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSBhIGN1c3RvbSBKU09OIGZpbGUgKGxvYWRlZCBieSB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uKS5cclxuICogMi4gVmFsdWVzIGZyb20gdGhlIGNvbW1hbmQgbGluZSBpbnRlcmZhY2UgKENMSSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRDbGlPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBBbiBhcnJheSBvZiBjb21tYW5kIGxpbmUgYXJndW1lbnRzIHVzZWRcclxuICogZm9yIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIHVwZGF0ZWQgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCByZWZsZWN0aW5nIHRoZSBtZXJnZWRcclxuICogY29uZmlndXJhdGlvbiBmcm9tIHNvdXJjZXMgcHJvdmlkZWQgdGhyb3VnaCB0aGUgQ0xJLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldENsaU9wdGlvbnMoY2xpQXJncykge1xyXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcclxuICBpZiAoY2xpQXJncyAmJiBBcnJheS5pc0FycmF5KGNsaUFyZ3MpICYmIGNsaUFyZ3MubGVuZ3RoKSB7XHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBjdXN0b20gSlNPTiBsb2FkZWQgdmlhIHRoZSBgLS1sb2FkQ29uZmlnYFxyXG4gICAgY29uc3QgY29uZmlnT3B0aW9ucyA9IF9sb2FkQ29uZmlnRmlsZShjbGlBcmdzKTtcclxuXHJcbiAgICAvLyBVcGRhdGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCB0aGUgdmFsdWVzIGZyb20gdGhlIGBjb25maWdPcHRpb25zYFxyXG4gICAgdXBkYXRlT3B0aW9ucyhjb25maWdPcHRpb25zKTtcclxuXHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBDTElcclxuICAgIGNvbnN0IGNsaU9wdGlvbnMgPSBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNsaU9wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9wdGlvbnNcclxuICByZXR1cm4gZ2V0T3B0aW9ucyhmYWxzZSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNYXBzIG9sZC1zdHJ1Y3R1cmVkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyAoUGhhbnRvbUpTKSB0byBhIG5ldyBmb3JtYXRcclxuICogKFB1cHBldGVlcikuIFRoaXMgZnVuY3Rpb24gY29udmVydHMgZmxhdCwgb2xkLXN0cnVjdHVyZWQgb3B0aW9ucyBpbnRvXHJcbiAqIGEgbmV3LCBuZXN0ZWQgY29uZmlndXJhdGlvbiBmb3JtYXQgYmFzZWQgb24gYSBwcmVkZWZpbmVkIG1hcHBpbmdcclxuICogKGBuZXN0ZWRQcm9wc2ApLiBUaGUgbmV3IGZvcm1hdCBpcyB1c2VkIGZvciBQdXBwZXRlZXIsIHdoaWxlIHRoZSBvbGQgZm9ybWF0XHJcbiAqIHdhcyB1c2VkIGZvciBQaGFudG9tSlMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtYXBUb05ld09wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9sZE9wdGlvbnMgLSBUaGUgb2xkLCBmbGF0IGNvbmZpZ3VyYXRpb24gb3B0aW9uc1xyXG4gKiB0byBiZSBjb252ZXJ0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgbmV3IG9iamVjdCBjb250YWluaW5nIG9wdGlvbnMgc3RydWN0dXJlZCBhY2NvcmRpbmdcclxuICogdG8gdGhlIG1hcHBpbmcgZGVmaW5lZCBpbiBgbmVzdGVkUHJvcHNgIG9yIGFuIGVtcHR5IG9iamVjdCBpZiB0aGUgcHJvdmlkZWRcclxuICogYG9sZE9wdGlvbnNgIGlzIG5vdCBhIGNvcnJlY3Qgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9ucyhvbGRPcHRpb25zKSB7XHJcbiAgLy8gQW4gb2JqZWN0IGZvciB0aGUgbmV3IHN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gIGNvbnN0IG5ld09wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgdmFsdWUgaXMgYSBjb3JyZWN0IG9iamVjdFxyXG4gIGlmIChpc09iamVjdChvbGRPcHRpb25zKSkge1xyXG4gICAgLy8gSXRlcmF0ZSBvdmVyIGVhY2gga2V5LXZhbHVlIHBhaXIgaW4gdGhlIG9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnNcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG9sZE9wdGlvbnMpKSB7XHJcbiAgICAgIC8vIElmIHRoZXJlIGlzIGEgbmVzdGVkIG1hcHBpbmcsIHNwbGl0IGl0IGludG8gYSBwcm9wZXJ0aWVzIGNoYWluXHJcbiAgICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW2tleV1cclxuICAgICAgICA/IG5lc3RlZFByb3BzW2tleV0uc3BsaXQoJy4nKVxyXG4gICAgICAgIDogW107XHJcblxyXG4gICAgICAvLyBJZiBpdCBpcyB0aGUgbGFzdCBwcm9wZXJ0eSBpbiB0aGUgY2hhaW4sIGFzc2lnbiB0aGUgdmFsdWUsIG90aGVyd2lzZSxcclxuICAgICAgLy8gY3JlYXRlIG9yIHJldXNlIHRoZSBuZXN0ZWQgb2JqZWN0XHJcbiAgICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoXHJcbiAgICAgICAgKG9iaiwgcHJvcCwgaW5kZXgpID0+XHJcbiAgICAgICAgICAob2JqW3Byb3BdID1cclxuICAgICAgICAgICAgcHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4ID8gdmFsdWUgOiBvYmpbcHJvcF0gfHwge30pLFxyXG4gICAgICAgIG5ld09wdGlvbnNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICAnW2NvbmZpZ10gTm8gY29ycmVjdCBvYmplY3Qgd2l0aCBvcHRpb25zIHdhcyBwcm92aWRlZC4gUmV0dXJuaW5nIGFuIGVtcHR5IGFycmF5LidcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIG5ldywgc3RydWN0dXJlZCBvcHRpb25zIG9iamVjdFxyXG4gIHJldHVybiBuZXdPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogVmFsaWRhdGVzLCBwYXJzZXMsIGFuZCBjaGVja3MgaWYgdGhlIHByb3ZpZGVkIGNvbmZpZyBpcyBhbGxvd2VkIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnXHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gY29uZmlnIC0gVGhlIGNvbmZpZyB0byBiZSB2YWxpZGF0ZWQgYW5kIHBhcnNlZCBhcyBhIHNldFxyXG4gKiBvZiBvcHRpb25zLiBNdXN0IGJlIGVpdGhlciBhbiBvYmplY3Qgb3IgYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3RvU3RyaW5nPWZhbHNlXSAtIFdoZXRoZXIgdG8gcmV0dXJuIGEgc3RyaW5naWZpZWQgdmVyc2lvblxyXG4gKiBvZiB0aGUgcGFyc2VkIGNvbmZpZy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICogQHBhcmFtIHtib29sZWFufSBbYWxsb3dGdW5jdGlvbnM9ZmFsc2VdIC0gV2hldGhlciB0byBhbGxvdyBmdW5jdGlvbnNcclxuICogaW4gdGhlIHBhcnNlZCBjb25maWcuIElmIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkLiBPdGhlcndpc2UsIHdoZW5cclxuICogYSBmdW5jdGlvbiBpcyBmb3VuZCwgbnVsbCBpcyByZXR1cm5lZC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8c3RyaW5nfG51bGwpfSBSZXR1cm5zIGEgcGFyc2VkIHNldCBvZiBvcHRpb25zIG9iamVjdCxcclxuICogYSBzdHJpbmdpZmllZCBzZXQgb2Ygb3B0aW9ucyBvYmplY3QgaWYgdGhlIGB0b1N0cmluZ2AgaXMgdHJ1ZSwgYW5kIG51bGxcclxuICogaWYgdGhlIGNvbmZpZyBpcyBub3QgYSB2YWxpZCBzZXQgb2Ygb3B0aW9ucyBvciBwYXJzaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzQWxsb3dlZENvbmZpZyhcclxuICBjb25maWcsXHJcbiAgdG9TdHJpbmcgPSBmYWxzZSxcclxuICBhbGxvd0Z1bmN0aW9ucyA9IGZhbHNlXHJcbikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBBY2NlcHQgb25seSBvYmplY3RzIGFuZCBzdHJpbmdzXHJcbiAgICBpZiAoIWlzT2JqZWN0KGNvbmZpZykgJiYgdHlwZW9mIGNvbmZpZyAhPT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gUmV0dXJuIG51bGwgaWYgYW55IG90aGVyIHR5cGVcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBvYmplY3QgcmVwcmVzZW50YXRpb24gb2YgdGhlIG9yaWdpbmFsIGNvbmZpZ1xyXG4gICAgY29uc3Qgb2JqZWN0Q29uZmlnID1cclxuICAgICAgdHlwZW9mIGNvbmZpZyA9PT0gJ3N0cmluZydcclxuICAgICAgICA/IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IGV2YWwoYCgke2NvbmZpZ30pYClcclxuICAgICAgICAgIDogSlNPTi5wYXJzZShjb25maWcpXHJcbiAgICAgICAgOiBjb25maWc7XHJcblxyXG4gICAgLy8gUHJlc2VydmUgb3IgcmVtb3ZlIHBvdGVudGlhbCBmdW5jdGlvbnMgYmFzZWQgb24gdGhlIGBhbGxvd0Z1bmN0aW9uc2AgZmxhZ1xyXG4gICAgY29uc3Qgc3RyaW5naWZpZWRPcHRpb25zID0gX29wdGlvbnNTdHJpbmdpZnkoXHJcbiAgICAgIG9iamVjdENvbmZpZyxcclxuICAgICAgYWxsb3dGdW5jdGlvbnMsXHJcbiAgICAgIGZhbHNlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFBhcnNlIHRoZSBjb25maWcgdG8gY2hlY2sgaWYgaXQgaXMgdmFsaWQgc2V0IG9mIG9wdGlvbnNcclxuICAgIGNvbnN0IHBhcnNlZE9wdGlvbnMgPSBhbGxvd0Z1bmN0aW9uc1xyXG4gICAgICA/IEpTT04ucGFyc2UoXHJcbiAgICAgICAgICBfb3B0aW9uc1N0cmluZ2lmeShvYmplY3RDb25maWcsIGFsbG93RnVuY3Rpb25zLCB0cnVlKSxcclxuICAgICAgICAgIChfLCB2YWx1ZSkgPT5cclxuICAgICAgICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJiB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpXHJcbiAgICAgICAgICAgICAgPyBldmFsKGAoJHt2YWx1ZX0pYClcclxuICAgICAgICAgICAgICA6IHZhbHVlXHJcbiAgICAgICAgKVxyXG4gICAgICA6IEpTT04ucGFyc2Uoc3RyaW5naWZpZWRPcHRpb25zKTtcclxuXHJcbiAgICAvLyBSZXR1cm4gc3RyaW5naWZpZWQgb3Igb2JqZWN0IG9wdGlvbnMgYmFzZWQgb24gdGhlIGB0b1N0cmluZ2AgZmxhZ1xyXG4gICAgcmV0dXJuIHRvU3RyaW5nID8gc3RyaW5naWZpZWRPcHRpb25zIDogcGFyc2VkT3B0aW9ucztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gUmV0dXJuIG51bGwgaWYgcGFyc2luZyBmYWlsc1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbywgdmVyc2lvbiwgYW5kIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludExpY2Vuc2VcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludExpY2Vuc2UoKSB7XHJcbiAgLy8gUHJpbnQgdGhlIGxvZ28gYW5kIHZlcnNpb24gaW5mb3JtYXRpb25cclxuICBwcmludFZlcnNpb24oKTtcclxuXHJcbiAgLy8gUHJpbnQgdGhlIGxpY2Vuc2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdUaGlzIHNvZnR3YXJlIHJlcXVpcmVzIGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGZvciBjb21tZXJjaWFsIHVzZS5cXG4nXHJcbiAgICAgIC55ZWxsb3csXHJcbiAgICAnXFxuRm9yIGEgZnVsbCBsaXN0IG9mIENMSSBvcHRpb25zLCB0eXBlOicsXHJcbiAgICAnXFxuaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyIC0taGVscFxcbicuZ3JlZW4sXHJcbiAgICAnXFxuSWYgeW91IGRvIG5vdCBoYXZlIGEgbGljZW5zZSwgb25lIGNhbiBiZSBvYnRhaW5lZCBoZXJlOicsXHJcbiAgICAnXFxuaHR0cHM6Ly9zaG9wLmhpZ2hzb2Z0LmNvbS9cXG4nLmdyZWVuLFxyXG4gICAgJ1xcblRvIGN1c3RvbWl6ZSB5b3VyIGluc3RhbGxhdGlvbiwgcGxlYXNlIHJlZmVyIHRvIHRoZSBSRUFETUUgZmlsZSBhdDonLFxyXG4gICAgJ1xcbmh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWVcXG4nLmdyZWVuXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByaW50cyB1c2FnZSBpbmZvcm1hdGlvbiBmb3IgQ0xJIGFyZ3VtZW50cywgZGlzcGxheWluZyBhdmFpbGFibGUgb3B0aW9uc1xyXG4gKiBhbmQgdGhlaXIgZGVzY3JpcHRpb25zLiBJdCBjYW4gbGlzdCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5IGlmIGNhdGVnb3JpZXNcclxuICogY29udGFpbiBuZXN0ZWQgb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50VXNhZ2VcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFVzYWdlKCkge1xyXG4gIC8vIERpc3BsYXkgUkVBRE1FIGFuZCBnZW5lcmFsIHVzYWdlIGluZm9ybWF0aW9uXHJcbiAgY29uc29sZS5sb2coXHJcbiAgICAnXFxuVXNhZ2Ugb2YgQ0xJIGFyZ3VtZW50czonLmJvbGQsXHJcbiAgICAnXFxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nLFxyXG4gICAgYFxcbkZvciBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uLCB2aXNpdCB0aGUgUkVBRE1FIGZpbGUgYXQ6ICR7J2h0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWUnLmdyZWVufS5cXG5gXHJcbiAgKTtcclxuXHJcbiAgLy8gSXRlcmF0ZSB0aHJvdWdoIGVhY2ggY2F0ZWdvcnkgaW4gdGhlIGBkZWZhdWx0Q29uZmlnYCBhbmQgZGlzcGxheSB1c2FnZSBpbmZvXHJcbiAgT2JqZWN0LmtleXMoZGVmYXVsdENvbmZpZykuZm9yRWFjaCgoY2F0ZWdvcnkpID0+IHtcclxuICAgIGNvbnNvbGUubG9nKGAke2NhdGVnb3J5LnRvVXBwZXJDYXNlKCl9YC5ib2xkLnJlZCk7XHJcbiAgICBfY3ljbGVDYXRlZ29yaWVzKGRlZmF1bHRDb25maWdbY2F0ZWdvcnldKTtcclxuICAgIGNvbnNvbGUubG9nKCcnKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByaW50cyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGxvZ28gb3IgdGV4dCB3aXRoIHRoZSB2ZXJzaW9uXHJcbiAqIGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW25vTG9nbz1mYWxzZV0gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24sIHdpdGhvdXQgdGhlIGxvZ28uIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRWZXJzaW9uKG5vTG9nbyA9IGZhbHNlKSB7XHJcbiAgLy8gR2V0IHBhY2thZ2UgdmVyc2lvbiBlaXRoZXIgZnJvbSBgLmVudmAgb3IgZnJvbSBgcGFja2FnZS5qc29uYFxyXG4gIGNvbnN0IHBhY2thZ2VWZXJzaW9uID0gSlNPTi5wYXJzZShcclxuICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ3BhY2thZ2UuanNvbicpLCAndXRmOCcpXHJcbiAgKS52ZXJzaW9uO1xyXG5cclxuICAvLyBQcmludCB0ZXh0IG9ubHlcclxuICBpZiAobm9Mb2dvKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHYke3BhY2thZ2VWZXJzaW9ufWApO1xyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBQcmludCB0aGUgbG9nb1xyXG4gICAgY29uc29sZS5sb2coXHJcbiAgICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ21zZycsICdzdGFydHVwLm1zZycpLCAndXRmOCcpLnRvU3RyaW5nKClcclxuICAgICAgICAuYm9sZC55ZWxsb3csXHJcbiAgICAgIGB2JHtwYWNrYWdlVmVyc2lvbn1cXG5gLmJvbGRcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgYW5kIHJldHVybnMgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCBiYXNlZCBvbiB0aGUgcHJvdmlkZWRcclxuICogY29uZmlndXJhdGlvbiwgc2V0dGluZyB2YWx1ZXMgZnJvbSBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogVGhlIHByaW9yaXR5IG9yZGVyIGZvciBzZXR0aW5nIHZhbHVlcyBpczpcclxuICpcclxuICogMS4gVmFsdWVzIGZyb20gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSAoZGVmYXVsdHMpLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgKHNwZWNpZmllZCBpbiB0aGUgYC5lbnZgIGZpbGUpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2luaXRPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgdXNlZCBmb3IgaW5pdGlhbGl6aW5nXHJcbiAqIHRoZSBnbG9iYWwgb3B0aW9ucy4gSXQgc2hvdWxkIGluY2x1ZGUgbmVzdGVkIHByb3BlcnRpZXMgd2l0aCBhIGB2YWx1ZWBcclxuICogYW5kIGFuIGBlbnZMaW5rYCBmb3IgbGlua2luZyB0byBlbnZpcm9ubWVudCB2YXJpYWJsZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBpbml0aWFsaXplZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHBvcHVsYXRlZCB3aXRoXHJcbiAqIHZhbHVlcyBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5XHJcbiAqIG9yZGVyLlxyXG4gKi9cclxuZnVuY3Rpb24gX2luaXRPcHRpb25zKGNvbmZpZykge1xyXG4gIC8vIEluaXQgdGhlIG9iamVjdCBmb3Igb3B0aW9uc1xyXG4gIGNvbnN0IG9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gU3RhcnQgaW5pdGlhbGl6aW5nIHRoZSBgb3B0aW9uc2Agb2JqZWN0IHJlY3Vyc2l2ZWx5XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoY29uZmlnKSkge1xyXG4gICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChpdGVtLCAndmFsdWUnKSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGNvcnJlY3QgdmFsdWUgYmFzZWQgb24gdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5IG9yZGVyXHJcbiAgICAgIGlmIChlbnZzW2l0ZW0uZW52TGlua10gIT09IHVuZGVmaW5lZCAmJiBlbnZzW2l0ZW0uZW52TGlua10gIT09IG51bGwpIHtcclxuICAgICAgICAvLyBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIHZhbHVlXHJcbiAgICAgICAgb3B0aW9uc1tuYW1lXSA9IGVudnNbaXRlbS5lbnZMaW5rXTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaGUgdmFsdWUgZnJvbSB0aGUgY29uZmlnIGZpbGVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gaXRlbS52YWx1ZTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIGEgc2VjdGlvbiBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICBvcHRpb25zW25hbWVdID0gX2luaXRPcHRpb25zKGl0ZW0pO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBjcmVhdGVkIGBvcHRpb25zYCBvYmplY3RcclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGNvbnNpZGVyaW5nIGFic29sdXRlIHByb3BlcnRpZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcmlnaW5hbE9wdGlvbnMgLSBPcmlnaW5hbCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBuZXdPcHRpb25zIC0gTmV3IGNvbmZpZ3VyYXRpb24gb3B0aW9ucyB0byBiZSBtZXJnZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IE1lcmdlZCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX21lcmdlT3B0aW9ucyhvcmlnaW5hbE9wdGlvbnMsIG5ld09wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgYG9yaWdpbmFsT3B0aW9uc2AgYW5kIGBuZXdPcHRpb25zYCBhcmUgY29ycmVjdCBvYmplY3RzXHJcbiAgaWYgKGlzT2JqZWN0KG9yaWdpbmFsT3B0aW9ucykgJiYgaXNPYmplY3QobmV3T3B0aW9ucykpIHtcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICAgIG9yaWdpbmFsT3B0aW9uc1trZXldID1cclxuICAgICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXHJcbiAgICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgPyBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9uc1trZXldLCB2YWx1ZSlcclxuICAgICAgICAgIDogdmFsdWUgIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICAgIDogb3JpZ2luYWxPcHRpb25zW2tleV0gfHwgbnVsbDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb3JpZ2luYWwgKG1vZGlmaWVkIG9yIG5vdCkgb3B0aW9uc1xyXG4gIHJldHVybiBvcmlnaW5hbE9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmdcclxuICogd2l0aCB0aGUgb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy4gSW4gb3JkZXIgZm9yIGEgZnVuY3Rpb25cclxuICogdG8gYmUgcHJlc2VydmVkLCBpdCBuZWVkcyB0byBmb2xsb3cgdGhlIGZvcm1hdCBgZnVuY3Rpb24gKC4uLikgey4uLn1gLlxyXG4gKiBTdWNoIGEgZnVuY3Rpb24gY2FuIGFsc28gYmUgc3RyaW5naWZpZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWRcclxuICogaW4gdGhlIG91dHB1dC4gT3RoZXJ3aXNlIGFuIGVycm9yIGlzIHRocm93bi5cclxuICogQHBhcmFtIHtib29sZWFufSBzdHJpbmdpZnlGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBzYXZlZFxyXG4gKiBhcyBzdHJpbmdzLiBUaGUgYGFsbG93RnVuY3Rpb25zYCBtdXN0IGJlIHNldCB0byB0cnVlIGFzIHdlbGwgZm9yIHRoaXMgdG8gdGFrZVxyXG4gKiBhbiBlZmZlY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgd2hlbiBmdW5jdGlvbnMgYXJlIG5vdCBhbGxvd2VkIGJ1dCBhcmVcclxuICogZm91bmQgaW4gcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX29wdGlvbnNTdHJpbmdpZnkob3B0aW9ucywgYWxsb3dGdW5jdGlvbnMsIHN0cmluZ2lmeUZ1bmN0aW9ucykge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAoXywgdmFsdWUpID0+IHtcclxuICAgIC8vIFRyaW0gc3RyaW5nIHZhbHVlc1xyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgdmFsdWUgaXMgYSBmdW5jdGlvbiBvciBzdHJpbmdpZmllZCBmdW5jdGlvblxyXG4gICAgaWYgKFxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgfHxcclxuICAgICAgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpICYmXHJcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBJZiBhbGxvd0Z1bmN0aW9ucyBpcyBzZXQgdG8gdHJ1ZSwgcHJlc2VydmUgZnVuY3Rpb25zXHJcbiAgICAgIGlmIChhbGxvd0Z1bmN0aW9ucykge1xyXG4gICAgICAgIC8vIEJhc2VkIG9uIHRoZSBgc3RyaW5naWZ5RnVuY3Rpb25zYCBvcHRpb25zLCBzZXQgZnVuY3Rpb24gdmFsdWVzXHJcbiAgICAgICAgcmV0dXJuIHN0cmluZ2lmeUZ1bmN0aW9uc1xyXG4gICAgICAgICAgPyAvLyBBcyBzdHJpbmdpZmllZCBmdW5jdGlvbnNcclxuICAgICAgICAgICAgYFwiRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOXCJgXHJcbiAgICAgICAgICA6IC8vIEFzIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOYDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaHJvdyBhbiBlcnJvciBvdGhlcndpc2VcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgc2ltcGx5IHJldHVybiB0aGUgdmFsdWVcclxuICAgIHJldHVybiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIHN0cmluZ2lmeUZ1bmN0aW9ucyA/IC9cXFxcXCJFWFBfRlVOfEVYUF9GVU5cXFxcXCIvZyA6IC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHByb3ZpZGVkIHZpYVxyXG4gKiB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGluIHRoZSBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2xvYWRDb25maWdGaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIHRvIHNlYXJjaFxyXG4gKiBmb3IgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgZmlsZSBwYXRoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGxvYWRlZCBmcm9tIHRoZSBzcGVjaWZpZWRcclxuICogZmlsZSwgb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBmaWxlIGlzIG5vdCBmb3VuZCwgaW52YWxpZCwgb3IgYW4gZXJyb3JcclxuICogb2NjdXJzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpIHtcclxuICAvLyBHZXQgdGhlIGFsbG93IGZsYWdzIGZvciB0aGUgY3VzdG9tIGxvZ2ljIGNoZWNrXHJcbiAgY29uc3QgeyBhbGxvd0NvZGVFeGVjdXRpb24sIGFsbG93RmlsZVJlc291cmNlcyB9ID0gZ2V0T3B0aW9ucygpLmN1c3RvbUxvZ2ljO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIHdhcyB1c2VkXHJcbiAgY29uc3QgY29uZmlnSW5kZXggPSBjbGlBcmdzLmZpbmRJbmRleChcclxuICAgIChhcmcpID0+IGFyZy5yZXBsYWNlKC8tL2csICcnKSA9PT0gJ2xvYWRDb25maWcnXHJcbiAgKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gdmFsdWVcclxuICBjb25zdCBjb25maWdGaWxlTmFtZSA9IGNvbmZpZ0luZGV4ID4gLTEgJiYgY2xpQXJnc1tjb25maWdJbmRleCArIDFdO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgYC0tbG9hZENvbmZpZ2AgaXMgcHJlc2VudCBhbmQgaGFzIGEgY29ycmVjdCB2YWx1ZVxyXG4gIGlmIChjb25maWdGaWxlTmFtZSAmJiBhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIExvYWQgYW4gb3B0aW9uYWwgY3VzdG9tIEpTT04gY29uZmlnIGZpbGVcclxuICAgICAgcmV0dXJuIGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGNvbmZpZ0ZpbGVOYW1lKSwgJ3V0ZjgnKSxcclxuICAgICAgICBmYWxzZSxcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGVycm9yLFxyXG4gICAgICAgIGBbY29uZmlnXSBVbmFibGUgdG8gbG9hZCB0aGUgY29uZmlndXJhdGlvbiBmcm9tIHRoZSAke2NvbmZpZ0ZpbGVOYW1lfSBmaWxlLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIE5vIGFkZGl0aW9uYWwgb3B0aW9ucyB0byByZXR1cm5cclxuICByZXR1cm4ge307XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQYXJzZXMgY29tbWFuZC1saW5lIGFyZ3VtZW50cyBhbmQgcGFpcnMgZWFjaCBhcmd1bWVudCB3aXRoIGl0cyBjb3JyZXNwb25kaW5nXHJcbiAqIG9wdGlvbiBpbiB0aGUgY29uZmlndXJhdGlvbi4gVGhlIHZhbHVlcyBhcmUgc3RydWN0dXJlZCBpbnRvIGEgbmVzdGVkIG9wdGlvbnNcclxuICogb2JqZWN0LCBiYXNlZCBvbiBwcmVkZWZpbmVkIG1hcHBpbmdzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3BhaXJBcmd1bWVudFZhbHVlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG5lc3RlZFByb3BzIC0gQW4gYXJyYXkgb2YgbmVzdGluZyBsZXZlbCBmb3IgYWxsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBBbiBhcnJheSBvZiBjb21tYW5kLWxpbmUgYXJndW1lbnRzXHJcbiAqIGNvbnRhaW5pbmcgb3B0aW9ucyBhbmQgdGhlaXIgYXNzb2NpYXRlZCB2YWx1ZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFuIHVwZGF0ZWQgb3B0aW9ucyBvYmplY3Qgd2hlcmUgZWFjaCBvcHRpb24gZnJvbVxyXG4gKiB0aGUgY29tbWFuZC1saW5lIGlzIHBhaXJlZCB3aXRoIGl0cyB2YWx1ZSwgc3RydWN0dXJlZCBpbnRvIG5lc3RlZCBvYmplY3RzXHJcbiAqIGFzIGRlZmluZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpIHtcclxuICAvLyBBbiBlbXB0eSBvYmplY3QgdG8gY29sbGVjdCBhbmQgc3RydWN0dXJpemUgZGF0YSBmcm9tIHRoZSBhcmdzXHJcbiAgY29uc3QgY2xpT3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDeWNsZSB0aHJvdWdoIGFsbCBDTEkgYXJncyBhbmQgZmlsdGVyIHRoZW1cclxuICBmb3IgKGxldCBpID0gMDsgaSA8IGNsaUFyZ3MubGVuZ3RoOyBpKyspIHtcclxuICAgIGNvbnN0IG9wdGlvbiA9IGNsaUFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW29wdGlvbl1cclxuICAgICAgPyBuZXN0ZWRQcm9wc1tvcHRpb25dLnNwbGl0KCcuJylcclxuICAgICAgOiBbXTtcclxuXHJcbiAgICAvLyBDcmVhdGUgb3B0aW9ucyBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSBDTEkgZm9yIGxhdGVyIHBhcnNpbmcgYW5kIG1lcmdpbmdcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcclxuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xyXG4gICAgICAgIGNvbnN0IHZhbHVlID0gY2xpQXJnc1srK2ldO1xyXG4gICAgICAgIGlmICghdmFsdWUpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSBDTEkgJy0tJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgb2JqW3Byb3BdID0gdmFsdWUgfHwgbnVsbDtcclxuICAgICAgfSBlbHNlIGlmIChvYmpbcHJvcF0gPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG9ialtwcm9wXSA9IHt9O1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBvYmpbcHJvcF07XHJcbiAgICB9LCBjbGlPcHRpb25zKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBwYXJzZWQgQ0xJIG9wdGlvbnNcclxuICByZXR1cm4gY2xpT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IHRyYXZlcnNlcyB0aGUgb3B0aW9ucyBvYmplY3QgdG8gcHJpbnQgdGhlIHVzYWdlIGluZm9ybWF0aW9uXHJcbiAqIGZvciBlYWNoIG9wdGlvbiBjYXRlZ29yeSBhbmQgaW5kaXZpZHVhbCBvcHRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3ljbGVDYXRlZ29yaWVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgQ0xJIG9wdGlvbnMuIEl0IG1heVxyXG4gKiBpbmNsdWRlIG5lc3RlZCBjYXRlZ29yaWVzIGFuZCBpbmRpdmlkdWFsIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3ljbGVDYXRlZ29yaWVzKG9wdGlvbnMpIHtcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKG9wdGlvbnMpKSB7XHJcbiAgICAvLyBJZiB0aGUgY3VycmVudCBlbnRyeSBpcyBhIGNhdGVnb3J5IGFuZCBub3QgYSBsZWFmIG9wdGlvbiwgcmVjdXJzZSBpbnRvIGl0XHJcbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvcHRpb24sICd2YWx1ZScpKSB7XHJcbiAgICAgIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9uKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIFByZXBhcmUgZGVzY3JpcHRpb25cclxuICAgICAgY29uc3QgZGVzY05hbWUgPSBgIC0tJHtvcHRpb24uY2xpTmFtZSB8fCBuYW1lfWA7XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIHZhbHVlXHJcbiAgICAgIGxldCBvcHRpb25WYWx1ZSA9IG9wdGlvbi52YWx1ZTtcclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhcnJheSBvZiBzdHJpbmdzXHJcbiAgICAgIGlmIChvcHRpb25WYWx1ZSAhPT0gbnVsbCAmJiBvcHRpb24udHlwZXMuaW5jbHVkZXMoJ3N0cmluZ1tdJykpIHtcclxuICAgICAgICBvcHRpb25WYWx1ZSA9XHJcbiAgICAgICAgICAnWycgKyBvcHRpb25WYWx1ZS5tYXAoKGl0ZW0pID0+IGAnJHtpdGVtfSdgKS5qb2luKCcsICcpICsgJ10nO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBQcmVwYXJlIHZhbHVlIGZvciBvcHRpb24gdGhhdCBpcyBub3QgbnVsbCBhbmQgaXMgYSBzdHJpbmdcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nJykpIHtcclxuICAgICAgICBvcHRpb25WYWx1ZSA9IGAnJHtvcHRpb25WYWx1ZX0nYDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRGlzcGxheSBjb3JyZWN0bHkgYWxpZ25lZCBtZXNzYWdlc1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICBkZXNjTmFtZS5ncmVlbixcclxuICAgICAgICBgJHsoJzwnICsgb3B0aW9uLnR5cGVzLmpvaW4oJ3wnKSArICc+JykueWVsbG93fWAsXHJcbiAgICAgICAgYCR7U3RyaW5nKG9wdGlvblZhbHVlKS5ib2xkfWAuYmx1ZSxcclxuICAgICAgICBgLSAke29wdGlvbi5kZXNjcmlwdGlvbn0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgdXBkYXRlT3B0aW9ucyxcclxuICBzZXRDbGlPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuICBpc0FsbG93ZWRDb25maWcsXHJcbiAgcHJpbnRMaWNlbnNlLFxyXG4gIHByaW50VXNhZ2UsXHJcbiAgcHJpbnRWZXJzaW9uXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBIVFRQIHV0aWxpdHkgbW9kdWxlIGZvciBmZXRjaGluZyBhbmQgcG9zdGluZyBkYXRhLiBTdXBwb3J0cyBib3RoXHJcbiAqIEhUVFAgYW5kIEhUVFBTIHByb3RvY29scywgcHJvdmlkaW5nIG1ldGhvZHMgdG8gbWFrZSBHRVQgYW5kIFBPU1QgcmVxdWVzdHNcclxuICogd2l0aCBjdXN0b21pemFibGUgb3B0aW9ucy4gSW5jbHVkZXMgcHJvdG9jb2wgZGV0ZXJtaW5hdGlvbiBiYXNlZCBvbiBVUkxcclxuICogYW5kIGF1Z21lbnRzIHJlc3BvbnNlIG9iamVjdHMgd2l0aCBhICd0ZXh0JyBwcm9wZXJ0eSBmb3IgZWFzaWVyIGRhdGEgYWNjZXNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgZGF0YSBmcm9tIHRoZSBzcGVjaWZpZWQgVVJMIHVzaW5nIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGZldGNoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGZldGNoIGRhdGEgZnJvbS5cclxuICogQHBhcmFtIHtPYmplY3R9IFtyZXF1ZXN0T3B0aW9ucz17fV0gLSBPcHRpb25zIGZvciB0aGUgSFRUUC9IVFRQUyByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBIVFRQL0hUVFBTIHJlc3BvbnNlXHJcbiAqIG9iamVjdCB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmZXRjaCh1cmwsIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgX2dldFByb3RvY29sTW9kdWxlKHVybClcclxuICAgICAgLmdldCh1cmwsIHJlcXVlc3RPcHRpb25zLCAocmVzcG9uc2UpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIXJlc3BvbnNlRGF0YSkge1xyXG4gICAgICAgICAgICByZWplY3QoJ05vdGhpbmcgd2FzIGZldGNoZWQgZnJvbSB0aGUgVVJMLicpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZW5kcyBhIFBPU1QgcmVxdWVzdCB0byB0aGUgc3BlY2lmaWVkIFVSTCB3aXRoIHRoZSBwcm92aWRlZCBKU09OIGJvZHkgdXNpbmdcclxuICogZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBzZW5kIHRoZSBQT1NUIHJlcXVlc3QgdG8uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbYm9keT17fV0gLSBUaGUgSlNPTiBib2R5IHRvIGluY2x1ZGUgaW4gdGhlIFBPU1QgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3QodXJsLCBib2R5ID0ge30sIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KGJvZHkpO1xyXG5cclxuICAgIC8vIFNldCBkZWZhdWx0IGhlYWRlcnMgYW5kIG1lcmdlIHdpdGggcmVxdWVzdE9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSBPYmplY3QuYXNzaWduKFxyXG4gICAgICB7XHJcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXHJcbiAgICAgICAgaGVhZGVyczoge1xyXG4gICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcclxuICAgICAgICAgICdDb250ZW50LUxlbmd0aCc6IGRhdGEubGVuZ3RoXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICByZXF1ZXN0T3B0aW9uc1xyXG4gICAgKTtcclxuXHJcbiAgICBjb25zdCByZXF1ZXN0ID0gX2dldFByb3RvY29sTW9kdWxlKHVybClcclxuICAgICAgLnJlcXVlc3QodXJsLCBvcHRpb25zLCAocmVzcG9uc2UpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAvLyBXcml0ZSB0aGUgcmVxdWVzdCBib2R5IGFuZCBlbmQgdGhlIHJlcXVlc3RcclxuICAgIHJlcXVlc3Qud3JpdGUoZGF0YSk7XHJcbiAgICByZXF1ZXN0LmVuZCgpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgYmFzZWQgb24gdGhlIHByb3ZpZGVkIFVSTC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9nZXRQcm90b2NvbE1vZHVsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBkZXRlcm1pbmUgdGhlIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgKGh0dHAgb3IgaHR0cHMpLlxyXG4gKi9cclxuZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlKHVybCkge1xyXG4gIHJldHVybiB1cmwuc3RhcnRzV2l0aCgnaHR0cHMnKSA/IGh0dHBzIDogaHR0cDtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGZldGNoLFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQSBjdXN0b20gZXJyb3IgY2xhc3MgZm9yIGhhbmRsaW5nIGV4cG9ydC1yZWxhdGVkIGVycm9ycy4gRXh0ZW5kcyB0aGUgbmF0aXZlXHJcbiAqIGBFcnJvcmAgY2xhc3MgdG8gaW5jbHVkZSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgbGlrZSBzdGF0dXMgY29kZSBhbmQgc3RhY2tcclxuICogdHJhY2UgZGV0YWlscy5cclxuICovXHJcbmNsYXNzIEV4cG9ydEVycm9yIGV4dGVuZHMgRXJyb3Ige1xyXG4gIC8qKlxyXG4gICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIFRoZSBlcnJvciBtZXNzYWdlIHRvIGJlIGRpc3BsYXllZC5cclxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzQ29kZSAtIE9wdGlvbmFsIEhUVFAgc3RhdHVzIGNvZGUgYXNzb2NpYXRlZFxyXG4gICAqIHdpdGggdGhlIGVycm9yIChlLmcuLCA0MDAsIDUwMCkuXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSwgc3RhdHVzQ29kZSkge1xyXG4gICAgc3VwZXIoKTtcclxuXHJcbiAgICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gICAgdGhpcy5zdGFja01lc3NhZ2UgPSBtZXNzYWdlO1xyXG5cclxuICAgIGlmIChzdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IHN0YXR1c0NvZGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXRzIG9yIHVwZGF0ZXMgdGhlIEhUVFAgc3RhdHVzIGNvZGUgZm9yIHRoZSBlcnJvci5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gVGhlIEhUVFAgc3RhdHVzIGNvZGUgdG8gYXNzaWduIHRvIHRoZSBlcnJvci5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtFeHBvcnRFcnJvcn0gVGhlIHVwZGF0ZWQgaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAgY2xhc3MuXHJcbiAgICovXHJcbiAgc2V0U3RhdHVzKHN0YXR1c0NvZGUpIHtcclxuICAgIHRoaXMuc3RhdHVzQ29kZSA9IHN0YXR1c0NvZGU7XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXRzIGFkZGl0aW9uYWwgZXJyb3IgZGV0YWlscyBiYXNlZCBvbiBhbiBleGlzdGluZyBlcnJvciBvYmplY3QuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIEFuIGVycm9yIG9iamVjdCBjb250YWluaW5nIGRldGFpbHMgdG8gcG9wdWxhdGVcclxuICAgKiB0aGUgYEV4cG9ydEVycm9yYCBpbnN0YW5jZS5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtFeHBvcnRFcnJvcn0gVGhlIHVwZGF0ZWQgaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAgY2xhc3MuXHJcbiAgICovXHJcbiAgc2V0RXJyb3IoZXJyb3IpIHtcclxuICAgIHRoaXMuZXJyb3IgPSBlcnJvcjtcclxuXHJcbiAgICBpZiAoZXJyb3IubmFtZSkge1xyXG4gICAgICB0aGlzLm5hbWUgPSBlcnJvci5uYW1lO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChlcnJvci5zdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XHJcbiAgICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcclxuICAgICAgdGhpcy5zdGFjayA9IGVycm9yLnN0YWNrO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgRXhwb3J0RXJyb3I7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgY2FjaGUgbWFuYWdlciBpcyByZXNwb25zaWJsZSBmb3IgaGFuZGxpbmcgYW5kIG1hbmFnaW5nXHJcbiAqIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYWxvbmcgd2l0aCBpdHMgZGVwZW5kZW5jaWVzLiBJdCBlbnN1cmVzIHRoYXQgdGhlc2VcclxuICogcmVzb3VyY2VzIGFyZSBzdG9yZWQgYW5kIHJldHJpZXZlZCBlZmZpY2llbnRseSB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZVxyXG4gKiBhbmQgcmVkdWNlIHJlZHVuZGFudCBuZXR3b3JrIHJlcXVlc3RzLiBUaGUgY2FjaGUgaXMgc3RvcmVkIGluIHRoZSBgLmNhY2hlYFxyXG4gKiBkaXJlY3RvcnkgYnkgZGVmYXVsdCwgd2hpY2ggc2VydmVzIGFzIGEgZGVkaWNhdGVkIGZvbGRlciBmb3Iga2VlcGluZyBjYWNoZWRcclxuICogZmlsZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IEh0dHBzUHJveHlBZ2VudCB9IGZyb20gJ2h0dHBzLXByb3h5LWFnZW50JztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGZldGNoIH0gZnJvbSAnLi9mZXRjaC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGluaXRpYWwgY2FjaGUgdGVtcGxhdGVcclxuY29uc3QgY2FjaGUgPSB7XHJcbiAgY2RuVXJsOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICBhY3RpdmVNYW5pZmVzdDoge30sXHJcbiAgc291cmNlczogJycsXHJcbiAgaGNWZXJzaW9uOiAnJ1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyB0aGUgY2FjaGUgZm9yIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLCB1cGRhdGVzIHRoZSBjYWNoZSBpZiBuZWVkZWQsXHJcbiAqIGFuZCBsb2FkcyB0aGUgc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjaGVja0FuZFVwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgc2VydmVyLnByb3h5YCBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGUoXHJcbiAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgc2VydmVyUHJveHlPcHRpb25zXHJcbikge1xyXG4gIHRyeSB7XHJcbiAgICBsZXQgZmV0Y2hlZE1vZHVsZXM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjYWNoZSBwYXRoXHJcbiAgICBjb25zdCBjYWNoZVBhdGggPSBnZXRDYWNoZVBhdGgoKTtcclxuXHJcbiAgICAvLyBQcmVwYXJlIHBhdGhzIHRvIG1hbmlmZXN0IGFuZCBzb3VyY2VzIGZyb20gdGhlIGNhY2hlIGZvbGRlclxyXG4gICAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgICBjb25zdCBzb3VyY2VQYXRoID0gam9pbihjYWNoZVBhdGgsICdzb3VyY2VzLmpzJyk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBjYWNoZSBkZXN0aW5hdGlvbiBpZiBpdCBkb2Vzbid0IGV4aXN0IGFscmVhZHlcclxuICAgICFleGlzdHNTeW5jKGNhY2hlUGF0aCkgJiYgbWtkaXJTeW5jKGNhY2hlUGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XHJcblxyXG4gICAgLy8gRmV0Y2ggYWxsIHRoZSBzY3JpcHRzIGVpdGhlciBpZiB0aGUgYG1hbmlmZXN0Lmpzb25gIGRvZXMgbm90IGV4aXN0XHJcbiAgICAvLyBvciBpZiB0aGUgYGZvcmNlRmV0Y2hgIG9wdGlvbiBpcyBlbmFibGVkXHJcbiAgICBpZiAoIWV4aXN0c1N5bmMobWFuaWZlc3RQYXRoKSB8fCBoaWdoY2hhcnRzT3B0aW9ucy5mb3JjZUZldGNoKSB7XHJcbiAgICAgIGxvZygzLCAnW2NhY2hlXSBGZXRjaGluZyBhbmQgY2FjaGluZyBIaWdoY2hhcnRzIGRlcGVuZGVuY2llcy4nKTtcclxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCBfdXBkYXRlQ2FjaGUoXHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgIHNvdXJjZVBhdGhcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XHJcblxyXG4gICAgICAvLyBSZWFkIHRoZSBtYW5pZmVzdCBKU09OXHJcbiAgICAgIGNvbnN0IG1hbmlmZXN0ID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMobWFuaWZlc3RQYXRoKSwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBtb2R1bGVzIGlzIGFuIGFycmF5LCBpZiBzbywgd2UgcmV3cml0ZSBpdCB0byBhIG1hcCB0byBtYWtlXHJcbiAgICAgIC8vIGl0IGVhc2llciB0byByZXNvbHZlIG1vZHVsZXMuXHJcbiAgICAgIGlmIChtYW5pZmVzdC5tb2R1bGVzICYmIEFycmF5LmlzQXJyYXkobWFuaWZlc3QubW9kdWxlcykpIHtcclxuICAgICAgICBjb25zdCBtb2R1bGVNYXAgPSB7fTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzLmZvckVhY2goKG0pID0+IChtb2R1bGVNYXBbbV0gPSAxKSk7XHJcbiAgICAgICAgbWFuaWZlc3QubW9kdWxlcyA9IG1vZHVsZU1hcDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSBhY3R1YWwgbnVtYmVyIG9mIHNjcmlwdHMgdG8gYmUgZmV0Y2hlZFxyXG4gICAgICBjb25zdCB7IGNvcmVTY3JpcHRzLCBtb2R1bGVTY3JpcHRzLCBpbmRpY2F0b3JTY3JpcHRzIH0gPVxyXG4gICAgICAgIGhpZ2hjaGFydHNPcHRpb25zO1xyXG4gICAgICBjb25zdCBudW1iZXJPZk1vZHVsZXMgPVxyXG4gICAgICAgIGNvcmVTY3JpcHRzLmxlbmd0aCArIG1vZHVsZVNjcmlwdHMubGVuZ3RoICsgaW5kaWNhdG9yU2NyaXB0cy5sZW5ndGg7XHJcblxyXG4gICAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgaGlnaGNoYXJ0cyBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAgIC8vIElmIHRoZXJlIGFyZSBjaGFuZ2VzLCBmZXRjaCByZXF1ZXN0ZWQgbW9kdWxlcyBhbmQgcHJvZHVjdHMsXHJcbiAgICAgIC8vIGFuZCBiYWtlIHRoZW0gaW50byBhIGdpYW50IGJsb2IuIFNhdmUgdGhlIGJsb2IuXHJcbiAgICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgICdbY2FjaGVdIEEgSGlnaGNoYXJ0cyB2ZXJzaW9uIG1pc21hdGNoIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC4nXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgICAgfSBlbHNlIGlmIChcclxuICAgICAgICBPYmplY3Qua2V5cyhtYW5pZmVzdC5tb2R1bGVzIHx8IHt9KS5sZW5ndGggIT09IG51bWJlck9mTW9kdWxlc1xyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gVGhlIGNhY2hlIGFuZCB0aGUgcmVxdWVzdGVkIG1vZHVsZXMgZG8gbm90IG1hdGNoLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENoZWNrIGVhY2ggbW9kdWxlLCBpZiBhbnl0aGluZyBpcyBtaXNzaW5nIHJlZmV0Y2ggZXZlcnl0aGluZ1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSAobW9kdWxlU2NyaXB0cyB8fCBbXSkuc29tZSgobW9kdWxlTmFtZSkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFtYW5pZmVzdC5tb2R1bGVzW21vZHVsZU5hbWVdKSB7XHJcbiAgICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICAgIGBbY2FjaGVdIFRoZSAke21vZHVsZU5hbWV9IGlzIG1pc3NpbmcgaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLmBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFVwZGF0ZSBjYWNoZSBpZiBuZWVkZWRcclxuICAgICAgaWYgKHJlcXVlc3RVcGRhdGUpIHtcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICAgIGhpZ2hjaGFydHNPcHRpb25zLFxyXG4gICAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDMsICdbY2FjaGVdIERlcGVuZGVuY3kgY2FjaGUgaXMgdXAgdG8gZGF0ZSwgcHJvY2VlZGluZy4nKTtcclxuXHJcbiAgICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xyXG4gICAgICAgIGNhY2hlLnNvdXJjZXMgPSByZWFkRmlsZVN5bmMoc291cmNlUGF0aCwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IGN1cnJlbnQgbW9kdWxlcyBtYXBcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XHJcblxyXG4gICAgICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICAgICAgY2FjaGUuaGNWZXJzaW9uID0gZXh0cmFjdFZlcnNpb24oY2FjaGUuc291cmNlcyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcclxuICAgIC8vIGluIGEgc2xpZ2h0bHkgZGlmZmVyZW50IGZvcm1hdFxyXG4gICAgYXdhaXQgX3NhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHNPcHRpb25zLCBmZXRjaGVkTW9kdWxlcyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gQ291bGQgbm90IGNvbmZpZ3VyZSBjYWNoZSBhbmQgY3JlYXRlIG9yIHVwZGF0ZSB0aGUgY29uZmlnIG1hbmlmZXN0LicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgdmVyc2lvbiBvZiBIaWdoY2hhcnRzIGZyb20gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0SGlnaGNoYXJ0c1ZlcnNpb25cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNhY2hlZCBIaWdoY2hhcnRzIHZlcnNpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSB7XHJcbiAgcmV0dXJuIGNhY2hlLmhjVmVyc2lvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBpbiB0aGUgYXBwbGllZCBjb25maWd1cmF0aW9uIGFuZCBjaGVja3NcclxuICogdGhlIGNhY2hlIGZvciB0aGUgbmV3IHZlcnNpb24uXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IG5ld1ZlcnNpb24gLSBUaGUgbmV3IEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uKG5ld1ZlcnNpb24pIHtcclxuICAvLyBVcGRhdGUgdG8gdGhlIG5ldyB2ZXJzaW9uXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgaGlnaGNoYXJ0czoge1xyXG4gICAgICB2ZXJzaW9uOiBuZXdWZXJzaW9uXHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIENoZWNrIGlmIGNhY2hlIG5lZWRzIHRvIGJlIHVwZGF0ZWRcclxuICBhd2FpdCBjaGVja0FuZFVwZGF0ZUNhY2hlKG9wdGlvbnMuaGlnaGNoYXJ0cywgb3B0aW9ucy5zZXJ2ZXIucHJveHkpO1xyXG59XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgSGlnaGNoYXJ0cyB2ZXJzaW9uIGZyb20gdGhlIGNhY2hlJ3Mgc291cmNlcyBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHRyYWN0VmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2FjaGVTb3VyY2VzIC0gVGhlIGNhY2hlIHNvdXJjZXMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgZXh0cmFjdGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0VmVyc2lvbihjYWNoZVNvdXJjZXMpIHtcclxuICByZXR1cm4gY2FjaGVTb3VyY2VzXHJcbiAgICAuc3Vic3RyaW5nKDAsIGNhY2hlU291cmNlcy5pbmRleE9mKCcqLycpKVxyXG4gICAgLnJlcGxhY2UoJy8qJywgJycpXHJcbiAgICAucmVwbGFjZSgnKi8nLCAnJylcclxuICAgIC5yZXBsYWNlKC9cXG4vZywgJycpXHJcbiAgICAudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgdGhlIEhpZ2hjaGFydHMgbW9kdWxlIG5hbWUgYmFzZWQgb24gdGhlIHNjcmlwdFBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0UGF0aCAtIFRoZSBwYXRoIG9mIHRoZSBzY3JpcHQgZnJvbSB3aGljaCB0aGUgbW9kdWxlXHJcbiAqIG5hbWUgd2lsbCBiZSBleHRyYWN0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgbW9kdWxlIG5hbWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0UGF0aCkge1xyXG4gIHJldHVybiBzY3JpcHRQYXRoLnJlcGxhY2UoXHJcbiAgICAvKC4qKVxcL3woLiopbW9kdWxlc1xcL3xzdG9ja1xcLyguKilpbmRpY2F0b3JzXFwvfG1hcHNcXC8oLiopbW9kdWxlc1xcLy9naSxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBjYWNoZSBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY2FjaGUgb2JqZWN0IGNvbnRhaW5pbmcgdmFyaW91cyBjYWNoZWQgZGF0YS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZSgpIHtcclxuICByZXR1cm4gY2FjaGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBjYWNoZSBwYXRoIGZvciBIaWdoY2hhcnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBhYnNvbHV0ZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnkgZm9yIEhpZ2hjaGFydHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoKCkge1xyXG4gIHJldHVybiBnZXRBYnNvbHV0ZVBhdGgoZ2V0T3B0aW9ucygpLmhpZ2hjaGFydHMuY2FjaGVQYXRoLCAndXRmOCcpOyAvLyAjNTYyXHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGEgc2luZ2xlIHNjcmlwdCBhbmQgdXBkYXRlcyB0aGUgYGZldGNoZWRNb2R1bGVzYCBhY2NvcmRpbmdseS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHQgLSBBIHBhdGggdG8gc2NyaXB0IHRvIGdldC5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gQWRkaXRpb25hbCBvcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnRcclxuICogdG8gdXNlIGZvciBhIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtzaG91bGRUaHJvd0Vycm9yPWZhbHNlXSAtIEEgZmxhZyB0byBpbmRpY2F0ZSBpZiB0aGUgZXJyb3JcclxuICogc2hvdWxkIGJlIHRocm93bi4gVGhpcyBzaG91bGQgYmUgdXNlZCBvbmx5IGZvciB0aGUgY29yZSBzY3JpcHRzLiBUaGUgZGVmYXVsdFxyXG4gKiB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgdGV4dCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgZmV0Y2hlZCBzY3JpcHQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhIHByb2JsZW1cclxuICogd2l0aCBmZXRjaGluZyB0aGUgc2NyaXB0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChcclxuICBzY3JpcHQsXHJcbiAgcmVxdWVzdE9wdGlvbnMsXHJcbiAgZmV0Y2hlZE1vZHVsZXMsXHJcbiAgc2hvdWxkVGhyb3dFcnJvciA9IGZhbHNlXHJcbikge1xyXG4gIC8vIEdldCByaWQgb2YgdGhlIC5qcyBmcm9tIHRoZSBjdXN0b20gc3RyaW5nc1xyXG4gIGlmIChzY3JpcHQuZW5kc1dpdGgoJy5qcycpKSB7XHJcbiAgICBzY3JpcHQgPSBzY3JpcHQuc3Vic3RyaW5nKDAsIHNjcmlwdC5sZW5ndGggLSAzKTtcclxuICB9XHJcbiAgbG9nKDQsIGBbY2FjaGVdIEZldGNoaW5nIHNjcmlwdCAtICR7c2NyaXB0fS5qc2ApO1xyXG5cclxuICAvLyBGZXRjaCB0aGUgc2NyaXB0XHJcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHtzY3JpcHR9LmpzYCwgcmVxdWVzdE9wdGlvbnMpO1xyXG5cclxuICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAgaWYgKHJlc3BvbnNlLnN0YXR1c0NvZGUgPT09IDIwMCAmJiB0eXBlb2YgcmVzcG9uc2UudGV4dCA9PSAnc3RyaW5nJykge1xyXG4gICAgaWYgKGZldGNoZWRNb2R1bGVzKSB7XHJcbiAgICAgIGNvbnN0IG1vZHVsZU5hbWUgPSBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHQpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlc1ttb2R1bGVOYW1lXSA9IDE7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gcmVzcG9uc2UudGV4dDtcclxuICB9XHJcblxyXG4gIC8vIEJhc2VkIG9uIHRoZSBgc2hvdWxkVGhyb3dFcnJvcmAgZmxhZywgZGVjaWRlIGhvdyB0byBzZXJ2ZSBlcnJvciBtZXNzYWdlXHJcbiAgaWYgKHNob3VsZFRocm93RXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbiAoc3RhdHVzIGNvZGU6ICR7cmVzcG9uc2Uuc3RhdHVzQ29kZX0pLmAsXHJcbiAgICAgIDQwNFxyXG4gICAgKS5zZXRFcnJvcihyZXNwb25zZSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxvZyhcclxuICAgICAgMixcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNhdmVzIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCBmZXRjaGVkIG1vZHVsZXMgdG8gdGhlIGNhY2hlIG1hbmlmZXN0XHJcbiAqIGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NhdmVDb25maWdUb01hbmlmZXN0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2ZldGNoZWRNb2R1bGVzPXt9XSAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzID0ge30pIHtcclxuICBjb25zdCBuZXdNYW5pZmVzdCA9IHtcclxuICAgIHZlcnNpb246IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24sXHJcbiAgICBtb2R1bGVzOiBmZXRjaGVkTW9kdWxlc1xyXG4gIH07XHJcblxyXG4gIC8vIFVwZGF0ZSBjYWNoZSBvYmplY3Qgd2l0aCB0aGUgY3VycmVudCBtb2R1bGVzXHJcbiAgY2FjaGUuYWN0aXZlTWFuaWZlc3QgPSBuZXdNYW5pZmVzdDtcclxuXHJcbiAgbG9nKDMsICdbY2FjaGVdIFdyaXRpbmcgYSBuZXcgbWFuaWZlc3QuJyk7XHJcbiAgdHJ5IHtcclxuICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgIGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdtYW5pZmVzdC5qc29uJyksXHJcbiAgICAgIEpTT04uc3RyaW5naWZ5KG5ld01hbmlmZXN0KSxcclxuICAgICAgJ3V0ZjgnXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIEVycm9yIHdyaXRpbmcgdGhlIGNhY2hlIG1hbmlmZXN0LicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBIaWdoY2hhcnRzIGBzY3JpcHRzYCBhbmQgYGN1c3RvbVNjcmlwdHNgIGZyb20gdGhlIGdpdmVuIENETnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2ZldGNoU2NyaXB0c1xyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjb3JlU2NyaXB0cyAtIEhpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBtb2R1bGVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBtb2R1bGVzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjdXN0b21TY3JpcHRzIC0gQ3VzdG9tIHNjcmlwdCBwYXRocyB0byBmZXRjaCAoZnVsbFxyXG4gKiBVUkxzKS5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZmV0Y2hlZCBzY3JpcHRzXHJcbiAqIGNvbnRlbnQgam9pbmVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIHdoaWxlXHJcbiAqIHNldHRpbmcgYW4gSFRUUCBBZ2VudCBmb3IgcHJveHkuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzKFxyXG4gIGNvcmVTY3JpcHRzLFxyXG4gIG1vZHVsZVNjcmlwdHMsXHJcbiAgY3VzdG9tU2NyaXB0cyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgZmV0Y2hlZE1vZHVsZXNcclxuKSB7XHJcbiAgLy8gQ29uZmlndXJlIHByb3h5IGlmIGV4aXN0c1xyXG4gIGxldCBwcm94eUFnZW50O1xyXG4gIGNvbnN0IHByb3h5SG9zdCA9IHNlcnZlclByb3h5T3B0aW9ucy5ob3N0O1xyXG4gIGNvbnN0IHByb3h5UG9ydCA9IHNlcnZlclByb3h5T3B0aW9ucy5wb3J0O1xyXG5cclxuICAvLyBUcnkgdG8gY3JlYXRlIGEgUHJveHkgQWdlbnRcclxuICBpZiAocHJveHlIb3N0ICYmIHByb3h5UG9ydCkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgcHJveHlBZ2VudCA9IG5ldyBIdHRwc1Byb3h5QWdlbnQoe1xyXG4gICAgICAgIGhvc3Q6IHByb3h5SG9zdCxcclxuICAgICAgICBwb3J0OiBwcm94eVBvcnRcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tjYWNoZV0gQ291bGQgbm90IGNyZWF0ZSBhIFByb3h5IEFnZW50LicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gSWYgZXhpc3RzLCBhZGQgcHJveHkgYWdlbnQgdG8gcmVxdWVzdCBvcHRpb25zXHJcbiAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSBwcm94eUFnZW50XHJcbiAgICA/IHtcclxuICAgICAgICBhZ2VudDogcHJveHlBZ2VudCxcclxuICAgICAgICB0aW1lb3V0OiBzZXJ2ZXJQcm94eU9wdGlvbnMudGltZW91dFxyXG4gICAgICB9XHJcbiAgICA6IHt9O1xyXG5cclxuICBjb25zdCBhbGxGZXRjaFByb21pc2VzID0gW1xyXG4gICAgLi4uY29yZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcywgdHJ1ZSlcclxuICAgICksXHJcbiAgICAuLi5tb2R1bGVTY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMpXHJcbiAgICApLFxyXG4gICAgLi4uY3VzdG9tU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMpXHJcbiAgICApXHJcbiAgXTtcclxuXHJcbiAgY29uc3QgZmV0Y2hlZFNjcmlwdHMgPSBhd2FpdCBQcm9taXNlLmFsbChhbGxGZXRjaFByb21pc2VzKTtcclxuICByZXR1cm4gZmV0Y2hlZFNjcmlwdHMuam9pbignO1xcbicpO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgbG9jYWwgY2FjaGUgd2l0aCBIaWdoY2hhcnRzIHNjcmlwdHMgYW5kIHRoZWlyIHZlcnNpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF91cGRhdGVDYWNoZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaGlnaGNoYXJ0c09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgaGlnaGNoYXJ0c2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzb3VyY2VQYXRoIC0gVGhlIHBhdGggdG8gdGhlIHNvdXJjZSBmaWxlIGluIHRoZSBjYWNoZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0IHJlcHJlc2VudGluZ1xyXG4gKiB0aGUgZmV0Y2hlZCBtb2R1bGVzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gaXNzdWUgdXBkYXRpbmdcclxuICogdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfdXBkYXRlQ2FjaGUoaGlnaGNoYXJ0c09wdGlvbnMsIHNlcnZlclByb3h5T3B0aW9ucywgc291cmNlUGF0aCkge1xyXG4gIC8vIEdldCBIaWdoY2hhcnRzIHZlcnNpb24gZm9yIHNjcmlwdHNcclxuICBjb25zdCBoY1ZlcnNpb24gPVxyXG4gICAgaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbiA9PT0gJ2xhdGVzdCdcclxuICAgICAgPyBudWxsXHJcbiAgICAgIDogYCR7aGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbn1gO1xyXG5cclxuICAvLyBHZXQgdGhlIENETiB1cmwgZm9yIHNjcmlwdHNcclxuICBjb25zdCBjZG5VcmwgPSBoaWdoY2hhcnRzT3B0aW9ucy5jZG5VcmwgfHwgY2FjaGUuY2RuVXJsO1xyXG5cclxuICB0cnkge1xyXG4gICAgY29uc3QgZmV0Y2hlZE1vZHVsZXMgPSB7fTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbY2FjaGVdIFVwZGF0aW5nIGNhY2hlIHZlcnNpb24gdG8gSGlnaGNoYXJ0czogJHtoY1ZlcnNpb24gfHwgJ2xhdGVzdCd9LmBcclxuICAgICk7XHJcblxyXG4gICAgY2FjaGUuc291cmNlcyA9IGF3YWl0IF9mZXRjaFNjcmlwdHMoXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5jb3JlU2NyaXB0cy5tYXAoKGMpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb24gPyBgJHtjZG5Vcmx9LyR7aGNWZXJzaW9ufS8ke2N9YCA6IGAke2NkblVybH0vJHtjfWBcclxuICAgICAgICApXHJcbiAgICAgIF0sXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5tb2R1bGVTY3JpcHRzLm1hcCgobSkgPT5cclxuICAgICAgICAgIG0gPT09ICdtYXAnXHJcbiAgICAgICAgICAgID8gaGNWZXJzaW9uXHJcbiAgICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L21hcHMvJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbWFwcy9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgIDogaGNWZXJzaW9uXHJcbiAgICAgICAgICAgICAgPyBgJHtjZG5Vcmx9LyR7aGNWZXJzaW9ufS9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgICAgOiBgJHtjZG5Vcmx9L21vZHVsZXMvJHttfWBcclxuICAgICAgICApLFxyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmluZGljYXRvclNjcmlwdHMubWFwKChpKSA9PlxyXG4gICAgICAgICAgaGNWZXJzaW9uXHJcbiAgICAgICAgICAgID8gYCR7Y2RuVXJsfS9zdG9jay8ke2hjVmVyc2lvbn0vaW5kaWNhdG9ycy8ke2l9YFxyXG4gICAgICAgICAgICA6IGAke2NkblVybH0vc3RvY2svaW5kaWNhdG9ycy8ke2l9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgaGlnaGNoYXJ0c09wdGlvbnMuY3VzdG9tU2NyaXB0cyxcclxuICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICBmZXRjaGVkTW9kdWxlc1xyXG4gICAgKTtcclxuXHJcbiAgICAvLyBFeHRyYWN0IGFuZCBzYXZlIHZlcnNpb24gb2YgY3VycmVudGx5IHVzZWQgSGlnaGNoYXJ0c1xyXG4gICAgY2FjaGUuaGNWZXJzaW9uID0gZXh0cmFjdFZlcnNpb24oY2FjaGUuc291cmNlcyk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgZmV0Y2hlZCBtb2R1bGVzIGludG8gY2FjaGVzJyBzb3VyY2UgSlNPTlxyXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcclxuICAgIHJldHVybiBmZXRjaGVkTW9kdWxlcztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBVbmFibGUgdG8gdXBkYXRlIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgY2hlY2tBbmRVcGRhdGVDYWNoZSxcclxuICBnZXRIaWdoY2hhcnRzVmVyc2lvbixcclxuICB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbixcclxuICBleHRyYWN0VmVyc2lvbixcclxuICBleHRyYWN0TW9kdWxlTmFtZSxcclxuICBnZXRDYWNoZSxcclxuICBnZXRDYWNoZVBhdGhcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1ldGhvZHMgZm9yIGluaXRpYWxpemluZyBIaWdoY2hhcnRzIHdpdGggY3VzdG9taXplZFxyXG4gKiBhbmltYXRpb24gc2V0dGluZ3MgYW5kIHRyaWdnZXJpbmcgdGhlIGNyZWF0aW9uIG9mIEhpZ2hjaGFydHMgY2hhcnRzIHdpdGhcclxuICogZXhwb3J0LXNwZWNpZmljIGNvbmZpZ3VyYXRpb25zIGluIHRoZSBwYWdlIGNvbnRleHQuIFN1cHBvcnRzIGR5bmFtaWMgb3B0aW9uXHJcbiAqIG1lcmdpbmcsIGN1c3RvbSBsb2dpYyBpbmplY3Rpb24sIGFuZCBjb250cm9sIG92ZXIgcmVuZGVyaW5nIGJlaGF2aW9ycy4gVXNlZFxyXG4gKiBieSB0aGUgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqL1xyXG5cclxuLyogZXNsaW50LWRpc2FibGUgbm8tdW5kZWYgKi9cclxuXHJcbi8qKlxyXG4gKiBTZXR0aW5nIHRoZSBgSGlnaGNoYXJ0cy5hbmltT2JqZWN0YCBmdW5jdGlvbi4gQ2FsbGVkIHdoZW4gaW5pdGluZyB0aGUgcGFnZS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0c1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0cygpIHtcclxuICBIaWdoY2hhcnRzLmFuaW1PYmplY3QgPSBmdW5jdGlvbiAoKSB7XHJcbiAgICByZXR1cm4geyBkdXJhdGlvbjogMCB9O1xyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIHRoZSBhY3R1YWwgSGlnaGNoYXJ0cyBjaGFydCBvbiBhIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY3JlYXRlQ2hhcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUNoYXJ0KGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEdldCByZXF1aXJlZCBmdW5jdGlvbnNcclxuICBjb25zdCB7IGdldE9wdGlvbnMsIHNldE9wdGlvbnMsIG1lcmdlLCB3cmFwIH0gPSBIaWdoY2hhcnRzO1xyXG5cclxuICAvLyBDcmVhdGUgYSBzZXBhcmF0ZSBvYmplY3QgZm9yIGEgcG90ZW50aWFsIGBzZXRPcHRpb25zYCB1c2FnZXMgaW4gb3JkZXJcclxuICAvLyB0byBwcmV2ZW50IGZyb20gcG9sbHV0aW5nIG90aGVyIGV4cG9ydHMgdGhhdCBjYW4gaGFwcGVuIG9uIHRoZSBzYW1lIHBhZ2VcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSBtZXJnZShmYWxzZSwge30sIGdldE9wdGlvbnMoKSk7XHJcblxyXG4gIC8vIE5PVEU6IElzIHRoaXMgdXNlZCBmb3IgYW55dGhpbmcgdXNlZnVsP1xyXG4gIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gZmFsc2U7XHJcbiAgd3JhcChIaWdoY2hhcnRzLkNoYXJ0LnByb3RvdHlwZSwgJ2luaXQnLCBmdW5jdGlvbiAocHJvY2VlZCwgdXNlck9wdGlvbnMsIGNiKSB7XHJcbiAgICAvLyBPdmVycmlkZSB0aGUgYHVzZXJPcHRpb25zYCB3aXRoIGltYWdlIGZyaWVuZGx5IG9wdGlvbnNcclxuICAgIHVzZXJPcHRpb25zID0gbWVyZ2UodXNlck9wdGlvbnMsIHtcclxuICAgICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgfSxcclxuICAgICAgcGxvdE9wdGlvbnM6IHtcclxuICAgICAgICBzZXJpZXM6IHtcclxuICAgICAgICAgIGxhYmVsOiB7XHJcbiAgICAgICAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICAvKiBFeHBlY3RzIHRvb2x0aXAgaW4gdGhlIGB1c2VyT3B0aW9uc2Agd2hlbiBgZm9yRXhwb3J0YCBpcyB0cnVlLlxyXG4gICAgICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL2hpZ2hjaGFydHMvYmxvYi8zYWQ0MzBhMzUzYjgwNTZiOWU3NjRhYTRlNWNkNjgyOGFhNDc5ZGIyL2pzL3BhcnRzL0NoYXJ0LmpzI0wyNDFcclxuICAgICAgICAqL1xyXG4gICAgICB0b29sdGlwOiB7fVxyXG4gICAgfSk7XHJcblxyXG4gICAgKHVzZXJPcHRpb25zLnNlcmllcyB8fCBbXSkuZm9yRWFjaChmdW5jdGlvbiAoc2VyaWVzKSB7XHJcbiAgICAgIHNlcmllcy5hbmltYXRpb24gPSBmYWxzZTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEFkZCBmbGFnIHRvIGtub3cgaWYgY2hhcnQgcmVuZGVyIGhhcyBiZWVuIGNhbGxlZC5cclxuICAgIGlmICghd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlcikge1xyXG4gICAgICB3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyID0gSGlnaGNoYXJ0cy5hZGRFdmVudCh0aGlzLCAncmVuZGVyJywgKCkgPT4ge1xyXG4gICAgICAgIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gdHJ1ZTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbdXNlck9wdGlvbnMsIGNiXSk7XHJcbiAgfSk7XHJcblxyXG4gIHdyYXAoSGlnaGNoYXJ0cy5TZXJpZXMucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCBjaGFydCwgb3B0aW9ucykge1xyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbY2hhcnQsIG9wdGlvbnNdKTtcclxuICB9KTtcclxuXHJcbiAgLy8gU29tZSBtYW5kYXRvcnkgYWRkaXRpb25hbCBgY2hhcnRgIGFuZCBgZXhwb3J0aW5nYCBvcHRpb25zXHJcbiAgY29uc3QgYWRkaXRpb25hbE9wdGlvbnMgPSB7XHJcbiAgICBjaGFydDoge1xyXG4gICAgICAvLyBCeSBkZWZhdWx0IGFuaW1hdGlvbiBpcyBkaXNhYmxlZFxyXG4gICAgICBhbmltYXRpb246IGZhbHNlLFxyXG4gICAgICAvLyBHZXQgdGhlIHJpZ2h0IHNpemUgdmFsdWVzXHJcbiAgICAgIGhlaWdodDogZXhwb3J0T3B0aW9ucy5oZWlnaHQsXHJcbiAgICAgIHdpZHRoOiBleHBvcnRPcHRpb25zLndpZHRoXHJcbiAgICB9LFxyXG4gICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgIC8vIE5vIG5lZWQgZm9yIHRoZSBleHBvcnRpbmcgYnV0dG9uXHJcbiAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IHRoZSBpbnB1dCB0byBleHBvcnQgZnJvbSB0aGUgYGluc3RyYCBvcHRpb25cclxuICBjb25zdCB1c2VyT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy5pbnN0cn1gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvblxyXG4gIGNvbnN0IHRoZW1lT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy5nbG9iYWxPcHRpb25zfWApKCk7XHJcblxyXG4gIC8vIE1lcmdlIHRoZSBmb2xsb3dpbmcgb3B0aW9ucyBvYmplY3RzIHRvIGNyZWF0ZSBmaW5hbCBvcHRpb25zXHJcbiAgY29uc3QgZmluYWxPcHRpb25zID0gbWVyZ2UoXHJcbiAgICBmYWxzZSxcclxuICAgIHRoZW1lT3B0aW9ucyxcclxuICAgIHVzZXJPcHRpb25zLFxyXG4gICAgLy8gUGxhY2VkIGl0IGhlcmUgaW5zdGVhZCBpbiB0aGUgaW5pdCBiZWNhdXNlIG9mIHRoZSBzaXplIGlzc3Vlc1xyXG4gICAgYWRkaXRpb25hbE9wdGlvbnNcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gIGNvbnN0IGZpbmFsQ2FsbGJhY2sgPSBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2tcclxuICAgID8gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2t9YCkoKVxyXG4gICAgOiBudWxsO1xyXG5cclxuICAvLyBUcmlnZ2VyIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSB7XHJcbiAgICBuZXcgRnVuY3Rpb24oJ29wdGlvbnMnLCBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkodXNlck9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBnbG9iYWwgb3B0aW9ucyBpZiBleGlzdFxyXG4gIGlmIChnbG9iYWxPcHRpb25zKSB7XHJcbiAgICBzZXRPcHRpb25zKGdsb2JhbE9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsbCB0aGUgY2hhcnQgY3JlYXRpb25cclxuICBIaWdoY2hhcnRzW2V4cG9ydE9wdGlvbnMuY29uc3RyXSgnY29udGFpbmVyJywgZmluYWxPcHRpb25zLCBmaW5hbENhbGxiYWNrKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBjdXJyZW50IGdsb2JhbCBvcHRpb25zXHJcbiAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIENsZWFyIGl0IGp1c3QgaW4gY2FzZSAoZS5nLiB0aGUgYHNldE9wdGlvbnNgIHdhcyB1c2VkIGluIHRoZSBgY3VzdG9tQ29kZWApXHJcbiAgZm9yIChjb25zdCBwcm9wIGluIGRlZmF1bHRPcHRpb25zKSB7XHJcbiAgICBpZiAodHlwZW9mIGRlZmF1bHRPcHRpb25zW3Byb3BdICE9PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgIGRlbGV0ZSBkZWZhdWx0T3B0aW9uc1twcm9wXTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgZGVmYXVsdCBvcHRpb25zIGJhY2tcclxuICBzZXRPcHRpb25zKEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaik7XHJcblxyXG4gIC8vIEVtcHR5IHRoZSBjdXN0b20gZ2xvYmFsIG9wdGlvbnMgb2JqZWN0XHJcbiAgSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqID0ge307XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzZXR1cEhpZ2hjaGFydHMsXHJcbiAgY3JlYXRlQ2hhcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgbWFuYWdpbmcgUHVwcGV0ZWVyIGJyb3dzZXJcclxuICogaW5zdGFuY2UsIGNyZWF0aW5nIGFuZCBjbGVhcmluZyBwYWdlcywgaW5qZWN0aW5nIGN1c3RvbSByZXNvdXJjZXMsXHJcbiAqIGFuZCBzZXR0aW5nIHVwIEhpZ2hjaGFydHMgZm9yIHNlcnZlci1zaWRlIHJlbmRlcmluZy4gVGhlIG1vZHVsZSBlbnN1cmVzXHJcbiAqIHRoYXQgcmVzb3VyY2VzIGFyZSBjb3JyZWN0bHkgbWFuYWdlZCBhbmQgY2FuIGhhbmRsZSBmYWlsdXJlcyBkdXJpbmdcclxuICogb3BlcmF0aW9ucyBsaWtlIGxhdW5jaGluZyB0aGUgYnJvd3NlciBvciBjcmVhdGluZyBuZXcgcGFnZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgcHVwcGV0ZWVyIGZyb20gJ3B1cHBldGVlcic7XHJcblxyXG5pbXBvcnQgeyBnZXRDYWNoZVBhdGggfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgc2V0dXBIaWdoY2hhcnRzIH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgcGFnZXNcclxuY29uc3QgdGVtcGxhdGUgPSByZWFkRmlsZVN5bmMoXHJcbiAgam9pbihfX2Rpcm5hbWUsICd0ZW1wbGF0ZXMnLCAndGVtcGxhdGUuaHRtbCcpLFxyXG4gICd1dGY4J1xyXG4pO1xyXG5cclxuLy8gVG8gc2F2ZSB0aGUgYnJvd3NlclxyXG5sZXQgYnJvd3NlciA9IG51bGw7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBleGlzdGluZyBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJyb3dzZXJcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEJyb3dzZXIoKSB7XHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBObyB2YWxpZCBicm93c2VyIGhhcyBiZWVuIGNyZWF0ZWQuJywgNTAwKTtcclxuICB9XHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVCcm93c2VyXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyXHJcbiAqIGxhdW5jaC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyXHJcbiAqIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBtYXggcmV0cmllcyB0byBvcGVuXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhcmUgcmVhY2hlZCwgb3IgaWYgbm8gYnJvd3NlciBpbnN0YW5jZSBpcyBmb3VuZCBhZnRlclxyXG4gKiByZXRyaWVzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncykge1xyXG4gIC8vIEdldCBgZGVidWdgIGFuZCBgb3RoZXJgIG9wdGlvbnNcclxuICBjb25zdCB7IGRlYnVnLCBvdGhlciB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZW5hYmxlOiBlbmFibGVkRGVidWcsIC4uLmRlYnVnT3B0aW9ucyB9ID0gZGVidWc7XHJcblxyXG4gIC8vIExhdW5jaCBvcHRpb25zIGZvciB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGNvbnN0IGxhdW5jaE9wdGlvbnMgPSB7XHJcbiAgICBoZWFkbGVzczogb3RoZXIuYnJvd3NlclNoZWxsTW9kZSA/ICdzaGVsbCcgOiB0cnVlLFxyXG4gICAgdXNlckRhdGFEaXI6ICd0bXAnLFxyXG4gICAgYXJnczogcHVwcGV0ZWVyQXJncyB8fCBbXSxcclxuICAgIGhhbmRsZVNJR0lOVDogZmFsc2UsXHJcbiAgICBoYW5kbGVTSUdURVJNOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR0hVUDogZmFsc2UsXHJcbiAgICB3YWl0Rm9ySW5pdGlhbFBhZ2U6IGZhbHNlLFxyXG4gICAgZGVmYXVsdFZpZXdwb3J0OiBudWxsLFxyXG4gICAgLi4uKGVuYWJsZWREZWJ1ZyAmJiBkZWJ1Z09wdGlvbnMpXHJcbiAgfTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlclxyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgLy8gQSBjb3VudGVyIGZvciB0aGUgYnJvd3NlcidzIGxhdW5jaCByZXRyaWVzXHJcbiAgICBsZXQgdHJ5Q291bnQgPSAwO1xyXG5cclxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbYnJvd3Nlcl0gQXR0ZW1wdGluZyB0byBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIExhdW5jaCB0aGUgYnJvd3NlclxyXG4gICAgICAgIGJyb3dzZXIgPSBhd2FpdCBwdXBwZXRlZXIubGF1bmNoKGxhdW5jaE9wdGlvbnMpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuXHJcbiAgICAgICAgICAvLyBXYWl0IGZvciBhIDQgc2Vjb25kcyBiZWZvcmUgdHJ5aW5nIGFnYWluXHJcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIDQwMDApKTtcclxuICAgICAgICAgIGF3YWl0IG9wZW4oKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIGF3YWl0IG9wZW4oKTtcclxuXHJcbiAgICAgIC8vIFNoZWxsIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChsYXVuY2hPcHRpb25zLmhlYWRsZXNzID09PSAnc2hlbGwnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBzaGVsbCBtb2RlLmApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEZWJ1ZyBtb2RlIGluZm9ybVxyXG4gICAgICBpZiAoZW5hYmxlZERlYnVnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1ticm93c2VyXSBNYXhpbXVtIHJldHJpZXMgdG8gb3BlbiBhIGJyb3dzZXIgaW5zdGFuY2UgcmVhY2hlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJywgNTAwKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGJyb3dzZXIgaW5zdGFuY2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyB0aGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgaWYgaXQgaXMgY29ubmVjdGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsb3NlQnJvd3NlclxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsb3NlQnJvd3NlcigpIHtcclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciB3aGVuIGNvbm5lY3RlZFxyXG4gIGlmIChicm93c2VyICYmIGJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgfVxyXG4gIGJyb3dzZXIgPSBudWxsO1xyXG4gIGxvZyg0LCAnW2Jyb3dzZXJdIENsb3NlZCB0aGUgYnJvd3Nlci4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBuZXcgUHVwcGV0ZWVyIHBhZ2Ugd2l0aGluIGFuIGV4aXN0aW5nIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqIFRoZSBmdW5jdGlvbiBjcmVhdGVzIGEgbmV3IHBhZ2UsIGRpc2FibGVzIGNhY2hpbmcsIHNldHMgY29udGVudCB1c2luZ1xyXG4gKiB0aGUgYF9zZXRQYWdlQ29udGVudCgpYCwgYW5kIHJldHVybnMgdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gbmV3UGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBgaWRgLFxyXG4gKiBgd29ya0NvdW50YCwgYW5kIGBwYWdlYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG5vIHZhbGlkIGJyb3dzZXJcclxuICogaGFzIGJlZW4gY29ubmVjdGVkIG9yIGlmIGEgcGFnZSBpcyBpbnZhbGlkIG9yIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBuZXdQYWdlKHBvb2xSZXNvdXJjZSkge1xyXG4gIC8vIEVycm9yIGluIGNhc2Ugb2Ygbm8gY29ubmVjdGVkIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIgfHwgIWJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFticm93c2VyXSBCcm93c2VyIGlzIG5vdCB5ZXQgY29ubmVjdGVkLmAsIDUwMCk7XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBwYWdlXHJcbiAgcG9vbFJlc291cmNlLnBhZ2UgPSBhd2FpdCBicm93c2VyLm5ld1BhZ2UoKTtcclxuXHJcbiAgLy8gRGlzYWJsZSBjYWNoZVxyXG4gIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IF9zZXRQYWdlQ29udGVudChwb29sUmVzb3VyY2UucGFnZSk7XHJcblxyXG4gIC8vIFNldCBwYWdlIGV2ZW50c1xyXG4gIF9zZXRQYWdlRXZlbnRzKHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIHBhZ2UgaXMgY29ycmVjdGx5IGNyZWF0ZWRcclxuICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlIHx8IHBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIFRoZSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLicsIDQwMCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLiBMb2dzXHJcbiAqIHRocm93biBlcnJvciBpZiBjbGVhcmluZyBvZiBhIHBhZ2UncyBjb250ZW50IGZhaWxzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBwYWdlIGFuZCBpZC5cclxuICogQHBhcmFtIHtib29sZWFufSBbaGFyZFJlc2V0PWZhbHNlXSAtIEEgZmxhZyBpbmRpY2F0aW5nIHRoZSB0eXBlIG9mIGNsZWFyaW5nXHJcbiAqIHRvIGJlIHBlcmZvcm1lZC4gSWYgdHJ1ZSwgbmF2aWdhdGVzIHRvIGBhYm91dDpibGFua2AgYW5kIHJlc2V0cyBjb250ZW50XHJcbiAqIGFuZCBzY3JpcHRzLiBJZiBmYWxzZSwgY2xlYXJzIHRoZSBib2R5IGNvbnRlbnQgYnkgc2V0dGluZyBhIHByZWRlZmluZWQgSFRNTFxyXG4gKiBzdHJ1Y3R1cmUuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0cnVlIHdoZW4gcGFnZVxyXG4gKiBpcyBjb3JyZWN0bHkgY2xlYXJlZCBhbmQgZmFsc2Ugd2hlbiBpdCBpcyBub3QuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlKHBvb2xSZXNvdXJjZSwgaGFyZFJlc2V0ID0gZmFsc2UpIHtcclxuICB0cnkge1xyXG4gICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIGlmIChoYXJkUmVzZXQpIHtcclxuICAgICAgICAvLyBOYXZpZ2F0ZSB0byBgYWJvdXQ6YmxhbmtgXHJcbiAgICAgICAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2UuZ290bygnYWJvdXQ6YmxhbmsnLCB7XHJcbiAgICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBTZXQgdGhlIGNvbnRlbnQgYW5kIGFuZCBzY3JpcHRzIGFnYWluXHJcbiAgICAgICAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBDbGVhciBib2R5IGNvbnRlbnRcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LmlubmVySFRNTCA9XHJcbiAgICAgICAgICAgICc8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+PGRpdiBpZD1cImNvbnRhaW5lclwiPjwvZGl2PjwvZGl2Pic7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgMixcclxuICAgICAgZXJyb3IsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIENvbnRlbnQgb2YgdGhlIHBhZ2UgY291bGQgbm90IGJlIGNsZWFyZWQuYFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgcG9vbFJlc291cmNlLndvcmtDb3VudCA9IGdldE9wdGlvbnMoKS5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgfVxyXG4gIHJldHVybiBmYWxzZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgY3VzdG9tIEpTIGFuZCBDU1MgcmVzb3VyY2VzIHRvIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHJlc291cmNlcyB3aWxsXHJcbiAqIGJlIGFkZGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxBcnJheS48T2JqZWN0Pj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIGFycmF5XHJcbiAqIG9mIGluamVjdGVkIHJlc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheVxyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIC8vIFVzZSByZXNvdXJjZXNcclxuICBjb25zdCByZXNvdXJjZXMgPSBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzO1xyXG4gIGlmIChyZXNvdXJjZXMpIHtcclxuICAgIGNvbnN0IGluamVjdGVkSnMgPSBbXTtcclxuXHJcbiAgICAvLyBMb2FkIGN1c3RvbSBKUyBjb2RlXHJcbiAgICBpZiAocmVzb3VyY2VzLmpzKSB7XHJcbiAgICAgIGluamVjdGVkSnMucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmpzXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExvYWQgc2NyaXB0cyBmcm9tIGFsbCBjdXN0b20gZmlsZXNcclxuICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICAgIGNvbnN0IGlzTG9jYWwgPSBmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IGZhbHNlIDogdHJ1ZTtcclxuXHJcbiAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICBpbmplY3RlZEpzLnB1c2goXHJcbiAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgID8ge1xyXG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChmaWxlKSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZvciAoY29uc3QganNSZXNvdXJjZSBvZiBpbmplY3RlZEpzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhqc1Jlc291cmNlKSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIFRoZSBKUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpbmplY3RlZEpzLmxlbmd0aCA9IDA7XHJcblxyXG4gICAgLy8gTG9hZCBDU1NcclxuICAgIGNvbnN0IGluamVjdGVkQ3NzID0gW107XHJcbiAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgcGF0aDogZ2V0QWJzb2x1dGVQYXRoKGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoY3NzUmVzb3VyY2UpKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICAgYFticm93c2VyXSBUaGUgQ1NTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaW5qZWN0ZWRDc3MubGVuZ3RoID0gMDtcclxuICAgIH1cclxuICB9XHJcbiAgcmV0dXJuIGluamVjdGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIG91dCBhbGwgc3RhdGUgc2V0IG9uIHRoZSBwYWdlIHdpdGggYGFkZFNjcmlwdFRhZ2AgYW5kIGBhZGRTdHlsZVRhZ2AuXHJcbiAqIFJlbW92ZXMgaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS5cclxuICogQWRkaXRpb25hbGx5LCBpdCBkZXN0cm95cyBwcmV2aW91c2x5IGV4aXN0aW5nIGNoYXJ0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtBcnJheS48T2JqZWN0Pn0gaW5qZWN0ZWRSZXNvdXJjZXMgLSBBcnJheSBvZiBpbmplY3RlZCByZXNvdXJjZXNcclxuICogdG8gYmUgY2xlYXJlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICB0cnkge1xyXG4gICAgZm9yIChjb25zdCByZXNvdXJjZSBvZiBpbmplY3RlZFJlc291cmNlcykge1xyXG4gICAgICBhd2FpdCByZXNvdXJjZS5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzIGFmdGVyIGV4cG9ydCBpcyBkb25lIGFuZCByZXNldCBhbGwgQ1NTIGFuZCBzY3JpcHQgdGFnc1xyXG4gICAgYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCB3aGVuIGRvaW5nIFNWRyBleHBvcnRzXHJcbiAgICAgIGlmICh0eXBlb2YgSGlnaGNoYXJ0cyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcclxuXHJcbiAgICAgICAgLy8gQ2hlY2sgaW4gYW55IGFscmVhZHkgZXhpc3RpbmcgY2hhcnRzXHJcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XHJcbiAgICAgICAgICAvLyBEZXN0cm95IG9sZCBjaGFydHNcclxuICAgICAgICAgIGZvciAoY29uc3Qgb2xkQ2hhcnQgb2Ygb2xkQ2hhcnRzKSB7XHJcbiAgICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcclxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICAgIEhpZ2hjaGFydHMuY2hhcnRzLnNoaWZ0KCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLnNjcmlwdHNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLCAuLi5zdHlsZXNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc3R5bGUnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5saW5rc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdsaW5rJyk7XHJcblxyXG4gICAgICAvLyBSZW1vdmUgdGFnc1xyXG4gICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgW1xyXG4gICAgICAgIC4uLnNjcmlwdHNUb1JlbW92ZSxcclxuICAgICAgICAuLi5zdHlsZXNUb1JlbW92ZSxcclxuICAgICAgICAuLi5saW5rc1RvUmVtb3ZlXHJcbiAgICAgIF0pIHtcclxuICAgICAgICBlbGVtZW50LnJlbW92ZSgpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciBwYWdlJ3MgcmVzb3VyY2VzLmApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvbnRlbnQgZm9yIGEgUHVwcGV0ZWVyIFBhZ2UgdXNpbmcgYSBwcmVkZWZpbmVkIHRlbXBsYXRlXHJcbiAqIGFuZCBhZGRpdGlvbmFsIHNjcmlwdHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VDb250ZW50XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgY29udGVudFxyXG4gKiBpcyBiZWluZyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnQocGFnZSkge1xyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBwYWdlIGNvbnRlbnRcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gIC8vIEFkZCBhbGwgcmVnaXN0ZXJlZCBIaWdjaGFydHMgc2NyaXB0cywgcXVpdGUgZGVtYW5kaW5nXHJcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBqb2luKGdldENhY2hlUGF0aCgpLCAnc291cmNlcy5qcycpIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGluaXRpYWwgYGFuaW1PYmplY3RgIGZvciBIaWdoY2hhcnRzXHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IGV2ZW50cyAobGlrZSBgcGFnZWVycm9yYCBhbmQgYGNvbnNvbGVgKSBmb3IgYSBQdXBwZXRlZXIgUGFnZSBpbiBvcmRlclxyXG4gKiB0byBjYXRjaCBhbmQgZGlzcGxheSBlcnJvcnMgYW5kIGNvbnNvbGUgbG9ncyBmcm9tIHRoZSB3aW5kb3cgY29udGV4dC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgbGlzdGVuZXJzXHJcbiAqIGFyZSBiZWluZyBzZXQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc2V0UGFnZUV2ZW50cyhwYWdlKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgcGFnZWVycm9yYCBsaXN0ZW5lclxyXG4gIHBhZ2Uub24oJ3BhZ2VlcnJvcicsIGFzeW5jICgpID0+IHtcclxuICAgIC8vIEl0IHdvdWxkIHNlZW0gbGlrZSB0aGlzIG1heSBmaXJlIGF0IHRoZSBzYW1lIHRpbWUgb3Igc2hvcnRseSBiZWZvcmVcclxuICAgIC8vIGEgcGFnZSBpcyBjbG9zZWQuXHJcbiAgICBpZiAocGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgY29uc29sZWAgbGlzdGVuZXIsIGlmIG5lZWRlZFxyXG4gIGlmIChkZWJ1Zy5lbmFibGUgJiYgZGVidWcubGlzdGVuVG9Db25zb2xlKSB7XHJcbiAgICBwYWdlLm9uKCdjb25zb2xlJywgKG1lc3NhZ2UpID0+IHtcclxuICAgICAgY29uc29sZS5sb2coYFtkZWJ1Z10gJHttZXNzYWdlLnRleHQoKX1gKTtcclxuICAgIH0pO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldEJyb3dzZXIsXHJcbiAgY3JlYXRlQnJvd3NlcixcclxuICBjbG9zZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSxcclxuICBjbGVhclBhZ2UsXHJcbiAgYWRkUGFnZVJlc291cmNlcyxcclxuICBjbGVhclBhZ2VSZXNvdXJjZXNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogVGhlIENTUyB0byBiZSB1c2VkIG9uIHRoZSBleHBvcnRlZCBwYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQ1NTIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoKSA9PiBgXHJcblxyXG5odG1sLCBib2R5IHtcclxuICBtYXJnaW46IDA7XHJcbiAgcGFkZGluZzogMDtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG59XHJcblxyXG4jdGFibGUtZGl2LCAjc2xpZGVycywgI2RhdGF0YWJsZSwgI2NvbnRyb2xzLCAubGQtcm93IHtcclxuICBkaXNwbGF5OiBub25lO1xyXG4gIGhlaWdodDogMDtcclxufVxyXG5cclxuI2NoYXJ0LWNvbnRhaW5lciB7XHJcbiAgYm94LXNpemluZzogYm9yZGVyLWJveDtcclxuICBtYXJnaW46IDA7XHJcbiAgb3ZlcmZsb3c6IGF1dG87XHJcbiAgZm9udC1zaXplOiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyID4gZmlndXJlLCBkaXYge1xyXG4gIG1hcmdpbi10b3A6IDAgIWltcG9ydGFudDtcclxuICBtYXJnaW4tYm90dG9tOiAwICFpbXBvcnRhbnQ7XHJcbn1cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNzc1RlbXBsYXRlIGZyb20gJy4vY3NzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBUaGUgU1ZHIHRlbXBsYXRlIHRvIHVzZSB3aGVuIGxvYWRpbmcgU1ZHIGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIGlucHV0IGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBTVkcgdGVtcGxhdGUuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoc3ZnKSA9PiBgXHJcbjwhRE9DVFlQRSBodG1sPlxyXG48aHRtbCBsYW5nPSdlbi1VUyc+XHJcbiAgPGhlYWQ+XHJcbiAgICA8bWV0YSBodHRwLWVxdWl2PVwiQ29udGVudC1UeXBlXCIgY29udGVudD1cInRleHQvaHRtbDsgY2hhcnNldD11dGYtOFwiPlxyXG4gICAgPHRpdGxlPkhpZ2hjaGFydHMgRXhwb3J0PC90aXRsZT5cclxuICA8L2hlYWQ+XHJcbiAgPHN0eWxlPlxyXG4gICAgJHtjc3NUZW1wbGF0ZSgpfVxyXG4gIDwvc3R5bGU+XHJcbiAgPGJvZHk+XHJcbiAgICA8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+XHJcbiAgICAgICR7c3ZnfVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgaGFuZGxlcyBjaGFydCBleHBvcnQgZnVuY3Rpb25hbGl0eSB1c2luZyBQdXBwZXRlZXIuXHJcbiAqIEl0IHN1cHBvcnRzIGV4cG9ydGluZyBjaGFydHMgYXMgU1ZHLCBQTkcsIEpQRUcsIGFuZCBQREYgZm9ybWF0cy4gVGhlIG1vZHVsZVxyXG4gKiBtYW5hZ2VzIHBhZ2UgcmVzb3VyY2VzLCBzZXRzIHVwIHRoZSBleHBvcnQgZW52aXJvbm1lbnQsIGFuZCBwcm9jZXNzZXMgY2hhcnRcclxuICogY29uZmlndXJhdGlvbnMgb3IgU1ZHIGlucHV0cyBmb3IgcmVuZGVyaW5nLiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2VcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFkZFBhZ2VSZXNvdXJjZXMsIGNsZWFyUGFnZVJlc291cmNlcyB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IGNyZWF0ZUNoYXJ0IH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IHN2Z1RlbXBsYXRlIGZyb20gJy4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8KHN0cmluZ3xCdWZmZXJ8RXhwb3J0RXJyb3IpPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXNcclxuICogdG8gdGhlIGV4cG9ydGVkIGRhdGEgb3IgcmVqZWN0aW5nIHdpdGggYW4gYEV4cG9ydEVycm9yYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGV4cG9ydCB0byBhbiB1bnN1cHBvcnRlZFxyXG4gKiBvdXRwdXQgZm9ybWF0IG9jY3Vycy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnQocGFnZSwgZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5IChhZGRpdGlvbmFsIEpTIGFuZCBDU1MpXHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxldCBpc1NWRyA9IGZhbHNlO1xyXG5cclxuICAgIC8vIERlY2lkZSBvbiB0aGUgZXhwb3J0IG1ldGhvZFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnKSB7XHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgU1ZHIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gSWYgdGhlIGB0eXBlYCBpcyBhbHNvIFNWRywgcmV0dXJuIHRoZSBpbnB1dFxyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAgIHJldHVybiBleHBvcnRPcHRpb25zLnN2ZztcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gTWFyayBhcyBTVkcgZXhwb3J0IGZvciB0aGUgbGF0ZXIgc2l6ZSBjb3JyZWN0aW9uc1xyXG4gICAgICBpc1NWRyA9IHRydWU7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2Uuc2V0Q29udGVudChzdmdUZW1wbGF0ZShleHBvcnRPcHRpb25zLnN2ZyksIHtcclxuICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xyXG4gICAgICB9KTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgSlNPTiBjb25maWcuJyk7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGV4cG9ydFxyXG4gICAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKGNyZWF0ZUNoYXJ0LCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXBzIHRyYWNrIG9mIGFsbCByZXNvdXJjZXMgYWRkZWQgb24gdGhlIHBhZ2Ugd2l0aCBhZGRYWFhUYWcuIGV0Y1xyXG4gICAgLy8gSXQncyBWSVRBTCB0aGF0IGFsbCBhZGRlZCByZXNvdXJjZXMgZW5kcyB1cCBoZXJlIHNvIHdlIGNhbiBjbGVhciB0aGluZ3NcclxuICAgIC8vIG91dCB3aGVuIGRvaW5nIGEgbmV3IGV4cG9ydCBpbiB0aGUgc2FtZSBwYWdlIVxyXG4gICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgLi4uKGF3YWl0IGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZWFsIGNoYXJ0IHNpemUgYW5kIHNldCB0aGUgem9vbSBhY2NvcmRpbmdseVxyXG4gICAgY29uc3Qgc2l6ZSA9IGlzU1ZHXHJcbiAgICAgID8gYXdhaXQgcGFnZS5ldmFsdWF0ZSgoc2NhbGUpID0+IHtcclxuICAgICAgICAgIGNvbnN0IHN2Z0VsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFxyXG4gICAgICAgICAgICAnI2NoYXJ0LWNvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZSdcclxuICAgICAgICAgICk7XHJcblxyXG4gICAgICAgICAgLy8gR2V0IHRoZSB2YWx1ZXMgY29ycmVjdGx5IHNjYWxlZFxyXG4gICAgICAgICAgY29uc3QgY2hhcnRIZWlnaHQgPSBzdmdFbGVtZW50LmhlaWdodC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcbiAgICAgICAgICBjb25zdCBjaGFydFdpZHRoID0gc3ZnRWxlbWVudC53aWR0aC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gSW4gY2FzZSBvZiBTVkcgdGhlIHpvb20gbXVzdCBiZSBzZXQgZGlyZWN0bHkgZm9yIGJvZHkgYXMgc2NhbGVcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUubWFyZ2luID0gJzBweCc7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSwgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKSlcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG5cclxuICAgICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXNcclxuICAgICAgICAgIC8vIG9mIGV4cG9ydHMuIFJlc2V0IHRoZSB6b29tIGZvciBvdGhlciBleHBvcnRzIHRoYW4gdG8gU1ZHc1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY2xpcCByZWdpb24gZm9yIHRoZSBwYWdlXHJcbiAgICBjb25zdCB7IHgsIHkgfSA9IGF3YWl0IF9nZXRDbGlwUmVnaW9uKHBhZ2UpO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgaGVpZ2h0YCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0SGVpZ2h0ID0gTWF0aC5hYnMoXHJcbiAgICAgIE1hdGguY2VpbChzaXplLmNoYXJ0SGVpZ2h0IHx8IGV4cG9ydE9wdGlvbnMuaGVpZ2h0KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgZmluYWwgYHdpZHRoYCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0V2lkdGggPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRXaWR0aCB8fCBleHBvcnRPcHRpb25zLndpZHRoKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGZpbmFsIHZpZXdwb3J0IG5vdyB0aGF0IHdlIGhhdmUgdGhlIHJlYWwgaGVpZ2h0XHJcbiAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcclxuICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgIGRldmljZVNjYWxlRmFjdG9yOiBpc1NWRyA/IDEgOiBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpXHJcbiAgICB9KTtcclxuXHJcbiAgICBsZXQgcmVzdWx0O1xyXG4gICAgLy8gUmFzdGVyaXphdGlvbiBwcm9jZXNzXHJcbiAgICBzd2l0Y2ggKGV4cG9ydE9wdGlvbnMudHlwZSkge1xyXG4gICAgICBjYXNlICdzdmcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVTVkcocGFnZSk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BuZyc6XHJcbiAgICAgIGNhc2UgJ2pwZWcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVJbWFnZShcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnR5cGUsXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgICB4LFxyXG4gICAgICAgICAgICB5XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BkZic6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVBERihcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgZGVmYXVsdDpcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdDogJHtleHBvcnRPcHRpb25zLnR5cGV9LmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENsZWFyIHByZXZpb3VzbHkgaW5qZWN0ZWQgSlMgYW5kIENTUyByZXNvdXJjZXNcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50XHJcbiAqIHdpdGggdGhlICdjaGFydC1jb250YWluZXInIGlkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgeGAsIGB5YCwgYHdpZHRoYCwgYW5kIGBoZWlnaHRgIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZ2V0Q2xpcFJlZ2lvbihwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoJyNjaGFydC1jb250YWluZXInLCAoZWxlbWVudCkgPT4ge1xyXG4gICAgY29uc3QgeyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH0gPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgeCxcclxuICAgICAgeSxcclxuICAgICAgd2lkdGgsXHJcbiAgICAgIGhlaWdodDogTWF0aC50cnVuYyhoZWlnaHQgPiAxID8gaGVpZ2h0IDogNTAwKVxyXG4gICAgfTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gU1ZHIGJ5IGV2YWx1YXRpbmcgdGhlIGBvdXRlckhUTUxgIG9mIHRoZSBmaXJzdCAnc3ZnJyBlbGVtZW50XHJcbiAqIGluc2lkZSBhbiBlbGVtZW50IHdpdGggdGhlIGlkICdjb250YWluZXInLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVTVkdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBTVkcgc3RyaW5nLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVNWRyhwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoXHJcbiAgICAnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsXHJcbiAgICAoZWxlbWVudCkgPT4gZWxlbWVudC5vdXRlckhUTUxcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBzY3JlZW5zaG90YCBmdW5jdGlvbmFsaXR5IHdpdGhcclxuICogc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZUltYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEltYWdlIHR5cGUuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjbGlwIC0gQ2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGltYWdlIGJ1ZmZlclxyXG4gKiBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgIGZvciB0aW1lb3V0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZUltYWdlKHBhZ2UsIHR5cGUsIGNsaXAsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgcmV0dXJuIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xyXG4gICAgICB0eXBlLFxyXG4gICAgICBjbGlwLFxyXG4gICAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICAgIGZ1bGxQYWdlOiBmYWxzZSxcclxuICAgICAgb3B0aW1pemVGb3JTcGVlZDogdHJ1ZSxcclxuICAgICAgY2FwdHVyZUJleW9uZFZpZXdwb3J0OiB0cnVlLFxyXG4gICAgICAuLi4odHlwZSAhPT0gJ3BuZycgPyB7IHF1YWxpdHk6IDgwIH0gOiB7fSksXHJcbiAgICAgIC8vIEFsd2F5cyByZW5kZXIgb24gYSB0cmFuc3BhcmVudCBwYWdlIGlmIHRoZSBleHBlY3RlZCB0eXBlIGZvcm1hdCBpcyBQTkdcclxuICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZycgLy8gIzQ0NywgIzQ2M1xyXG4gICAgfSksXHJcbiAgICBuZXcgUHJvbWlzZSgoX3Jlc29sdmUsIHJlamVjdCkgPT5cclxuICAgICAgc2V0VGltZW91dChcclxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEV4cG9ydEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnLCA0MDgpKSxcclxuICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgICAgIClcclxuICAgIClcclxuICBdKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQREYgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBgcGRmYCBmdW5jdGlvbmFsaXR5IHdpdGggc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVBERlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtudW1iZXJ9IGhlaWdodCAtIFBERiBoZWlnaHQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aCAtIFBERiB3aWR0aC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBQREYgYnVmZmVyLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVBERihwYWdlLCBoZWlnaHQsIHdpZHRoLCByYXN0ZXJpemF0aW9uVGltZW91dCkge1xyXG4gIGF3YWl0IHBhZ2UuZW11bGF0ZU1lZGlhVHlwZSgnc2NyZWVuJyk7XHJcbiAgcmV0dXJuIHBhZ2UucGRmKHtcclxuICAgIC8vIFRoaXMgd2lsbCByZW1vdmUgYW4gZXh0cmEgZW1wdHkgcGFnZSBpbiBQREYgZXhwb3J0c1xyXG4gICAgaGVpZ2h0OiBoZWlnaHQgKyAxLFxyXG4gICAgd2lkdGgsXHJcbiAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICB0aW1lb3V0OiByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBwdXBwZXRlZXJFeHBvcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGEgd29ya2VyIHBvb2wgaW1wbGVtZW50YXRpb24gZm9yIG1hbmFnaW5nXHJcbiAqIHRoZSBicm93c2VyIGluc3RhbmNlIGFuZCBwYWdlcywgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIGZvciB1c2Ugd2l0aFxyXG4gKiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBvcHRpbWl6ZXMgcmVzb3VyY2VzIHVzYWdlIGFuZCBwZXJmb3JtYW5jZVxyXG4gKiBieSBtYWludGFpbmluZyBhIHBvb2wgb2Ygd29ya2VycyB0aGF0IGNhbiBoYW5kbGUgY29uY3VycmVudCBleHBvcnQgdGFza3NcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgY3JlYXRlQnJvd3NlciwgY2xvc2VCcm93c2VyLCBuZXdQYWdlLCBjbGVhclBhZ2UgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBwdXBwZXRlZXJFeHBvcnQgfSBmcm9tICcuL2V4cG9ydC5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXROZXdEYXRlVGltZSwgbWVhc3VyZVRpbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgcG9vbCBpbnN0YW5jZVxyXG5sZXQgcG9vbCA9IG51bGw7XHJcblxyXG4vLyBQb29sIHN0YXRpc3RpY3NcclxuY29uc3QgcG9vbFN0YXRzID0ge1xyXG4gIGV4cG9ydHNBdHRlbXB0ZWQ6IDAsXHJcbiAgZXhwb3J0c1BlcmZvcm1lZDogMCxcclxuICBleHBvcnRzRHJvcHBlZDogMCxcclxuICBleHBvcnRzRnJvbVN2ZzogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnM6IDAsXHJcbiAgZXhwb3J0c0Zyb21TdmdBdHRlbXB0czogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0czogMCxcclxuICB0aW1lU3BlbnQ6IDAsXHJcbiAgdGltZVNwZW50QXZlcmFnZTogMFxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNldHRpbmcgdXAgd29ya2VyIHJlc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0UG9vbFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gZW5kaW5nIHRoZSBmdW5jdGlvblxyXG4gKiBleGVjdXRpb24gd2hlbiBhbiBhbHJlYWR5IGluaXRpYWxpemVkIHBvb2wgb2YgcmVzb3VyY2VzIGlzIGZvdW5kLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbFxyXG4gKiBvZiB3b3JrZXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRQb29sKHBvb2xPcHRpb25zLCBwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBwdXBwZXRlZXIgYXJndW1lbnRzXHJcbiAgYXdhaXQgY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBJbml0aWFsaXppbmcgcG9vbCB3aXRoIHdvcmtlcnM6IG1pbiAke3Bvb2xPcHRpb25zLm1pbldvcmtlcnN9LCBtYXggJHtwb29sT3B0aW9ucy5tYXhXb3JrZXJzfS5gXHJcbiAgICApO1xyXG5cclxuICAgIGlmIChwb29sKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgICdbcG9vbF0gQWxyZWFkeSBpbml0aWFsaXplZCwgcGxlYXNlIGtpbGwgaXQgYmVmb3JlIGNyZWF0aW5nIGEgbmV3IG9uZS4nXHJcbiAgICAgICk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBLZWVwIGFuIGV5ZSBvbiBhIGNvcnJlY3QgbWluIGFuZCBtYXggd29ya2VycyBudW1iZXJcclxuICAgIGlmIChwb29sT3B0aW9ucy5taW5Xb3JrZXJzID4gcG9vbE9wdGlvbnMubWF4V29ya2Vycykge1xyXG4gICAgICBwb29sT3B0aW9ucy5taW5Xb3JrZXJzID0gcG9vbE9wdGlvbnMubWF4V29ya2VycztcclxuICAgIH1cclxuXHJcbiAgICAvLyBDcmVhdGUgYSBwb29sIGFsb25nIHdpdGggYSBtaW5pbWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIHBvb2wgPSBuZXcgUG9vbCh7XHJcbiAgICAgIC8vIEdldCB0aGUgYGNyZWF0ZWAsIGB2YWxpZGF0ZWAsIGFuZCBgZGVzdHJveWAgZnVuY3Rpb25zXHJcbiAgICAgIC4uLl9mYWN0b3J5KHBvb2xPcHRpb25zKSxcclxuICAgICAgbWluOiBwb29sT3B0aW9ucy5taW5Xb3JrZXJzLFxyXG4gICAgICBtYXg6IHBvb2xPcHRpb25zLm1heFdvcmtlcnMsXHJcbiAgICAgIGFjcXVpcmVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5hY3F1aXJlVGltZW91dCxcclxuICAgICAgY3JlYXRlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlVGltZW91dCxcclxuICAgICAgZGVzdHJveVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmRlc3Ryb3lUaW1lb3V0LFxyXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuaWRsZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXM6IHBvb2xPcHRpb25zLmNyZWF0ZVJldHJ5SW50ZXJ2YWwsXHJcbiAgICAgIHJlYXBJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMucmVhcGVySW50ZXJ2YWwsXHJcbiAgICAgIHByb3BhZ2F0ZUNyZWF0ZUVycm9yOiBmYWxzZVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGV2ZW50c1xyXG4gICAgcG9vbC5vbigncmVsZWFzZScsIGFzeW5jIChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBDbGVhciBwYWdlXHJcbiAgICAgIGNvbnN0IGNsZWFyU3RhdHVzID0gYXdhaXQgY2xlYXJQYWdlKHJlc291cmNlLCBmYWxzZSk7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gUmVsZWFzaW5nIGEgd29ya2VyLiBDbGVhciBwYWdlIHN0YXR1czogJHtjbGVhclN0YXR1c30uYFxyXG4gICAgICApO1xyXG4gICAgfSk7XHJcblxyXG4gICAgcG9vbC5vbignZGVzdHJveVN1Y2Nlc3MnLCAoX2V2ZW50SWQsIHJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gRGVzdHJveWVkIGEgd29ya2VyIHN1Y2Nlc3NmdWxseS5gXHJcbiAgICAgICk7XHJcbiAgICAgIHJlc291cmNlLnBhZ2UgPSBudWxsO1xyXG4gICAgfSk7XHJcblxyXG4gICAgY29uc3QgaW5pdGlhbFJlc291cmNlcyA9IFtdO1xyXG4gICAgLy8gQ3JlYXRlIGFuIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwb29sT3B0aW9ucy5taW5Xb3JrZXJzOyBpKyspIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcbiAgICAgICAgaW5pdGlhbFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSBhbiBpbml0aWFsIHJlc291cmNlLicpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHJlc291cmNlKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGggPyBgIHdpdGggJHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aH0gaW5pdGlhbCByZXNvdXJjZXMgd2FpdGluZy5gIDogJy4nfWBcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1twb29sXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBjcmVhdGUgdGhlIHBvb2wgb2Ygd29ya2Vycy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFRlcm1pbmF0ZXMgYWxsIHdvcmtlcnMgaW4gdGhlIHBvb2wsIGRlc3Ryb3lzIHRoZSBwb29sLCBhbmQgY2xvc2VzIHRoZSBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGtpbGxQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIGFsbCB3b3JrZXJzIGFyZVxyXG4gKiB0ZXJtaW5hdGVkLCB0aGUgcG9vbCBpcyBkZXN0cm95ZWQsIGFuZCB0aGUgYnJvd3NlciBpcyBzdWNjZXNzZnVsbHkgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGtpbGxQb29sKCkge1xyXG4gIGxvZygzLCAnW3Bvb2xdIEtpbGxpbmcgcG9vbCB3aXRoIGFsbCB3b3JrZXJzIGFuZCBjbG9zaW5nIGJyb3dzZXIuJyk7XHJcblxyXG4gIC8vIElmIHN0aWxsIGFsaXZlLCBkZXN0cm95IHRoZSBwb29sIG9mIHBhZ2VzIGJlZm9yZSBjbG9zaW5nIGEgYnJvd3NlclxyXG4gIGlmIChwb29sKSB7XHJcbiAgICAvLyBGcmVlIHVwIG5vdCByZWxlYXNlZCB3b3JrZXJzXHJcbiAgICBmb3IgKGNvbnN0IHdvcmtlciBvZiBwb29sLnVzZWQpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlci5yZXNvdXJjZSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSB0aGUgcG9vbCBpZiBpdCBpcyBzdGlsbCBhdmFpbGFibGVcclxuICAgIGlmICghcG9vbC5kZXN0cm95ZWQpIHtcclxuICAgICAgYXdhaXQgcG9vbC5kZXN0cm95KCk7XHJcbiAgICAgIGxvZyg0LCAnW3Bvb2xdIERlc3Ryb3llZCB0aGUgcG9vbCBvZiByZXNvdXJjZXMuJyk7XHJcbiAgICB9XHJcbiAgICBwb29sID0gbnVsbDtcclxuICB9XHJcblxyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgYXdhaXQgY2xvc2VCcm93c2VyKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcm9jZXNzZXMgdGhlIGV4cG9ydCB3b3JrIHVzaW5nIGEgd29ya2VyIGZyb20gdGhlIHBvb2wuIEFjcXVpcmVzIGEgd29ya2VyXHJcbiAqIGhhbmRsZSBmcm9tIHRoZSBwb29sLCBwZXJmb3JtcyB0aGUgZXhwb3J0IHVzaW5nIHB1cHBldGVlciwgYW5kIHJlbGVhc2VzXHJcbiAqIHRoZSB3b3JrZXIgaGFuZGxlIGJhY2sgdG8gdGhlIHBvb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFdvcmtcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGV4cG9ydCByZXN1bHRcclxuICogYW5kIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0V29yayhvcHRpb25zKSB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIHN0YXJ0aW5nIHRvIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gQW4gZXhwb3J0IGF0dGVtcHQgY291bnRlZFxyXG4gICAgKytwb29sU3RhdHMuZXhwb3J0c0F0dGVtcHRlZDtcclxuXHJcbiAgICAvLyBEaXNwbGF5IHRoZSBwb29sIGluZm9ybWF0aW9uIGlmIG5lZWRlZFxyXG4gICAgaWYgKG9wdGlvbnMucG9vbC5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgZ2V0UG9vbEluZm8oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpbiBjYXNlIG9mIGxhY2tpbmcgdGhlIHBvb2wgaW5zdGFuY2VcclxuICAgIGlmICghcG9vbCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1twb29sXSBXb3JrIHJlY2VpdmVkLCBidXQgcG9vbCBoYXMgbm90IGJlZW4gc3RhcnRlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRoZSBhY3F1aXJlIGNvdW50ZXJcclxuICAgIGNvbnN0IGFjcXVpcmVDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBUcnkgdG8gYWNxdWlyZSB0aGUgd29ya2VyIGFsb25nIHdpdGggdGhlIGlkLCB3b3JrcyBjb3VudCBhbmQgcGFnZVxyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuXHJcbiAgICAgIC8vIEFjcXVpcmUgYSBwb29sIHJlc291cmNlXHJcbiAgICAgIHdvcmtlckhhbmRsZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcblxyXG4gICAgICAvLyBDaGVjayB0aGUgcGFnZSBhY3F1aXJlIHRpbWVcclxuICAgICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDUsXHJcbiAgICAgICAgICBgW2JlbmNobWFya10gJHtvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJyd9YCxcclxuICAgICAgICAgIGBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlIHRvb2sgJHthY3F1aXJlQ291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgfUVycm9yIGVuY291bnRlcmVkIHdoZW4gYWNxdWlyaW5nIGFuIGF2YWlsYWJsZSBlbnRyeTogJHthY3F1aXJlQ291bnRlcigpfW1zLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyZWQgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgIGlmICghd29ya2VySGFuZGxlLnBhZ2UpIHtcclxuICAgICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1twb29sXSBSZXNvbHZlZCB3b3JrZXIgcGFnZSBpcyBpbnZhbGlkOiB0aGUgcG9vbCBzZXR1cCBpcyB3b25reS4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgdGhlIHN0YXJ0IHRpbWVcclxuICAgIGNvbnN0IHdvcmtTdGFydCA9IGdldE5ld0RhdGVUaW1lKCk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICA0LFxyXG4gICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7d29ya2VySGFuZGxlLmlkfV0gLSBTdGFydGluZyB3b3JrIG9uIHRoaXMgcG9vbCBlbnRyeS5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFN0YXJ0IG1lYXN1cmluZyBleHBvcnQgdGltZVxyXG4gICAgY29uc3QgZXhwb3J0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnQgb24gYSBwdXBwZXRlZXIgbGV2ZWxcclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHB1cHBldGVlckV4cG9ydChcclxuICAgICAgd29ya2VySGFuZGxlLnBhZ2UsXHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICApO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIGl0J3MgYW4gZXJyb3JcclxuICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBFcnJvcikge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJZiB0aGVyZSdzIGEgcmFzdGVyaXphdGlvbiB0aW1lb3V0LCB3ZSB3YW50IG5lZWQgdG8gZmx1c2ggdGhlIHBhZ2UuXHJcbiAgICAgIC8vIFRoaXMgaXMgYmVjYXVzZSB0aGUgcGFnZSBtYXkgYmUgaW4gYSBzdGF0ZSB3aGVyZSBpdCdzIHdhaXRpbmcgZm9yXHJcbiAgICAgIC8vIHRoZSBzY3JlZW5zaG90IHRvIGZpbmlzaCBldmVuIHRob3VnaCB0aGUgdGltZW91dCBoYXMgb2NjdXJlZC5cclxuICAgICAgLy8gV2hpY2ggb2YgY291cnNlIGNhdXNlcyBhIGxvdCBvZiBpc3N1ZXMgd2l0aCB0aGUgZXZlbnQgc3lzdGVtLFxyXG4gICAgICAvLyBhbmQgcGFnZSBjb25zaXN0ZW5jeS5cclxuICAgICAgLy9cclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gT25seSBwYWdlLnNjcmVlbnNob3Qgd2lsbCB0aHJvdyB0aGlzLCB0aW1lb3V0cyBmb3IgUERGJ3MgYXJlXHJcbiAgICAgIC8vIGhhbmRsZWQgYnkgdGhlIHBhZ2UucGRmIGZ1bmN0aW9uIGl0c2VsZi5cclxuICAgICAgLy9cclxuICAgICAgLy8gLi4ueWVzLCB0aGlzIGlzIHVnbHkuXHJcbiAgICAgIGlmIChyZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpIHtcclxuICAgICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICAgIHdvcmtlckhhbmRsZS53b3JrQ291bnQgPSBvcHRpb25zLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICAgICAgICB3b3JrZXJIYW5kbGUucGFnZSA9IG51bGw7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChcclxuICAgICAgICByZXN1bHQubmFtZSA9PT0gJ1RpbWVvdXRFcnJvcicgfHxcclxuICAgICAgICByZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCdcclxuICAgICAgKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICAgIH1SYXN0ZXJpemF0aW9uIHRpbWVvdXQ6IHlvdXIgY2hhcnQgbWF5IGJlIHRvbyBjb21wbGV4IG9yIGxhcmdlLCBhbmQgZmFpbGVkIHRvIHJlbmRlciB3aXRoaW4gdGhlIGFsbG90dGVkIHRpbWUuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfUVycm9yIGVuY291bnRlcmVkIGR1cmluZyBleHBvcnQ6ICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApLnNldEVycm9yKHJlc3VsdCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayB0aGUgUHVwcGV0ZWVyIGV4cG9ydCB0aW1lXHJcbiAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA1LFxyXG4gICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgIGBFeHBvcnRpbmcgYSBjaGFydCBzdWNlc3NmdWxseSB0b29rICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXHJcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXHJcbiAgICBjb25zdCB3b3JrRW5kID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuICAgIGNvbnN0IGV4cG9ydFRpbWUgPSB3b3JrRW5kIC0gd29ya1N0YXJ0O1xyXG5cclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnQgKz0gZXhwb3J0VGltZTtcclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnRBdmVyYWdlID1cclxuICAgICAgcG9vbFN0YXRzLnRpbWVTcGVudCAvICsrcG9vbFN0YXRzLmV4cG9ydHNQZXJmb3JtZWQ7XHJcblxyXG4gICAgbG9nKDQsIGBbcG9vbF0gV29yayBjb21wbGV0ZWQgaW4gJHtleHBvcnRUaW1lfW1zLmApO1xyXG5cclxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gdGhlIHJlc3VsdFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzdWx0LFxyXG4gICAgICBvcHRpb25zXHJcbiAgICB9O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzRHJvcHBlZDtcclxuXHJcbiAgICBpZiAod29ya2VySGFuZGxlKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG4gICAgfVxyXG5cclxuICAgIHRocm93IGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZSBpZiBpbml0aWFsaXplZCwgb3IgbnVsbFxyXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sKCkge1xyXG4gIHJldHVybiBwb29sO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgc3RhdGlzdGljIG9mIGEgcG9vbCBpbnN0YWNlIGFib3V0IGV4cG9ydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sU3RhdHNcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGN1cnJlbnQgcG9vbCBzdGF0aXN0aWNzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xTdGF0cygpIHtcclxuICByZXR1cm4gcG9vbFN0YXRzO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXHJcbiAqIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZSByZXF1ZXN0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBQb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTigpIHtcclxuICByZXR1cm4ge1xyXG4gICAgbWluOiBwb29sLm1pbixcclxuICAgIG1heDogcG9vbC5tYXgsXHJcbiAgICB1c2VkOiBwb29sLm51bVVzZWQoKSxcclxuICAgIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXHJcbiAgICBhbGxDcmVhdGVkOiBwb29sLm51bVVzZWQoKSArIHBvb2wubnVtRnJlZSgpLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXM6IHBvb2wubnVtUGVuZGluZ0NyZWF0ZXMoKSxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9uczogcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSxcclxuICAgIHBlbmRpbmdEZXN0cm95czogcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoLFxyXG4gICAgYWJzb2x1dGVBbGw6XHJcbiAgICAgIHBvb2wubnVtVXNlZCgpICtcclxuICAgICAgcG9vbC5udW1GcmVlKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSArXHJcbiAgICAgIHBvb2wucGVuZGluZ0Rlc3Ryb3lzLmxlbmd0aFxyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cclxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXHJcbiAqIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mbygpIHtcclxuICBjb25zdCB7XHJcbiAgICBtaW4sXHJcbiAgICBtYXgsXHJcbiAgICB1c2VkLFxyXG4gICAgYXZhaWxhYmxlLFxyXG4gICAgYWxsQ3JlYXRlZCxcclxuICAgIHBlbmRpbmdBY3F1aXJlcyxcclxuICAgIHBlbmRpbmdDcmVhdGVzLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzLFxyXG4gICAgYWJzb2x1dGVBbGxcclxuICB9ID0gZ2V0UG9vbEluZm9KU09OKCk7XHJcblxyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21pbn0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWF4fS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHVzZWQgcmVzb3VyY2VzOiAke3VzZWR9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgZnJlZSByZXNvdXJjZXM6ICR7YXZhaWxhYmxlfS5gKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCBjcmVhdGVkICh1c2VkIGFuZCBmcmVlKSByZXNvdXJjZXM6ICR7YWxsQ3JlYXRlZH0uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBhY3F1aXJlZDogJHtwZW5kaW5nQWNxdWlyZXN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgY3JlYXRlZDogJHtwZW5kaW5nQ3JlYXRlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSB2YWxpZGF0ZWQ6ICR7cGVuZGluZ1ZhbGlkYXRpb25zfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGRlc3Ryb3llZDogJHtwZW5kaW5nRGVzdHJveXN9LmBcclxuICApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIHJlc291cmNlczogJHthYnNvbHV0ZUFsbH0uYCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGYWN0b3J5IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbiBvYmplY3Qgd2l0aCBgY3JlYXRlYCwgYHZhbGlkYXRlYCxcclxuICogYW5kIGBkZXN0cm95YCBmdW5jdGlvbnMgZm9yIHRoZSBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZhY3RvcnlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgXHJcbiAqIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmFjdG9yeShwb29sT3B0aW9ucykge1xyXG4gIHJldHVybiB7XHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiBjcmVhdGVcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3RcclxuICAgICAqIGNvbnRhaW5pbmcgdGhlIHdvcmtlciBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIGluaXRpYWxcclxuICAgICAqIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGFuIGVycm9yIGR1cmluZ1xyXG4gICAgICogdGhlIGNyZWF0aW9uIG9mIHRoZSBuZXcgcGFnZS5cclxuICAgICAqL1xyXG4gICAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgIC8vIEluaXQgdGhlIHJlc291cmNlIHdpdGggdW5pcXVlIGlkIGFuZCB3b3JrIGNvdW50XHJcbiAgICAgIGNvbnN0IHBvb2xSZXNvdXJjZSA9IHtcclxuICAgICAgICBpZDogdXVpZCgpLFxyXG4gICAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sT3B0aW9ucy53b3JrTGltaXQgLyAyKSlcclxuICAgICAgfTtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gU3RhcnQgbWVhc3VyaW5nIGEgcGFnZSBjcmVhdGlvbiB0aW1lXHJcbiAgICAgICAgY29uc3Qgc3RhcnREYXRlID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICAgICAgLy8gQ3JlYXRlIGEgbmV3IHBhZ2VcclxuICAgICAgICBhd2FpdCBuZXdQYWdlKHBvb2xSZXNvdXJjZSk7XHJcblxyXG4gICAgICAgIC8vIE1lYXN1cmUgdGhlIHRpbWUgb2YgZnVsbCBjcmVhdGlvbiBhbmQgY29uZmlndXJhdGlvbiBvZiBhIHBhZ2VcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIsIHRvb2sgJHtcclxuICAgICAgICAgICAgZ2V0TmV3RGF0ZVRpbWUoKSAtIHN0YXJ0RGF0ZVxyXG4gICAgICAgICAgfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBSZXR1cm4gcmVhZHkgcG9vbCByZXNvdXJjZVxyXG4gICAgICAgIHJldHVybiBwb29sUmVzb3VyY2U7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIEVycm9yIGVuY291bnRlcmVkIHdoZW4gY3JlYXRpbmcgYSBuZXcgcGFnZS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIFZhbGlkYXRlcyBhIHdvcmtlciBwYWdlIGluIHRoZSBleHBvcnQgcG9vbCwgY2hlY2tpbmcgaWYgaXQgaGFzIGV4Y2VlZGVkXHJcbiAgICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiB2YWxpZGF0ZVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgaWYgdGhlIHdvcmtlclxyXG4gICAgICogaXMgdmFsaWQgYW5kIHdpdGhpbiB0aGUgd29yayBsaW1pdDsgb3RoZXJ3aXNlLCB0byBmYWxzZS5cclxuICAgICAqL1xyXG4gICAgdmFsaWRhdGU6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSW4gY2VydGFpbiBjYXNlcyBhY3F1aXJpbmcgdGhyb3dzIGEgVGFyZ2V0Q2xvc2VFcnJvciwgd2hpY2ggbWF5XHJcbiAgICAgIC8vIGJlIGNhdXNlZCBieSB0d28gdGhpbmdzOlxyXG4gICAgICAvLyAtIFRoZSBwYWdlIGlzIGNsb3NlZCBhbmQgYXR0ZW1wdGVkIHRvIGJlIHJldXNlZC5cclxuICAgICAgLy8gLSBMb3N0IGNvbnRhY3Qgd2l0aCB0aGUgYnJvd3Nlci5cclxuICAgICAgLy9cclxuICAgICAgLy8gV2hhdCB3ZSdyZSBzZWVpbmcgaW4gbG9ncyBpcyB0aGF0IHN1Y2Nlc3NpdmUgZXhwb3J0cyB0eXBpY2FsbHlcclxuICAgICAgLy8gc3VjY2VlZHMsIGFuZCB0aGUgc2VydmVyIHJlY292ZXJzLCBpbmRpY2F0aW5nIHRoYXQgaXQncyBsaWtlbHlcclxuICAgICAgLy8gdGhlIGZpcnN0IGNhc2UuIFRoaXMgaXMgYW4gYXR0ZW1wdCBhdCBhbGxpZXZhdGluZyB0aGUgaXNzdWUgYnlcclxuICAgICAgLy8gc2ltcGx5IG5vdCB2YWxpZGF0aW5nIHRoZSB3b3JrZXIgaWYgdGhlIHBhZ2UgaXMgbnVsbCBvciBjbG9zZWQuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIFRoZSBhY3R1YWwgcmVzdWx0IGZyb20gd2hlbiB0aGlzIGhhcHBlbmVkLCB3YXMgdGhhdCBhIHdvcmtlciB3b3VsZFxyXG4gICAgICAvLyBiZSBjb21wbGV0ZWx5IGxvY2tlZCwgc3RvcHBpbmcgaXQgZnJvbSBiZWluZyBhY3F1aXJlZCB1bnRpbFxyXG4gICAgICAvLyBpdHMgd29yayBjb3VudCByZWFjaGVkIHRoZSBsaW1pdC5cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgdmFsaWRcclxuICAgICAgaWYgKCFwb29sUmVzb3VyY2UucGFnZSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAobm8gdmFsaWQgcGFnZSBpcyBmb3VuZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHBhZ2VgIGlzIGNsb3NlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAocGFnZSBpcyBjbG9zZWQgb3IgaW52YWxpZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYG1haW5GcmFtZWAgaXMgZGV0YWNoZWRcclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlLm1haW5GcmFtZSgpLmRldGFjaGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlJ3MgZnJhbWUgaXMgZGV0YWNoZWQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGB3b3JrTGltaXRgIGlzIGV4Y2VlZGVkXHJcbiAgICAgIGlmIChcclxuICAgICAgICBwb29sT3B0aW9ucy53b3JrTGltaXQgJiZcclxuICAgICAgICArK3Bvb2xSZXNvdXJjZS53b3JrQ291bnQgPiBwb29sT3B0aW9ucy53b3JrTGltaXRcclxuICAgICAgKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChleGNlZWRlZCB0aGUgJHtwb29sT3B0aW9ucy53b3JrTGltaXR9IHdvcmtzIHBlciByZXNvdXJjZSBsaW1pdCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBUaGUgYHBvb2xSZXNvdXJjZWAgaXMgdmFsaWRhdGVkXHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIERlc3Ryb3lzIGEgd29ya2VyIGVudHJ5IGluIHRoZSBleHBvcnQgcG9vbCwgY2xvc2luZyBpdHMgYXNzb2NpYXRlZCBwYWdlLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGRlc3Ryb3lcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICAgKiB0aGUgd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAgICovXHJcbiAgICBkZXN0cm95OiBhc3luYyAocG9vbFJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIERlc3Ryb3lpbmcgYSB3b3JrZXIuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFJlbW92ZSBhbGwgYXR0YWNoZWQgZXZlbnQgbGlzdGVuZXJzIGZyb20gdGhlIHJlc291cmNlXHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3BhZ2VlcnJvcicpO1xyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdjb25zb2xlJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2ZyYW1lZGV0YWNoZWQnKTtcclxuXHJcbiAgICAgICAgICAvLyBXZSBuZWVkIHRvIHdhaXQgYXJvdW5kIGZvciB0aGlzXHJcbiAgICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5jbG9zZSgpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFBhZ2UgY291bGQgbm90IGJlIGNsb3NlZCB1cG9uIGRlc3Ryb3lpbmcuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH07XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBpbml0UG9vbCxcclxuICBraWxsUG9vbCxcclxuICBwb3N0V29yayxcclxuICBnZXRQb29sLFxyXG4gIGdldFBvb2xTdGF0cyxcclxuICBnZXRQb29sSW5mbyxcclxuICBnZXRQb29sSW5mb0pTT05cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFVzZWQgdG8gc2FuaXRpemUgdGhlIHN0cmluZ3MgY29taW5nIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVcclxuICogdG8gcHJldmVudCBYU1MgYXR0YWNrcyAod2l0aCB0aGUgRE9NUHVyaWZ5IGxpYnJhcnkpLlxyXG4gKi9cclxuXHJcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcclxuaW1wb3J0IHsgSlNET00gfSBmcm9tICdqc2RvbSc7XHJcblxyXG4vKipcclxuICogU2FuaXRpemVzIGEgZ2l2ZW4gSFRNTCBzdHJpbmcgYnkgcmVtb3ZpbmcgPHNjcmlwdD4gdGFncy4gVGhpcyBmdW5jdGlvbiB1c2VzXHJcbiAqIGEgcmVndWxhciBleHByZXNzaW9uIHRvIGZpbmQgYW5kIHJlbW92ZSBhbGwgb2NjdXJyZW5jZXMgb2YgPHNjcmlwdD48L3NjcmlwdD5cclxuICogdGFncyBhbmQgYW55IGNvbnRlbnQgd2l0aGluIHRoZW0uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzYW5pdGl6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgSFRNTCBzdHJpbmcgdG8gYmUgc2FuaXRpemVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2FuaXRpemVkIEhUTUwgc3RyaW5nLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplKGlucHV0KSB7XHJcbiAgLy8gR2V0IHRoZSB2aXJ0dWFsIERPTVxyXG4gIGNvbnN0IHdpbmRvdyA9IG5ldyBKU0RPTSgnJykud2luZG93O1xyXG5cclxuICAvLyBDcmVhdGUgYSBwdXJpZnlpbmcgaW5zdGFuY2VcclxuICBjb25zdCBwdXJpZnkgPSBET01QdXJpZnkod2luZG93KTtcclxuXHJcbiAgLy8gUmV0dXJuIHNhbml0aXplZCBpbnB1dFxyXG4gIHJldHVybiBwdXJpZnkuc2FuaXRpemUoaW5wdXQsIHsgQUREX1RBR1M6IFsnZm9yZWlnbk9iamVjdCddIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2FuaXRpemVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyB0byBwcmVwYXJlIGZvciB0aGUgZXhwb3J0aW5nIGNoYXJ0c1xyXG4gKiBpbnRvIHZhcmlvdXMgaW1hZ2Ugb3V0cHV0IGZvcm1hdHMgc3VjaCBhcyBKUEVHLCBQTkcsIFBERiwgYW5kIFNWR3MuXHJcbiAqIEl0IHN1cHBvcnRzIHNpbmdsZSBhbmQgYmF0Y2ggZXhwb3J0IG9wZXJhdGlvbnMgYW5kIGFsbG93cyBjdXN0b21pemF0aW9uXHJcbiAqIHRocm91Z2ggb3B0aW9ucyBwYXNzZWQgZnJvbSBjb25maWd1cmF0aW9ucyBvciBBUElzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGlzQWxsb3dlZENvbmZpZywgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldFBvb2xTdGF0cywga2lsbFBvb2wsIHBvc3RXb3JrIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2FuaXRpemUgfSBmcm9tICcuL3Nhbml0aXplLmpzJztcclxuaW1wb3J0IHtcclxuICBmaXhDb25zdHIsXHJcbiAgZml4T3V0ZmlsZSxcclxuICBmaXhUeXBlLFxyXG4gIGdldEFic29sdXRlUGF0aCxcclxuICBnZXRCYXNlNjQsXHJcbiAgaXNPYmplY3QsXHJcbiAgcm91bmROdW1iZXIsXHJcbiAgd3JhcEFyb3VuZFxyXG59IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBnbG9iYWwgZmxhZyBmb3IgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb25cclxubGV0IGFsbG93Q29kZUV4ZWN1dGlvbiA9IGZhbHNlO1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIHNpbmdsZSBleHBvcnQgcHJvY2VzcyBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG9wdGlvbnMgYW5kIHNhdmVzXHJcbiAqIHRoZSByZXN1bHRpbmcgaW1hZ2UgdG8gdGhlIHByb3ZpZGVkIG91dHB1dCBmaWxlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHNpbmdsZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBUaGUgb2JqZWN0IG11c3QgY29udGFpbiBhdCBsZWFzdCBvbmVcclxuICogb2YgdGhlIGZvbGxvd2luZyBgZXhwb3J0YCBwcm9wZXJ0aWVzOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYFxyXG4gKiB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgc2luZ2xlIGV4cG9ydFxyXG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIHNpbmdsZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaW5nbGVFeHBvcnQob3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBleHBvcnQgbWFrZXMgc2Vuc2VcclxuICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLmV4cG9ydCkge1xyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnRcclxuICAgIGF3YWl0IHN0YXJ0RXhwb3J0KFxyXG4gICAgICB7IGV4cG9ydDogb3B0aW9ucy5leHBvcnQsIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljIH0sXHJcbiAgICAgIGFzeW5jIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICBvdXRmaWxlIHx8IGBjaGFydC4ke3R5cGV9YCxcclxuICAgICAgICAgICAgICB0eXBlICE9PSAnc3ZnJyA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JykgOiBkYXRhLnJlc3VsdFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAgICdbY2hhcnRdIEVycm9yIHdoaWxlIHNhdmluZyBhIGNoYXJ0LicsXHJcbiAgICAgICAgICAgIDUwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBLaWxsIHBvb2wgYW5kIGNsb3NlIGJyb3dzZXIgYWZ0ZXIgZmluaXNoaW5nIHNpbmdsZSBleHBvcnRcclxuICAgICAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG4gICAgICB9XHJcbiAgICApO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIE5vIGV4cGVjdGVkIGBleHBvcnRgIG9wdGlvbnMgd2VyZSBmb3VuZC4gUGxlYXNlIHByb3ZpZGUgb25lIG9mIHRoZSBmb2xsb3dpbmcgb3B0aW9uczogYGluZmlsZWAsIGBpbnN0cmAsIGBvcHRpb25zYCwgb3IgYHN2Z2AgdG8gZ2VuZXJhdGUgYSB2YWxpZCBpbWFnZS4nLFxyXG4gICAgICA0MDBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgYmF0Y2ggZXhwb3J0IHByb2Nlc3MgZm9yIG11bHRpcGxlIGNoYXJ0cyBiYXNlZCBvbiBpbmZvcm1hdGlvblxyXG4gKiBwcm92aWRlZCBpbiB0aGUgYGJhdGNoYCBvcHRpb24uIFRoZSBgYmF0Y2hgIGlzIGEgc3RyaW5nIGluIHRoZSBmb2xsb3dpbmdcclxuICogZm9ybWF0OiBcImluZmlsZTEuanNvbj1vdXRmaWxlMS5wbmc7aW5maWxlMi5qc29uPW91dGZpbGUyLnBuZzsuLi5cIi4gUmVzdWx0c1xyXG4gKiBhcmUgc2F2ZWQgdG8gdGhlIHNwZWNpZmllZCBvdXRwdXQgZmlsZXMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYmF0Y2hFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgYG9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3NcclxuICogZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhbiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGVcclxuICogc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSXQgbXVzdCBjb250YWluIHRoZSBgYmF0Y2hgIG9wdGlvbiBmcm9tXHJcbiAqIHRoZSBgZXhwb3J0YCBzZWN0aW9uIHRvIGdlbmVyYXRlIHZhbGlkIGltYWdlcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGJhdGNoIGV4cG9ydFxyXG4gKiBwcm9jZXNzZXMgYXJlIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogYW55IG9mIHRoZSBiYXRjaCBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBiYXRjaEV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0ICYmIG9wdGlvbnMuZXhwb3J0LmJhdGNoKSB7XHJcbiAgICAvLyBBbiBhcnJheSBmb3IgY29sbGVjdGluZyBiYXRjaCBleHBvcnRzXHJcbiAgICBjb25zdCBiYXRjaEZ1bmN0aW9ucyA9IFtdO1xyXG5cclxuICAgIC8vIFNwbGl0IGFuZCBwYWlyIHRoZSBgYmF0Y2hgIGFyZ3VtZW50c1xyXG4gICAgZm9yIChsZXQgcGFpciBvZiBvcHRpb25zLmV4cG9ydC5iYXRjaC5zcGxpdCgnOycpIHx8IFtdKSB7XHJcbiAgICAgIHBhaXIgPSBwYWlyLnNwbGl0KCc9Jyk7XHJcbiAgICAgIGlmIChwYWlyLmxlbmd0aCA9PT0gMikge1xyXG4gICAgICAgIGJhdGNoRnVuY3Rpb25zLnB1c2goXHJcbiAgICAgICAgICBzdGFydEV4cG9ydChcclxuICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgICAgICAgICAgLi4ub3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgICAgICAgICBpbmZpbGU6IHBhaXJbMF0sXHJcbiAgICAgICAgICAgICAgICBvdXRmaWxlOiBwYWlyWzFdXHJcbiAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICBjdXN0b21Mb2dpYzogb3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAoZXJyb3IsIGRhdGEpID0+IHtcclxuICAgICAgICAgICAgICAvLyBFeGl0IHByb2Nlc3Mgd2hlbiBlcnJvciBleGlzdHNcclxuICAgICAgICAgICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgLy8gR2V0IHRoZSBgYjY0YCwgYG91dGZpbGVgLCBhbmQgYHR5cGVgIGZvciBhIGNoYXJ0XHJcbiAgICAgICAgICAgICAgY29uc3QgeyBiNjQsIG91dGZpbGUsIHR5cGUgfSA9IGRhdGEub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgICAgICAgICAgIC8vIFNhdmUgdGhlIHJlc3VsdFxyXG4gICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBpZiAoYjY0KSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgQmFzZTY0IHN0cmluZyB0byBhIHR4dCBmaWxlXHJcbiAgICAgICAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgICAgICAgYCR7b3V0ZmlsZS5zcGxpdCgnLicpLnNoaWZ0KCkgfHwgJ2NoYXJ0J30udHh0YCxcclxuICAgICAgICAgICAgICAgICAgICBnZXRCYXNlNjQoZGF0YS5yZXN1bHQsIHR5cGUpXHJcbiAgICAgICAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAvLyBBcyBhIGNvcnJlY3QgaW1hZ2UgZm9ybWF0XHJcbiAgICAgICAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgICAgICAgb3V0ZmlsZSxcclxuICAgICAgICAgICAgICAgICAgICB0eXBlICE9PSAnc3ZnJ1xyXG4gICAgICAgICAgICAgICAgICAgICAgPyBCdWZmZXIuZnJvbShkYXRhLnJlc3VsdCwgJ2Jhc2U2NCcpXHJcbiAgICAgICAgICAgICAgICAgICAgICA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIClcclxuICAgICAgICApO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGxvZygyLCAnW2NoYXJ0XSBObyBjb3JyZWN0IHBhaXIgZm91bmQgZm9yIHRoZSBiYXRjaCBleHBvcnQuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBBd2FpdCBhbGwgZXhwb3J0cyBhcmUgZG9uZVxyXG4gICAgY29uc3QgYmF0Y2hSZXN1bHRzID0gYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKGJhdGNoRnVuY3Rpb25zKTtcclxuXHJcbiAgICAvLyBLaWxsIHBvb2wgYW5kIGNsb3NlIGJyb3dzZXIgYWZ0ZXIgZmluaXNoaW5nIGJhdGNoIGV4cG9ydFxyXG4gICAgYXdhaXQga2lsbFBvb2woKTtcclxuXHJcbiAgICAvLyBMb2cgZXJyb3JzIGlmIGZvdW5kXHJcbiAgICBiYXRjaFJlc3VsdHMuZm9yRWFjaCgocmVzdWx0LCBpbmRleCkgPT4ge1xyXG4gICAgICAvLyBMb2cgdGhlIGVycm9yIHdpdGggc3RhY2sgYWJvdXQgdGhlIHNwZWNpZmljIGJhdGNoIGV4cG9ydFxyXG4gICAgICBpZiAocmVzdWx0LnJlYXNvbikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICByZXN1bHQucmVhc29uLFxyXG4gICAgICAgICAgYFtjaGFydF0gQmF0Y2ggZXhwb3J0IG51bWJlciAke2luZGV4ICsgMX0gY291bGQgbm90IGJlIGNvcnJlY3RseSBjb21wbGV0ZWQuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIE5vIGV4cGVjdGVkIGBleHBvcnRgIG9wdGlvbnMgd2VyZSBmb3VuZC4gUGxlYXNlIHByb3ZpZGUgdGhlIGBiYXRjaGAgb3B0aW9uIHRvIGdlbmVyYXRlIHZhbGlkIGltYWdlcy4nLFxyXG4gICAgICA0MDBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIGV4cG9ydCBwcm9jZXNzLiBUaGUgYGltYWdlT3B0aW9uc2AgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0XHJcbiAqIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW5cclxuICogYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIElmIHBhcnRpYWxcclxuICogb3B0aW9ucyBhcmUgcHJvdmlkZWQsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBgZW5kQ2FsbGJhY2tgIGZ1bmN0aW9uIGlzIGludm9rZWQgdXBvbiB0aGUgY29tcGxldGlvbiBvZiB0aGUgZXhwb3J0LFxyXG4gKiBlaXRoZXIgc3VjY2Vzc2Z1bGx5IG9yIHdpdGggYW4gZXJyb3IuIFRoZSBgZXJyb3JgIG9iamVjdCBpcyBwcm92aWRlZFxyXG4gKiBhcyB0aGUgZmlyc3QgYXJndW1lbnQsIGFuZCB0aGUgYGRhdGFgIG9iamVjdCBpcyB0aGUgc2Vjb25kLCBjb250YWluaW5nXHJcbiAqIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YCBwcm9wZXJ0eVxyXG4gKiBhbmQgdGhlIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGluIHRoZSBgb3B0aW9uc2AgcHJvcGVydHkuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGltYWdlT3B0aW9ucyAtIFRoZSBgaW1hZ2VPcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZFxyXG4gKiBpbmNsdWRlIHNldHRpbmdzIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW5cclxuICogYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIElmIHRoZSBwcm92aWRlZFxyXG4gKiBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlcyB3aWxsIGJlIG1lcmdlZCB3aXRoIHRoZSBjdXJyZW50IGdsb2JhbFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkIHVwb25cclxuICogZmluYWxpemluZyB0aGUgZXhwb3J0IHByb2Nlc3Mgb3IgdXBvbiBlbmNvdW50ZXJpbmcgYW4gZXJyb3IuIFRoZSBmaXJzdFxyXG4gKiBhcmd1bWVudCBpcyB0aGUgYGVycm9yYCBvYmplY3QsIGFuZCB0aGUgc2Vjb25kIGFyZ3VtZW50IGlzIHRoZSBgZGF0YWAgb2JqZWN0LFxyXG4gKiB3aGljaCBpbmNsdWRlcyB0aGUgQmFzZTY0IHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCBpbiB0aGUgYHJlc3VsdGBcclxuICogcHJvcGVydHkgYW5kIHRoZSBmdWxsIHNldCBvZiBvcHRpb25zIGluIHRoZSBgb3B0aW9uc2AgcHJvcGVydHkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBUaGlzIGZ1bmN0aW9uIGRvZXMgbm90IHJldHVybiBhIHZhbHVlIGRpcmVjdGx5LlxyXG4gKiBJbnN0ZWFkLCBpdCBjb21tdW5pY2F0ZXMgcmVzdWx0cyB2aWEgdGhlIGBlbmRDYWxsYmFja2AuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhIHByb2JsZW0gd2l0aFxyXG4gKiBwcm9jZXNzaW5nIGlucHV0IG9mIGFueSB0eXBlLiBUaGUgZXJyb3IgaXMgcGFzc2VkIGludG8gdGhlIGBlbmRDYWxsYmFja2BcclxuICogZnVuY3Rpb24gYW5kIHByb2Nlc3NlZCB0aGVyZS5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydEV4cG9ydChpbWFnZU9wdGlvbnMsIGVuZENhbGxiYWNrKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHByb3ZpZGVkIG9wdGlvbnMgYXJlIGluIGFuIG9iamVjdFxyXG4gICAgaWYgKCFpc09iamVjdChpbWFnZU9wdGlvbnMpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NoYXJ0XSBJbmNvcnJlY3QgdmFsdWUgb2YgdGhlIHByb3ZpZGVkIGBpbWFnZU9wdGlvbnNgLiBOZWVkcyB0byBiZSBhbiBvYmplY3QuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBNZXJnZSBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gdGhlIGNvcHkgb2YgdGhlIGluc3RhbmNlIG9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKFxyXG4gICAgICB7XHJcbiAgICAgICAgZXhwb3J0OiBpbWFnZU9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljOiBpbWFnZU9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICAgfSxcclxuICAgICAgdHJ1ZVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGBleHBvcnRgIG9wdGlvbnNcclxuICAgIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAvLyBTdGFydGluZyBleHBvcnRpbmcgcHJvY2VzcyBtZXNzYWdlXHJcbiAgICBsb2coNCwgJ1tjaGFydF0gU3RhcnRpbmcgdGhlIGV4cG9ydGluZyBwcm9jZXNzLicpO1xyXG5cclxuICAgIC8vIEV4cG9ydCB1c2luZyBvcHRpb25zIGZyb20gdGhlIGZpbGUgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluZmlsZSAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBhIGZpbGUgaW5wdXQuJyk7XHJcblxyXG4gICAgICBsZXQgZmlsZUNvbnRlbnQ7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gVHJ5IHRvIHJlYWQgdGhlIGZpbGUgdG8gZ2V0IHRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb25cclxuICAgICAgICBmaWxlQ29udGVudCA9IHJlYWRGaWxlU3luYyhcclxuICAgICAgICAgIGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zLmluZmlsZSksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEVycm9yIGxvYWRpbmcgY29udGVudCBmcm9tIGEgZmlsZSBpbnB1dC4nLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBmaWxlJ3MgZXh0ZW5zaW9uXHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLmluZmlsZS5lbmRzV2l0aCgnLnN2ZycpKSB7XHJcbiAgICAgICAgLy8gU2V0IHRvIHRoZSBgc3ZnYCBvcHRpb25cclxuICAgICAgICBleHBvcnRPcHRpb25zLnN2ZyA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2UgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuanNvbicpKSB7XHJcbiAgICAgICAgLy8gU2V0IHRvIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgPSBmaWxlQ29udGVudDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW2NoYXJ0XSBJbmNvcnJlY3QgdmFsdWUgb2YgdGhlIGBpbmZpbGVgIG9wdGlvbi4nLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEV4cG9ydCB1c2luZyBTVkcgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLnN2ZyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBhbiBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBhdHRlbXB0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21TdmdBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIGFuIFNWRyBzdHJpbmdcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21TdmcoXHJcbiAgICAgICAgc2FuaXRpemUoZXhwb3J0T3B0aW9ucy5zdmcpLCAvLyAjMjA5XHJcbiAgICAgICAgb3B0aW9uc1xyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gU1ZHIGV4cG9ydHMgY291bnRlclxyXG4gICAgICArK2dldFBvb2xTdGF0cygpLmV4cG9ydHNGcm9tU3ZnO1xyXG5cclxuICAgICAgLy8gUGFzcyBTVkcgZXhwb3J0IHJlc3VsdCB0byB0aGUgZW5kIGNhbGxiYWNrXHJcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhudWxsLCByZXN1bHQpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEV4cG9ydCB1c2luZyBvcHRpb25zIGFzIGFuIGlucHV0XHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy5pbnN0ciAhPT0gbnVsbCB8fCBleHBvcnRPcHRpb25zLm9wdGlvbnMgIT09IG51bGwpIHtcclxuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gb3B0aW9ucyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBhdHRlbXB0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHM7XHJcblxyXG4gICAgICAvLyBFeHBvcnQgZnJvbSBvcHRpb25zXHJcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IF9leHBvcnRGcm9tT3B0aW9ucyhcclxuICAgICAgICBleHBvcnRPcHRpb25zLmluc3RyIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyxcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGV4cG9ydHMgY291bnRlclxyXG4gICAgICArK2dldFBvb2xTdGF0cygpLmV4cG9ydHNGcm9tT3B0aW9ucztcclxuXHJcbiAgICAgIC8vIFBhc3Mgb3B0aW9ucyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTm8gaW5wdXQgc3BlY2lmaWVkLCBwYXNzIGFuIGVycm9yIG1lc3NhZ2UgdG8gdGhlIGNhbGxiYWNrXHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgIG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW2NoYXJ0XSBObyB2YWxpZCBpbnB1dCBzcGVjaWZpZWQuIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMgaXMgY29ycmVjdGx5IHNldDogJ2luZmlsZScsICdpbnN0cicsICdvcHRpb25zJywgb3IgJ3N2ZycuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKVxyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYW5kIHJldHVybnMgdGhlIGN1cnJlbnQgc3RhdHVzIG9mIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUaGUgdmFsdWUgb2YgdGhlIGdsb2JhbCBgYWxsb3dDb2RlRXhlY3V0aW9uYCBvcHRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWxsb3dDb2RlRXhlY3V0aW9uKCkge1xyXG4gIHJldHVybiBhbGxvd0NvZGVFeGVjdXRpb247XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBib29sZWFuIHZhbHVlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdmFsdWUgLSBUaGUgYm9vbGVhbiB2YWx1ZSB0byBiZSBhc3NpZ25lZCB0byB0aGUgZ2xvYmFsXHJcbiAqIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb24odmFsdWUpIHtcclxuICBhbGxvd0NvZGVFeGVjdXRpb24gPSB2YWx1ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEV4cG9ydHMgZnJvbSBhbiBTVkcgYmFzZWQgaW5wdXQgd2l0aCB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZXhwb3J0RnJvbVN2Z1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBTVkcgYmFzZWQgaW5wdXQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgY29tcGxldGUgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhIHJlc3VsdCBvZiB0aGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBub3QgYSBjb3JyZWN0IFNWR1xyXG4gKiBpbnB1dC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiBpdCBpcyBTVkdcclxuICBpZiAoXHJcbiAgICB0eXBlb2YgaW5wdXRUb0V4cG9ydCA9PT0gJ3N0cmluZycgJiZcclxuICAgIChpbnB1dFRvRXhwb3J0LmluZGV4T2YoJzxzdmcnKSA+PSAwIHx8IGlucHV0VG9FeHBvcnQuaW5kZXhPZignPD94bWwnKSA+PSAwKVxyXG4gICkge1xyXG4gICAgbG9nKDQsICdbY2hhcnRdIFBhcnNpbmcgaW5wdXQgYXMgU1ZHLicpO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZXhwb3J0IGlucHV0IGFzIFNWR1xyXG4gICAgb3B0aW9ucy5leHBvcnQuc3ZnID0gaW5wdXRUb0V4cG9ydDtcclxuXHJcbiAgICAvLyBSZXNldCB0aGUgcmVzdCBvZiB0aGUgZXhwb3J0IGlucHV0IG9wdGlvbnNcclxuICAgIG9wdGlvbnMuZXhwb3J0Lmluc3RyID0gbnVsbDtcclxuICAgIG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnMgPSBudWxsO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYW4gU1ZHIHN0cmluZyBhcyBhbiBleHBvcnQgaW5wdXRcclxuICAgIHJldHVybiBfcHJlcGFyZUV4cG9ydChvcHRpb25zKTtcclxuICB9IGVsc2Uge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2hhcnRdIE5vdCBhIGNvcnJlY3QgU1ZHIGlucHV0LicsIDQwMCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIG9wdGlvbnMgYmFzZWQgaW5wdXQgd2l0aCB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZXhwb3J0RnJvbU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0VG9FeHBvcnQgLSBUaGUgb3B0aW9ucyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3RcclxuICogY2hhcnQgb3B0aW9ucyBpbnB1dC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9ucyhpbnB1dFRvRXhwb3J0LCBvcHRpb25zKSB7XHJcbiAgbG9nKDQsICdbY2hhcnRdIFBhcnNpbmcgaW5wdXQgZnJvbSBvcHRpb25zLicpO1xyXG5cclxuICAvLyBUcnkgdG8gY2hlY2ssIHZhbGlkYXRlIGFuZCBwYXJzZSB0byBzdHJpbmdpZmllZCBvcHRpb25zXHJcbiAgY29uc3Qgc3RyaW5naWZpZWRPcHRpb25zID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgaW5wdXRUb0V4cG9ydCxcclxuICAgIHRydWUsXHJcbiAgICBvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICk7XHJcblxyXG4gIC8vIENoZWNrIGlmIGEgY29ycmVjdCBzdHJpbmdpZmllZCBvcHRpb25zXHJcbiAgaWYgKFxyXG4gICAgc3RyaW5naWZpZWRPcHRpb25zID09PSBudWxsIHx8XHJcbiAgICB0eXBlb2Ygc3RyaW5naWZpZWRPcHRpb25zICE9PSAnc3RyaW5nJyB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5zdGFydHNXaXRoKCd7JykgfHxcclxuICAgICFzdHJpbmdpZmllZE9wdGlvbnMuZW5kc1dpdGgoJ30nKVxyXG4gICkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NoYXJ0XSBJbnZhbGlkIGNvbmZpZ3VyYXRpb24gcHJvdmlkZWQgLSBPbmx5IG9wdGlvbnMgY29uZmlndXJhdGlvbnMgYW5kIFNWRyBhcmUgYWxsb3dlZCBmb3IgdGhpcyBzZXJ2ZXIuIElmIHRoaXMgaXMgeW91ciBzZXJ2ZXIsIEphdmFTY3JpcHQgY3VzdG9tIGNvZGUgY2FuIGJlIGVuYWJsZWQgYnkgc3RhcnRpbmcgdGhlIHNlcnZlciB3aXRoIHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBvcHRpb25zIHNldCB0byB0cnVlLicsXHJcbiAgICAgIDQwM1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgZXhwb3J0IGlucHV0IGFzIGEgc3RyaW5naWZpZWQgY2hhcnQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0Lmluc3RyID0gc3RyaW5naWZpZWRPcHRpb25zO1xyXG5cclxuICAvLyBSZXNldCB0aGUgcmVzdCBvZiB0aGUgZXhwb3J0IGlucHV0IG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydC5zdmcgPSBudWxsO1xyXG5cclxuICAvLyBDYWxsIHRoZSBmdW5jdGlvbiB3aXRoIGEgc3RyaW5naWZpZWQgY2hhcnQgb3B0aW9uc1xyXG4gIHJldHVybiBfcHJlcGFyZUV4cG9ydChvcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZ1bmN0aW9uIGZvciBmaW5hbGl6aW5nIG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb25zIGJlZm9yZSBleHBvcnQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpIHtcclxuICBjb25zdCB7IGV4cG9ydDogZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWM6IGN1c3RvbUxvZ2ljT3B0aW9ucyB9ID0gb3B0aW9ucztcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYHR5cGVgIG9wdGlvblxyXG4gIGV4cG9ydE9wdGlvbnMudHlwZSA9IGZpeFR5cGUoZXhwb3J0T3B0aW9ucy50eXBlLCBleHBvcnRPcHRpb25zLm91dGZpbGUpO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5vdXRmaWxlID0gZml4T3V0ZmlsZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjb25zdHJgIG9wdGlvblxyXG4gIGV4cG9ydE9wdGlvbnMuY29uc3RyID0gZml4Q29uc3RyKGV4cG9ydE9wdGlvbnMuY29uc3RyKTtcclxuXHJcbiAgLy8gTm90aWZ5IGFib3V0IHRoZSBjdXN0b20gbG9naWMgdXNhZ2Ugc3RhdHVzXHJcbiAgbG9nKFxyXG4gICAgMyxcclxuICAgIGBbY2hhcnRdIFRoZSBjdXN0b20gbG9naWMgaXMgJHtjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uID8gJ2FsbG93ZWQnIDogJ2Rpc2FsbG93ZWQnfS5gXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgKGBjdXN0b21Db2RlYCwgYGNhbGxiYWNrYCwgYHJlc291cmNlc2ApXHJcbiAgX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICAgIGV4cG9ydE9wdGlvbnMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBhbmQgYHNjYWxlYCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQgPSB7XHJcbiAgICAuLi5leHBvcnRPcHRpb25zLFxyXG4gICAgLi4uX2ZpbmRDaGFydFNpemUoZXhwb3J0T3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBQb3N0IHRoZSB3b3JrIHRvIHRoZSBwb29sXHJcbiAgcmV0dXJuIHBvc3RXb3JrKG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyB0aGUgYGhlaWdodGAsIGB3aWR0aGAgYW5kIGBzY2FsZWAgZm9yIGNoYXJ0IGV4cG9ydHMgYmFzZWRcclxuICogb24gdGhlIHByb3ZpZGVkIGV4cG9ydCBvcHRpb25zLlxyXG4gKlxyXG4gKiBUaGUgZnVuY3Rpb24gcHJpb3JpdGl6ZXMgdmFsdWVzIGluIHRoZSBmb2xsb3dpbmcgb3JkZXI6XHJcbiAqIDEuIFRoZSBgaGVpZ2h0YCwgYHdpZHRoYCwgYHNjYWxlYCBmcm9tIHRoZSBgZXhwb3J0T3B0aW9uc2AuXHJcbiAqIDIuIE9wdGlvbnMgZnJvbSB0aGUgY2hhcnQgY29uZmlndXJhdGlvbiAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDMuIE9wdGlvbnMgZnJvbSB0aGUgZ2xvYmFsIG9wdGlvbnMgKGZyb20gYGV4cG9ydGluZ2AgYW5kIGBjaGFydGApLlxyXG4gKiA0LiBPcHRpb25zIGZyb20gdGhlIHRoZW1lIG9wdGlvbnMgKGZyb20gYGV4cG9ydGluZ2AgYW5kIGBjaGFydGAgc2VjdGlvbnMpLlxyXG4gKiA1LiBGYWxsYmFjayBkZWZhdWx0IHZhbHVlcyAoYGhlaWdodCA9IDQwMGAsIGB3aWR0aCA9IDYwMGAsIGBzY2FsZSA9IDFgKS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9maW5kQ2hhcnRTaXplXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIG9iamVjdCBjb250YWluaW5nIGNhbGN1bGF0ZWQgYGhlaWdodGAsIGB3aWR0aGBcclxuICogYW5kIGBzY2FsZWAgdmFsdWVzIGZvciB0aGUgY2hhcnQgZXhwb3J0LlxyXG4gKi9cclxuZnVuY3Rpb24gX2ZpbmRDaGFydFNpemUoZXhwb3J0T3B0aW9ucykge1xyXG4gIC8vIENoZWNrIHRoZSBgb3B0aW9uc2AgYW5kIGBpbnN0cmAgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiBvcHRpb25zQ2hhcnQsIGV4cG9ydGluZzogb3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGV4cG9ydE9wdGlvbnMub3B0aW9ucyB8fCBpc0FsbG93ZWRDb25maWcoZXhwb3J0T3B0aW9ucy5pbnN0cikgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgZ2xvYmFsT3B0aW9uc2AgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiBnbG9iYWxPcHRpb25zQ2hhcnQsIGV4cG9ydGluZzogZ2xvYmFsT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnMpIHx8IGZhbHNlO1xyXG5cclxuICAvLyBDaGVjayB0aGUgYHRoZW1lT3B0aW9uc2AgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiB0aGVtZU9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiB0aGVtZU9wdGlvbnNFeHBvcnRpbmcgfSA9XHJcbiAgICBpc0FsbG93ZWRDb25maWcoZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnMpIHx8IGZhbHNlO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgc2NhbGVgIHZhbHVlOlxyXG4gIC8vIC0gSXQgY2Fubm90IGJlIGxvd2VyIHRoYW4gMC4xXHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgaGlnaGVyIHRoYW4gNS4wXHJcbiAgLy8gLSBJdCBtdXN0IGJlIHJvdW5kZWQgdG8gMiBkZWNpbWFsIHBsYWNlcyAoZS5nLiAwLjIzMjM0IC0+IDAuMjMpXHJcbiAgY29uc3Qgc2NhbGUgPSByb3VuZE51bWJlcihcclxuICAgIE1hdGgubWF4KFxyXG4gICAgICAwLjEsXHJcbiAgICAgIE1hdGgubWluKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc2NhbGUgfHxcclxuICAgICAgICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5kZWZhdWx0U2NhbGUgfHxcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgNS4wXHJcbiAgICAgIClcclxuICAgICksXHJcbiAgICAyXHJcbiAgKTtcclxuXHJcbiAgLy8gRmluZCB0aGUgYGhlaWdodGAgdmFsdWVcclxuICBjb25zdCBoZWlnaHQgPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5oZWlnaHQgfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8uaGVpZ2h0IHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRIZWlnaHQgfHxcclxuICAgIDQwMDtcclxuXHJcbiAgLy8gRmluZCB0aGUgYHdpZHRoYCB2YWx1ZVxyXG4gIGNvbnN0IHdpZHRoID1cclxuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py53aWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRXaWR0aCB8fFxyXG4gICAgNjAwO1xyXG5cclxuICAvLyBHYXRoZXIgYGhlaWdodGAsIGB3aWR0aGAgYW5kIGBzY2FsZWAgaW5mb3JtYXRpb24gaW4gb25lIG9iamVjdFxyXG4gIGNvbnN0IHNpemUgPSB7IGhlaWdodCwgd2lkdGgsIHNjYWxlIH07XHJcblxyXG4gIC8vIEdldCByaWQgb2YgcG90ZW50aWFsIGBweGAgYW5kIGAlYFxyXG4gIGZvciAobGV0IFtwYXJhbSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHNpemUpKSB7XHJcbiAgICBzaXplW3BhcmFtXSA9XHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgPyArdmFsdWUucmVwbGFjZSgvcHh8JS9naSwgJycpIDogdmFsdWU7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIHNpemUgb2JqZWN0XHJcbiAgcmV0dXJuIHNpemU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleGVjdXRpb24gb2YgY3VzdG9tIGxvZ2ljIG9wdGlvbnMsIGluY2x1ZGluZyBsb2FkaW5nIGByZXNvdXJjZXNgLFxyXG4gKiBgY3VzdG9tQ29kZWAsIGFuZCBgY2FsbGJhY2tgLiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLCBpdCBwcm9jZXNzZXNcclxuICogdGhlIGN1c3RvbSBsb2dpYyBvcHRpb25zIGFjY29yZGluZ2x5LiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBub3QgYWxsb3dlZCxcclxuICogaXQgZGlzYWJsZXMgdGhlIHVzYWdlIG9mIHJlc291cmNlcywgY3VzdG9tIGNvZGUgYW5kIGNhbGxiYWNrLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY29kZSBleGVjdXRpb25cclxuICogaXMgbm90IGFsbG93ZWQgYnV0IGN1c3RvbSBsb2dpYyBvcHRpb25zIGFyZSBzdGlsbCBwcm92aWRlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVDdXN0b21Mb2dpYyhjdXN0b21Mb2dpY09wdGlvbnMsIGFsbG93Q29kZUV4ZWN1dGlvbikge1xyXG4gIC8vIEluIGNhc2Ugb2YgYWxsb3dpbmcgY29kZSBleGVjdXRpb25cclxuICBpZiAoYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgcmVzb3VyY2VzYCBvcHRpb25cclxuICAgIGlmICh0eXBlb2YgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gQ3VzdG9tIHN0cmluZ2lmaWVkIHJlc291cmNlc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmICghY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIExvYWQgdGhlIGRlZmF1bHQgb25lXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9IF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKCdyZXNvdXJjZXMuanNvbicpLCAndXRmOCcpLFxyXG4gICAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgIHRydWVcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZygyLCAnW2NoYXJ0XSBVbmFibGUgdG8gbG9hZCB0aGUgZGVmYXVsdCBgcmVzb3VyY2VzLmpzb25gIGZpbGUuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBUcnkgdG8gbG9hZCBjdXN0b20gY29kZSBhbmQgd3JhcCBhcm91bmQgaXQgaW4gYSBzZWxmIGludm9raW5nIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY3VzdG9tQ29kZWAgY2Fubm90IGJlIGxvYWRlZC4nKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUHJvY2VzcyB0aGUgYGNhbGxiYWNrYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGNhbGxiYWNrIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IHdyYXBBcm91bmQoXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW2NoYXJ0XSBUaGUgYGNhbGxiYWNrYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjdXN0b21Db2RlYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBjdXN0b21Db2RlYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjYWxsYmFja2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjaykpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGNhbGxiYWNrYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGByZXNvdXJjZXNgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgcmVzb3VyY2VzYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIElmIHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBmbGFnIGlzIHNldCB0byBmYWxzZSwgd2Ugc2hvdWxkIHJlZnVzZVxyXG4gICAgLy8gdGhlIHVzYWdlIG9mIHRoZSBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYCwgYW5kIGBjdXN0b21Db2RlYCBvcHRpb25zLlxyXG4gICAgLy8gQWRkaXRpb25hbGx5LCB0aGUgd29ya2VyIHdpbGwgcmVmdXNlIHRvIHJ1biBhcmJpdHJhcnkgSmF2YVNjcmlwdC5cclxuICAgIGlmIChcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGVcclxuICAgICkge1xyXG4gICAgICAvLyBSZXNldCBhbGwgY3VzdG9tIGNvZGUgb3B0aW9uc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gbnVsbDtcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBudWxsO1xyXG5cclxuICAgICAgLy8gU2VuZCBhIG1lc3NhZ2Ugc2F5aW5nIHRoYXQgdGhlIGV4cG9ydGVyIGRvZXMgbm90IHN1cHBvcnQgdGhlc2Ugc2V0dGluZ3NcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJywgYW5kICdjdXN0b21Db2RlJyBvcHRpb25zIGhhdmUgYmVlbiBkaXNhYmxlZCBmb3IgdGhpcyBzZXJ2ZXIuYCxcclxuICAgICAgICA0MDNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIGFuZCB2YWxpZGF0ZXMgcmVzb3VyY2VzIGZyb20gdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3IgZXhwb3J0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8c3RyaW5nfG51bGwpfSBbcmVzb3VyY2VzPW51bGxdIC0gVGhlIHJlc291cmNlcyB0byBiZSBoYW5kbGVkLlxyXG4gKiBDYW4gYmUgZWl0aGVyIGEgSlNPTiBvYmplY3QsIHN0cmluZ2lmaWVkIEpTT04sIGEgcGF0aCB0byBhIEpTT04gZmlsZSxcclxuICogb3IgbnVsbC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYG51bGxgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8bnVsbCl9IFRoZSBoYW5kbGVkIHJlc291cmNlcyBvciBudWxsIGlmIG5vIHZhbGlkIHJlc291cmNlc1xyXG4gKiBhcmUgZm91bmQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gIHJlc291cmNlcyA9IG51bGwsXHJcbiAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4pIHtcclxuICAvLyBMaXN0IG9mIGFsbG93ZWQgc2VjdGlvbnMgaW4gdGhlIHJlc291cmNlcyBKU09OXHJcbiAgY29uc3QgYWxsb3dlZFByb3BzID0gWydqcycsICdjc3MnLCAnZmlsZXMnXTtcclxuXHJcbiAgbGV0IGhhbmRsZWRSZXNvdXJjZXMgPSByZXNvdXJjZXM7XHJcbiAgbGV0IGNvcnJlY3RSZXNvdXJjZXMgPSBmYWxzZTtcclxuXHJcbiAgLy8gVHJ5IHRvIGxvYWQgcmVzb3VyY2VzIGZyb20gYSBmaWxlXHJcbiAgaWYgKGFsbG93RmlsZVJlc291cmNlcyAmJiByZXNvdXJjZXMuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChyZXNvdXJjZXMpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCB7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0FsbG93ZWRDb25maWcocmVzb3VyY2VzLCBmYWxzZSwgYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgICAvLyBHZXQgcmlkIG9mIHRoZSBmaWxlcyBzZWN0aW9uXHJcbiAgICBpZiAoaGFuZGxlZFJlc291cmNlcyAmJiAhYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRmlsdGVyIGZyb20gdW5uZWNlc3NhcnkgcHJvcGVydGllc1xyXG4gIGZvciAoY29uc3QgcHJvcE5hbWUgaW4gaGFuZGxlZFJlc291cmNlcykge1xyXG4gICAgaWYgKCFhbGxvd2VkUHJvcHMuaW5jbHVkZXMocHJvcE5hbWUpKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzW3Byb3BOYW1lXTtcclxuICAgIH0gZWxzZSBpZiAoIWNvcnJlY3RSZXNvdXJjZXMpIHtcclxuICAgICAgY29ycmVjdFJlc291cmNlcyA9IHRydWU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBhdCBsZWFzdCBvbmUgb2YgYWxsb3dlZCBwcm9wZXJ0aWVzIGlzIHByZXNlbnRcclxuICBpZiAoIWNvcnJlY3RSZXNvdXJjZXMpIHtcclxuICAgIHJldHVybiBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gSGFuZGxlIGZpbGVzIHNlY3Rpb25cclxuICBpZiAoaGFuZGxlZFJlc291cmNlcy5maWxlcykge1xyXG4gICAgaGFuZGxlZFJlc291cmNlcy5maWxlcyA9IGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMubWFwKChpdGVtKSA9PiBpdGVtLnRyaW0oKSk7XHJcbiAgICBpZiAoIWhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgfHwgaGFuZGxlZFJlc291cmNlcy5maWxlcy5sZW5ndGggPD0gMCkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiByZXNvdXJjZXNcclxuICByZXR1cm4gaGFuZGxlZFJlc291cmNlcztcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGxvYWRpbmcgYW5kIHZhbGlkYXRpb24gb2YgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2BcclxuICogaW4gdGhlIGV4cG9ydCBvcHRpb25zLiBJZiB0aGUgb3B0aW9uIGlzIGEgc3RyaW5nIGFuZCByZWZlcmVuY2VzIGEgSlNPTiBmaWxlXHJcbiAqICh3aGVuIHRoZSBgYWxsb3dGaWxlUmVzb3VyY2VzYCBpcyB0cnVlKSwgaXQgcmVhZHMgYW5kIHBhcnNlcyB0aGUgZmlsZS5cclxuICogT3RoZXJ3aXNlLCBpdCBhdHRlbXB0cyB0byBwYXJzZSB0aGUgc3RyaW5nIG9yIG9iamVjdCBhcyBKU09OLiBJZiBhbnkgZXJyb3JzXHJcbiAqIG9jY3VyIGR1cmluZyB0aGlzIHByb2Nlc3MsIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuIElmIHRoZXJlIGlzIGFuIGVycm9yXHJcbiAqIGxvYWRpbmcgb3IgcGFyc2luZyB0aGUgYGdsb2JhbE9wdGlvbnNgIG9yIGB0aGVtZU9wdGlvbnNgLCB0aGUgZXJyb3IgaXMgbG9nZ2VkXHJcbiAqIGFuZCB0aGUgb3B0aW9uIGlzIHNldCB0byBudWxsLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUdsb2JhbEFuZFRoZW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGxvYWRpbmdcclxuICogcmVzb3VyY2VzIGZyb20gZmlsZXMgaXMgYWxsb3dlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWUoXHJcbiAgZXhwb3J0T3B0aW9ucyxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIENoZWNrIHRoZSBgZ2xvYmFsT3B0aW9uc2AgYW5kIGB0aGVtZU9wdGlvbnNgIG9wdGlvbnNcclxuICBbJ2dsb2JhbE9wdGlvbnMnLCAndGhlbWVPcHRpb25zJ10uZm9yRWFjaCgob3B0aW9uc05hbWUpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBvcHRpb24gZXhpc3RzXHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSkge1xyXG4gICAgICAgIC8vIENoZWNrIGlmIGl0IGlzIGEgc3RyaW5nIGFuZCBhIGZpbGUgbmFtZSB3aXRoIHRoZSBgLmpzb25gIGV4dGVuc2lvblxyXG4gICAgICAgIGlmIChcclxuICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyAmJlxyXG4gICAgICAgICAgdHlwZW9mIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0uZW5kc1dpdGgoJy5qc29uJylcclxuICAgICAgICApIHtcclxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSBmaWxlIGNvbnRlbnQgY2FuIGJlIGEgY29uZmlnLCBhbmQgc2F2ZSBpdCBhcyBhIHN0cmluZ1xyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0pLCAndXRmOCcpLFxyXG4gICAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSB2YWx1ZSBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0sXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGVycm9yLFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSBcXGAke29wdGlvbnNOYW1lfVxcYCBjYW5ub3QgYmUgbG9hZGVkLmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IG51bGw7XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGlzIHRoZSBgZ2xvYmFsT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnMpKSB7XHJcbiAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3B0aW9uIGZvdW5kLicpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGB0aGVtZU9wdGlvbnNgIHByZXNlbnRcclxuICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnMpKSB7XHJcbiAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgdGhlbWVPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIGdldEFsbG93Q29kZUV4ZWN1dGlvbixcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBpbnRlcnZhbHNcclxuICogYW5kIHRpbWVvdXRzIGluIGEgY2VudHJhbGl6ZWQgbWFubmVyLiBJdCBtYWludGFpbnMgYSByZWdpc3RyeSBvZiBhbGwgYWN0aXZlXHJcbiAqIHRpbWVycyBhbmQgYWxsb3dzIGZvciB0aGVpciBlZmZpY2llbnQgY2xlYW51cCB3aGVuIG5lZWRlZC4gVGhpcyBjYW4gYmUgdXNlZnVsXHJcbiAqIGluIGFwcGxpY2F0aW9ucyB3aGVyZSBwcm9wZXIgcmVzb3VyY2UgbWFuYWdlbWVudCBhbmQgY2xlYW4gc2h1dGRvd24gb2YgdGltZXJzXHJcbiAqIGFyZSBjcml0aWNhbCB0byBhdm9pZCBtZW1vcnkgbGVha3Mgb3IgdW5pbnRlbmRlZCBiZWhhdmlvci5cclxuICovXHJcblxyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG4vLyBBcnJheSB0aGF0IGNvbnRhaW5zIGlkcyBvZiBhbGwgb25nb2luZyBpbnRlcnZhbHMgYW5kIHRpbWVvdXRzXHJcbmNvbnN0IHRpbWVySWRzID0gW107XHJcblxyXG4vKipcclxuICogQWRkcyBpZCBvZiB0aGUgYHNldEludGVydmFsYCBvciBgc2V0VGltZW91dGAgYW5kIHRvIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gYWRkVGltZXJcclxuICpcclxuICogQHBhcmFtIHtOb2RlSlMuVGltZW91dH0gaWQgLSBJZCBvZiBhbiBpbnRlcnZhbCBvciBhIHRpbWVvdXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gYWRkVGltZXIoaWQpIHtcclxuICB0aW1lcklkcy5wdXNoKGlkKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbGwgb2Ygb25nb2luZyBpbnRlcnZhbHMgYW5kIHRpbWVvdXRzIGJ5IGlkcyBnYXRoZXJlZFxyXG4gKiBpbiB0aGUgYHRpbWVySWRzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIGNsZWFyQWxsVGltZXJzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnMoKSB7XHJcbiAgbG9nKDQsIGBbdGltZXJdIENsZWFyaW5nIGFsbCByZWdpc3RlcmVkIGludGVydmFscyBhbmQgdGltZW91dHMuYCk7XHJcbiAgZm9yIChjb25zdCBpZCBvZiB0aW1lcklkcykge1xyXG4gICAgY2xlYXJJbnRlcnZhbChpZCk7XHJcbiAgICBjbGVhclRpbWVvdXQoaWQpO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGFkZFRpbWVyLFxyXG4gIGNsZWFyQWxsVGltZXJzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtaWRkbGV3YXJlIGZ1bmN0aW9ucyBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZXNcclxuICogYW5kIGhhbmRsaW5nIGVycm9yIHJlc3BvbnNlcyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2dXaXRoU3RhY2sgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIGxvZ2dpbmcgZXJyb3JzIHdpdGggc3RhY2sgdHJhY2UgYW5kIGhhbmRsaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9IFRoZSBjYWxsIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24gd2l0aFxyXG4gKiB0aGUgcGFzc2VkIGVycm9yLlxyXG4gKi9cclxuZnVuY3Rpb24gbG9nRXJyb3JNaWRkbGV3YXJlKGVycm9yLCByZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIC8vIERpc3BsYXkgdGhlIGVycm9yIHdpdGggc3RhY2sgaW4gYSBjb3JyZWN0IGZvcm1hdFxyXG4gIGxvZ1dpdGhTdGFjaygxLCBlcnJvcik7XHJcblxyXG4gIC8vIERlbGV0ZSB0aGUgc3RhY2sgZm9yIHRoZSBlbnZpcm9ubWVudCBvdGhlciB0aGFuIHRoZSBkZXZlbG9wbWVudFxyXG4gIGlmIChnZXRPcHRpb25zKCkub3RoZXIubm9kZUVudiAhPT0gJ2RldmVsb3BtZW50Jykge1xyXG4gICAgZGVsZXRlIGVycm9yLnN0YWNrO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsbCB0aGUgYHJldHVybkVycm9yTWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gIHJldHVybiBuZXh0KGVycm9yKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHJldHVybmluZyBlcnJvciByZXNwb25zZS5cclxuICpcclxuICogQGZ1bmN0aW9uIHJldHVybkVycm9yTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKi9cclxuZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlKGVycm9yLCByZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIC8vIEdhdGhlciBhbGwgcmVxdWllZCBpbmZvcm1hdGlvbiBmb3IgdGhlIHJlc3BvbnNlXHJcbiAgY29uc3QgeyBtZXNzYWdlLCBzdGFjayB9ID0gZXJyb3I7XHJcblxyXG4gIC8vIFVzZSB0aGUgZXJyb3IncyBzdGF0dXMgY29kZSBvciB0aGUgZGVmYXVsdCA0MDBcclxuICBjb25zdCBzdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZSB8fCA0MDA7XHJcblxyXG4gIC8vIFNldCBhbmQgcmV0dXJuIHJlc3BvbnNlXHJcbiAgcmVzcG9uc2Uuc3RhdHVzKHN0YXR1c0NvZGUpLmpzb24oeyBzdGF0dXNDb2RlLCBtZXNzYWdlLCBzdGFjayB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGVycm9yIG1pZGRsZXdhcmVzIHRvIHRoZSBwYXNzZWQgZXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gZXJyb3JNaWRkbGV3YXJlKGFwcCkge1xyXG4gIC8vIEFkZCBsb2cgZXJyb3IgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UobG9nRXJyb3JNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHNldCBzdGF0dXMgYW5kIHJldHVybiBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShyZXR1cm5FcnJvck1pZGRsZXdhcmUpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtaWRkbGV3YXJlIGZ1bmN0aW9ucyBmb3IgY29uZmlndXJpbmcgYW5kIGVuYWJsaW5nIHJhdGVcclxuICogbGltaXRpbmcgaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgcmF0ZUxpbWl0IGZyb20gJ2V4cHJlc3MtcmF0ZS1saW1pdCc7XHJcblxyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgZW5hYmxpbmcgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc3BlY2lmaWVkIEV4cHJlc3MgYXBwLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHJhdGVMaW1pdGluZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0XHJcbiAqIHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgcmF0ZUxpbWl0aW5nT3B0aW9ucykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBDaGVjayBpZiB0aGUgcmF0ZSBsaW1pdGluZyBpcyBlbmFibGVkIGFuZCB0aGUgYXBwIGV4aXN0c1xyXG4gICAgaWYgKGFwcCAmJiByYXRlTGltaXRpbmdPcHRpb25zLmVuYWJsZSkge1xyXG4gICAgICBjb25zdCBtZXNzYWdlID1cclxuICAgICAgICAnVG9vIG1hbnkgcmVxdWVzdHMsIHlvdSBoYXZlIGJlZW4gcmF0ZSBsaW1pdGVkLiBQbGVhc2UgdHJ5IGFnYWluIGxhdGVyLic7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICAgIGNvbnN0IHJhdGVPcHRpb25zID0ge1xyXG4gICAgICAgIHdpbmRvdzogcmF0ZUxpbWl0aW5nT3B0aW9ucy53aW5kb3cgfHwgMSxcclxuICAgICAgICBtYXhSZXF1ZXN0czogcmF0ZUxpbWl0aW5nT3B0aW9ucy5tYXhSZXF1ZXN0cyB8fCAzMCxcclxuICAgICAgICBkZWxheTogcmF0ZUxpbWl0aW5nT3B0aW9ucy5kZWxheSB8fCAwLFxyXG4gICAgICAgIHRydXN0UHJveHk6IHJhdGVMaW1pdGluZ09wdGlvbnMudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgICAgICBza2lwS2V5OiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBLZXkgfHwgbnVsbCxcclxuICAgICAgICBza2lwVG9rZW46IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcFRva2VuIHx8IG51bGxcclxuICAgICAgfTtcclxuXHJcbiAgICAgIC8vIFNldCBpZiBiZWhpbmQgYSBwcm94eVxyXG4gICAgICBpZiAocmF0ZU9wdGlvbnMudHJ1c3RQcm94eSkge1xyXG4gICAgICAgIGFwcC5lbmFibGUoJ3RydXN0IHByb3h5Jyk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENyZWF0ZSBhIGxpbWl0ZXJcclxuICAgICAgY29uc3QgbGltaXRlciA9IHJhdGVMaW1pdCh7XHJcbiAgICAgICAgLy8gVGltZSBmcmFtZSBmb3Igd2hpY2ggcmVxdWVzdHMgYXJlIGNoZWNrZWQgYW5kIHJlbWVtYmVyZWRcclxuICAgICAgICB3aW5kb3dNczogcmF0ZU9wdGlvbnMud2luZG93ICogNjAgKiAxMDAwLFxyXG4gICAgICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciBgd2luZG93TXNgXHJcbiAgICAgICAgbGltaXQ6IHJhdGVPcHRpb25zLm1heFJlcXVlc3RzLFxyXG4gICAgICAgIC8vIERpc2FibGUgZGVsYXlpbmcsIGZ1bGwgc3BlZWQgdW50aWwgdGhlIG1heCBsaW1pdCBpcyByZWFjaGVkXHJcbiAgICAgICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICAgICAgaGFuZGxlcjogKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5mb3JtYXQoe1xyXG4gICAgICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZCh7IG1lc3NhZ2UgfSk7XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIGRlZmF1bHQ6ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKG1lc3NhZ2UpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICB9LFxyXG4gICAgICAgIHNraXA6IChyZXF1ZXN0KSA9PiB7XHJcbiAgICAgICAgICAvLyBBbGxvdyBieXBhc3NpbmcgdGhlIGxpbWl0ZXIgaWYgYSB2YWxpZCBrZXkvdG9rZW4gaGFzIGJlZW4gc2VudFxyXG4gICAgICAgICAgaWYgKFxyXG4gICAgICAgICAgICByYXRlT3B0aW9ucy5za2lwS2V5ICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBUb2tlbiAhPT0gbnVsbCAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmtleSA9PT0gcmF0ZU9wdGlvbnMuc2tpcEtleSAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmFjY2Vzc190b2tlbiA9PT0gcmF0ZU9wdGlvbnMuc2tpcFRva2VuXHJcbiAgICAgICAgICApIHtcclxuICAgICAgICAgICAgbG9nKDQsICdbcmF0ZSBsaW1pdGluZ10gU2tpcHBpbmcgcmF0ZSBsaW1pdGVyLicpO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgLy8gVXNlIGEgbGltaXRlciBhcyBhIG1pZGRsZXdhcmVcclxuICAgICAgYXBwLnVzZShsaW1pdGVyKTtcclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcmF0ZSBsaW1pdGluZ10gRW5hYmxlZCByYXRlIGxpbWl0aW5nIHdpdGggJHtyYXRlT3B0aW9ucy5tYXhSZXF1ZXN0c30gcmVxdWVzdHMgcGVyICR7cmF0ZU9wdGlvbnMud2luZG93fSBtaW51dGUgZm9yIGVhY2ggSVAsIHRydXN0aW5nIHByb3h5OiAke3JhdGVPcHRpb25zLnRydXN0UHJveHl9LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3JhdGUgbGltaXRpbmddIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHNldCB0aGUgcmF0ZSBsaW1pdGluZyBvcHRpb25zLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtaWRkbGV3YXJlIGZ1bmN0aW9ucyBmb3IgdmFsaWRhdGluZyBpbmNvbWluZyBIVFRQIHJlcXVlc3RzXHJcbiAqIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uIFRoaXMgbW9kdWxlIGVuc3VyZXMgdGhhdCByZXF1ZXN0cyBjb250YWluXHJcbiAqIGFwcHJvcHJpYXRlIGNvbnRlbnQgdHlwZXMgYW5kIHZhbGlkIHJlcXVlc3QgYm9kaWVzLCBpbmNsdWRpbmcgcHJvcGVyIEpTT05cclxuICogc3RydWN0dXJlcyBhbmQgY2hhcnQgZGF0YSBmb3IgZXhwb3J0cy4gSXQgY2hlY2tzIGZvciBwb3RlbnRpYWwgaXNzdWVzIHN1Y2hcclxuICogYXMgbWlzc2luZyBvciBtYWxmb3JtZWQgZGF0YSwgcHJpdmF0ZSByYW5nZSBVUkxzIGluIFNWRyBwYXlsb2FkcywgYW5kIGFsbG93c1xyXG4gKiBmb3IgZmxleGlibGUgb3B0aW9ucyB2YWxpZGF0aW9uLiBUaGUgbWlkZGxld2FyZSBsb2dzIGRldGFpbGVkIGluZm9ybWF0aW9uXHJcbiAqIGFuZCBoYW5kbGVzIGVycm9ycyByZWxhdGVkIHRvIGluY29ycmVjdCBwYXlsb2FkcywgY2hhcnQgZGF0YSwgYW5kIHByaXZhdGUgVVJMXHJcbiAqIHVzYWdlLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7IGdldEFsbG93Q29kZUV4ZWN1dGlvbiB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaXNPYmplY3RFbXB0eSwgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIGNvbnRlbnQtdHlwZSBoZWFkZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9IFRoZSBjYWxsIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY29udGVudC10eXBlXHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gY29udGVudFR5cGVNaWRkbGV3YXJlKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgY29udGVudCB0eXBlIGhlYWRlclxyXG4gICAgY29uc3QgY29udGVudFR5cGUgPSByZXF1ZXN0LmhlYWRlcnNbJ2NvbnRlbnQtdHlwZSddIHx8ICcnO1xyXG5cclxuICAgIC8vIEFsbG93IG9ubHkgSlNPTiwgVVJMLWVuY29kZWQgYW5kIGZvcm0gZGF0YSB3aXRob3V0IGZpbGVzIHR5cGVzIG9mIGRhdGFcclxuICAgIGlmIChcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdhcHBsaWNhdGlvbi9qc29uJykgJiZcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ211bHRpcGFydC9mb3JtLWRhdGEnKVxyXG4gICAgKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3ZhbGlkYXRpb25dIENvbnRlbnQtVHlwZSBtdXN0IGJlIGFwcGxpY2F0aW9uL2pzb24sIGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCwgb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YS4nLFxyXG4gICAgICAgIDQxNVxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENhbGwgdGhlIGByZXF1ZXN0Qm9keU1pZGRsZXdhcmVgIG1pZGRsZXdhcmVcclxuICAgIHJldHVybiBuZXh0KCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciB2YWxpZGF0aW5nIHRoZSByZXF1ZXN0J3MgYm9keS5cclxuICpcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RCb2R5TWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBib2R5IGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGNoYXJ0IGRhdGEgZnJvbSB0aGUgYm9keVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGluIGNhc2Ugb2YgdGhlIHByaXZhdGUgcmFuZ2VcclxuICogdXJsIGVycm9yLlxyXG4gKi9cclxuZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgcmVxdWVzdCBib2R5XHJcbiAgICBjb25zdCBib2R5ID0gcmVxdWVzdC5ib2R5O1xyXG5cclxuICAgIC8vIENyZWF0ZSBhIHVuaXF1ZSBJRCBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSB1dWlkKCk7XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgaXMgbm8gY29ycmVjdCBib2R5XHJcbiAgICBpZiAoIWJvZHkgfHwgaXNPYmplY3RFbXB0eShib2R5KSkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIGVtcHR5LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBib2R5IGlzIHJlcXVpcmVkLiBQbGVhc2UgZW5zdXJlIHRoYXQgeW91ciBDb250ZW50LVR5cGUgaGVhZGVyIGlzIGNvcnJlY3QuIEFjY2VwdGVkIHR5cGVzIGFyZSAnYXBwbGljYXRpb24vanNvbicgYW5kICdtdWx0aXBhcnQvZm9ybS1kYXRhJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIG9wdGlvbiBmb3IgdGhlIHNlcnZlclxyXG4gICAgY29uc3QgYWxsb3dDb2RlRXhlY3V0aW9uID0gZ2V0QWxsb3dDb2RlRXhlY3V0aW9uKCk7XHJcblxyXG4gICAgLy8gRmluZCBhIGNvcnJlY3QgY2hhcnQgb3B0aW9uc1xyXG4gICAgY29uc3QgaW5zdHIgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgIC8vIFVzZSBvbmUgb2YgdGhlIGJlbG93XHJcbiAgICAgIGJvZHkuaW5zdHIgfHwgYm9keS5vcHRpb25zIHx8IGJvZHkuaW5maWxlIHx8IGJvZHkuZGF0YSxcclxuICAgICAgLy8gU3RyaW5naWZ5IG9wdGlvbnNcclxuICAgICAgdHJ1ZSxcclxuICAgICAgLy8gQWxsb3cgb3IgZGlzYWxsb3cgZnVuY3Rpb25zXHJcbiAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGNoYXJ0IGRhdGFcclxuICAgIGlmIChpbnN0ciA9PT0gbnVsbCAmJiAhYm9keS5zdmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fCByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcGF5bG9hZCBpcyBtaXNzaW5nIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3IgZXhwb3J0OiAke0pTT04uc3RyaW5naWZ5KGJvZHkpfS5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBbdmFsaWRhdGlvbl0gTm8gY29ycmVjdCBjaGFydCBkYXRhIGZvdW5kLiBFbnN1cmUgdGhhdCB5b3UgYXJlIHVzaW5nIGVpdGhlciBhcHBsaWNhdGlvbi9qc29uIG9yIG11bHRpcGFydC9mb3JtLWRhdGEgaGVhZGVycy4gSWYgc2VuZGluZyBKU09OLCBtYWtlIHN1cmUgdGhlIGNoYXJ0IGRhdGEgaXMgaW4gdGhlICdpbmZpbGUnLCAnb3B0aW9ucycsIG9yICdkYXRhJyBhdHRyaWJ1dGUuIElmIHNlbmRpbmcgU1ZHLCBlbnN1cmUgaXQgaXMgaW4gdGhlICdzdmcnIGF0dHJpYnV0ZS5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRlc3Qgb2YgeGxpbms6aHJlZiBlbGVtZW50cyBmcm9tIHBheWxvYWQncyBTVkcgZmFpbHNcclxuICAgIGlmIChib2R5LnN2ZyAmJiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKGJvZHkuc3ZnKSkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBbdmFsaWRhdGlvbl0gU1ZHIHBvdGVudGlhbGx5IGNvbnRhaW4gYXQgbGVhc3Qgb25lIGZvcmJpZGRlbiBVUkwgaW4gJ3hsaW5rOmhyZWYnIGVsZW1lbnQuIFBsZWFzZSByZXZpZXcgdGhlIFNWRyBjb250ZW50IGFuZCBlbnN1cmUgdGhhdCBhbGwgcmVmZXJlbmNlZCBVUkxzIGNvbXBseSB3aXRoIHNlY3VyaXR5IHBvbGljaWVzLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IG9wdGlvbnMgYW5kIHN0b3JlIHBhcnNlZCBzdHJ1Y3R1cmUgaW4gdGhlIHJlcXVlc3RcclxuICAgIHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucyA9IHtcclxuICAgICAgLy8gU2V0IHRoZSBjcmVhdGVkIElEIGFzIGEgYHJlcXVlc3RJZGAgcHJvcGVydHkgaW4gdGhlIG9wdGlvbnNcclxuICAgICAgcmVxdWVzdElkLFxyXG4gICAgICBleHBvcnQ6IHtcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICBzdmc6IGJvZHkuc3ZnLFxyXG4gICAgICAgIG91dGZpbGU6XHJcbiAgICAgICAgICBib2R5Lm91dGZpbGUgfHxcclxuICAgICAgICAgIGAke3JlcXVlc3QucGFyYW1zLmZpbGVuYW1lIHx8ICdjaGFydCd9LiR7Ym9keS50eXBlIHx8ICdwbmcnfWAsXHJcbiAgICAgICAgdHlwZTogYm9keS50eXBlLFxyXG4gICAgICAgIGNvbnN0cjogYm9keS5jb25zdHIsXHJcbiAgICAgICAgYjY0OiBib2R5LmI2NCxcclxuICAgICAgICBub0Rvd25sb2FkOiBib2R5Lm5vRG93bmxvYWQsXHJcbiAgICAgICAgaGVpZ2h0OiBib2R5LmhlaWdodCxcclxuICAgICAgICB3aWR0aDogYm9keS53aWR0aCxcclxuICAgICAgICBzY2FsZTogYm9keS5zY2FsZSxcclxuICAgICAgICBnbG9iYWxPcHRpb25zOiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICBib2R5Lmdsb2JhbE9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKSxcclxuICAgICAgICB0aGVtZU9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkudGhlbWVPcHRpb25zLFxyXG4gICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgIClcclxuICAgICAgfSxcclxuICAgICAgY3VzdG9tTG9naWM6IHtcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzOiBmYWxzZSxcclxuICAgICAgICBjdXN0b21Db2RlOiBib2R5LmN1c3RvbUNvZGUsXHJcbiAgICAgICAgY2FsbGJhY2s6IGJvZHkuY2FsbGJhY2ssXHJcbiAgICAgICAgcmVzb3VyY2VzOiBpc0FsbG93ZWRDb25maWcoYm9keS5yZXNvdXJjZXMsIHRydWUsIGFsbG93Q29kZUV4ZWN1dGlvbilcclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBuZXh0IG1pZGRsZXdhcmVcclxuICAgIHJldHVybiBuZXh0KCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVzIHRvIHRoZSBwYXNzZWQgZXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmFsaWRhdGlvbk1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGNvbnRlbnQgdHlwZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCBjb250ZW50VHlwZU1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgcmVxdWVzdCBib2R5IHJlcXVlc3QgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgYXBwLnBvc3QoWycvJywgJy86ZmlsZW5hbWUnXSwgcmVxdWVzdEJvZHlNaWRkbGV3YXJlKTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyB0aGUgZXhwb3J0IHJvdXRlcyBhbmQgbG9naWMgZm9yIGhhbmRsaW5nIGNoYXJ0IGV4cG9ydFxyXG4gKiByZXF1ZXN0cyBpbiBhbiBFeHByZXNzIHNlcnZlci4gVGhpcyBtb2R1bGUgcHJvY2Vzc2VzIGluY29taW5nIHJlcXVlc3RzXHJcbiAqIHRvIGV4cG9ydCBjaGFydHMgaW4gdmFyaW91cyBmb3JtYXRzIChlLmcuIEpQRUcsIFBORywgUERGLCBTVkcpLiBJdCBpbnRlZ3JhdGVzXHJcbiAqIHdpdGggSGlnaGNoYXJ0cycgY29yZSBmdW5jdGlvbmFsaXRpZXMgYW5kIHN1cHBvcnRzIGJvdGggaW1tZWRpYXRlIGRvd25sb2FkXHJcbiAqIHJlc3BvbnNlcyBhbmQgQmFzZTY0LWVuY29kZWQgY29udGVudCByZXR1cm5zLiBUaGUgY29kZSBhbHNvIGZlYXR1cmVzXHJcbiAqIGJlbmNobWFya2luZyBmb3IgcGVyZm9ybWFuY2UgbW9uaXRvcmluZy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBzdGFydEV4cG9ydCB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0QmFzZTY0LCBtZWFzdXJlVGltZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xyXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XHJcbiAgcG5nOiAnaW1hZ2UvcG5nJyxcclxuICBqcGVnOiAnaW1hZ2UvanBlZycsXHJcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcclxuICBwZGY6ICdhcHBsaWNhdGlvbi9wZGYnLFxyXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXHJcbn07XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgZXhwb3J0IHJlcXVlc3RzIGZyb20gdGhlIGNsaWVudC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0KHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0IGNvdW50aW5nIHRpbWUgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgcmVxdWVzdENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIEluIGNhc2UgdGhlIGNvbm5lY3Rpb24gaXMgY2xvc2VkLCBmb3JjZSB0byBhYm9ydCBmdXJ0aGVyIGFjdGlvbnNcclxuICAgIGxldCBjb25uZWN0aW9uQWJvcnRlZCA9IGZhbHNlO1xyXG4gICAgcmVxdWVzdC5zb2NrZXQub24oJ2Nsb3NlJywgKGhhZEVycm9ycykgPT4ge1xyXG4gICAgICBpZiAoaGFkRXJyb3JzKSB7XHJcbiAgICAgICAgY29ubmVjdGlvbkFib3J0ZWQgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIG9wdGlvbnMgcHJldmlvdXNseSB2YWxpZGF0ZWQgaW4gdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gICAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGlkXHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSByZXF1ZXN0T3B0aW9ucy5yZXF1ZXN0SWQ7XHJcblxyXG4gICAgLy8gSW5mbyBhYm91dCBhbiBpbmNvbWluZyByZXF1ZXN0IHdpdGggY29ycmVjdCBkYXRhXHJcbiAgICBsb2coNCwgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBHb3QgYW4gaW5jb21pbmcgSFRUUCByZXF1ZXN0LmApO1xyXG5cclxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQocmVxdWVzdE9wdGlvbnMsIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAvLyBSZW1vdmUgdGhlIGNsb3NlIGV2ZW50IGZyb20gdGhlIHNvY2tldFxyXG4gICAgICByZXF1ZXN0LnNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2Nsb3NlJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkLCBkbyBub3RoaW5nXHJcbiAgICAgIGlmIChjb25uZWN0aW9uQWJvcnRlZCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSBjbGllbnQgY2xvc2VkIHRoZSBjb25uZWN0aW9uIGJlZm9yZSB0aGUgY2hhcnQgZmluaXNoZWQgcHJvY2Vzc2luZy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGVycm9yLCBsb2cgaXQgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGRhdGEgaXMgbWlzc2luZywgbG9nIHRoZSBtZXNzYWdlIGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmICghZGF0YSB8fCAhZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBSZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fFxyXG4gICAgICAgICAgICByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCByZXN1bHQgaXMgJHtkYXRhLnJlc3VsdH0uYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVW5leHBlY3RlZCByZXR1cm4gb2YgdGhlIGV4cG9ydCByZXN1bHQgZnJvbSB0aGUgY2hhcnQgZ2VuZXJhdGlvbi4gUGxlYXNlIGNoZWNrIHlvdXIgcmVxdWVzdCBkYXRhLmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBSZXR1cm4gdGhlIHJlc3VsdCBpbiBhbiBhcHByb3ByaWF0ZSBmb3JtYXRcclxuICAgICAgaWYgKGRhdGEucmVzdWx0KSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzIHRvb2sgJHtyZXF1ZXN0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIGB0eXBlYCwgYGI2NGAsIGBub0Rvd25sb2FkYCwgYW5kIGBvdXRmaWxlYCBmcm9tIG9wdGlvbnNcclxuICAgICAgICBjb25zdCB7IHR5cGUsIGI2NCwgbm9Eb3dubG9hZCwgb3V0ZmlsZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgLy8gSWYgb25seSBCYXNlNjQgaXMgcmVxdWlyZWQsIHJldHVybiBpdFxyXG4gICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gU2V0IGNvcnJlY3QgY29udGVudCB0eXBlXHJcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVyKCdDb250ZW50LVR5cGUnLCByZXZlcnNlZE1pbWVbdHlwZV0gfHwgJ2ltYWdlL3BuZycpO1xyXG5cclxuICAgICAgICAvLyBEZWNpZGUgd2hldGhlciB0byBkb3dubG9hZCBvciBub3QgY2hhcnQgZmlsZVxyXG4gICAgICAgIGlmICghbm9Eb3dubG9hZCkge1xyXG4gICAgICAgICAgcmVzcG9uc2UuYXR0YWNobWVudChvdXRmaWxlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIElmIFNWRywgcmV0dXJuIHBsYWluIGNvbnRlbnQsIG90aGVyd2lzZSBhIGI2NCBzdHJpbmcgZnJvbSBhIGJ1ZmZlclxyXG4gICAgICAgIHJldHVybiB0eXBlID09PSAnc3ZnJ1xyXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGRhdGEucmVzdWx0KVxyXG4gICAgICAgICAgOiByZXNwb25zZS5zZW5kKEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JykpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBleHBvcnRgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4cG9ydFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGV4cG9ydFJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3RcclxuICAgKiBlbmRwb2ludC5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLycsIHJlcXVlc3RFeHBvcnQpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvOmZpbGVuYW1lJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgd2l0aFxyXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLzpmaWxlbmFtZScsIHJlcXVlc3RFeHBvcnQpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHNlcnZlciBoZWFsdGggbW9uaXRvcmluZywgaW5jbHVkaW5nXHJcbiAqIHVwdGltZSwgc3VjY2VzcyByYXRlcywgYW5kIG90aGVyIHNlcnZlciBzdGF0aXN0aWNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldFBvb2xTdGF0cywgZ2V0UG9vbEluZm9KU09OIH0gZnJvbSAnLi4vLi4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGFkZFRpbWVyIH0gZnJvbSAnLi4vLi4vdGltZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGdldE5ld0RhdGVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLy8gU2V0IHRoZSBzdGFydCBkYXRlIG9mIHRoZSBzZXJ2ZXJcclxuY29uc3Qgc2VydmVyU3RhcnRUaW1lID0gbmV3IERhdGUoKTtcclxuXHJcbi8vIEdldCB0aGUgYHBhY2thZ2UuanNvbmAgY29udGVudFxyXG5jb25zdCBwYWNrYWdlRmlsZSA9IEpTT04ucGFyc2UoXHJcbiAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuKTtcclxuXHJcbi8vIEFuIGFycmF5IGZvciBzdWNjZXNzIHJhdGUgcmF0aW9zXHJcbmNvbnN0IHN1Y2Nlc3NSYXRlcyA9IFtdO1xyXG5cclxuLy8gUmVjb3JkIGV2ZXJ5IG1pbnV0ZVxyXG5jb25zdCByZWNvcmRJbnRlcnZhbCA9IDYwICogMTAwMDtcclxuXHJcbi8vIDMwIG1pbnV0ZXNcclxuY29uc3Qgd2luZG93U2l6ZSA9IDMwO1xyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgbW92aW5nIGF2ZXJhZ2UgaW5kaWNhdG9yIGJhc2VkIG9uIHRoZSBkYXRhIGZyb20gdGhlIGBzdWNjZXNzUmF0ZXNgXHJcbiAqIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2VcclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gQSBtb3ZpbmcgYXZlcmFnZSBmb3Igc3VjY2VzcyByYXRpbyBvZiB0aGUgc2VydmVyIGV4cG9ydHMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpIHtcclxuICByZXR1cm4gc3VjY2Vzc1JhdGVzLnJlZHVjZSgoYSwgYikgPT4gYSArIGIsIDApIC8gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyB0aGUgaW50ZXJ2YWwgcmVzcG9uc2libGUgZm9yIGNhbGN1bGF0aW5nIGN1cnJlbnQgc3VjY2VzcyByYXRlIHJhdGlvXHJcbiAqIGFuZCBjb2xsZWN0cyByZWNvcmRzIHRvIHRoZSBgc3VjY2Vzc1JhdGVzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtOb2RlSlMuVGltZW91dH0gSWQgb2YgYW4gaW50ZXJ2YWwuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc3RhcnRTdWNjZXNzUmF0ZSgpIHtcclxuICByZXR1cm4gc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgIGNvbnN0IHN1Y2Nlc3NSYXRpbyA9XHJcbiAgICAgIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQgPT09IDBcclxuICAgICAgICA/IDFcclxuICAgICAgICA6IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDA7XHJcblxyXG4gICAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcclxuICAgIGlmIChzdWNjZXNzUmF0ZXMubGVuZ3RoID4gd2luZG93U2l6ZSkge1xyXG4gICAgICBzdWNjZXNzUmF0ZXMuc2hpZnQoKTtcclxuICAgIH1cclxuICB9LCByZWNvcmRJbnRlcnZhbCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgaGVhbHRoYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBoZWFsdGhSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBoZWFsdGhSb3V0ZXMoYXBwKSB7XHJcbiAgLy8gU3RhcnQgcHJvY2Vzc2luZyBzdWNjZXNzIHJhdGUgcmF0aW8gaW50ZXJ2YWwgYW5kIHNhdmUgaXRzIGlkIHRvIHRoZSBhcnJheVxyXG4gIC8vIGZvciB0aGUgZ3JhY2VmdWwgY2xlYXJpbmcgb24gc2h1dGRvd24gd2l0aCBpbmplY3RlZCBgYWRkVGltZXJgIGZ1bnRpb25cclxuICBhZGRUaW1lcihfc3RhcnRTdWNjZXNzUmF0ZSgpKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgR0VUICcvaGVhbHRoJyAtIEEgcm91dGUgZm9yIGdldHRpbmcgdGhlIGJhc2ljIHN0YXRzIG9mIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldCgnL2hlYWx0aCcsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbaGVhbHRoXSBSZXR1cm5pbmcgc2VydmVyIGhlYWx0aC4nKTtcclxuXHJcbiAgICAgIGNvbnN0IHN0YXRzID0gZ2V0UG9vbFN0YXRzKCk7XHJcbiAgICAgIGNvbnN0IHBlcmlvZCA9IHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbiAgICAgIGNvbnN0IG1vdmluZ0F2ZXJhZ2UgPSBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpO1xyXG5cclxuICAgICAgLy8gU2VuZCB0aGUgc2VydmVyJ3Mgc3RhdGlzdGljc1xyXG4gICAgICByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAvLyBTdGF0dXMgYW5kIHRpbWVzXHJcbiAgICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICAgIGJvb3RUaW1lOiBzZXJ2ZXJTdGFydFRpbWUsXHJcbiAgICAgICAgdXB0aW1lOiBgJHtNYXRoLmZsb29yKChnZXROZXdEYXRlVGltZSgpIC0gc2VydmVyU3RhcnRUaW1lLmdldFRpbWUoKSkgLyAxMDAwIC8gNjApfSBtaW51dGVzYCxcclxuXHJcbiAgICAgICAgLy8gVmVyc2lvbnNcclxuICAgICAgICBzZXJ2ZXJWZXJzaW9uOiBwYWNrYWdlRmlsZS52ZXJzaW9uLFxyXG4gICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG5cclxuICAgICAgICAvLyBFeHBvcnRzXHJcbiAgICAgICAgYXZlcmFnZUV4cG9ydFRpbWU6IHN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UsXHJcbiAgICAgICAgYXR0ZW1wdGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCxcclxuICAgICAgICBwZXJmb3JtZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzUGVyZm9ybWVkLFxyXG4gICAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNEcm9wcGVkLFxyXG4gICAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCAvIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQpICogMTAwLFxyXG5cclxuICAgICAgICAvLyBQb29sXHJcbiAgICAgICAgcG9vbDogZ2V0UG9vbEluZm9KU09OKCksXHJcblxyXG4gICAgICAgIC8vIE1vdmluZyBhdmVyYWdlXHJcbiAgICAgICAgcGVyaW9kLFxyXG4gICAgICAgIG1vdmluZ0F2ZXJhZ2UsXHJcbiAgICAgICAgbWVzc2FnZTpcclxuICAgICAgICAgIGlzTmFOKG1vdmluZ0F2ZXJhZ2UpIHx8ICFzdWNjZXNzUmF0ZXMubGVuZ3RoXHJcbiAgICAgICAgICAgID8gJ1RvbyBlYXJseSB0byByZXBvcnQuIE5vIGV4cG9ydHMgbWFkZSB5ZXQuIFBsZWFzZSBjaGVjayBiYWNrIHNvb24uJ1xyXG4gICAgICAgICAgICA6IGBMYXN0ICR7cGVyaW9kfSBtaW51dGVzIGhhZCBhIHN1Y2Nlc3MgcmF0ZSBvZiAke21vdmluZ0F2ZXJhZ2UudG9GaXhlZCgyKX0lLmAsXHJcblxyXG4gICAgICAgIC8vIFNWRyBhbmQgSlNPTiBleHBvcnRzXHJcbiAgICAgICAgc3ZnRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21TdmcsXHJcbiAgICAgICAganNvbkV4cG9ydHM6IHN0YXRzLmV4cG9ydHNGcm9tT3B0aW9ucyxcclxuICAgICAgICBzdmdFeHBvcnRzQXR0ZW1wdHM6IHN0YXRzLmV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMsXHJcbiAgICAgICAganNvbkV4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHNcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmluZyB0aGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyXHJcbiAqIHdoZW4gZW5hYmxlZC5cclxuICovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB1aWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdWlSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB1aVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy8nIC0gQSByb3V0ZSBmb3IgYSBVSSB3aGVuIGVuYWJsZWQgb24gdGhlIGV4cG9ydCBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldChnZXRPcHRpb25zKCkudWkucm91dGUgfHwgJy8nLCAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3VpXSBSZXR1cm5pbmcgVUkgZm9yIHRoZSBleHBvcnQuJyk7XHJcblxyXG4gICAgICByZXNwb25zZS5zZW5kRmlsZShqb2luKF9fZGlybmFtZSwgJ3B1YmxpYycsICdpbmRleC5odG1sJyksIHtcclxuICAgICAgICBhY2NlcHRSYW5nZXM6IGZhbHNlXHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHVwZGF0aW5nIHRoZSBIaWdoY2hhcnRzIHZlcnNpb25cclxuICogb24gdGhlIHNlcnZlciwgd2l0aCBhdXRoZW50aWNhdGlvbiBhbmQgdmFsaWRhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiwgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuLi8uLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB2ZXJzaW9uX2NoYW5nZWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHZlcnNpb25DaGFuZ2VSb3V0ZXMoYXBwKSB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnL3ZlcnNpb25fY2hhbmdlLzpuZXdWZXJzaW9uJyAtIEEgcm91dGUgZm9yIGNoYW5naW5nXHJcbiAgICogdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBvbiB0aGUgc2VydmVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nLCBhc3luYyAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3ZlcnNpb25dIENoYW5naW5nIEhpZ2hjaGFydHMgdmVyc2lvbi4nKTtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZGlyZWN0bHkgZnJvbSBlbnZzXHJcbiAgICAgIGNvbnN0IGFkbWluVG9rZW4gPSBlbnZzLkhJR0hDSEFSVFNfQURNSU5fVE9LRU47XHJcblxyXG4gICAgICAvLyBDaGVjayB0aGUgZXhpc3RlbmNlIG9mIHRoZSB0b2tlblxyXG4gICAgICBpZiAoIWFkbWluVG9rZW4gfHwgIWFkbWluVG9rZW4ubGVuZ3RoKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1t2ZXJzaW9uXSBUaGUgc2VydmVyIGlzIG5vdCBjb25maWd1cmVkIHRvIHBlcmZvcm0gcnVuLXRpbWUgdmVyc2lvbiBjaGFuZ2VzOiBISUdIQ0hBUlRTX0FETUlOX1RPS0VOIGlzIG5vdCBzZXQuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZnJvbSB0aGUgaGMtYXV0aCBoZWFkZXJcclxuICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXHJcbiAgICAgIGlmICghdG9rZW4gfHwgdG9rZW4gIT09IGFkbWluVG9rZW4pIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIEludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdmVyc2lvbnNcclxuICAgICAgbGV0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG4gICAgICBpZiAobmV3VmVyc2lvbikge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAvLyBVcGRhdGUgdmVyc2lvblxyXG4gICAgICAgICAgYXdhaXQgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbik7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgYFt2ZXJzaW9uXSBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgIDQwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbdmVyc2lvbl0gTm8gbmV3IHZlcnNpb24gc3VwcGxpZWQuJywgNDAwKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSB0aGF0IHNldHMgdXAgYW5kIG1hbmFnZXMgSFRUUCBhbmQgSFRUUFMgc2VydmVyc1xyXG4gKiBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci4gSXQgaGFuZGxlcyBzZXJ2ZXIgaW5pdGlhbGl6YXRpb24sXHJcbiAqIGNvbmZpZ3VyYXRpb24sIGVycm9yIGhhbmRsaW5nLCBtaWRkbGV3YXJlIHNldHVwLCByb3V0ZSBkZWZpbml0aW9uLCBhbmQgcmF0ZVxyXG4gKiBsaW1pdGluZy4gVGhlIG1vZHVsZSBleHBvcnRzIGZ1bmN0aW9ucyB0byBzdGFydCwgc3RvcCwgYW5kIG1hbmFnZSBzZXJ2ZXJcclxuICogaW5zdGFuY2VzLCBhcyB3ZWxsIGFzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBkZWZpbmluZyByb3V0ZXMgYW5kIGF0dGFjaGluZ1xyXG4gKiBtaWRkbGV3YXJlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBjb3JzIGZyb20gJ2NvcnMnO1xyXG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcbmltcG9ydCBtdWx0ZXIgZnJvbSAnbXVsdGVyJztcclxuXHJcbmltcG9ydCB7IHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IGVycm9yTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL2Vycm9yLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdGluZ01pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy9yYXRlTGltaXRpbmcuanMnO1xyXG5pbXBvcnQgdmFsaWRhdGlvbk1pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzJztcclxuXHJcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcclxuaW1wb3J0IGhlYWx0aFJvdXRlcyBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xyXG5pbXBvcnQgdmVyc2lvbkNoYW5nZVJvdXRlcyBmcm9tICcuL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcclxuY29uc3QgYWN0aXZlU2VydmVycyA9IG5ldyBNYXAoKTtcclxuXHJcbi8vIENyZWF0ZSBleHByZXNzIGFwcFxyXG5jb25zdCBhcHAgPSBleHByZXNzKCk7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIEhUVFAgYW5kL29yIEhUVFBTIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi5cclxuICogVGhlIGBzZXJ2ZXJPcHRpb25zYCBvYmplY3QgY29udGFpbnMgc2VydmVyLXJlbGF0ZWQgcHJvcGVydGllcyAocmVmZXJcclxuICogdG8gdGhlIGBzZXJ2ZXJgIHNlY3Rpb24gaW4gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgZGV0YWlscykuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRTZXJ2ZXJcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlck9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIHRoZSBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBkZWZhdWx0XHJcbiAqIHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGVpdGhlclxyXG4gKiBub3QgZW5hYmxlZCBvciBubyB2YWxpZCBFeHByZXNzIGFwcCBpcyBmb3VuZCwgc2lnbmFsaW5nIHRoZSBlbmQgb2YgdGhlXHJcbiAqIGZ1bmN0aW9uJ3MgZXhlY3V0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIHNlcnZlciBjYW5ub3RcclxuICogYmUgY29uZmlndXJlZCBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFNlcnZlcihzZXJ2ZXJPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgc2VydmVyOiBzZXJ2ZXJPcHRpb25zXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBVc2UgdmFsaWRhdGVkIG9wdGlvbnNcclxuICAgIHNlcnZlck9wdGlvbnMgPSBvcHRpb25zLnNlcnZlcjtcclxuXHJcbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXHJcbiAgICBpZiAoIXNlcnZlck9wdGlvbnMuZW5hYmxlIHx8ICFhcHApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbc2VydmVyXSBTZXJ2ZXIgY2Fubm90IGJlIHN0YXJ0ZWQgKG5vdCBlbmFibGVkIG9yIG5vIGNvcnJlY3QgRXhwcmVzcyBhcHAgZm91bmQpLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVG9vIGJpZyBsaW1pdHMgbGVhZCB0byB0aW1lb3V0cyBpbiB0aGUgZXhwb3J0IHByb2Nlc3Mgd2hlblxyXG4gICAgLy8gdGhlIHJhc3Rlcml6YXRpb24gdGltZW91dCBpcyBzZXQgdG9vIGxvd1xyXG4gICAgY29uc3QgdXBsb2FkTGltaXRCeXRlcyA9IHNlcnZlck9wdGlvbnMudXBsb2FkTGltaXQgKiAxMDI0ICogMTAyNDtcclxuXHJcbiAgICAvLyBNZW1vcnkgc3RvcmFnZSBmb3IgbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHN0b3JhZ2UgPSBtdWx0ZXIubWVtb3J5U3RvcmFnZSgpO1xyXG5cclxuICAgIC8vIEVuYWJsZSBwYXJzaW5nIG9mIGZvcm0gZGF0YSAoZmlsZXMpIHdpdGggbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHVwbG9hZCA9IG11bHRlcih7XHJcbiAgICAgIHN0b3JhZ2UsXHJcbiAgICAgIGxpbWl0czoge1xyXG4gICAgICAgIGZpZWxkU2l6ZTogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXHJcbiAgICBhcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XHJcblxyXG4gICAgLy8gRW5hYmxlIENPUlMgc3VwcG9ydFxyXG4gICAgYXBwLnVzZShcclxuICAgICAgY29ycyh7XHJcbiAgICAgICAgbWV0aG9kczogWydQT1NUJywgJ0dFVCcsICdPUFRJT05TJ11cclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0dGluZyBhIGxvdCBvZiBgUmFuZ2VOb3RTYXRpc2ZpYWJsZUVycm9yYCBleGNlcHRpb25zIChldmVuIHRob3VnaCB0aGlzXHJcbiAgICAvLyBpcyBhIGRlcHJlY2F0ZWQgb3B0aW9ucywgbGV0J3MgdHJ5IHRvIHNldCBpdCB0byBmYWxzZSlcclxuICAgIGFwcC51c2UoKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgIHJlc3BvbnNlLnNldCgnQWNjZXB0LVJhbmdlcycsICdub25lJyk7XHJcbiAgICAgIG5leHQoKTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgSlNPTiBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLmpzb24oe1xyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgVVJMLWVuY29kZWQgZm9ybSBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLnVybGVuY29kZWQoe1xyXG4gICAgICAgIGV4dGVuZGVkOiB0cnVlLFxyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xyXG4gICAgYXBwLnVzZSh1cGxvYWQubm9uZSgpKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgc3RhdGljIGZvbGRlcidzIHJvdXRlXHJcbiAgICBhcHAudXNlKGV4cHJlc3Muc3RhdGljKGpvaW4oX19kaXJuYW1lLCAncHVibGljJykpKTtcclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5zc2wuZm9yY2UpIHtcclxuICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFApXHJcbiAgICAgIGNvbnN0IGh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApO1xyXG5cclxuICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIC8vIExpc3RlblxyXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnBvcnQsIHNlcnZlck9wdGlvbnMuaG9zdCwgKCkgPT4ge1xyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMucG9ydCwgaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnBvcnR9LmBcclxuICAgICAgICApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgICBpZiAoc2VydmVyT3B0aW9ucy5zc2wuZW5hYmxlKSB7XHJcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cclxuICAgICAgbGV0IGtleSwgY2VydDtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XHJcbiAgICAgICAga2V5ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmtleScpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wgY2VydGlmaWNhdGVcclxuICAgICAgICBjZXJ0ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmNydCcpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFVuYWJsZSB0byBsb2FkIGtleS9jZXJ0aWZpY2F0ZSBmcm9tIHRoZSAnJHtzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChrZXkgJiYgY2VydCkge1xyXG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcclxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xyXG5cclxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgICAgX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAvLyBMaXN0ZW5cclxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyT3B0aW9ucy5zc2wucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUFMgc2VydmVyXHJcbiAgICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAzLFxyXG4gICAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQUyBzZXJ2ZXIgb24gJHtzZXJ2ZXJPcHRpb25zLmhvc3R9OiR7c2VydmVyT3B0aW9ucy5zc2wucG9ydH0uYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgc2VydmVyT3B0aW9ucy5yYXRlTGltaXRpbmcpO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgdmFsaWRhdGlvbiBoYW5kbGVyXHJcbiAgICB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGV4cG9ydFJvdXRlcyhhcHApO1xyXG4gICAgaGVhbHRoUm91dGVzKGFwcCk7XHJcbiAgICB1aVJvdXRlcyhhcHApO1xyXG4gICAgdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgY2VudHJhbGl6ZWQgZXJyb3IgaGFuZGxlclxyXG4gICAgZXJyb3JNaWRkbGV3YXJlKGFwcCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tzZXJ2ZXJdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHN0YXJ0IHRoZSBzZXJ2ZXIuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbG9zZXMgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzKCkge1xyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBzZXJ2ZXJzIHdvcmtpbmdcclxuICBpZiAoYWN0aXZlU2VydmVycy5zaXplID4gMCkge1xyXG4gICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zaW5nIGFsbCBzZXJ2ZXJzLmApO1xyXG5cclxuICAgIC8vIENsb3NlIGVhY2ggb25lIG9mIHNlcnZlcnNcclxuICAgIGZvciAoY29uc3QgW3BvcnQsIHNlcnZlcl0gb2YgYWN0aXZlU2VydmVycykge1xyXG4gICAgICBzZXJ2ZXIuY2xvc2UoKCkgPT4ge1xyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuZGVsZXRlKHBvcnQpO1xyXG4gICAgICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2VkIHNlcnZlciBvbiBwb3J0OiAke3BvcnR9LmApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0U2VydmVyc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPE9iamVjdD59IFNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFNlcnZlcnMoKSB7XHJcbiAgcmV0dXJuIGFjdGl2ZVNlcnZlcnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRFeHByZXNzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRFeHByZXNzKCkge1xyXG4gIHJldHVybiBleHByZXNzO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEFwcFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RXhwcmVzc30gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEFwcCgpIHtcclxuICByZXR1cm4gYXBwO1xyXG59XHJcblxyXG4vKipcclxuICogRW5hYmxlIHJhdGUgbGltaXRpbmcgZm9yIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVSYXRlTGltaXRpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHJhdGVMaW1pdGluZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0XHJcbiAqIG9mIHRoZSBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXNcclxuICogd2lsbCBkZWZhdWx0IHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZyhyYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgIHNlcnZlcjoge1xyXG4gICAgICByYXRlTGltaXRpbmc6IHJhdGVMaW1pdGluZ09wdGlvbnNcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnNcclxuICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgb3B0aW9ucy5zZXJ2ZXIucmF0ZUxpbWl0aW5nT3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBcHBseSBtaWRkbGV3YXJlKHMpIHRvIGEgc3BlY2lmaWMgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIHVzZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLnVzZShwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIEdFVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5nZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBQT1NUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHBvc3RcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAucG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgdG8gdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KGh0dHAuU2VydmVyfGh0dHBzLlNlcnZlcil9IHNlcnZlciAtIFRoZSBIVFRQL0hUVFBTIHNlcnZlciBpbnN0YW5jZS5cclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKHNlcnZlcikge1xyXG4gIHNlcnZlci5vbignY2xpZW50RXJyb3InLCAoZXJyb3IsIHNvY2tldCkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAxLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtzZXJ2ZXJdIENsaWVudCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfSwgZGVzdHJveWluZyBzb2NrZXQuYFxyXG4gICAgKTtcclxuICAgIHNvY2tldC5kZXN0cm95KCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNlcnZlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Nvbm5lY3Rpb24nLCAoc29ja2V0KSA9PiB7XHJcbiAgICBzb2NrZXQub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNvY2tldCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzdGFydFNlcnZlcixcclxuICBjbG9zZVNlcnZlcnMsXHJcbiAgZ2V0U2VydmVycyxcclxuICBnZXRFeHByZXNzLFxyXG4gIGdldEFwcCxcclxuICBlbmFibGVSYXRlTGltaXRpbmcsXHJcbiAgdXNlLFxyXG4gIGdldCxcclxuICBwb3N0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBIYW5kbGVzIGdyYWNlZnVsIHNodXRkb3duIG9mIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIsIGVuc3VyaW5nXHJcbiAqIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlcyBzdWNoIGFzIGJyb3dzZXIsIHBhZ2VzLCBzZXJ2ZXJzLCBhbmQgdGltZXJzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgY2xlYXJBbGxUaW1lcnMgfSBmcm9tICcuL3RpbWVyLmpzJztcclxuXHJcbmltcG9ydCB7IGNsb3NlU2VydmVycyB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogUGVyZm9ybXMgY2xlYW51cCBvcGVyYXRpb25zIHRvIGVuc3VyZSBhIGdyYWNlZnVsIHNodXRkb3duIG9mIHRoZSBwcm9jZXNzLlxyXG4gKiBUaGlzIGluY2x1ZGVzIGNsZWFyaW5nIGFsbCByZWdpc3RlcmVkIHRpbWVvdXRzL2ludGVydmFscywgY2xvc2luZyBhY3RpdmVcclxuICogc2VydmVycywgdGVybWluYXRpbmcgcmVzb3VyY2VzIChwYWdlcykgb2YgdGhlIHBvb2wsIHBvb2wgaXRzZWxmLCBhbmQgY2xvc2luZ1xyXG4gKiB0aGUgYnJvd3Nlci5cclxuICpcclxuICogQGZ1bmN0aW9uIHNodXRkb3duQ2xlYW5VcFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2V4aXRDb2RlPTBdIC0gVGhlIGV4aXQgY29kZSB0byB1c2Ugd2l0aCBgcHJvY2Vzcy5leGl0KClgLlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgMGAuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwKGV4aXRDb2RlID0gMCkge1xyXG4gIC8vIEF3YWl0IGZyZWVpbmcgYWxsIHJlc291cmNlc1xyXG4gIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChbXHJcbiAgICAvLyBDbGVhciBhbGwgb25nb2luZyBpbnRlcnZhbHNcclxuICAgIGNsZWFyQWxsVGltZXJzKCksXHJcblxyXG4gICAgLy8gR2V0IGF2YWlsYWJsZSBzZXJ2ZXIgaW5zdGFuY2VzIChIVFRQL0hUVFBTKSBhbmQgY2xvc2UgdGhlbVxyXG4gICAgY2xvc2VTZXJ2ZXJzKCksXHJcblxyXG4gICAgLy8gQ2xvc2UgYW4gYWN0aXZlIHBvb2wgYWxvbmcgd2l0aCBpdHMgd29ya2VycyBhbmQgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICAgIGtpbGxQb29sKClcclxuICBdKTtcclxuXHJcbiAgLy8gRXhpdCBwcm9jZXNzIHdpdGggYSBjb3JyZWN0IGNvZGVcclxuICBwcm9jZXNzLmV4aXQoZXhpdENvZGUpO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2h1dGRvd25DbGVhblVwXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBDb3JlIG1vZHVsZSBmb3IgaW5pdGlhbGl6aW5nIGFuZCBtYW5hZ2luZyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnRcclxuICogU2VydmVyLiBQcm92aWRlcyBmdW5jdGlvbmFsaXRpZXMgZm9yIGNvbmZpZ3VyaW5nIGV4cG9ydHMsIHNldHRpbmcgdXAgc2VydmVyXHJcbiAqIG9wZXJhdGlvbnMsIGxvZ2dpbmcsIHNjcmlwdHMgY2FjaGluZywgcmVzb3VyY2UgcG9vbGluZywgYW5kIGdyYWNlZnVsIHByb2Nlc3NcclxuICogY2xlYW51cC5cclxuICovXHJcblxyXG5pbXBvcnQgJ2NvbG9ycyc7XHJcblxyXG5pbXBvcnQgeyBjaGVja0FuZFVwZGF0ZUNhY2hlIH0gZnJvbSAnLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7XHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59IGZyb20gJy4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCB1cGRhdGVPcHRpb25zLCBtYXBUb05ld09wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbFxyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2h1dGRvd25DbGVhblVwIH0gZnJvbSAnLi9yZXNvdXJjZVJlbGVhc2UuanMnO1xyXG5cclxuaW1wb3J0IHNlcnZlciBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiB0aGUgY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHJlc291cmNlIHBvb2wgb2NjdXIgZHVyaW5nIHRoaXNcclxuICogc3RhZ2UuXHJcbiAqXHJcbiAqIFRoaXMgZnVuY3Rpb24gbXVzdCBiZSBjYWxsZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXRcclxuICogdXAgYSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW5pdE9wdGlvbnMgLSBUaGUgYGluaXRPcHRpb25zYCBvYmplY3QsIHdoaWNoIG1heVxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nXHJcbiAqIHZhbHVlcyB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdEV4cG9ydChpbml0T3B0aW9ucykge1xyXG4gIC8vIEluaXQgYW5kIHVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyhpbml0T3B0aW9ucyk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcblxyXG4gIC8vIEluaXQgdGhlIHBvb2xcclxuICBhd2FpdCBpbml0UG9vbChvcHRpb25zLnBvb2wsIG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoZXMgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MsIGVuc3VyaW5nIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlc1xyXG4gKiBhbmQgdGVybWluYXRpb24gb24gZXhpdCBzaWduYWxzLiBIYW5kbGVzICdleGl0JywgJ1NJR0lOVCcsICdTSUdURVJNJ1xyXG4gKiBhbmQgJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnNcclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LmApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0lOVCdcclxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHVEVSTSdcclxuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0hVUCdcclxuICBwcm9jZXNzLm9uKCdTSUdIVVAnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAndW5jYXVnaHRFeGNlcHRpb24nXHJcbiAgcHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCBhc3luYyAoZXJyb3IsIG5hbWUpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIC4uLnNlcnZlcixcclxuXHJcbiAgLy8gT3B0aW9uc1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgdXBkYXRlT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcblxyXG4gIC8vIEV4cG9ydGluZ1xyXG4gIGluaXRFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG5cclxuICAvLyBSZWxlYXNlXHJcbiAga2lsbFBvb2wsXHJcbiAgc2h1dGRvd25DbGVhblVwLFxyXG5cclxuICAvLyBMb2dzXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbDogZnVuY3Rpb24gKGxldmVsKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICBsZXZlbFxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgc2V0TG9nTGV2ZWwob3B0aW9ucy5sb2dnaW5nLmxldmVsKTtcclxuICB9LFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nOiBmdW5jdGlvbiAodG9Db25zb2xlKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICB0b0NvbnNvbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUNvbnNvbGVMb2dnaW5nKG9wdGlvbnMubG9nZ2luZy50b0NvbnNvbGUpO1xyXG4gIH0sXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmc6IGZ1bmN0aW9uIChkZXN0LCBmaWxlLCB0b0ZpbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGRlc3QsXHJcbiAgICAgICAgZmlsZSxcclxuICAgICAgICB0b0ZpbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUZpbGVMb2dnaW5nKFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZGVzdCxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLmZpbGUsXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy50b0ZpbGVcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG4iXSwibmFtZXMiOlsiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJiYXNlVVJJIiwiZGVlcENvcHkiLCJvYmpBcnIiLCJvYmpBcnJDb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwiT2JqZWN0IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiZml4Q29uc3RyIiwiY29uc3RyIiwiZml4ZWRDb25zdHIiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJpbmNsdWRlcyIsImZpeE91dGZpbGUiLCJ0eXBlIiwib3V0ZmlsZSIsImdldEFic29sdXRlUGF0aCIsInNwbGl0Iiwic2hpZnQiLCJmaXhUeXBlIiwibWltZVR5cGVzIiwiZm9ybWF0cyIsInZhbHVlcyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsInBhdGgiLCJpc0Fic29sdXRlIiwiam9pbiIsImdldEJhc2U2NCIsImlucHV0IiwiQnVmZmVyIiwiZnJvbSIsInRvU3RyaW5nIiwiZ2V0TmV3RGF0ZSIsIkRhdGUiLCJ0cmltIiwiZ2V0TmV3RGF0ZVRpbWUiLCJnZXRUaW1lIiwiaXNPYmplY3QiLCJpdGVtIiwiaXNPYmplY3RFbXB0eSIsImtleXMiLCJsZW5ndGgiLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwic29tZSIsInBhdHRlcm4iLCJ0ZXN0IiwibWVhc3VyZVRpbWUiLCJzdGFydCIsInByb2Nlc3MiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJyb3VuZE51bWJlciIsInZhbHVlIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsIk1hdGgiLCJwb3ciLCJyb3VuZCIsIndyYXBBcm91bmQiLCJjdXN0b21Db2RlIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiaXNDYWxsYmFjayIsImVuZHNXaXRoIiwicmVhZEZpbGVTeW5jIiwic3RhcnRzV2l0aCIsImNvbG9ycyIsImxvZ2dpbmciLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsInBhdGhUb0xvZyIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibG9nIiwiYXJncyIsIm5ld0xldmVsIiwidGV4dHMiLCJsZXZlbCIsInByZWZpeCIsIl9sb2dUb0ZpbGUiLCJjb25zb2xlIiwiYXBwbHkiLCJ1bmRlZmluZWQiLCJjb25jYXQiLCJsb2dXaXRoU3RhY2siLCJlcnJvciIsImN1c3RvbU1lc3NhZ2UiLCJtYWluTWVzc2FnZSIsIm1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInB1c2giLCJpbml0TG9nZ2luZyIsImxvZ2dpbmdPcHRpb25zIiwiZGVzdCIsImZpbGUiLCJzZXRMb2dMZXZlbCIsImVuYWJsZUNvbnNvbGVMb2dnaW5nIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJpc0ludGVnZXIiLCJleGlzdHNTeW5jIiwibWtkaXJTeW5jIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiX2luaXRPcHRpb25zIiwiZ2V0T3B0aW9ucyIsImdldENvcHkiLCJ1cGRhdGVPcHRpb25zIiwibmV3T3B0aW9ucyIsIl9tZXJnZU9wdGlvbnMiLCJtYXBUb05ld09wdGlvbnMiLCJvbGRPcHRpb25zIiwiZW50cmllcyIsInByb3BlcnRpZXNDaGFpbiIsInJlZHVjZSIsIm9iaiIsInByb3AiLCJpbmRleCIsImlzQWxsb3dlZENvbmZpZyIsImFsbG93RnVuY3Rpb25zIiwib2JqZWN0Q29uZmlnIiwiZXZhbCIsIkpTT04iLCJzdHJpbmdpZmllZE9wdGlvbnMiLCJfb3B0aW9uc1N0cmluZ2lmeSIsInBhcnNlZE9wdGlvbnMiLCJfIiwibmFtZSIsIm9yaWdpbmFsT3B0aW9ucyIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJFcnJvciIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwiX2dldFByb3RvY29sTW9kdWxlIiwiZ2V0IiwicmVzcG9uc2UiLCJyZXNwb25zZURhdGEiLCJvbiIsImNodW5rIiwidGV4dCIsImh0dHBzIiwiaHR0cCIsIkV4cG9ydEVycm9yIiwiY29uc3RydWN0b3IiLCJzdGF0dXNDb2RlIiwic3VwZXIiLCJ0aGlzIiwic2V0U3RhdHVzIiwic2V0RXJyb3IiLCJjYWNoZSIsImFjdGl2ZU1hbmlmZXN0Iiwic291cmNlcyIsImhjVmVyc2lvbiIsImNoZWNrQW5kVXBkYXRlQ2FjaGUiLCJoaWdoY2hhcnRzT3B0aW9ucyIsInNlcnZlclByb3h5T3B0aW9ucyIsImZldGNoZWRNb2R1bGVzIiwiZ2V0Q2FjaGVQYXRoIiwibWFuaWZlc3RQYXRoIiwic291cmNlUGF0aCIsInJlY3Vyc2l2ZSIsIl91cGRhdGVDYWNoZSIsInJlcXVlc3RVcGRhdGUiLCJtYW5pZmVzdCIsIm1vZHVsZXMiLCJtb2R1bGVNYXAiLCJtIiwibnVtYmVyT2ZNb2R1bGVzIiwibW9kdWxlTmFtZSIsImV4dHJhY3RWZXJzaW9uIiwiX3NhdmVDb25maWdUb01hbmlmZXN0IiwiZ2V0SGlnaGNoYXJ0c1ZlcnNpb24iLCJ1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiIsIm5ld1ZlcnNpb24iLCJjYWNoZVNvdXJjZXMiLCJpbmRleE9mIiwiZXh0cmFjdE1vZHVsZU5hbWUiLCJzY3JpcHRQYXRoIiwiX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdCIsInNjcmlwdCIsInNob3VsZFRocm93RXJyb3IiLCJuZXdNYW5pZmVzdCIsIndyaXRlRmlsZVN5bmMiLCJfZmV0Y2hTY3JpcHRzIiwicHJveHlBZ2VudCIsInByb3h5SG9zdCIsInByb3h5UG9ydCIsIkh0dHBzUHJveHlBZ2VudCIsImFnZW50IiwiYWxsRmV0Y2hQcm9taXNlcyIsImFsbCIsImMiLCJpIiwic2V0dXBIaWdoY2hhcnRzIiwiSGlnaGNoYXJ0cyIsImFuaW1PYmplY3QiLCJkdXJhdGlvbiIsImNyZWF0ZUNoYXJ0IiwiZXhwb3J0T3B0aW9ucyIsImN1c3RvbUxvZ2ljT3B0aW9ucyIsInNldE9wdGlvbnMiLCJtZXJnZSIsIndyYXAiLCJzZXRPcHRpb25zT2JqIiwiaXNSZW5kZXJDb21wbGV0ZSIsIkNoYXJ0IiwicHJvY2VlZCIsInVzZXJPcHRpb25zIiwiY2IiLCJleHBvcnRpbmciLCJlbmFibGVkIiwicGxvdE9wdGlvbnMiLCJzZXJpZXMiLCJsYWJlbCIsInRvb2x0aXAiLCJhbmltYXRpb24iLCJvbkhpZ2hjaGFydHNSZW5kZXIiLCJhZGRFdmVudCIsIlNlcmllcyIsImNoYXJ0IiwiYWRkaXRpb25hbE9wdGlvbnMiLCJGdW5jdGlvbiIsImZpbmFsT3B0aW9ucyIsImZpbmFsQ2FsbGJhY2siLCJkZWZhdWx0T3B0aW9ucyIsInRlbXBsYXRlIiwiYnJvd3NlciIsImNyZWF0ZUJyb3dzZXIiLCJwdXBwZXRlZXJBcmdzIiwiZW5hYmxlZERlYnVnIiwiZGVidWdPcHRpb25zIiwibGF1bmNoT3B0aW9ucyIsInVzZXJEYXRhRGlyIiwiaGFuZGxlU0lHSU5UIiwiaGFuZGxlU0lHVEVSTSIsImhhbmRsZVNJR0hVUCIsIndhaXRGb3JJbml0aWFsUGFnZSIsImRlZmF1bHRWaWV3cG9ydCIsInRyeUNvdW50Iiwib3BlbiIsImxhdW5jaCIsInNldFRpbWVvdXQiLCJjbG9zZUJyb3dzZXIiLCJjb25uZWN0ZWQiLCJjbG9zZSIsIm5ld1BhZ2UiLCJwb29sUmVzb3VyY2UiLCJwYWdlIiwic2V0Q2FjaGVFbmFibGVkIiwiX3NldFBhZ2VDb250ZW50IiwiX3NldFBhZ2VFdmVudHMiLCJpc0Nsb3NlZCIsImNsZWFyUGFnZSIsImhhcmRSZXNldCIsImdvdG8iLCJ3YWl0VW50aWwiLCJldmFsdWF0ZSIsImJvZHkiLCJpbm5lckhUTUwiLCJpZCIsIndvcmtDb3VudCIsImFkZFBhZ2VSZXNvdXJjZXMiLCJpbmplY3RlZFJlc291cmNlcyIsImluamVjdGVkSnMiLCJqcyIsImNvbnRlbnQiLCJmaWxlcyIsImlzTG9jYWwiLCJqc1Jlc291cmNlIiwiYWRkU2NyaXB0VGFnIiwiaW5qZWN0ZWRDc3MiLCJjc3MiLCJjc3NJbXBvcnRzIiwibWF0Y2giLCJjc3NJbXBvcnRQYXRoIiwiY3NzUmVzb3VyY2UiLCJhZGRTdHlsZVRhZyIsImNsZWFyUGFnZVJlc291cmNlcyIsInJlc291cmNlIiwiZGlzcG9zZSIsIm9sZENoYXJ0cyIsImNoYXJ0cyIsIm9sZENoYXJ0IiwiZGVzdHJveSIsInNjcmlwdHNUb1JlbW92ZSIsImdldEVsZW1lbnRzQnlUYWdOYW1lIiwic3R5bGVzVG9SZW1vdmUiLCJsaW5rc1RvUmVtb3ZlIiwiZWxlbWVudCIsInJlbW92ZSIsInNldENvbnRlbnQiLCJjc3NUZW1wbGF0ZSIsInN2Z1RlbXBsYXRlIiwicHVwcGV0ZWVyRXhwb3J0IiwiaXNTVkciLCJzaXplIiwic3ZnRWxlbWVudCIsInF1ZXJ5U2VsZWN0b3IiLCJjaGFydEhlaWdodCIsImJhc2VWYWwiLCJjaGFydFdpZHRoIiwic3R5bGUiLCJ6b29tIiwibWFyZ2luIiwieCIsInkiLCJfZ2V0Q2xpcFJlZ2lvbiIsInZpZXdwb3J0SGVpZ2h0IiwiYWJzIiwiY2VpbCIsInZpZXdwb3J0V2lkdGgiLCJyZXN1bHQiLCJzZXRWaWV3cG9ydCIsImRldmljZVNjYWxlRmFjdG9yIiwiX2NyZWF0ZVNWRyIsIl9jcmVhdGVJbWFnZSIsIl9jcmVhdGVQREYiLCIkZXZhbCIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsInRydW5jIiwib3V0ZXJIVE1MIiwiY2xpcCIsInJhY2UiLCJzY3JlZW5zaG90IiwiZW5jb2RpbmciLCJmdWxsUGFnZSIsIm9wdGltaXplRm9yU3BlZWQiLCJjYXB0dXJlQmV5b25kVmlld3BvcnQiLCJxdWFsaXR5Iiwib21pdEJhY2tncm91bmQiLCJfcmVzb2x2ZSIsImVtdWxhdGVNZWRpYVR5cGUiLCJwZGYiLCJwb29sU3RhdHMiLCJleHBvcnRzQXR0ZW1wdGVkIiwiZXhwb3J0c1BlcmZvcm1lZCIsImV4cG9ydHNEcm9wcGVkIiwiZXhwb3J0c0Zyb21TdmciLCJleHBvcnRzRnJvbU9wdGlvbnMiLCJleHBvcnRzRnJvbVN2Z0F0dGVtcHRzIiwiZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHMiLCJ0aW1lU3BlbnQiLCJ0aW1lU3BlbnRBdmVyYWdlIiwiaW5pdFBvb2wiLCJwb29sT3B0aW9ucyIsIlBvb2wiLCJfZmFjdG9yeSIsImFjcXVpcmVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlVGltZW91dE1pbGxpcyIsImRlc3Ryb3lUaW1lb3V0TWlsbGlzIiwiaWRsZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzIiwicmVhcEludGVydmFsTWlsbGlzIiwicHJvcGFnYXRlQ3JlYXRlRXJyb3IiLCJjbGVhclN0YXR1cyIsIl9ldmVudElkIiwiaW5pdGlhbFJlc291cmNlcyIsImFjcXVpcmUiLCJwcm9taXNlIiwicmVsZWFzZSIsImtpbGxQb29sIiwid29ya2VyIiwidXNlZCIsImRlc3Ryb3llZCIsInBvc3RXb3JrIiwid29ya2VySGFuZGxlIiwiZ2V0UG9vbEluZm8iLCJhY3F1aXJlQ291bnRlciIsInJlcXVlc3RJZCIsIndvcmtTdGFydCIsImV4cG9ydENvdW50ZXIiLCJleHBvcnRUaW1lIiwiZ2V0UG9vbFN0YXRzIiwiZ2V0UG9vbEluZm9KU09OIiwibnVtVXNlZCIsImF2YWlsYWJsZSIsIm51bUZyZWUiLCJhbGxDcmVhdGVkIiwicGVuZGluZ0FjcXVpcmVzIiwibnVtUGVuZGluZ0FjcXVpcmVzIiwicGVuZGluZ0NyZWF0ZXMiLCJudW1QZW5kaW5nQ3JlYXRlcyIsInBlbmRpbmdWYWxpZGF0aW9ucyIsIm51bVBlbmRpbmdWYWxpZGF0aW9ucyIsInBlbmRpbmdEZXN0cm95cyIsImFic29sdXRlQWxsIiwiY3JlYXRlIiwidXVpZCIsInJhbmRvbSIsInN0YXJ0RGF0ZSIsInZhbGlkYXRlIiwibWFpbkZyYW1lIiwiZGV0YWNoZWQiLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJzYW5pdGl6ZSIsIkpTRE9NIiwiRE9NUHVyaWZ5IiwiQUREX1RBR1MiLCJzaW5nbGVFeHBvcnQiLCJzdGFydEV4cG9ydCIsImRhdGEiLCJiYXRjaEV4cG9ydCIsImJhdGNoRnVuY3Rpb25zIiwicGFpciIsImJhdGNoUmVzdWx0cyIsImFsbFNldHRsZWQiLCJyZWFzb24iLCJpbWFnZU9wdGlvbnMiLCJlbmRDYWxsYmFjayIsImZpbGVDb250ZW50IiwiX2V4cG9ydEZyb21TdmciLCJfZXhwb3J0RnJvbU9wdGlvbnMiLCJnZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJzZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJpbnB1dFRvRXhwb3J0IiwiX3ByZXBhcmVFeHBvcnQiLCJfaGFuZGxlQ3VzdG9tTG9naWMiLCJfaGFuZGxlR2xvYmFsQW5kVGhlbWUiLCJfZmluZENoYXJ0U2l6ZSIsIm9wdGlvbnNDaGFydCIsIm9wdGlvbnNFeHBvcnRpbmciLCJnbG9iYWxPcHRpb25zQ2hhcnQiLCJnbG9iYWxPcHRpb25zRXhwb3J0aW5nIiwidGhlbWVPcHRpb25zQ2hhcnQiLCJ0aGVtZU9wdGlvbnNFeHBvcnRpbmciLCJzb3VyY2VIZWlnaHQiLCJzb3VyY2VXaWR0aCIsInBhcmFtIiwiX2hhbmRsZVJlc291cmNlcyIsImFsbG93ZWRQcm9wcyIsImhhbmRsZWRSZXNvdXJjZXMiLCJjb3JyZWN0UmVzb3VyY2VzIiwicHJvcE5hbWUiLCJvcHRpb25zTmFtZSIsInRpbWVySWRzIiwiYWRkVGltZXIiLCJjbGVhckFsbFRpbWVycyIsImNsZWFySW50ZXJ2YWwiLCJjbGVhclRpbWVvdXQiLCJsb2dFcnJvck1pZGRsZXdhcmUiLCJyZXF1ZXN0IiwibmV4dCIsInJldHVybkVycm9yTWlkZGxld2FyZSIsInN0YXR1cyIsImpzb24iLCJlcnJvck1pZGRsZXdhcmUiLCJhcHAiLCJ1c2UiLCJyYXRlTGltaXRpbmdNaWRkbGV3YXJlIiwicmF0ZUxpbWl0aW5nT3B0aW9ucyIsInJhdGVPcHRpb25zIiwibGltaXRlciIsInJhdGVMaW1pdCIsIndpbmRvd01zIiwibGltaXQiLCJkZWxheU1zIiwiaGFuZGxlciIsImZvcm1hdCIsInNlbmQiLCJkZWZhdWx0Iiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwiY29udGVudFR5cGVNaWRkbGV3YXJlIiwiY29udGVudFR5cGUiLCJoZWFkZXJzIiwicmVxdWVzdEJvZHlNaWRkbGV3YXJlIiwiY29ubmVjdGlvbiIsInJlbW90ZUFkZHJlc3MiLCJ2YWxpZGF0ZWRPcHRpb25zIiwicGFyYW1zIiwiZmlsZW5hbWUiLCJ2YWxpZGF0aW9uTWlkZGxld2FyZSIsInBvc3QiLCJyZXZlcnNlZE1pbWUiLCJwbmciLCJqcGVnIiwiZ2lmIiwicmVxdWVzdEV4cG9ydCIsInJlcXVlc3RDb3VudGVyIiwiY29ubmVjdGlvbkFib3J0ZWQiLCJzb2NrZXQiLCJoYWRFcnJvcnMiLCJoZWFkZXIiLCJhdHRhY2htZW50IiwiZXhwb3J0Um91dGVzIiwic2VydmVyU3RhcnRUaW1lIiwicGFja2FnZUZpbGUiLCJzdWNjZXNzUmF0ZXMiLCJyZWNvcmRJbnRlcnZhbCIsIndpbmRvd1NpemUiLCJfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSIsImEiLCJiIiwiX3N0YXJ0U3VjY2Vzc1JhdGUiLCJzZXRJbnRlcnZhbCIsInN0YXRzIiwic3VjY2Vzc1JhdGlvIiwiaGVhbHRoUm91dGVzIiwicGVyaW9kIiwibW92aW5nQXZlcmFnZSIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJzZXJ2ZXJWZXJzaW9uIiwiaGlnaGNoYXJ0c1ZlcnNpb24iLCJhdmVyYWdlRXhwb3J0VGltZSIsImF0dGVtcHRlZEV4cG9ydHMiLCJwZXJmb3JtZWRFeHBvcnRzIiwiZmFpbGVkRXhwb3J0cyIsInN1Y2Vzc1JhdGlvIiwidG9GaXhlZCIsInN2Z0V4cG9ydHMiLCJqc29uRXhwb3J0cyIsInN2Z0V4cG9ydHNBdHRlbXB0cyIsImpzb25FeHBvcnRzQXR0ZW1wdHMiLCJ1aVJvdXRlcyIsInNlbmRGaWxlIiwiYWNjZXB0UmFuZ2VzIiwidmVyc2lvbkNoYW5nZVJvdXRlcyIsImFkbWluVG9rZW4iLCJ0b2tlbiIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJPcHRpb25zIiwidXBsb2FkTGltaXRCeXRlcyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRTaXplIiwiZGlzYWJsZSIsImNvcnMiLCJtZXRob2RzIiwic2V0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsInN0YXRpYyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyIsImxpc3RlbiIsImNlcnQiLCJodHRwc1NlcnZlciIsImNsb3NlU2VydmVycyIsImRlbGV0ZSIsImdldFNlcnZlcnMiLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwiZW5hYmxlUmF0ZUxpbWl0aW5nIiwibWlkZGxld2FyZXMiLCJzaHV0ZG93bkNsZWFuVXAiLCJleGl0Q29kZSIsImV4aXQiLCJpbml0RXhwb3J0IiwiaW5pdE9wdGlvbnMiLCJfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMiLCJjb2RlIl0sIm1hcHBpbmdzIjoid2xCQTJCTyxNQUFNQSxZQUFZQyxJQUFh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 diff --git a/dist/index.esm.js b/dist/index.esm.js index b0acab16..b7fc6154 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -1,2 +1,2 @@ -import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import{readFile}from"fs/promises";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?e:join(__dirname,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(){return deepCopy(globalOptions)}function updateOptions(e,t=!1){return _mergeOptions(t?deepCopy(globalOptions):globalOptions,e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:join(__dirname,e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=await readFile(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; +import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?e:join(__dirname,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; //# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index f896f5e7..2e4e3aee 100644 --- a/dist/index.esm.js.map +++ b/dist/index.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? path : join(__dirname, path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object.\r\n *\r\n * @function getOptions\r\n *\r\n * @returns {Object} A reference to the global options object.\r\n */\r\nexport function getOptions() {\r\n return deepCopy(globalOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with the provided options.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(\r\n getCopy ? deepCopy(globalOptions) : globalOptions,\r\n newOptions\r\n );\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs, globalOptions.customLogic);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return global options\r\n return globalOptions;\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs, customLogicOptions) {\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && customLogicOptions.allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n customLogicOptions.allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n 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(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `exportingOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} exportingOptions - The `exportingOptions` object, which\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(exportingOptions, endCallback) {\r\n try {\r\n // Check if provided options is an object\r\n if (!isObject(exportingOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: exportingOptions.export,\r\n customLogic: exportingOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFile } from 'fs/promises';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 readFile(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = await readFile(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","join","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","updateOptions","newOptions","getCopy","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","resolve","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","exportingOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","readFile","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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CKA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMkB,cAAgB,CAC3BC,UAAW,CACT9B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFsD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbpG,KAAM,OACNqG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP7D,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbpG,KAAM,SAGVwG,OAAQ,CACN9D,MAAO,8BACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbpG,KAAM,SAGVyG,WAAY,CACV/D,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbpG,KAAM,WAGV0G,UAAW,CACThE,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbpG,KAAM,SAGV2G,YAAa,CACXjE,MAAO,CAAC,aAAc,kBAAmB,iBACzCsD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbpG,KAAM,cACN4G,aAAc,0DAGlBC,cAAe,CACbnE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? path : join(__dirname, path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getCopy=true] - Specifies whether to return a copied\r\n * object of the global options (`true`) or a reference to the global options\r\n * object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} A copy of the global options object, or a reference\r\n * to the global options object.\r\n */\r\nexport function getOptions(getCopy = true) {\r\n return getCopy ? deepCopy(globalOptions) : globalOptions;\r\n}\r\n\r\n/**\r\n * Updates a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(getOptions(getCopy), newOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return reference to the global options\r\n return getOptions(false);\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Get the allow flags for the custom logic check\r\n const { allowCodeExecution, allowFileResources } = getOptions().customLogic;\r\n\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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') ? false : true;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n path: getAbsolutePath(cssImportPath)\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `imageOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} imageOptions - The `imageOptions` object, which should\r\n * include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(imageOptions, endCallback) {\r\n try {\r\n // Check if provided options are in an object\r\n if (!isObject(imageOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: imageOptions.export,\r\n customLogic: imageOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 = readFileSync(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = readFileSync(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","join","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","getCopy","updateOptions","newOptions","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","resolve","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","imageOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"wiBA2BO,MAAMA,UAAYC,cAAc,IAAIC,IAAI,mBAAoBC,MA+B5D,SAASC,SAASC,GAEvB,GAAe,OAAXA,GAAqC,iBAAXA,EAC5B,OAAOA,EAIT,MAAMC,EAAaC,MAAMC,QAAQH,GAAU,GAAK,GAGhD,IAAK,MAAMI,KAAOJ,EACZK,OAAOC,UAAUC,eAAeC,KAAKR,EAAQI,KAC/CH,EAAWG,GAAOL,SAASC,EAAOI,KAKtC,OAAOH,CACT,CA2DO,SAASQ,UAAUC,GACxB,IAEE,MAAMC,EAAc,GAAGD,EAAOE,cAAcC,QAAQ,QAAS,WAQ7D,MALoB,UAAhBF,GACFA,EAAYC,cAIP,CAAC,QAAS,aAAc,WAAY,cAAcE,SACvDH,GAEEA,EACA,OACR,CAAI,MAEA,MAAO,OACR,CACH,CAYO,SAASI,WAAWC,EAAMC,GAO/B,MAAO,GALUC,gBAAgBD,GAAW,SACzCE,MAAM,KACNC,WAGmBJ,GACxB,CAaO,SAASK,QAAQL,EAAMC,EAAU,MAEtC,MAAMK,EAAY,CAChB,YAAa,MACb,aAAc,OACd,kBAAmB,MACnB,gBAAiB,OAIbC,EAAUlB,OAAOmB,OAAOF,GAG9B,GAAIL,EAAS,CACX,MAAMQ,EAAUR,EAAQE,MAAM,KAAKO,MAGnB,QAAZD,EACFT,EAAO,OACEO,EAAQT,SAASW,IAAYT,IAASS,IAC/CT,EAAOS,EAEV,CAGD,OAAOH,EAAUN,IAASO,EAAQI,MAAMC,GAAMA,IAAMZ,KAAS,KAC/D,CAYO,SAASE,gBAAgBW,GAC9B,OAAOC,WAAWD,GAAQA,EAAOE,KAAKpC,UAAWkC,EACnD,CAYO,SAASG,UAAUC,EAAOjB,GAE/B,MAAa,QAATA,GAA0B,OAARA,EACbkB,OAAOC,KAAKF,EAAO,QAAQG,SAAS,UAItCH,CACT,CAOO,SAASI,aAEd,OAAO,IAAIC,MAAOF,WAAWjB,MAAM,KAAK,GAAGoB,MAC7C,CAOO,SAASC,iBACd,OAAO,IAAIF,MAAOG,SACpB,CAWO,SAASC,SAASC,GACvB,MAAgD,oBAAzCtC,OAAOC,UAAU8B,SAAS5B,KAAKmC,EACxC,CAWO,SAASC,cAAcD,GAC5B,MACkB,iBAATA,IACNzC,MAAMC,QAAQwC,IACN,OAATA,GAC6B,IAA7BtC,OAAOwC,KAAKF,GAAMG,MAEtB,CAWO,SAASC,uBAAuBJ,GASrC,MARsB,CACpB,mDACA,uEACA,wEACA,uFACA,qEAGmBK,MAAMC,GAAYA,EAAQC,KAAKP,IACtD,CASO,SAASQ,cACd,MAAMC,EAAQC,QAAQC,OAAOC,SAC7B,MAAO,IAAMC,OAAOH,QAAQC,OAAOC,SAAWH,GAAS,GACzD,CAYO,SAASK,YAAYC,EAAOC,EAAY,GAC7C,MAAMC,EAAaC,KAAKC,IAAI,GAAIH,GAAa,GAC7C,OAAOE,KAAKE,OAAOL,EAAQE,GAAcA,CAC3C,CA6BO,SAASI,WAAWC,EAAYC,EAAoBC,GAAa,GACtE,GAAIF,GAAoC,iBAAfA,EAGvB,OAFAA,EAAaA,EAAW1B,QAET6B,SAAS,OAEfF,EACHF,WACEK,aAAanD,gBAAgB+C,GAAa,QAC1CC,EACAC,GAEF,MAEHA,IACAF,EAAWK,WAAW,eACrBL,EAAWK,WAAW,gBACtBL,EAAWK,WAAW,SACtBL,EAAWK,WAAW,UAGjB,IAAIL,OAINA,EAAWpD,QAAQ,KAAM,GAEpC,CCvXA,MAAM0D,OAAS,CAAC,MAAO,SAAU,OAAQ,OAAQ,SAG3CC,QAAU,CAEdC,WAAW,EACXC,QAAQ,EACRC,aAAa,EAEbC,UAAW,GAEXC,WAAY,CACV,CACEC,MAAO,QACPC,MAAOR,OAAO,IAEhB,CACEO,MAAO,UACPC,MAAOR,OAAO,IAEhB,CACEO,MAAO,SACPC,MAAOR,OAAO,IAEhB,CACEO,MAAO,UACPC,MAAOR,OAAO,IAEhB,CACEO,MAAO,YACPC,MAAOR,OAAO,MAkBb,SAASS,OAAOC,GACrB,MAAOC,KAAaC,GAASF,GAGvBJ,WAAEA,EAAUO,MAAEA,GAAUZ,QAG9B,GACe,IAAbU,IACc,IAAbA,GAAkBA,EAAWE,GAASA,EAAQP,EAAW/B,QAE1D,OAIF,MAAMuC,EAAS,GAAGhD,iBAAiBwC,EAAWK,EAAW,GAAGJ,WAGxDN,QAAQE,QACVY,WAAWH,EAAOE,GAIhBb,QAAQC,WACVc,QAAQP,IAAIQ,WACVC,EACA,CAACJ,EAAOjD,WAAWoC,QAAQK,WAAWK,EAAW,GAAGH,QAAQW,OAAOP,GAGzE,CAgBO,SAASQ,aAAaT,EAAUU,EAAOC,GAE5C,MAAMC,EAAcD,GAAkBD,GAASA,EAAMG,SAAY,IAG3DX,MAAEA,EAAKP,WAAEA,GAAeL,QAG9B,GAAiB,IAAbU,GAAkBA,EAAWE,GAASA,EAAQP,EAAW/B,OAC3D,OAIF,MAAMuC,EAAS,GAAGhD,iBAAiBwC,EAAWK,EAAW,GAAGJ,WAGtDkB,EAAeJ,GAASA,EAAMK,MAG9Bd,EAAQ,CAACW,GACXE,GACFb,EAAMe,KAAK,KAAMF,GAIfxB,QAAQE,QACVY,WAAWH,EAAOE,GAIhBb,QAAQC,WACVc,QAAQP,IAAIQ,WACVC,EACA,CAACJ,EAAOjD,WAAWoC,QAAQK,WAAWK,EAAW,GAAGH,QAAQW,OAAO,CACjEP,EAAM/D,QAAQmD,OAAOW,EAAW,OAC7BC,IAIX,CAUO,SAASgB,YAAYC,GAE1B,MAAMhB,MAAEA,EAAKiB,KAAEA,EAAIC,KAAEA,EAAI7B,UAAEA,EAASC,OAAEA,GAAW0B,EAGjD5B,QAAQG,aAAc,EACtBH,QAAQI,UAAY,GAGpB2B,YAAYnB,GAGZoB,qBAAqB/B,GAGrBgC,kBAAkBJ,EAAMC,EAAM5B,EAChC,CAUO,SAAS6B,YAAYnB,GAExB5B,OAAOkD,UAAUtB,IACjBA,GAAS,GACTA,GAASZ,QAAQK,WAAW/B,SAG5B0B,QAAQY,MAAQA,EAEpB,CASO,SAASoB,qBAAqB/B,GAEnCD,QAAQC,YAAcA,CACxB,CAaO,SAASgC,kBAAkBJ,EAAMC,EAAM5B,GAE5CF,QAAQE,SAAWA,EAGfF,QAAQE,SACVF,QAAQ6B,KAAOA,GAAQ,GACvB7B,QAAQ8B,KAAOA,GAAQ,GAE3B,CAYA,SAAShB,WAAWH,EAAOE,GACpBb,QAAQG,eAEVgC,WAAWzF,gBAAgBsD,QAAQ6B,QAClCO,UAAU1F,gBAAgBsD,QAAQ6B,OAGpC7B,QAAQI,UAAY1D,gBAAgBa,KAAKyC,QAAQ6B,KAAM7B,QAAQ8B,OAI/D9B,QAAQG,aAAc,GAIxBkC,WACErC,QAAQI,UACR,CAACS,GAAQK,OAAOP,GAAOpD,KAAK,KAAO,MAClC6D,IACKA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMkB,cAAgB,CAC3BC,UAAW,CACT9B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFsD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbpG,KAAM,OACNqG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP7D,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbpG,KAAM,SAGVwG,OAAQ,CACN9D,MAAO,8BACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbpG,KAAM,SAGVyG,WAAY,CACV/D,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbpG,KAAM,WAGV0G,UAAW,CACThE,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbpG,KAAM,SAGV2G,YAAa,CACXjE,MAAO,CAAC,aAAc,kBAAmB,iBACzCsD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbpG,KAAM,cACN4G,aAAc,0DAGlBC,cAAe,CACbnE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,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"} \ No newline at end of file diff --git a/lib/browser.js b/lib/browser.js index df625324..eafb1076 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -293,7 +293,7 @@ export async function addPageResources(page, customLogicOptions) { // Load scripts from all custom files if (resources.files) { for (const file of resources.files) { - const isLocal = !file.startsWith('http') ? true : false; + const isLocal = file.startsWith('http') ? false : true; // Add each custom script from resources' files injectedJs.push( @@ -341,7 +341,7 @@ export async function addPageResources(page, customLogicOptions) { }); } else if (customLogicOptions.allowFileResources) { injectedCss.push({ - path: join(__dirname, cssImportPath) + path: getAbsolutePath(cssImportPath) }); } } diff --git a/lib/cache.js b/lib/cache.js index e1987ccd..8971dd08 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -29,7 +29,7 @@ import { HttpsProxyAgent } from 'https-proxy-agent'; import { getOptions, updateOptions } from './config.js'; import { fetch } from './fetch.js'; import { log } from './logger.js'; -import { __dirname, getAbsolutePath } from './utils.js'; +import { getAbsolutePath } from './utils.js'; import ExportError from './errors/ExportError.js'; diff --git a/lib/chart.js b/lib/chart.js index 75332c48..6c0e6937 100644 --- a/lib/chart.js +++ b/lib/chart.js @@ -215,7 +215,7 @@ export async function batchExport(options) { } /** - * Starts an export process. The `exportingOptions` parameter is an object that + * Starts an export process. The `imageOptions` parameter is an object that * should include settings from the `export` and `customLogic` sections. It can * be a partial or complete set of options from these sections. If partial * options are provided, missing values will be merged with the current global @@ -230,8 +230,8 @@ export async function batchExport(options) { * @async * @function startExport * - * @param {Object} exportingOptions - The `exportingOptions` object, which - * should include settings from the `export` and `customLogic` sections. It can + * @param {Object} imageOptions - The `imageOptions` object, which should + * include settings from the `export` and `customLogic` sections. It can * be a partial or complete set of options from these sections. If the provided * options are partial, missing values will be merged with the current global * options. @@ -248,12 +248,12 @@ export async function batchExport(options) { * processing input of any type. The error is passed into the `endCallback` * function and processed there. */ -export async function startExport(exportingOptions, endCallback) { +export async function startExport(imageOptions, endCallback) { try { - // Check if provided options is an object - if (!isObject(exportingOptions)) { + // Check if provided options are in an object + if (!isObject(imageOptions)) { throw new ExportError( - '[chart] Incorrect value of the provided `exportingOptions`. Needs to be an object.', + '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.', 400 ); } @@ -261,8 +261,8 @@ export async function startExport(exportingOptions, endCallback) { // Merge additional options to the copy of the instance options const options = updateOptions( { - export: exportingOptions.export, - customLogic: exportingOptions.customLogic + export: imageOptions.export, + customLogic: imageOptions.customLogic }, true ); diff --git a/lib/config.js b/lib/config.js index 9be870d5..2260fa0c 100644 --- a/lib/config.js +++ b/lib/config.js @@ -33,18 +33,25 @@ import { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js'; const globalOptions = _initOptions(defaultConfig); /** - * Retrieves a copy of the global options object. + * Retrieves a copy of the global options object or a reference to the global + * options object, based on the `getCopy` flag. * * @function getOptions * - * @returns {Object} A reference to the global options object. + * @param {boolean} [getCopy=true] - Specifies whether to return a copied + * object of the global options (`true`) or a reference to the global options + * object (`false`). The default value is `false`. + * + * @returns {Object} A copy of the global options object, or a reference + * to the global options object. */ -export function getOptions() { - return deepCopy(globalOptions); +export function getOptions(getCopy = true) { + return getCopy ? deepCopy(globalOptions) : globalOptions; } /** - * Updates the global options with the provided options. + * Updates a copy of the global options object or a reference to the global + * options object, based on the `getCopy` flag. * * @function updateOptions * @@ -59,10 +66,7 @@ export function getOptions() { */ export function updateOptions(newOptions, getCopy = false) { // Merge new options to the global options or its copy and return the result - return _mergeOptions( - getCopy ? deepCopy(globalOptions) : globalOptions, - newOptions - ); + return _mergeOptions(getOptions(getCopy), newOptions); } /** @@ -88,7 +92,7 @@ export function setCliOptions(cliArgs) { // Only for the CLI usage if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) { // Get options from the custom JSON loaded via the `--loadConfig` - const configOptions = _loadConfigFile(cliArgs, globalOptions.customLogic); + const configOptions = _loadConfigFile(cliArgs); // Update global options with the values from the `configOptions` updateOptions(configOptions); @@ -100,8 +104,8 @@ export function setCliOptions(cliArgs) { updateOptions(cliOptions); } - // Return global options - return globalOptions; + // Return reference to the global options + return getOptions(false); } /** @@ -430,14 +434,15 @@ export function _optionsStringify(options, allowFunctions, stringifyFunctions) { * * @param {Array.} cliArgs - Command-line arguments to search * for the `--loadConfig` option and the corresponding file path. - * @param {Object} customLogicOptions - The configuration object containing - * `customLogic` options. * * @returns {Object} The additional configuration loaded from the specified * file, or an empty object if the file is not found, invalid, or an error * occurs. */ -function _loadConfigFile(cliArgs, customLogicOptions) { +function _loadConfigFile(cliArgs) { + // Get the allow flags for the custom logic check + const { allowCodeExecution, allowFileResources } = getOptions().customLogic; + // Check if the `--loadConfig` option was used const configIndex = cliArgs.findIndex( (arg) => arg.replace(/-/g, '') === 'loadConfig' @@ -447,13 +452,13 @@ function _loadConfigFile(cliArgs, customLogicOptions) { const configFileName = configIndex > -1 && cliArgs[configIndex + 1]; // Check if the `--loadConfig` is present and has a correct value - if (configFileName && customLogicOptions.allowFileResources) { + if (configFileName && allowFileResources) { try { // Load an optional custom JSON config file return isAllowedConfig( readFileSync(getAbsolutePath(configFileName), 'utf8'), false, - customLogicOptions.allowCodeExecution + allowCodeExecution ); } catch (error) { logWithStack( diff --git a/lib/prompt.js b/lib/prompt.js index d4ac3f6b..794d74b2 100644 --- a/lib/prompt.js +++ b/lib/prompt.js @@ -18,8 +18,7 @@ See LICENSE file in root for details. * a configuration file. */ -import { existsSync, readFileSync } from 'fs'; -import { writeFile } from 'fs/promises'; +import { existsSync, readFileSync, writeFileSync } from 'fs'; import prompts from 'prompts'; @@ -167,7 +166,7 @@ export async function manualConfig(configFileName, allowCodeExecution) { if (++questionsCounter === allQuestions.length) { try { // Save the prompt result - await writeFile( + writeFileSync( getAbsolutePath(configFileName), isAllowedConfig(configFile, true, allowCodeExecution), 'utf8' diff --git a/lib/server/server.js b/lib/server/server.js index 49e4f6a9..3a07e0b4 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -21,7 +21,7 @@ See LICENSE file in root for details. * middlewares. */ -import { readFile } from 'fs/promises'; +import { readFileSync } from 'fs'; import { join } from 'path'; import cors from 'cors'; @@ -169,13 +169,13 @@ export async function startServer(serverOptions) { try { // Get the SSL key - key = await readFile( + key = readFileSync( join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'), 'utf8' ); // Get the SSL certificate - cert = await readFile( + cert = readFileSync( join(getAbsolutePath(serverOptions.ssl.certPath), 'server.crt'), 'utf8' ); From 42ceaf78348d0fc0f291a7b35948264cb89ffe93 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Thu, 23 Jan 2025 01:22:59 +0100 Subject: [PATCH 083/102] Updated paths handling and other small corrections. --- README.md | 6 +++++- dist/index.cjs | 4 ++-- dist/index.esm.js | 2 +- dist/index.esm.js.map | 2 +- lib/utils.js | 4 ++-- package-lock.json | 20 +++++++++---------- ...=> doNotAllowGlobalAndThemeFromFiles.json} | 0 7 files changed, 21 insertions(+), 17 deletions(-) rename tests/http/scenarios/{globalAndThemeFromFiles.json => doNotAllowGlobalAndThemeFromFiles.json} (100%) diff --git a/README.md b/README.md index c09c9f53..79532e48 100644 --- a/README.md +++ b/README.md @@ -689,7 +689,7 @@ const customOptions = { // Logic must be triggered in an asynchronous function (async () => { // Must initialize exporting before being able to export charts - await exporter.initExport(options); + await exporter.initExport(); // Perform an export await exporter.startExport(options, async (error, data) => { @@ -851,6 +851,10 @@ Samples and tests for every mentioned export method can be found in the `./sampl Typing `highcharts-export-server --v` will display information about the current version of the Export Server, and `highcharts-export-server --h` will display information about available CLI options. +## Note About Paths + +All path-related options (such as those for loading additional resources and logic) can be either relative or absolute. If they are relative, they will be resolved based on the current working directory (the directory from which the Node.js process was started). This is especially important to remember when running a custom script in your application that imports the `highcharts-export-server` npm package and uses provided exporting API functions. + ## Note About Deprecated Options At some point during the transition process from the `PhantomJS` solution, certain options were deprecated. Here is a list of options that no longer work with the server based on `Puppeteer`: diff --git a/dist/index.cjs b/dist/index.cjs index c86a4b9b..62632696 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?e:path.join(__dirname$1,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIGpvaW4gfSBmcm9tICdwYXRoJztcclxuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCc7XHJcblxyXG5jb25zdCBNQVhfQkFDS09GRl9BVFRFTVBUUyA9IDY7XHJcblxyXG4vLyBUaGUgZGlyZWN0b3J5IHBhdGhcclxuZXhwb3J0IGNvbnN0IF9fZGlybmFtZSA9IGZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLi4vLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbmQgc3RhbmRhcmRpemVzIHRleHQgYnkgcmVwbGFjaW5nIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2VcclxuICogY2hhcmFjdGVycyB3aXRoIGEgc2luZ2xlIHNwYWNlIGFuZCB0cmltbWluZyBhbnkgbGVhZGluZyBvciB0cmFpbGluZ1xyXG4gKiB3aGl0ZXNwYWNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJUZXh0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0IC0gVGhlIGlucHV0IHRleHQgdG8gYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtSZWdFeHB9IFtydWxlPS9cXHNcXHMrL2ddIC0gVGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBydWxlIHRvIG1hdGNoXHJcbiAqIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgdGhlICcvXFxzXFxzKy9nJyBSZWdFeHAuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcmVwbGFjZXI9JyAnXSAtIFRoZSBzdHJpbmcgdXNlZCB0byByZXBsYWNlIG11bHRpcGxlXHJcbiAqIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2UgY2hhcmFjdGVycy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgdGhlICcgJyBzdHJpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjbGVhcmVkIGFuZCBzdGFuZGFyZGl6ZWQgdGV4dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhclRleHQodGV4dCwgcnVsZSA9IC9cXHNcXHMrL2csIHJlcGxhY2VyID0gJyAnKSB7XHJcbiAgcmV0dXJuIHRleHQucmVwbGFjZUFsbChydWxlLCByZXBsYWNlcikudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIGRlZXAgY29weSBvZiB0aGUgZ2l2ZW4gb2JqZWN0IG9yIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZGVlcENvcHlcclxuICpcclxuICogQHBhcmFtIHsoT2JqZWN0fEFycmF5KX0gb2JqQXJyIC0gVGhlIG9iamVjdCBvciBhcnJheSB0byBiZSBkZWVwbHkgY29waWVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxBcnJheSl9IFRoZSBkZWVwIGNvcHkgb2YgdGhlIHByb3ZpZGVkIG9iamVjdCBvciBhcnJheS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBkZWVwQ29weShvYmpBcnIpIHtcclxuICAvLyBJZiB0aGUgYG9iakFycmAgaXMgbnVsbCBvciBub3Qgb2YgdGhlIGBvYmplY3RgIHR5cGUsIHJldHVybiBpdFxyXG4gIGlmIChvYmpBcnIgPT09IG51bGwgfHwgdHlwZW9mIG9iakFyciAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmpBcnI7XHJcbiAgfVxyXG5cclxuICAvLyBQcmVwYXJlIGVpdGhlciBhIG5ldyBhcnJheSBvciBhIG5ldyBvYmplY3RcclxuICBjb25zdCBvYmpBcnJDb3B5ID0gQXJyYXkuaXNBcnJheShvYmpBcnIpID8gW10gOiB7fTtcclxuXHJcbiAgLy8gUmVjdXJzaXZlbHkgY29weSBlYWNoIHByb3BlcnR5XHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqQXJyKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iakFyciwga2V5KSkge1xyXG4gICAgICBvYmpBcnJDb3B5W2tleV0gPSBkZWVwQ29weShvYmpBcnJba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNvcGllZCBvYmplY3RcclxuICByZXR1cm4gb2JqQXJyQ29weTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEltcGxlbWVudHMgYW4gZXhwb25lbnRpYWwgYmFja29mZiBzdHJhdGVneSBmb3IgcmV0cnlpbmcgYSBmdW5jdGlvbiB1bnRpbFxyXG4gKiBhIGNlcnRhaW4gbnVtYmVyIG9mIGF0dGVtcHRzIGFyZSByZWFjaGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGV4cEJhY2tvZmZcclxuICpcclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZm4gLSBUaGUgZnVuY3Rpb24gdG8gYmUgcmV0cmllZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IFthdHRlbXB0PTBdIC0gVGhlIGN1cnJlbnQgYXR0ZW1wdCBudW1iZXIuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGAwYC5cclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0XHJcbiAqIG9mIHRoZSBmdW5jdGlvbiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwQmFja29mZihmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlcGVhdCwgdGhyb3cgYW4gZXJyb3JcclxuICAgIGlmICgrK2F0dGVtcHQgPj0gTUFYX0JBQ0tPRkZfQVRURU1QVFMpIHtcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxyXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc3BvbnNlKSA9PiBzZXRUaW1lb3V0KHJlc3BvbnNlLCBkZWxheUluTXMpKTtcclxuXHJcbiAgICAvLy8gVE8gRE86IENvcnJlY3RcclxuICAgIC8vIC8vIEluZm9ybWF0aW9uIGFib3V0IHRoZSByZXNvdXJjZSB0aW1lb3V0XHJcbiAgICAvLyBsb2coXHJcbiAgICAvLyAgIDMsXHJcbiAgICAvLyAgIGBbdXRpbHNdIFdhaXRlZCAke2RlbGF5SW5Nc31tcyB1bnRpbCBuZXh0IGNhbGwgZm9yIHRoZSByZXNvdXJjZSBvZiBJRDogJHthcmdzWzBdfS5gXHJcbiAgICAvLyApO1xyXG5cclxuICAgIC8vIFRyeSBhZ2FpblxyXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkanVzdHMgdGhlIGNvbnN0cnVjdG9yIG5hbWUgYnkgdHJhbnNmb3JtaW5nIGFuZCBub3JtYWxpemluZyBpdCBiYXNlZFxyXG4gKiBvbiBjb21tb24gY2hhcnQgdHlwZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhDb25zdHJcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnN0ciAtIFRoZSBvcmlnaW5hbCBjb25zdHJ1Y3RvciBuYW1lIHRvIGJlIGZpeGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGNvbnN0cnVjdG9yIG5hbWUsIG9yICdjaGFydCcgaWYgdGhlIGlucHV0XHJcbiAqIGlzIG5vdCByZWNvZ25pemVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeENvbnN0cihjb25zdHIpIHtcclxuICB0cnkge1xyXG4gICAgLy8gRml4IHRoZSBjb25zdHJ1Y3RvciBieSBsb3dlcmluZyBjYXNpbmdcclxuICAgIGNvbnN0IGZpeGVkQ29uc3RyID0gYCR7Y29uc3RyLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnY2hhcnQnLCAnJyl9Q2hhcnRgO1xyXG5cclxuICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgcmVzdWx0IGlzIGp1c3QgJ0NoYXJ0J1xyXG4gICAgaWYgKGZpeGVkQ29uc3RyID09PSAnQ2hhcnQnKSB7XHJcbiAgICAgIGZpeGVkQ29uc3RyLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IsIG90aGVyd2lzZSBkZWZhdWx0IHRvICdjaGFydCdcclxuICAgIHJldHVybiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddLmluY2x1ZGVzKFxyXG4gICAgICBmaXhlZENvbnN0clxyXG4gICAgKVxyXG4gICAgICA/IGZpeGVkQ29uc3RyXHJcbiAgICAgIDogJ2NoYXJ0JztcclxuICB9IGNhdGNoIHtcclxuICAgIC8vIERlZmF1bHQgdG8gJ2NoYXJ0JyBpbiBjYXNlIG9mIGFueSBlcnJvclxyXG4gICAgcmV0dXJuICdjaGFydCc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIG91dGZpbGUgYmFzZWQgb24gcHJvdmlkZWQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeE91dGZpbGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBvdXRmaWxlIC0gVGhlIGZpbGUgcGF0aCBvciBuYW1lLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIG91dGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4T3V0ZmlsZSh0eXBlLCBvdXRmaWxlKSB7XHJcbiAgLy8gR2V0IHRoZSBmaWxlIG5hbWUgZnJvbSB0aGUgYG91dGZpbGVgIG9wdGlvblxyXG4gIGNvbnN0IGZpbGVOYW1lID0gZ2V0QWJzb2x1dGVQYXRoKG91dGZpbGUgfHwgJ2NoYXJ0JylcclxuICAgIC5zcGxpdCgnLicpXHJcbiAgICAuc2hpZnQoKTtcclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCBvdXRmaWxlXHJcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke3R5cGV9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBmaXhUeXBlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW291dGZpbGU9bnVsbF0gLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuIFRoZSBkZWZhdWx0IHZhbHVlXHJcbiAqIGlzIGBudWxsYC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNvcnJlY3RlZCBleHBvcnQgdHlwZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhUeXBlKHR5cGUsIG91dGZpbGUgPSBudWxsKSB7XHJcbiAgLy8gTUlNRSB0eXBlc1xyXG4gIGNvbnN0IG1pbWVUeXBlcyA9IHtcclxuICAgICdpbWFnZS9wbmcnOiAncG5nJyxcclxuICAgICdpbWFnZS9qcGVnJzogJ2pwZWcnLFxyXG4gICAgJ2FwcGxpY2F0aW9uL3BkZic6ICdwZGYnLFxyXG4gICAgJ2ltYWdlL3N2Zyt4bWwnOiAnc3ZnJ1xyXG4gIH07XHJcblxyXG4gIC8vIEdldCBmb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IE9iamVjdC52YWx1ZXMobWltZVR5cGVzKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdHlwZSBhbmQgb3V0ZmlsZSdzIGV4dGVuc2lvbnMgYXJlIHRoZSBzYW1lXHJcbiAgaWYgKG91dGZpbGUpIHtcclxuICAgIGNvbnN0IG91dFR5cGUgPSBvdXRmaWxlLnNwbGl0KCcuJykucG9wKCk7XHJcblxyXG4gICAgLy8gU3VwcG9ydCB0aGUgSlBHIHR5cGVcclxuICAgIGlmIChvdXRUeXBlID09PSAnanBnJykge1xyXG4gICAgICB0eXBlID0gJ2pwZWcnO1xyXG4gICAgfSBlbHNlIGlmIChmb3JtYXRzLmluY2x1ZGVzKG91dFR5cGUpICYmIHR5cGUgIT09IG91dFR5cGUpIHtcclxuICAgICAgdHlwZSA9IG91dFR5cGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IHR5cGVcclxuICByZXR1cm4gbWltZVR5cGVzW3R5cGVdIHx8IGZvcm1hdHMuZmluZCgodCkgPT4gdCA9PT0gdHlwZSkgfHwgJ3BuZyc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIHBhdGggaXMgcmVsYXRpdmUgb3IgYWJzb2x1dGUgYW5kIHJldHVybnMgdGhlIGNvcnJlY3RlZCxcclxuICogYWJzb2x1dGUgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzQWJzb2x1dGVQYXRoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gYmUgY2hlY2tlZCBvbi5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGguXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWJzb2x1dGVQYXRoKHBhdGgpIHtcclxuICByZXR1cm4gaXNBYnNvbHV0ZShwYXRoKSA/IHBhdGggOiBqb2luKF9fZGlybmFtZSwgcGF0aCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBpbnB1dCBkYXRhIHRvIGEgQmFzZTY0IHN0cmluZyBiYXNlZCBvbiB0aGUgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRCYXNlNjRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IC0gVGhlIGlucHV0IHRvIGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmb3JtYXQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQmFzZTY0IHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QmFzZTY0KGlucHV0LCB0eXBlKSB7XHJcbiAgLy8gRm9yIHBkZiBhbmQgc3ZnIHR5cGVzIHRoZSBpbnB1dCBtdXN0IGJlIHRyYW5zZm9ybWVkIHRvIEJhc2U2NCBmcm9tIGEgYnVmZmVyXHJcbiAgaWYgKHR5cGUgPT09ICdwZGYnIHx8IHR5cGUgPT0gJ3N2ZycpIHtcclxuICAgIHJldHVybiBCdWZmZXIuZnJvbShpbnB1dCwgJ3V0ZjgnKS50b1N0cmluZygnYmFzZTY0Jyk7XHJcbiAgfVxyXG5cclxuICAvLyBGb3IgcG5nIGFuZCBqcGVnIGlucHV0IGlzIGFscmVhZHkgYSBCYXNlNjQgc3RyaW5nXHJcbiAgcmV0dXJuIGlucHV0O1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyBzdHJpbmdpZmllZCBkYXRlIHdpdGhvdXQgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGUoKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgR01UIHRleHQgaW5mb3JtYXRpb25cclxuICByZXR1cm4gbmV3IERhdGUoKS50b1N0cmluZygpLnNwbGl0KCcoJylbMF0udHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgc3RvcmVkIHRpbWUgdmFsdWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0TmV3RGF0ZVRpbWVcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXROZXdEYXRlVGltZSgpIHtcclxuICByZXR1cm4gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIGl0ZW0gaXMgYW4gb2JqZWN0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNPYmplY3RcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIGl0ZW0gdG8gYmUgY2hlY2tlZC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgdGhlIGl0ZW0gaXMgYW4gb2JqZWN0LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3QoaXRlbSkge1xyXG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoaXRlbSkgPT09ICdbb2JqZWN0IE9iamVjdF0nO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdEVtcHR5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGVtcHR5LCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNPYmplY3RFbXB0eShpdGVtKSB7XHJcbiAgcmV0dXJuIChcclxuICAgIHR5cGVvZiBpdGVtID09PSAnb2JqZWN0JyAmJlxyXG4gICAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICAgIGl0ZW0gIT09IG51bGwgJiZcclxuICAgIE9iamVjdC5rZXlzKGl0ZW0pLmxlbmd0aCA9PT0gMFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCBpbiB0aGUgZ2l2ZW4gc3RyaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaXRlbSAtIFRoZSBzdHJpbmcgdG8gYmUgY2hlY2tlZCBmb3IgYSBwcml2YXRlIElQIHJhbmdlIFVSTC5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoaXRlbSkge1xyXG4gIGNvbnN0IHJlZ2V4UGF0dGVybnMgPSBbXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/bG9jYWxob3N0XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTI3XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xNzJcXC4oMVs2LTldfDJbMC05XXwzWzAtMV0pXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTkyXFwuMTY4XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi9cclxuICBdO1xyXG5cclxuICByZXR1cm4gcmVnZXhQYXR0ZXJucy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3QoaXRlbSkpO1xyXG59XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIGVsYXBzZWQgdGltZSB1c2luZyB0aGUgTm9kZS5qcyBgcHJvY2Vzcy5ocnRpbWUoKWAgbWV0aG9kLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbWVhc3VyZVRpbWVcclxuICpcclxuICogQHJldHVybnMge0Z1bmN0aW9ufSBBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZWxhcHNlZCB0aW1lIGluIG1pbGxpc2Vjb25kcy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBtZWFzdXJlVGltZSgpIHtcclxuICBjb25zdCBzdGFydCA9IHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO1xyXG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xyXG59XHJcblxyXG4vKipcclxuICogUm91bmRzIGEgbnVtYmVyIHRvIHRoZSBzcGVjaWZpZWQgcHJlY2lzaW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcm91bmROdW1iZXJcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IHZhbHVlIC0gVGhlIG51bWJlciB0byBiZSByb3VuZGVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcHJlY2lzaW9uIC0gVGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyB0byByb3VuZCB0by5cclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gVGhlIHJvdW5kZWQgbnVtYmVyLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHJvdW5kTnVtYmVyKHZhbHVlLCBwcmVjaXNpb24gPSAxKSB7XHJcbiAgY29uc3QgbXVsdGlwbGllciA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24gfHwgMCk7XHJcbiAgcmV0dXJuIE1hdGgucm91bmQoK3ZhbHVlICogbXVsdGlwbGllcikgLyBtdWx0aXBsaWVyO1xyXG59XHJcblxyXG4vKipcclxuICogQ29udmVydHMgYSB2YWx1ZSB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB0b0Jvb2xlYW5cclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBpdGVtIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCB0byBhIGJvb2xlYW4uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUaGUgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQgdmFsdWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdG9Cb29sZWFuKGl0ZW0pIHtcclxuICByZXR1cm4gWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBXcmFwcyBjdXN0b20gY29kZSB0byBleGVjdXRlIGl0IHNhZmVseS5cclxuICpcclxuICogQGZ1bmN0aW9uIHdyYXBBcm91bmRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tIGNvZGUgdG8gYmUgd3JhcHBlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBGbGFnIHRvIGFsbG93IGxvYWRpbmcgY29kZSBmcm9tIGEgZmlsZS5cclxuICogQHBhcmFtIHtib29sZWFufSBbaXNDYWxsYmFjaz1mYWxzZV0gLSBGbGFnIHRoYXQgaW5kaWNhdGVzIHRoZSByZXR1cm5lZCBjb2RlXHJcbiAqIG11c3QgYmUgaW4gYSBjYWxsYmFjayBmb3JtYXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoc3RyaW5nfG51bGwpfSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBudWxsIGlmIHdyYXBwaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHdyYXBBcm91bmQoY3VzdG9tQ29kZSwgYWxsb3dGaWxlUmVzb3VyY2VzLCBpc0NhbGxiYWNrID0gZmFsc2UpIHtcclxuICBpZiAoY3VzdG9tQ29kZSAmJiB0eXBlb2YgY3VzdG9tQ29kZSA9PT0gJ3N0cmluZycpIHtcclxuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcclxuXHJcbiAgICBpZiAoY3VzdG9tQ29kZS5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgICAgLy8gTG9hZCBhIGZpbGUgaWYgdGhlIGZpbGUgcmVzb3VyY2VzIGFyZSBhbGxvd2VkXHJcbiAgICAgIHJldHVybiBhbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgICA/IHdyYXBBcm91bmQoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY3VzdG9tQ29kZSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgICAgaXNDYWxsYmFja1xyXG4gICAgICAgICAgKVxyXG4gICAgICAgIDogbnVsbDtcclxuICAgIH0gZWxzZSBpZiAoXHJcbiAgICAgICFpc0NhbGxiYWNrICYmXHJcbiAgICAgIChjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24gKCknKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFRyZWF0IGEgZnVuY3Rpb24gYXMgYSBzZWxmLWludm9raW5nIGV4cHJlc3Npb25cclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gT3IgcmV0dXJuIGFzIGEgc3RyaW5naWZpZWQgY29kZVxyXG4gICAgcmV0dXJuIGN1c3RvbUNvZGUucmVwbGFjZSgvOyQvLCAnJyk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgX19kaXJuYW1lLFxyXG4gIGNsZWFyVGV4dCxcclxuICBkZWVwQ29weSxcclxuICBleHBCYWNrb2ZmLFxyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBnZXROZXdEYXRlLFxyXG4gIGdldE5ld0RhdGVUaW1lLFxyXG4gIGlzT2JqZWN0LFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBtZWFzdXJlVGltZSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQSBtb2R1bGUgZm9yIG1hbmFnaW5nIGxvZ2dpbmcgZnVuY3Rpb25hbGl0eSB3aXRoIGN1c3RvbWl6YWJsZVxyXG4gKiBsb2cgbGV2ZWxzLCBjb25zb2xlIGFuZCBmaWxlIGxvZ2dpbmcgb3B0aW9ucywgYW5kIGVycm9yIGhhbmRsaW5nIHN1cHBvcnQuXHJcbiAqIFRoZSBtb2R1bGUgYWxzbyBlbnN1cmVzIHRoYXQgZmlsZS1iYXNlZCBsb2dzIGFyZSBzdG9yZWQgaW4gYSBzdHJ1Y3R1cmVkXHJcbiAqIGRpcmVjdG9yeSwgY3JlYXRpbmcgdGhlIG5lY2Vzc2FyeSBwYXRocyBhdXRvbWF0aWNhbGx5IGlmIHRoZXkgZG8gbm90IGV4aXN0LlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWJzb2x1dGVQYXRoLCBnZXROZXdEYXRlIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG4vLyBUaGUgYXZhaWxhYmxlIGNvbG9yc1xyXG5jb25zdCBjb2xvcnMgPSBbJ3JlZCcsICd5ZWxsb3cnLCAnYmx1ZScsICdncmF5JywgJ2dyZWVuJ107XHJcblxyXG4vLyBUaGUgZGVmYXVsdCBsb2dnaW5nIGNvbmZpZ1xyXG5jb25zdCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBGdWxsIHBhdGggdG8gdGhlIGxvZyBmaWxlXHJcbiAgcGF0aFRvTG9nOiAnJyxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdXHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhIG1lc3NhZ2Ugd2l0aCBhIHNwZWNpZmllZCBsb2cgbGV2ZWwuIEFjY2VwdHMgYSB2YXJpYWJsZSBudW1iZXJcclxuICogb2YgYXJndW1lbnRzLiBUaGUgYXJndW1lbnRzIGFmdGVyIHRoZSBgbGV2ZWxgIGFyZSBwYXNzZWQgdG8gYGNvbnNvbGUubG9nYFxyXG4gKiBhbmQvb3IgdXNlZCB0byBjb25zdHJ1Y3QgYW5kIGFwcGVuZCBtZXNzYWdlcyB0byBhIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Li4udW5rbm93bn0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZ1xyXG4gKiBsZXZlbCBhbmQgdGhlIHJlbWFpbmluZyBhcmUgc3RyaW5ncyB1c2VkIHRvIGJ1aWxkIHRoZSBsb2cgbWVzc2FnZS5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZyguLi5hcmdzKSB7XHJcbiAgY29uc3QgW25ld0xldmVsLCAuLi50ZXh0c10gPSBhcmdzO1xyXG5cclxuICAvLyBDdXJyZW50IGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgbGV2ZWxzRGVzYywgbGV2ZWwgfSA9IGxvZ2dpbmc7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZSBvciBpcyBpdCBhIGJlbmNobWFyayBsb2dcclxuICBpZiAoXHJcbiAgICBuZXdMZXZlbCAhPT0gNSAmJlxyXG4gICAgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aClcclxuICApIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtnZXROZXdEYXRlKCl9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgYW4gZXJyb3IgbWVzc2FnZSBhbG9uZyB3aXRoIGl0cyBzdGFjayB0cmFjZS4gT3B0aW9uYWxseSwgYSBjdXN0b20gbWVzc2FnZVxyXG4gKiBjYW4gYmUgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dXaXRoU3RhY2tcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IG5ld0xldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHN0YWNrIHRyYWNlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGluY2x1ZGVkXHJcbiAqIGluIHRoZSBsb2cgYWxvbmdzaWRlIHRoZSBlcnJvci5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IEV4aXRzIHRoZSBmdW5jdGlvbiBleGVjdXRpb24gaWYgYXR0ZW1wdGluZyB0byBsb2cgYXQgYSBsZXZlbFxyXG4gKiBoaWdoZXIgdGhhbiBhbGxvd2VkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGxvZ1dpdGhTdGFjayhuZXdMZXZlbCwgZXJyb3IsIGN1c3RvbU1lc3NhZ2UpIHtcclxuICAvLyBHZXQgdGhlIG1haW4gbWVzc2FnZVxyXG4gIGNvbnN0IG1haW5NZXNzYWdlID0gY3VzdG9tTWVzc2FnZSB8fCAoZXJyb3IgJiYgZXJyb3IubWVzc2FnZSkgfHwgJyc7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlXHJcbiAgaWYgKG5ld0xldmVsID09PSAwIHx8IG5ld0xldmVsID4gbGV2ZWwgfHwgbGV2ZWwgPiBsZXZlbHNEZXNjLmxlbmd0aCkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIEFkZCB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9IGVycm9yICYmIGVycm9yLnN0YWNrO1xyXG5cclxuICAvLyBDb21iaW5lIGN1c3RvbSBtZXNzYWdlIG9yIGVycm9yIG1lc3NhZ2Ugd2l0aCBlcnJvciBzdGFjayBtZXNzYWdlLCBpZiBleGlzdHNcclxuICBjb25zdCB0ZXh0cyA9IFttYWluTWVzc2FnZV07XHJcbiAgaWYgKHN0YWNrTWVzc2FnZSkge1xyXG4gICAgdGV4dHMucHVzaCgnXFxuJywgc3RhY2tNZXNzYWdlKTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgdGV4dHMuc2hpZnQoKVtjb2xvcnNbbmV3TGV2ZWwgLSAxXV0sXHJcbiAgICAgICAgLi4udGV4dHNcclxuICAgICAgXSlcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgbG9nZ2luZyBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaW5pdExvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGxvZ2dpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGxvZ2dpbmdgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaW5pdExvZ2dpbmcobG9nZ2luZ09wdGlvbnMpIHtcclxuICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBgbG9nZ2luZ09wdGlvbnNgIG9iamVjdFxyXG4gIGNvbnN0IHsgbGV2ZWwsIGRlc3QsIGZpbGUsIHRvQ29uc29sZSwgdG9GaWxlIH0gPSBsb2dnaW5nT3B0aW9ucztcclxuXHJcbiAgLy8gUmVzZXQgZmxhZ3MgdG8gdGhlIGRlZmF1bHQgdmFsdWVzXHJcbiAgbG9nZ2luZy5wYXRoQ3JlYXRlZCA9IGZhbHNlO1xyXG4gIGxvZ2dpbmcucGF0aFRvTG9nID0gJyc7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nZ2luZyBsZXZlbFxyXG4gIHNldExvZ0xldmVsKGxldmVsKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb25zb2xlIGxvZ2dpbmdcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpO1xyXG5cclxuICAvLyBTZXQgdGhlIGZpbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKGAwYCA9IG5vIGxvZ2dpbmcsXHJcbiAqIGAxYCA9IGVycm9yLCBgMmAgPSB3YXJuaW5nLCBgM2AgPSBub3RpY2UsIGA0YCA9IHZlcmJvc2UsIG9yIGA1YCA9IGJlbmNobWFyaykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRMb2dMZXZlbFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgbG9nIGxldmVsIHRvIGJlIHNldC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRMb2dMZXZlbChsZXZlbCkge1xyXG4gIGlmIChcclxuICAgIE51bWJlci5pc0ludGVnZXIobGV2ZWwpICYmXHJcbiAgICBsZXZlbCA+PSAwICYmXHJcbiAgICBsZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoXHJcbiAgKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYGxldmVsYCBvcHRpb25cclxuICAgIGxvZ2dpbmcubGV2ZWwgPSBsZXZlbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGNvbnNvbGUgbG9nZ2luZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUNvbnNvbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9Db25zb2xlIC0gVGhlIGZsYWcgZm9yIHNldHRpbmcgdGhlIGxvZ2dpbmcgdG8gdGhlIGNvbnNvbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmcodG9Db25zb2xlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0NvbnNvbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9Db25zb2xlID0gISF0b0NvbnNvbGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlIG5hbWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVGaWxlTG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZGVzdCAtIFRoZSBkZXN0aW5hdGlvbiBwYXRoIHdoZXJlIHRoZSBsb2cgZmlsZSBzaG91bGRcclxuICogYmUgc2F2ZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlIC0gVGhlIG5hbWUgb2YgdGhlIGxvZyBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvRmlsZSAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9nZ2luZyBzaG91bGRcclxuICogYmUgZGlyZWN0ZWQgdG8gYSBmaWxlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgdG9GaWxlYCBvcHRpb25cclxuICBsb2dnaW5nLnRvRmlsZSA9ICEhdG9GaWxlO1xyXG5cclxuICAvLyBTZXQgdGhlIGBkZXN0YCBhbmQgYGZpbGVgIG9wdGlvbnMgb25seSBpZiB0aGUgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWRcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIGxvZ2dpbmcuZGVzdCA9IGRlc3QgfHwgJyc7XHJcbiAgICBsb2dnaW5nLmZpbGUgPSBmaWxlIHx8ICcnO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgdGhlIHByb3ZpZGVkIHRleHRzIHRvIGEgZmlsZSwgaWYgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWQuIEl0IGNyZWF0ZXNcclxuICogdGhlIG5lY2Vzc2FyeSBkaXJlY3Rvcnkgc3RydWN0dXJlIGlmIG5vdCBhbHJlYWR5IGNyZWF0ZWQgYW5kIGFwcGVuZHNcclxuICogdGhlIGNvbnRlbnQsIGluY2x1ZGluZyBhbiBvcHRpb25hbCBwcmVmaXgsIHRvIHRoZSBzcGVjaWZpZWQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9nVG9GaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHRleHRzIC0gQW4gYXJyYXkgb2YgdGV4dHMgdG8gYmUgbG9nZ2VkLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4IC0gQW4gb3B0aW9uYWwgcHJlZml4IHRvIGJlIGFkZGVkIHRvIGVhY2ggbG9nIGVudHJ5LlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KSB7XHJcbiAgaWYgKCFsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAvLyBDcmVhdGUgaWYgZG9lcyBub3QgZXhpc3RcclxuICAgICFleGlzdHNTeW5jKGdldEFic29sdXRlUGF0aChsb2dnaW5nLmRlc3QpKSAmJlxyXG4gICAgICBta2RpclN5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpO1xyXG5cclxuICAgIC8vIENyZWF0ZSB0aGUgZnVsbCBwYXRoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyA9IGdldEFic29sdXRlUGF0aChqb2luKGxvZ2dpbmcuZGVzdCwgbG9nZ2luZy5maWxlKSk7XHJcblxyXG4gICAgLy8gV2Ugbm93IGFzc3VtZSB0aGUgcGF0aCBpcyBhdmFpbGFibGUsIGUuZy4gaXQncyB0aGUgcmVzcG9uc2liaWxpdHlcclxuICAgIC8vIG9mIHRoZSB1c2VyIHRvIGNyZWF0ZSB0aGUgcGF0aCB3aXRoIHRoZSBjb3JyZWN0IGFjY2VzcyByaWdodHMuXHJcbiAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICB9XHJcblxyXG4gIC8vIEFkZCB0aGUgY29udGVudCB0byBhIGZpbGVcclxuICBhcHBlbmRGaWxlKFxyXG4gICAgbG9nZ2luZy5wYXRoVG9Mb2csXHJcbiAgICBbcHJlZml4XS5jb25jYXQodGV4dHMpLmpvaW4oJyAnKSArICdcXG4nLFxyXG4gICAgKGVycm9yKSA9PiB7XHJcbiAgICAgIGlmIChlcnJvciAmJiBsb2dnaW5nLnRvRmlsZSAmJiBsb2dnaW5nLnBhdGhDcmVhdGVkKSB7XHJcbiAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmcsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmdcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvbmZpZ3VyYXRpb24gbWFuYWdlbWVudCBtb2R1bGUgZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuXHJcbiAqIFByb3ZpZGVzIGRlZmF1bHQgY29uZmlndXJhdGlvbnMgdGhhdCBzdXBwb3J0IGVudmlyb25tZW50IHZhcmlhYmxlcywgQ0xJXHJcbiAqIGFyZ3VtZW50cywgYW5kIGludGVyYWN0aXZlIHByb21wdHMgZm9yIGN1c3RvbWl6YXRpb24gb2Ygb3B0aW9ucyBhbmQgZmVhdHVyZXMuXHJcbiAqIEFkZGl0aW9uYWxseSwgaXQgbWFwcyBsZWdhY3kgb3B0aW9ucyB0byBtb2Rlcm4gc3RydWN0dXJlcywgZ2VuZXJhdGVzIG5lc3RlZFxyXG4gKiBhcmd1bWVudCBtYXBwaW5ncywgYW5kIGRpc3BsYXlzIENMSSB1c2FnZSBpbmZvcm1hdGlvbi5cclxuICovXHJcblxyXG4vKipcclxuICogVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYWxsIGF2YWlsYWJsZSBvcHRpb25zLCBvcmdhbml6ZWRcclxuICogYnkgc2VjdGlvbnMuXHJcbiAqXHJcbiAqIFRoaXMgb2JqZWN0IGluY2x1ZGVzOlxyXG4gKiAtIERlZmF1bHQgdmFsdWVzIGZvciBlYWNoIG9wdGlvblxyXG4gKiAtIERhdGEgdHlwZXMgZm9yIHZhbGlkYXRpb25cclxuICogLSBOYW1lcyBvZiBjb3JyZXNwb25kaW5nIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiAtIERlc2NyaXB0aW9ucyBvZiBlYWNoIHByb3BlcnR5XHJcbiAqIC0gSW5mb3JtYXRpb24gdXNlZCBmb3IgcHJvbXB0cyBpbiBpbnRlcmFjdGl2ZSBjb25maWd1cmF0aW9uXHJcbiAqIC0gW09wdGlvbmFsXSBDb3JyZXNwb25kaW5nIENMSSBhcmd1bWVudCBuYW1lcyBmb3IgQ0xJIHVzYWdlXHJcbiAqIC0gW09wdGlvbmFsXSBMZWdhY3kgbmFtZXMgZnJvbSB0aGUgcHJldmlvdXMgUGhhbnRvbUpTLWJhc2VkIHNlcnZlclxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZmF1bHRDb25maWcgPSB7XHJcbiAgcHVwcGV0ZWVyOiB7XHJcbiAgICBhcmdzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJy0tYWxsb3ctcnVubmluZy1pbnNlY3VyZS1jb250ZW50JyxcclxuICAgICAgICAnLS1hc2gtbm8tbnVkZ2VzJyxcclxuICAgICAgICAnLS1hdXRvcGxheS1wb2xpY3k9dXNlci1nZXN0dXJlLXJlcXVpcmVkJyxcclxuICAgICAgICAnLS1ibG9jay1uZXctd2ViLWNvbnRlbnRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWFjY2VsZXJhdGVkLTJkLWNhbnZhcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLW5ldHdvcmtpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1icmVha3BhZCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jaGVja2VyLWltYWdpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC1leHRlbnNpb25zLXdpdGgtYmFja2dyb3VuZC1wYWdlcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtdXBkYXRlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRlZmF1bHQtYXBwcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZXYtc2htLXVzYWdlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRvbWFpbi1yZWxpYWJpbGl0eScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1leHRlbnNpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWZlYXR1cmVzPUNhbGN1bGF0ZU5hdGl2ZVdpbk9jY2x1c2lvbixJbnRlcmVzdEZlZWRDb250ZW50U3VnZ2VzdGlvbnMsV2ViT1RQJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1sb2dnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXBvcHVwLWJsb2NraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1yZW5kZXJlci1iYWNrZ3JvdW5kaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlYXJjaC1lbmdpbmUtY2hvaWNlLXNjcmVlbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNpdGUtaXNvbGF0aW9uLXRyaWFscycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zcGVlY2gtYXBpJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXN5bmMnLFxyXG4gICAgICAgICctLWVuYWJsZS11bnNhZmUtd2ViZ3B1JyxcclxuICAgICAgICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcclxuICAgICAgICAnLS1oaWRlLXNjcm9sbGJhcnMnLFxyXG4gICAgICAgICctLW1ldHJpY3MtcmVjb3JkaW5nLW9ubHknLFxyXG4gICAgICAgICctLW11dGUtYXVkaW8nLFxyXG4gICAgICAgICctLW5vLWRlZmF1bHQtYnJvd3Nlci1jaGVjaycsXHJcbiAgICAgICAgJy0tbm8tZmlyc3QtcnVuJyxcclxuICAgICAgICAnLS1uby1waW5ncycsXHJcbiAgICAgICAgJy0tbm8tc2FuZGJveCcsXHJcbiAgICAgICAgJy0tbm8tc3RhcnR1cC13aW5kb3cnLFxyXG4gICAgICAgICctLW5vLXp5Z290ZScsXHJcbiAgICAgICAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxyXG4gICAgICAgICctLXByb2Nlc3MtcGVyLXRhYicsXHJcbiAgICAgICAgJy0tdXNlLW1vY2sta2V5Y2hhaW4nXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdQVVBQRVRFRVJfQVJHUycsXHJcbiAgICAgIGNsaU5hbWU6ICdwdXBwZXRlZXJBcmdzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBcnJheSBvZiBQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgICBzZXBhcmF0b3I6ICc7J1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBoaWdoY2hhcnRzOiB7XHJcbiAgICB2ZXJzaW9uOiB7XHJcbiAgICAgIHZhbHVlOiAnbGF0ZXN0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgdmVyc2lvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNkblVybDoge1xyXG4gICAgICB2YWx1ZTogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmb3JjZUZldGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmxhZyB0byByZWZldGNoIHNjcmlwdHMgYWZ0ZXIgZWFjaCBzZXJ2ZXIgcmVydW4nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhY2hlUGF0aDoge1xyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DQUNIRV9QQVRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEaXJlY3RvcnkgcGF0aCBmb3IgY2FjaGVkIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2hpZ2hjaGFydHMnLCAnaGlnaGNoYXJ0cy1tb3JlJywgJ2hpZ2hjaGFydHMtM2QnXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbW9kdWxlU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdzdG9jaycsXHJcbiAgICAgICAgJ21hcCcsXHJcbiAgICAgICAgJ2dhbnR0JyxcclxuICAgICAgICAnZXhwb3J0aW5nJyxcclxuICAgICAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgICAgICdhY2Nlc3NpYmlsaXR5JyxcclxuICAgICAgICAvLyAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgICAgICdib29zdCcsXHJcbiAgICAgICAgJ2RhdGEnLFxyXG4gICAgICAgICdkYXRhLXRvb2xzJyxcclxuICAgICAgICAnZHJhZ2dhYmxlLXBvaW50cycsXHJcbiAgICAgICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAgICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICAgICAnaGVhdG1hcCcsXHJcbiAgICAgICAgJ3RpbGVtYXAnLFxyXG4gICAgICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAgICAgJ3RpbWVsaW5lJyxcclxuICAgICAgICAndHJlZW1hcCcsXHJcbiAgICAgICAgJ3RyZWVncmFwaCcsXHJcbiAgICAgICAgJ2l0ZW0tc2VyaWVzJyxcclxuICAgICAgICAnZHJpbGxkb3duJyxcclxuICAgICAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAgICAgJ2J1bGxldCcsXHJcbiAgICAgICAgJ2Z1bm5lbCcsXHJcbiAgICAgICAgJ2Z1bm5lbDNkJyxcclxuICAgICAgICAnZ2VvaGVhdG1hcCcsXHJcbiAgICAgICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAgICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAgICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzZXJpZXMtb24tcG9pbnQnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgLy8gJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaScsXHJcbiAgICAgICAgJ2Zsb3dtYXAnLFxyXG4gICAgICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAgICAgJ25hdmlnYXRvcicsXHJcbiAgICAgICAgJ3RleHRwYXRoJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBtb2R1bGUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogWydpbmRpY2F0b3JzLWFsbCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBpbmRpY2F0b3Igc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQuanMvMi4zMC4xL21vbWVudC5taW4uanMnLFxyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQtdGltZXpvbmUvMC41LjQ1L21vbWVudC10aW1lem9uZS13aXRoLWRhdGEubWluLmpzJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DVVNUT01fU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBjdXN0b20gc2NyaXB0cyBvciBkZXBlbmRlbmNpZXMgdG8gZmV0Y2gnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGV4cG9ydDoge1xyXG4gICAgaW5maWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0lORklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnB1dCBmaWxlbmFtZSB3aXRoIHR5cGUsIGZvcm1hdHRlZCBjb3JyZWN0bHkgYXMgSlNPTiBvciBTVkcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbnN0cjoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5TVFInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3ZlcnJpZGVzIHRoZSBgaW5maWxlYCB3aXRoIEpTT04sIHN0cmluZ2lmaWVkIEpTT04sIG9yIFNWRyBpbnB1dCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09QVElPTlMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FsaWFzIGZvciB0aGUgYGluc3RyYCBvcHRpb24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzdmc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU1ZHJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTVkcgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCB0byByZW5kZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CQVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdCYXRjaCBqb2Igc3RyaW5nIHdpdGggaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09VVEZJTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3V0cHV0IGZpbGVuYW1lIHdpdGggdHlwZS4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcgYW5kIGlnbm9yZXMgYHR5cGVgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIGV4cG9ydCBmb3JtYXQuIENhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBwbmcnLFxyXG4gICAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0NPTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDaGFydCBjb25zdHJ1Y3Rvci4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgICBoaW50OiAnRGVmYXVsdDogY2hhcnQnLFxyXG4gICAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGI2NDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CNjQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gdGhlIGNoYXJ0IHNob3VsZCBiZSByZWNlaXZlZCBpbiBCYXNlNjQgZm9ybWF0IGluc3RlYWQgb2YgYmluYXJ5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBub0Rvd25sb2FkOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX05PX0RPV05MT0FEJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZXRoZXIgb3Igbm90IHRvIGluY2x1ZGUgb3IgZXhjbHVkZSBhdHRhY2htZW50IGhlYWRlcnMgaW4gdGhlIHJlc3BvbnNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSEVJR0hUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1NDQUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0RlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgbWluOiAwLjEsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9HTE9CQUxfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggZ2xvYmFsIG9wdGlvbnMgZm9yIEhpZ2hjaGFydHMuc2V0T3B0aW9ucycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVEhFTUVfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHdlYnBhZ2UgcmVuZGVyaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgZHVyaW5nIGV4cG9ydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FsbG93cyBvciBkaXNhbGxvd3MgaW5qZWN0aW9uIG9mIGZpbGVzeXN0ZW0gcmVzb3VyY2VzIChkaXNhYmxlZCBpbiBzZXJ2ZXIgbW9kZSknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DQUxMQkFDSycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIENhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgLmpzIGZpbGVuYW1lJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZXMgYXMgSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgZmlsZW5hbWUsIGNvbnRhaW5pbmcgZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSB3aXRoIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdQcm9tcHQtYmFzZWQgb3B0aW9uIHNldHRpbmcsIHNhdmVkIHRvIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVTZXJ2ZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0cyB0aGUgc2VydmVyIHdoZW4gdHJ1ZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaG9zdDoge1xyXG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIb3N0bmFtZSBvZiB0aGUgc2VydmVyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUG9ydCBudW1iZXIgZm9yIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHVwbG9hZExpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiAzLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9VUExPQURfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVxdWVzdCBib2R5IHNpemUgaW4gTUInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAnc2VydmVyQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Rpc3BsYXlzIG9yIG5vdCBhY3Rpb24gZHVyYXRpb25zIGluIG1pbGxpc2Vjb25kcyBkdXJpbmcgc2VydmVyIHJlcXVlc3RzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwcm94eToge1xyXG4gICAgICBob3N0OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eUhvc3QnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnSG9zdCBvZiB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB3aW5kb3c6IHtcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RpbWUgd2luZG93IGluIG1pbnV0ZXMgZm9yIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnRGVsYXkgZHVyYXRpb24gYmV0d2VlbiBzdWNjZXNzaXZlIHJlcXVlc3RzIGJlZm9yZSByZWFjaGluZyB0aGUgbGltaXQnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcEtleToge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0tleSB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwVG9rZW5gJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBUb2tlbjoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVG9rZW4gdG8gYnlwYXNzIHRoZSByYXRlIGxpbWl0ZXIsIHVzZWQgd2l0aCBgc2tpcEtleWAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdGb3JjZXMgdGhlIHNlcnZlciB0byB1c2UgSFRUUFMgb25seSB3aGVuIHRydWUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IGZvciB0aGUgU1NMIHNlcnZlcicsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xDZXJ0UGF0aCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWluaW11bSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICB3b3JrTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDQwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTnVtYmVyIG9mIHRhc2tzIGEgd29ya2VyIGNhbiBoYW5kbGUgYmVmb3JlIHJlc3RhcnRpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpZGxlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMzAwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGlkbGUgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAyMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGJlZm9yZSByZXRyeWluZyByZXNvdXJjZSBjcmVhdGlvbiBvbiBmYWlsdXJlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgdG8gY2hlY2sgYW5kIGRlc3Ryb3kgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1Nob3dzIHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdMb2dnaW5nIHZlcmJvc2l0eSBsZXZlbCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICByb3VuZDogMCxcclxuICAgICAgICBtaW46IDAsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnTG9nIGZpbGUgbmFtZS4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCB0byBiZSBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZXN0OiB7XHJcbiAgICAgIHZhbHVlOiAnbG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFJlcXVpcmVzIGBsb2dUb0ZpbGVgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBjb25zb2xlIGxvZ2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvRmlsZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICB1aToge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVVpJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGVuZHBvaW50IHJvdXRlIGZvciB0aGUgVUknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBOb2RlLmpzIGVudmlyb25tZW50IHR5cGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0Rpc3BsYXkgb3Igc2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoYXJkUmVzZXRQYWdlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfSEFSRF9SRVNFVF9QQUdFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaGV0aGVyIG9yIG5vdCB0byByZXNldCB0aGUgcGFnZSBjb250ZW50IGVudGlyZWx5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBicm93c2VyU2hlbGxNb2RlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHNldCB0aGUgYnJvd3NlciB0byBydW4gaW4gc2hlbGwgbW9kZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBkZWJ1Zzoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZURlYnVnJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSB1bmRlcmx5aW5nIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlYWRsZXNzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBoZWFkbGVzcyBtb2RlIGR1cmluZyBkZWJ1Z2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRldnRvb2xzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgRGV2VG9vbHMgaW4gaGVhZGZ1bCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGxpc3RlbmluZyB0byBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGR1bXBpbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RVTVBJTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdSZWRpcmVjdHMgb3Igbm90IGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gcHJvY2Vzcy5zdGRvdXQgYW5kIHByb2Nlc3Muc3RkZXJyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzbG93TW86IHtcclxuICAgICAgdmFsdWU6IDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVsYXlzIFB1cHBldGVlciBvcGVyYXRpb25zIGJ5IHRoZSBzcGVjaWZpZWQgbWlsbGlzZWNvbmRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IHVzZWQgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLy8gUHJvcGVydGllcyBuZXN0aW5nIGxldmVsIG9mIGFsbCBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRQcm9wcyA9IF9jcmVhdGVOZXN0ZWRQcm9wcyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8vIFByb3BlcnRpZXMgbmFtZXMgdGhhdCBzaG91bGQgbm90IGJlIHJlY3Vyc2l2ZWx5IG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IF9jcmVhdGVBYnNvbHV0ZVByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdlbmVyYXRlcyBhIG1hcHBpbmcgb2YgbmVzdGVkIGFyZ3VtZW50IGNoYWlucyBmcm9tIGEgbmVzdGVkXHJcbiAqIGNvbmZpZyBvYmplY3QuIFRoaXMgZnVuY3Rpb24gdHJhdmVyc2VzIGEgbmVzdGVkIG9iamVjdCBhbmQgY3JlYXRlcyBhIG1hcHBpbmdcclxuICogd2hlcmUgZWFjaCBrZXkgaXMgYW4gYXJndW1lbnQgbmFtZSAoZWl0aGVyIGZyb20gYGNsaU5hbWVgLCBgbGVnYWN5TmFtZWAsXHJcbiAqIG9yIHRoZSBvcmlnaW5hbCBrZXkpIGFuZCBlYWNoIHZhbHVlIGlzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY2hhaW5cclxuICogb2YgbmVzdGVkIHByb3BlcnRpZXMgbGVhZGluZyB0byB0aGF0IGFyZ3VtZW50LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbbmVzdGVkUHJvcHM9e31dIC0gVGhlIGFjY3VtdWxhdG9yIG9iamVjdCBmb3Igc3RvcmluZ1xyXG4gKiB0aGUgcmVzdWx0aW5nIGFyZ3VtZW50cyBjaGFpbnMuICBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbcHJvcENoYWluPScnXSAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzLFxyXG4gKiB1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbi4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gb2JqZWN0IG1hcHBpbmcgYXJndW1lbnQgbmFtZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZ1xyXG4gKiBuZXN0ZWQgcHJvcGVydHkgY2hhaW5zLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzKGNvbmZpZywgbmVzdGVkUHJvcHMgPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgX2NyZWF0ZU5lc3RlZFByb3BzKGVudHJ5LCBuZXN0ZWRQcm9wcywgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBuZXN0ZWRQcm9wc1tlbnRyeS5jbGlOYW1lIHx8IGtleV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgaWYgKGVudHJ5LmxlZ2FjeU5hbWUgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2tleX1gLnN1YnN0cmluZygxKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBSZXR1cm4gdGhlIG9iamVjdCB3aXRoIG5lc3RlZCBhcmd1bWVudCBjaGFpbnNcclxuICByZXR1cm4gbmVzdGVkUHJvcHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSBnYXRoZXJzIHRoZSBuYW1lcyBvZiBwcm9wZXJ0aWVzIGZyb20gYSBjb25maWd1cmF0aW9uIG9iamVjdCB0aGF0XHJcbiAqIGNhbiBiZSB0cmVhdGVkIGFzIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZXNlIHByb3BlcnRpZXMgaGF2ZSB2YWx1ZXMgdGhhdFxyXG4gKiBhcmUgb2JqZWN0cyBhbmQgZG8gbm90IGNvbnRhaW4gZnVydGhlciBuZXN0ZWQgZGVwdGggd2hlbiBtZXJnaW5nIGFuIG9iamVjdFxyXG4gKiBjb250YWluaW5nIHRoZXNlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBbYWJzb2x1dGVQcm9wcz1bXV0gLSBBbiBhcnJheSB0byBjb2xsZWN0IHRoZSBuYW1lc1xyXG4gKiBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5LjxzdHJpbmc+fSBBbiBhcnJheSBjb250YWluaW5nIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZVxyXG4gKiBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NyZWF0ZUFic29sdXRlUHJvcHMoY29uZmlnLCBhYnNvbHV0ZVByb3BzID0gW10pIHtcclxuICBPYmplY3Qua2V5cyhjb25maWcpLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgLy8gR2V0IHRoZSBzcGVjaWZpYyBzZWN0aW9uXHJcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ1trZXldO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHN0aWxsIG1vcmUgZGVwdGggdG8gdHJhdmVyc2VcclxuICAgIGlmICh0eXBlb2YgZW50cnkudHlwZXMgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIFJlY3Vyc2UgaW50byBkZWVwZXIgbGV2ZWxzXHJcbiAgICAgIF9jcmVhdGVBYnNvbHV0ZVByb3BzKGVudHJ5LCBhYnNvbHV0ZVByb3BzKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIHRoZSBvcHRpb24gY2FuIGJlIGFuIG9iamVjdCwgc2F2ZSBpdHMgdHlwZSBpbiB0aGUgYXJyYXlcclxuICAgICAgaWYgKGVudHJ5LnR5cGVzLmluY2x1ZGVzKCdPYmplY3QnKSkge1xyXG4gICAgICAgIGFic29sdXRlUHJvcHMucHVzaChrZXkpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgYXJyYXkgd2l0aCB0aGUgbmFtZXMgb2YgYWJzb2x1dGUgcHJvcGVydGllc1xyXG4gIHJldHVybiBhYnNvbHV0ZVByb3BzO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRQcm9wcyxcclxuICBhYnNvbHV0ZVByb3BzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG4gKiB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZFxyXG4gKiB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBgZW52c2AuIFdlIHNob3VsZCBub3QgdXNlIHRoZSBgcHJvY2Vzcy5lbnZgXHJcbiAqIGRpcmVjdGx5IGluIHRoZSBhcHBsaWNhdGlvbiBhcyB0aGVzZSB3b3VsZCBub3QgYmUgcGFyc2VkIHByb3Blcmx5LlxyXG4gKlxyXG4gKiBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSBwYXJzZWQgYW5kIHZhbGlkYXRlZCBvbmx5IG9uY2Ugd2hlblxyXG4gKiB0aGUgYXBwbGljYXRpb24gc3RhcnRzLiBXZSBzaG91bGQgd3JpdGUgYSBjdXN0b20gdmFsaWRhdG9yIG9yIGEgdHJhbnNmb3JtZXJcclxuICogZm9yIGVhY2ggb2YgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gcHVwcGV0ZWVyXHJcbiAgUFVQUEVURUVSX0FSR1M6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIGhpZ2hjaGFydHNcclxuICBISUdIQ0hBUlRTX1ZFUlNJT046IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PiAvXihsYXRlc3R8XFxkKyhcXC5cXGQrKXswLDJ9KSQvLnRlc3QodmFsdWUpIHx8IHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vJykgfHxcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcclxuICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgSElHSENIQVJUU19DRE5fVVJMLiBJdCBzaG91bGQgc3RhcnQgd2l0aCBodHRwOi8vIG9yIGh0dHBzOi8vLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTOiB2LmFycmF5KGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jb3JlU2NyaXB0cy52YWx1ZSksXHJcbiAgSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5tb2R1bGVTY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9JTkZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0lOU1RSOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9TVkc6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX0JBVENIOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9PVVRGSUxFOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9UWVBFOiB2LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLFxyXG4gIEVYUE9SVF9DT05TVFI6IHYuZW51bShbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddKSxcclxuICBFWFBPUlRfQjY0OiB2LmJvb2xlYW4oKSxcclxuICBFWFBPUlRfTk9fRE9XTkxPQUQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0dMT0JBTF9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9USEVNRV9PUFRJT05TOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19DVVNUT01fQ09ERTogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ0FMTEJBQ0s6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX1JFU09VUkNFUzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfTE9BRF9DT05GSUc6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUc6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlclxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1VQTE9BRF9MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgTE9HR0lOR19UT19GSUxFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gdWlcclxuICBVSV9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PREVfRU5WOiB2LmVudW0oWydkZXZlbG9wbWVudCcsICdwcm9kdWN0aW9uJywgJ3Rlc3QnXSksXHJcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0hBUkRfUkVTRVRfUEFHRTogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgTWFuYWdlcyBjb25maWd1cmF0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGJ5IGxvYWRpbmdcclxuICogYW5kIG1lcmdpbmcgb3B0aW9ucyBmcm9tIG11bHRpcGxlIHNvdXJjZXMsIHN1Y2ggYXMgZGVmYXVsdCBzZXR0aW5ncyxcclxuICogZW52aXJvbm1lbnQgdmFyaWFibGVzLCB1c2VyLXByb3ZpZGVkIG9wdGlvbnMsIGFuZCBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKiBFbnN1cmVzIHRoZSBnbG9iYWwgb3B0aW9ucyBhcmUgdXAtdG8tZGF0ZSB3aXRoIHRoZSBoaWdoZXN0IHByaW9yaXR5IHZhbHVlcy5cclxuICogUHJvdmlkZXMgZnVuY3Rpb25zIGZvciBhY2Nlc3NpbmcgYW5kIHVwZGF0aW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZGVlcENvcHksIGdldEFic29sdXRlUGF0aCwgaXNPYmplY3QgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCB7IGFic29sdXRlUHJvcHMsIG5lc3RlZFByb3BzLCBkZWZhdWx0Q29uZmlnIH0gZnJvbSAnLi9zY2hlbWFzL2NvbmZpZy5qcyc7XHJcblxyXG4vLyBTZXRzIHRoZSBnbG9iYWwgb3B0aW9ucyB3aXRoIGluaXRpYWwgdmFsdWVzIGZyb20gdGhlIGRlZmF1bHQgY29uZmlnXHJcbmNvbnN0IGdsb2JhbE9wdGlvbnMgPSBfaW5pdE9wdGlvbnMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IG9yIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWxcclxuICogb3B0aW9ucyBvYmplY3QsIGJhc2VkIG9uIHRoZSBgZ2V0Q29weWAgZmxhZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbZ2V0Q29weT10cnVlXSAtIFNwZWNpZmllcyB3aGV0aGVyIHRvIHJldHVybiBhIGNvcGllZFxyXG4gKiBvYmplY3Qgb2YgdGhlIGdsb2JhbCBvcHRpb25zIChgdHJ1ZWApIG9yIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gKiBvYmplY3QgKGBmYWxzZWApLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBIGNvcHkgb2YgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCwgb3IgYSByZWZlcmVuY2VcclxuICogdG8gdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRPcHRpb25zKGdldENvcHkgPSB0cnVlKSB7XHJcbiAgcmV0dXJuIGdldENvcHkgPyBkZWVwQ29weShnbG9iYWxPcHRpb25zKSA6IGdsb2JhbE9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IG9yIGEgcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWxcclxuICogb3B0aW9ucyBvYmplY3QsIGJhc2VkIG9uIHRoZSBgZ2V0Q29weWAgZmxhZy5cclxuICpcclxuICogQGZ1bmN0aW9uIHVwZGF0ZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgbmV3IG9wdGlvbnMgdG8gYmVcclxuICogbWVyZ2VkIGludG8gdGhlIGdsb2JhbCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PWZhbHNlXSAtIERldGVybWluZXMgd2hldGhlciB0byBtZXJnZSB0aGUgbmV3XHJcbiAqIG9wdGlvbnMgaW50byBhIGNvcHkgb2YgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYHRydWVgKSBvciBkaXJlY3RseSBpbnRvXHJcbiAqIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3QgKGBmYWxzZWApLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBvcHRpb25zIG9iamVjdCwgZWl0aGVyIHRoZSBtb2RpZmllZCBnbG9iYWxcclxuICogb3B0aW9ucyBvciBhIG1vZGlmaWVkIGNvcHksIGJhc2VkIG9uIHRoZSB2YWx1ZSBvZiBgZ2V0Q29weWAuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdXBkYXRlT3B0aW9ucyhuZXdPcHRpb25zLCBnZXRDb3B5ID0gZmFsc2UpIHtcclxuICAvLyBNZXJnZSBuZXcgb3B0aW9ucyB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb3IgaXRzIGNvcHkgYW5kIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgcmV0dXJuIF9tZXJnZU9wdGlvbnMoZ2V0T3B0aW9ucyhnZXRDb3B5KSwgbmV3T3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHZhbHVlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEksIGtlZXBpbmdcclxuICogdGhlIHByaW5jaXBsZSBvZiBvcHRpb25zIGxvYWQgcHJpb3JpdHkuIFRoaXMgZnVuY3Rpb24gYWNjZXB0cyBhIGBjbGlBcmdzYFxyXG4gKiBhcnJheSBjb250YWluaW5nIGFyZ3VtZW50cyBmcm9tIHRoZSBDTEksIHdoaWNoIHdpbGwgYmUgdmFsaWRhdGVkIGFuZCBhcHBsaWVkXHJcbiAqIGlmIHByb3ZpZGVkLlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSBhIGN1c3RvbSBKU09OIGZpbGUgKGxvYWRlZCBieSB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uKS5cclxuICogMi4gVmFsdWVzIGZyb20gdGhlIGNvbW1hbmQgbGluZSBpbnRlcmZhY2UgKENMSSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRDbGlPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBBbiBhcnJheSBvZiBjb21tYW5kIGxpbmUgYXJndW1lbnRzIHVzZWRcclxuICogZm9yIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIHVwZGF0ZWQgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCByZWZsZWN0aW5nIHRoZSBtZXJnZWRcclxuICogY29uZmlndXJhdGlvbiBmcm9tIHNvdXJjZXMgcHJvdmlkZWQgdGhyb3VnaCB0aGUgQ0xJLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldENsaU9wdGlvbnMoY2xpQXJncykge1xyXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcclxuICBpZiAoY2xpQXJncyAmJiBBcnJheS5pc0FycmF5KGNsaUFyZ3MpICYmIGNsaUFyZ3MubGVuZ3RoKSB7XHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBjdXN0b20gSlNPTiBsb2FkZWQgdmlhIHRoZSBgLS1sb2FkQ29uZmlnYFxyXG4gICAgY29uc3QgY29uZmlnT3B0aW9ucyA9IF9sb2FkQ29uZmlnRmlsZShjbGlBcmdzKTtcclxuXHJcbiAgICAvLyBVcGRhdGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCB0aGUgdmFsdWVzIGZyb20gdGhlIGBjb25maWdPcHRpb25zYFxyXG4gICAgdXBkYXRlT3B0aW9ucyhjb25maWdPcHRpb25zKTtcclxuXHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBDTElcclxuICAgIGNvbnN0IGNsaU9wdGlvbnMgPSBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNsaU9wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9wdGlvbnNcclxuICByZXR1cm4gZ2V0T3B0aW9ucyhmYWxzZSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNYXBzIG9sZC1zdHJ1Y3R1cmVkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyAoUGhhbnRvbUpTKSB0byBhIG5ldyBmb3JtYXRcclxuICogKFB1cHBldGVlcikuIFRoaXMgZnVuY3Rpb24gY29udmVydHMgZmxhdCwgb2xkLXN0cnVjdHVyZWQgb3B0aW9ucyBpbnRvXHJcbiAqIGEgbmV3LCBuZXN0ZWQgY29uZmlndXJhdGlvbiBmb3JtYXQgYmFzZWQgb24gYSBwcmVkZWZpbmVkIG1hcHBpbmdcclxuICogKGBuZXN0ZWRQcm9wc2ApLiBUaGUgbmV3IGZvcm1hdCBpcyB1c2VkIGZvciBQdXBwZXRlZXIsIHdoaWxlIHRoZSBvbGQgZm9ybWF0XHJcbiAqIHdhcyB1c2VkIGZvciBQaGFudG9tSlMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtYXBUb05ld09wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9sZE9wdGlvbnMgLSBUaGUgb2xkLCBmbGF0IGNvbmZpZ3VyYXRpb24gb3B0aW9uc1xyXG4gKiB0byBiZSBjb252ZXJ0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgbmV3IG9iamVjdCBjb250YWluaW5nIG9wdGlvbnMgc3RydWN0dXJlZCBhY2NvcmRpbmdcclxuICogdG8gdGhlIG1hcHBpbmcgZGVmaW5lZCBpbiBgbmVzdGVkUHJvcHNgIG9yIGFuIGVtcHR5IG9iamVjdCBpZiB0aGUgcHJvdmlkZWRcclxuICogYG9sZE9wdGlvbnNgIGlzIG5vdCBhIGNvcnJlY3Qgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9ucyhvbGRPcHRpb25zKSB7XHJcbiAgLy8gQW4gb2JqZWN0IGZvciB0aGUgbmV3IHN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gIGNvbnN0IG5ld09wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgdmFsdWUgaXMgYSBjb3JyZWN0IG9iamVjdFxyXG4gIGlmIChpc09iamVjdChvbGRPcHRpb25zKSkge1xyXG4gICAgLy8gSXRlcmF0ZSBvdmVyIGVhY2gga2V5LXZhbHVlIHBhaXIgaW4gdGhlIG9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnNcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG9sZE9wdGlvbnMpKSB7XHJcbiAgICAgIC8vIElmIHRoZXJlIGlzIGEgbmVzdGVkIG1hcHBpbmcsIHNwbGl0IGl0IGludG8gYSBwcm9wZXJ0aWVzIGNoYWluXHJcbiAgICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW2tleV1cclxuICAgICAgICA/IG5lc3RlZFByb3BzW2tleV0uc3BsaXQoJy4nKVxyXG4gICAgICAgIDogW107XHJcblxyXG4gICAgICAvLyBJZiBpdCBpcyB0aGUgbGFzdCBwcm9wZXJ0eSBpbiB0aGUgY2hhaW4sIGFzc2lnbiB0aGUgdmFsdWUsIG90aGVyd2lzZSxcclxuICAgICAgLy8gY3JlYXRlIG9yIHJldXNlIHRoZSBuZXN0ZWQgb2JqZWN0XHJcbiAgICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoXHJcbiAgICAgICAgKG9iaiwgcHJvcCwgaW5kZXgpID0+XHJcbiAgICAgICAgICAob2JqW3Byb3BdID1cclxuICAgICAgICAgICAgcHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4ID8gdmFsdWUgOiBvYmpbcHJvcF0gfHwge30pLFxyXG4gICAgICAgIG5ld09wdGlvbnNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICAnW2NvbmZpZ10gTm8gY29ycmVjdCBvYmplY3Qgd2l0aCBvcHRpb25zIHdhcyBwcm92aWRlZC4gUmV0dXJuaW5nIGFuIGVtcHR5IGFycmF5LidcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIG5ldywgc3RydWN0dXJlZCBvcHRpb25zIG9iamVjdFxyXG4gIHJldHVybiBuZXdPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogVmFsaWRhdGVzLCBwYXJzZXMsIGFuZCBjaGVja3MgaWYgdGhlIHByb3ZpZGVkIGNvbmZpZyBpcyBhbGxvd2VkIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnXHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gY29uZmlnIC0gVGhlIGNvbmZpZyB0byBiZSB2YWxpZGF0ZWQgYW5kIHBhcnNlZCBhcyBhIHNldFxyXG4gKiBvZiBvcHRpb25zLiBNdXN0IGJlIGVpdGhlciBhbiBvYmplY3Qgb3IgYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3RvU3RyaW5nPWZhbHNlXSAtIFdoZXRoZXIgdG8gcmV0dXJuIGEgc3RyaW5naWZpZWQgdmVyc2lvblxyXG4gKiBvZiB0aGUgcGFyc2VkIGNvbmZpZy4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICogQHBhcmFtIHtib29sZWFufSBbYWxsb3dGdW5jdGlvbnM9ZmFsc2VdIC0gV2hldGhlciB0byBhbGxvdyBmdW5jdGlvbnNcclxuICogaW4gdGhlIHBhcnNlZCBjb25maWcuIElmIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkLiBPdGhlcndpc2UsIHdoZW5cclxuICogYSBmdW5jdGlvbiBpcyBmb3VuZCwgbnVsbCBpcyByZXR1cm5lZC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8c3RyaW5nfG51bGwpfSBSZXR1cm5zIGEgcGFyc2VkIHNldCBvZiBvcHRpb25zIG9iamVjdCxcclxuICogYSBzdHJpbmdpZmllZCBzZXQgb2Ygb3B0aW9ucyBvYmplY3QgaWYgdGhlIGB0b1N0cmluZ2AgaXMgdHJ1ZSwgYW5kIG51bGxcclxuICogaWYgdGhlIGNvbmZpZyBpcyBub3QgYSB2YWxpZCBzZXQgb2Ygb3B0aW9ucyBvciBwYXJzaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzQWxsb3dlZENvbmZpZyhcclxuICBjb25maWcsXHJcbiAgdG9TdHJpbmcgPSBmYWxzZSxcclxuICBhbGxvd0Z1bmN0aW9ucyA9IGZhbHNlXHJcbikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBBY2NlcHQgb25seSBvYmplY3RzIGFuZCBzdHJpbmdzXHJcbiAgICBpZiAoIWlzT2JqZWN0KGNvbmZpZykgJiYgdHlwZW9mIGNvbmZpZyAhPT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gUmV0dXJuIG51bGwgaWYgYW55IG90aGVyIHR5cGVcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBvYmplY3QgcmVwcmVzZW50YXRpb24gb2YgdGhlIG9yaWdpbmFsIGNvbmZpZ1xyXG4gICAgY29uc3Qgb2JqZWN0Q29uZmlnID1cclxuICAgICAgdHlwZW9mIGNvbmZpZyA9PT0gJ3N0cmluZydcclxuICAgICAgICA/IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IGV2YWwoYCgke2NvbmZpZ30pYClcclxuICAgICAgICAgIDogSlNPTi5wYXJzZShjb25maWcpXHJcbiAgICAgICAgOiBjb25maWc7XHJcblxyXG4gICAgLy8gUHJlc2VydmUgb3IgcmVtb3ZlIHBvdGVudGlhbCBmdW5jdGlvbnMgYmFzZWQgb24gdGhlIGBhbGxvd0Z1bmN0aW9uc2AgZmxhZ1xyXG4gICAgY29uc3Qgc3RyaW5naWZpZWRPcHRpb25zID0gX29wdGlvbnNTdHJpbmdpZnkoXHJcbiAgICAgIG9iamVjdENvbmZpZyxcclxuICAgICAgYWxsb3dGdW5jdGlvbnMsXHJcbiAgICAgIGZhbHNlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFBhcnNlIHRoZSBjb25maWcgdG8gY2hlY2sgaWYgaXQgaXMgdmFsaWQgc2V0IG9mIG9wdGlvbnNcclxuICAgIGNvbnN0IHBhcnNlZE9wdGlvbnMgPSBhbGxvd0Z1bmN0aW9uc1xyXG4gICAgICA/IEpTT04ucGFyc2UoXHJcbiAgICAgICAgICBfb3B0aW9uc1N0cmluZ2lmeShvYmplY3RDb25maWcsIGFsbG93RnVuY3Rpb25zLCB0cnVlKSxcclxuICAgICAgICAgIChfLCB2YWx1ZSkgPT5cclxuICAgICAgICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJiB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpXHJcbiAgICAgICAgICAgICAgPyBldmFsKGAoJHt2YWx1ZX0pYClcclxuICAgICAgICAgICAgICA6IHZhbHVlXHJcbiAgICAgICAgKVxyXG4gICAgICA6IEpTT04ucGFyc2Uoc3RyaW5naWZpZWRPcHRpb25zKTtcclxuXHJcbiAgICAvLyBSZXR1cm4gc3RyaW5naWZpZWQgb3Igb2JqZWN0IG9wdGlvbnMgYmFzZWQgb24gdGhlIGB0b1N0cmluZ2AgZmxhZ1xyXG4gICAgcmV0dXJuIHRvU3RyaW5nID8gc3RyaW5naWZpZWRPcHRpb25zIDogcGFyc2VkT3B0aW9ucztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gUmV0dXJuIG51bGwgaWYgcGFyc2luZyBmYWlsc1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbywgdmVyc2lvbiwgYW5kIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludExpY2Vuc2VcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludExpY2Vuc2UoKSB7XHJcbiAgLy8gUHJpbnQgdGhlIGxvZ28gYW5kIHZlcnNpb24gaW5mb3JtYXRpb25cclxuICBwcmludFZlcnNpb24oKTtcclxuXHJcbiAgLy8gUHJpbnQgdGhlIGxpY2Vuc2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdUaGlzIHNvZnR3YXJlIHJlcXVpcmVzIGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGZvciBjb21tZXJjaWFsIHVzZS5cXG4nXHJcbiAgICAgIC55ZWxsb3csXHJcbiAgICAnXFxuRm9yIGEgZnVsbCBsaXN0IG9mIENMSSBvcHRpb25zLCB0eXBlOicsXHJcbiAgICAnXFxuaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyIC0taGVscFxcbicuZ3JlZW4sXHJcbiAgICAnXFxuSWYgeW91IGRvIG5vdCBoYXZlIGEgbGljZW5zZSwgb25lIGNhbiBiZSBvYnRhaW5lZCBoZXJlOicsXHJcbiAgICAnXFxuaHR0cHM6Ly9zaG9wLmhpZ2hzb2Z0LmNvbS9cXG4nLmdyZWVuLFxyXG4gICAgJ1xcblRvIGN1c3RvbWl6ZSB5b3VyIGluc3RhbGxhdGlvbiwgcGxlYXNlIHJlZmVyIHRvIHRoZSBSRUFETUUgZmlsZSBhdDonLFxyXG4gICAgJ1xcbmh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWVcXG4nLmdyZWVuXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByaW50cyB1c2FnZSBpbmZvcm1hdGlvbiBmb3IgQ0xJIGFyZ3VtZW50cywgZGlzcGxheWluZyBhdmFpbGFibGUgb3B0aW9uc1xyXG4gKiBhbmQgdGhlaXIgZGVzY3JpcHRpb25zLiBJdCBjYW4gbGlzdCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5IGlmIGNhdGVnb3JpZXNcclxuICogY29udGFpbiBuZXN0ZWQgb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50VXNhZ2VcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFVzYWdlKCkge1xyXG4gIC8vIERpc3BsYXkgUkVBRE1FIGFuZCBnZW5lcmFsIHVzYWdlIGluZm9ybWF0aW9uXHJcbiAgY29uc29sZS5sb2coXHJcbiAgICAnXFxuVXNhZ2Ugb2YgQ0xJIGFyZ3VtZW50czonLmJvbGQsXHJcbiAgICAnXFxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0nLFxyXG4gICAgYFxcbkZvciBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uLCB2aXNpdCB0aGUgUkVBRE1FIGZpbGUgYXQ6ICR7J2h0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWUnLmdyZWVufS5cXG5gXHJcbiAgKTtcclxuXHJcbiAgLy8gSXRlcmF0ZSB0aHJvdWdoIGVhY2ggY2F0ZWdvcnkgaW4gdGhlIGBkZWZhdWx0Q29uZmlnYCBhbmQgZGlzcGxheSB1c2FnZSBpbmZvXHJcbiAgT2JqZWN0LmtleXMoZGVmYXVsdENvbmZpZykuZm9yRWFjaCgoY2F0ZWdvcnkpID0+IHtcclxuICAgIGNvbnNvbGUubG9nKGAke2NhdGVnb3J5LnRvVXBwZXJDYXNlKCl9YC5ib2xkLnJlZCk7XHJcbiAgICBfY3ljbGVDYXRlZ29yaWVzKGRlZmF1bHRDb25maWdbY2F0ZWdvcnldKTtcclxuICAgIGNvbnNvbGUubG9nKCcnKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByaW50cyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGxvZ28gb3IgdGV4dCB3aXRoIHRoZSB2ZXJzaW9uXHJcbiAqIGluZm9ybWF0aW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW25vTG9nbz1mYWxzZV0gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24sIHdpdGhvdXQgdGhlIGxvZ28uIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRWZXJzaW9uKG5vTG9nbyA9IGZhbHNlKSB7XHJcbiAgLy8gR2V0IHBhY2thZ2UgdmVyc2lvbiBlaXRoZXIgZnJvbSBgLmVudmAgb3IgZnJvbSBgcGFja2FnZS5qc29uYFxyXG4gIGNvbnN0IHBhY2thZ2VWZXJzaW9uID0gSlNPTi5wYXJzZShcclxuICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ3BhY2thZ2UuanNvbicpLCAndXRmOCcpXHJcbiAgKS52ZXJzaW9uO1xyXG5cclxuICAvLyBQcmludCB0ZXh0IG9ubHlcclxuICBpZiAobm9Mb2dvKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHYke3BhY2thZ2VWZXJzaW9ufWApO1xyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBQcmludCB0aGUgbG9nb1xyXG4gICAgY29uc29sZS5sb2coXHJcbiAgICAgIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ21zZycsICdzdGFydHVwLm1zZycpLCAndXRmOCcpLnRvU3RyaW5nKClcclxuICAgICAgICAuYm9sZC55ZWxsb3csXHJcbiAgICAgIGB2JHtwYWNrYWdlVmVyc2lvbn1cXG5gLmJvbGRcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgYW5kIHJldHVybnMgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCBiYXNlZCBvbiB0aGUgcHJvdmlkZWRcclxuICogY29uZmlndXJhdGlvbiwgc2V0dGluZyB2YWx1ZXMgZnJvbSBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogVGhlIHByaW9yaXR5IG9yZGVyIGZvciBzZXR0aW5nIHZhbHVlcyBpczpcclxuICpcclxuICogMS4gVmFsdWVzIGZyb20gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSAoZGVmYXVsdHMpLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgKHNwZWNpZmllZCBpbiB0aGUgYC5lbnZgIGZpbGUpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2luaXRPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgdXNlZCBmb3IgaW5pdGlhbGl6aW5nXHJcbiAqIHRoZSBnbG9iYWwgb3B0aW9ucy4gSXQgc2hvdWxkIGluY2x1ZGUgbmVzdGVkIHByb3BlcnRpZXMgd2l0aCBhIGB2YWx1ZWBcclxuICogYW5kIGFuIGBlbnZMaW5rYCBmb3IgbGlua2luZyB0byBlbnZpcm9ubWVudCB2YXJpYWJsZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBpbml0aWFsaXplZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHBvcHVsYXRlZCB3aXRoXHJcbiAqIHZhbHVlcyBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5XHJcbiAqIG9yZGVyLlxyXG4gKi9cclxuZnVuY3Rpb24gX2luaXRPcHRpb25zKGNvbmZpZykge1xyXG4gIC8vIEluaXQgdGhlIG9iamVjdCBmb3Igb3B0aW9uc1xyXG4gIGNvbnN0IG9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gU3RhcnQgaW5pdGlhbGl6aW5nIHRoZSBgb3B0aW9uc2Agb2JqZWN0IHJlY3Vyc2l2ZWx5XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoY29uZmlnKSkge1xyXG4gICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChpdGVtLCAndmFsdWUnKSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGNvcnJlY3QgdmFsdWUgYmFzZWQgb24gdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5IG9yZGVyXHJcbiAgICAgIGlmIChlbnZzW2l0ZW0uZW52TGlua10gIT09IHVuZGVmaW5lZCAmJiBlbnZzW2l0ZW0uZW52TGlua10gIT09IG51bGwpIHtcclxuICAgICAgICAvLyBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIHZhbHVlXHJcbiAgICAgICAgb3B0aW9uc1tuYW1lXSA9IGVudnNbaXRlbS5lbnZMaW5rXTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaGUgdmFsdWUgZnJvbSB0aGUgY29uZmlnIGZpbGVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gaXRlbS52YWx1ZTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIGEgc2VjdGlvbiBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICBvcHRpb25zW25hbWVdID0gX2luaXRPcHRpb25zKGl0ZW0pO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBjcmVhdGVkIGBvcHRpb25zYCBvYmplY3RcclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIE1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGNvbnNpZGVyaW5nIGFic29sdXRlIHByb3BlcnRpZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcmlnaW5hbE9wdGlvbnMgLSBPcmlnaW5hbCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBuZXdPcHRpb25zIC0gTmV3IGNvbmZpZ3VyYXRpb24gb3B0aW9ucyB0byBiZSBtZXJnZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IE1lcmdlZCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX21lcmdlT3B0aW9ucyhvcmlnaW5hbE9wdGlvbnMsIG5ld09wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgYG9yaWdpbmFsT3B0aW9uc2AgYW5kIGBuZXdPcHRpb25zYCBhcmUgY29ycmVjdCBvYmplY3RzXHJcbiAgaWYgKGlzT2JqZWN0KG9yaWdpbmFsT3B0aW9ucykgJiYgaXNPYmplY3QobmV3T3B0aW9ucykpIHtcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICAgIG9yaWdpbmFsT3B0aW9uc1trZXldID1cclxuICAgICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXHJcbiAgICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgPyBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9uc1trZXldLCB2YWx1ZSlcclxuICAgICAgICAgIDogdmFsdWUgIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICAgIDogb3JpZ2luYWxPcHRpb25zW2tleV0gfHwgbnVsbDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb3JpZ2luYWwgKG1vZGlmaWVkIG9yIG5vdCkgb3B0aW9uc1xyXG4gIHJldHVybiBvcmlnaW5hbE9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmdcclxuICogd2l0aCB0aGUgb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy4gSW4gb3JkZXIgZm9yIGEgZnVuY3Rpb25cclxuICogdG8gYmUgcHJlc2VydmVkLCBpdCBuZWVkcyB0byBmb2xsb3cgdGhlIGZvcm1hdCBgZnVuY3Rpb24gKC4uLikgey4uLn1gLlxyXG4gKiBTdWNoIGEgZnVuY3Rpb24gY2FuIGFsc28gYmUgc3RyaW5naWZpZWQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWRcclxuICogaW4gdGhlIG91dHB1dC4gT3RoZXJ3aXNlIGFuIGVycm9yIGlzIHRocm93bi5cclxuICogQHBhcmFtIHtib29sZWFufSBzdHJpbmdpZnlGdW5jdGlvbnMgLSBJZiBzZXQgdG8gdHJ1ZSwgZnVuY3Rpb25zIGFyZSBzYXZlZFxyXG4gKiBhcyBzdHJpbmdzLiBUaGUgYGFsbG93RnVuY3Rpb25zYCBtdXN0IGJlIHNldCB0byB0cnVlIGFzIHdlbGwgZm9yIHRoaXMgdG8gdGFrZVxyXG4gKiBhbiBlZmZlY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGBFcnJvcmAgd2hlbiBmdW5jdGlvbnMgYXJlIG5vdCBhbGxvd2VkIGJ1dCBhcmVcclxuICogZm91bmQgaW4gcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gX29wdGlvbnNTdHJpbmdpZnkob3B0aW9ucywgYWxsb3dGdW5jdGlvbnMsIHN0cmluZ2lmeUZ1bmN0aW9ucykge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAoXywgdmFsdWUpID0+IHtcclxuICAgIC8vIFRyaW0gc3RyaW5nIHZhbHVlc1xyXG4gICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgdmFsdWUgaXMgYSBmdW5jdGlvbiBvciBzdHJpbmdpZmllZCBmdW5jdGlvblxyXG4gICAgaWYgKFxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgfHxcclxuICAgICAgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpICYmXHJcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBJZiBhbGxvd0Z1bmN0aW9ucyBpcyBzZXQgdG8gdHJ1ZSwgcHJlc2VydmUgZnVuY3Rpb25zXHJcbiAgICAgIGlmIChhbGxvd0Z1bmN0aW9ucykge1xyXG4gICAgICAgIC8vIEJhc2VkIG9uIHRoZSBgc3RyaW5naWZ5RnVuY3Rpb25zYCBvcHRpb25zLCBzZXQgZnVuY3Rpb24gdmFsdWVzXHJcbiAgICAgICAgcmV0dXJuIHN0cmluZ2lmeUZ1bmN0aW9uc1xyXG4gICAgICAgICAgPyAvLyBBcyBzdHJpbmdpZmllZCBmdW5jdGlvbnNcclxuICAgICAgICAgICAgYFwiRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOXCJgXHJcbiAgICAgICAgICA6IC8vIEFzIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xccysvZywgJyAnKX1FWFBfRlVOYDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaHJvdyBhbiBlcnJvciBvdGhlcndpc2VcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEluIGFsbCBvdGhlciBjYXNlcywgc2ltcGx5IHJldHVybiB0aGUgdmFsdWVcclxuICAgIHJldHVybiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIHN0cmluZ2lmeUZ1bmN0aW9ucyA/IC9cXFxcXCJFWFBfRlVOfEVYUF9GVU5cXFxcXCIvZyA6IC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHByb3ZpZGVkIHZpYVxyXG4gKiB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGluIHRoZSBjb21tYW5kLWxpbmUgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2xvYWRDb25maWdGaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIHRvIHNlYXJjaFxyXG4gKiBmb3IgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgZmlsZSBwYXRoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGxvYWRlZCBmcm9tIHRoZSBzcGVjaWZpZWRcclxuICogZmlsZSwgb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBmaWxlIGlzIG5vdCBmb3VuZCwgaW52YWxpZCwgb3IgYW4gZXJyb3JcclxuICogb2NjdXJzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpIHtcclxuICAvLyBHZXQgdGhlIGFsbG93IGZsYWdzIGZvciB0aGUgY3VzdG9tIGxvZ2ljIGNoZWNrXHJcbiAgY29uc3QgeyBhbGxvd0NvZGVFeGVjdXRpb24sIGFsbG93RmlsZVJlc291cmNlcyB9ID0gZ2V0T3B0aW9ucygpLmN1c3RvbUxvZ2ljO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIHdhcyB1c2VkXHJcbiAgY29uc3QgY29uZmlnSW5kZXggPSBjbGlBcmdzLmZpbmRJbmRleChcclxuICAgIChhcmcpID0+IGFyZy5yZXBsYWNlKC8tL2csICcnKSA9PT0gJ2xvYWRDb25maWcnXHJcbiAgKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gdmFsdWVcclxuICBjb25zdCBjb25maWdGaWxlTmFtZSA9IGNvbmZpZ0luZGV4ID4gLTEgJiYgY2xpQXJnc1tjb25maWdJbmRleCArIDFdO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgYC0tbG9hZENvbmZpZ2AgaXMgcHJlc2VudCBhbmQgaGFzIGEgY29ycmVjdCB2YWx1ZVxyXG4gIGlmIChjb25maWdGaWxlTmFtZSAmJiBhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIExvYWQgYW4gb3B0aW9uYWwgY3VzdG9tIEpTT04gY29uZmlnIGZpbGVcclxuICAgICAgcmV0dXJuIGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGNvbmZpZ0ZpbGVOYW1lKSwgJ3V0ZjgnKSxcclxuICAgICAgICBmYWxzZSxcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGVycm9yLFxyXG4gICAgICAgIGBbY29uZmlnXSBVbmFibGUgdG8gbG9hZCB0aGUgY29uZmlndXJhdGlvbiBmcm9tIHRoZSAke2NvbmZpZ0ZpbGVOYW1lfSBmaWxlLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIE5vIGFkZGl0aW9uYWwgb3B0aW9ucyB0byByZXR1cm5cclxuICByZXR1cm4ge307XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQYXJzZXMgY29tbWFuZC1saW5lIGFyZ3VtZW50cyBhbmQgcGFpcnMgZWFjaCBhcmd1bWVudCB3aXRoIGl0cyBjb3JyZXNwb25kaW5nXHJcbiAqIG9wdGlvbiBpbiB0aGUgY29uZmlndXJhdGlvbi4gVGhlIHZhbHVlcyBhcmUgc3RydWN0dXJlZCBpbnRvIGEgbmVzdGVkIG9wdGlvbnNcclxuICogb2JqZWN0LCBiYXNlZCBvbiBwcmVkZWZpbmVkIG1hcHBpbmdzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3BhaXJBcmd1bWVudFZhbHVlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG5lc3RlZFByb3BzIC0gQW4gYXJyYXkgb2YgbmVzdGluZyBsZXZlbCBmb3IgYWxsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNsaUFyZ3MgLSBBbiBhcnJheSBvZiBjb21tYW5kLWxpbmUgYXJndW1lbnRzXHJcbiAqIGNvbnRhaW5pbmcgb3B0aW9ucyBhbmQgdGhlaXIgYXNzb2NpYXRlZCB2YWx1ZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFuIHVwZGF0ZWQgb3B0aW9ucyBvYmplY3Qgd2hlcmUgZWFjaCBvcHRpb24gZnJvbVxyXG4gKiB0aGUgY29tbWFuZC1saW5lIGlzIHBhaXJlZCB3aXRoIGl0cyB2YWx1ZSwgc3RydWN0dXJlZCBpbnRvIG5lc3RlZCBvYmplY3RzXHJcbiAqIGFzIGRlZmluZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWUobmVzdGVkUHJvcHMsIGNsaUFyZ3MpIHtcclxuICAvLyBBbiBlbXB0eSBvYmplY3QgdG8gY29sbGVjdCBhbmQgc3RydWN0dXJpemUgZGF0YSBmcm9tIHRoZSBhcmdzXHJcbiAgY29uc3QgY2xpT3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDeWNsZSB0aHJvdWdoIGFsbCBDTEkgYXJncyBhbmQgZmlsdGVyIHRoZW1cclxuICBmb3IgKGxldCBpID0gMDsgaSA8IGNsaUFyZ3MubGVuZ3RoOyBpKyspIHtcclxuICAgIGNvbnN0IG9wdGlvbiA9IGNsaUFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW29wdGlvbl1cclxuICAgICAgPyBuZXN0ZWRQcm9wc1tvcHRpb25dLnNwbGl0KCcuJylcclxuICAgICAgOiBbXTtcclxuXHJcbiAgICAvLyBDcmVhdGUgb3B0aW9ucyBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSBDTEkgZm9yIGxhdGVyIHBhcnNpbmcgYW5kIG1lcmdpbmdcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcclxuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xyXG4gICAgICAgIGNvbnN0IHZhbHVlID0gY2xpQXJnc1srK2ldO1xyXG4gICAgICAgIGlmICghdmFsdWUpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSBDTEkgJy0tJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgb2JqW3Byb3BdID0gdmFsdWUgfHwgbnVsbDtcclxuICAgICAgfSBlbHNlIGlmIChvYmpbcHJvcF0gPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG9ialtwcm9wXSA9IHt9O1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBvYmpbcHJvcF07XHJcbiAgICB9LCBjbGlPcHRpb25zKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBwYXJzZWQgQ0xJIG9wdGlvbnNcclxuICByZXR1cm4gY2xpT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IHRyYXZlcnNlcyB0aGUgb3B0aW9ucyBvYmplY3QgdG8gcHJpbnQgdGhlIHVzYWdlIGluZm9ybWF0aW9uXHJcbiAqIGZvciBlYWNoIG9wdGlvbiBjYXRlZ29yeSBhbmQgaW5kaXZpZHVhbCBvcHRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3ljbGVDYXRlZ29yaWVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgQ0xJIG9wdGlvbnMuIEl0IG1heVxyXG4gKiBpbmNsdWRlIG5lc3RlZCBjYXRlZ29yaWVzIGFuZCBpbmRpdmlkdWFsIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3ljbGVDYXRlZ29yaWVzKG9wdGlvbnMpIHtcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKG9wdGlvbnMpKSB7XHJcbiAgICAvLyBJZiB0aGUgY3VycmVudCBlbnRyeSBpcyBhIGNhdGVnb3J5IGFuZCBub3QgYSBsZWFmIG9wdGlvbiwgcmVjdXJzZSBpbnRvIGl0XHJcbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvcHRpb24sICd2YWx1ZScpKSB7XHJcbiAgICAgIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9uKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIFByZXBhcmUgZGVzY3JpcHRpb25cclxuICAgICAgY29uc3QgZGVzY05hbWUgPSBgIC0tJHtvcHRpb24uY2xpTmFtZSB8fCBuYW1lfWA7XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIHZhbHVlXHJcbiAgICAgIGxldCBvcHRpb25WYWx1ZSA9IG9wdGlvbi52YWx1ZTtcclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhcnJheSBvZiBzdHJpbmdzXHJcbiAgICAgIGlmIChvcHRpb25WYWx1ZSAhPT0gbnVsbCAmJiBvcHRpb24udHlwZXMuaW5jbHVkZXMoJ3N0cmluZ1tdJykpIHtcclxuICAgICAgICBvcHRpb25WYWx1ZSA9XHJcbiAgICAgICAgICAnWycgKyBvcHRpb25WYWx1ZS5tYXAoKGl0ZW0pID0+IGAnJHtpdGVtfSdgKS5qb2luKCcsICcpICsgJ10nO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBQcmVwYXJlIHZhbHVlIGZvciBvcHRpb24gdGhhdCBpcyBub3QgbnVsbCBhbmQgaXMgYSBzdHJpbmdcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nJykpIHtcclxuICAgICAgICBvcHRpb25WYWx1ZSA9IGAnJHtvcHRpb25WYWx1ZX0nYDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRGlzcGxheSBjb3JyZWN0bHkgYWxpZ25lZCBtZXNzYWdlc1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICBkZXNjTmFtZS5ncmVlbixcclxuICAgICAgICBgJHsoJzwnICsgb3B0aW9uLnR5cGVzLmpvaW4oJ3wnKSArICc+JykueWVsbG93fWAsXHJcbiAgICAgICAgYCR7U3RyaW5nKG9wdGlvblZhbHVlKS5ib2xkfWAuYmx1ZSxcclxuICAgICAgICBgLSAke29wdGlvbi5kZXNjcmlwdGlvbn0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgdXBkYXRlT3B0aW9ucyxcclxuICBzZXRDbGlPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuICBpc0FsbG93ZWRDb25maWcsXHJcbiAgcHJpbnRMaWNlbnNlLFxyXG4gIHByaW50VXNhZ2UsXHJcbiAgcHJpbnRWZXJzaW9uXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBIVFRQIHV0aWxpdHkgbW9kdWxlIGZvciBmZXRjaGluZyBhbmQgcG9zdGluZyBkYXRhLiBTdXBwb3J0cyBib3RoXHJcbiAqIEhUVFAgYW5kIEhUVFBTIHByb3RvY29scywgcHJvdmlkaW5nIG1ldGhvZHMgdG8gbWFrZSBHRVQgYW5kIFBPU1QgcmVxdWVzdHNcclxuICogd2l0aCBjdXN0b21pemFibGUgb3B0aW9ucy4gSW5jbHVkZXMgcHJvdG9jb2wgZGV0ZXJtaW5hdGlvbiBiYXNlZCBvbiBVUkxcclxuICogYW5kIGF1Z21lbnRzIHJlc3BvbnNlIG9iamVjdHMgd2l0aCBhICd0ZXh0JyBwcm9wZXJ0eSBmb3IgZWFzaWVyIGRhdGEgYWNjZXNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgZGF0YSBmcm9tIHRoZSBzcGVjaWZpZWQgVVJMIHVzaW5nIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGZldGNoXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGZldGNoIGRhdGEgZnJvbS5cclxuICogQHBhcmFtIHtPYmplY3R9IFtyZXF1ZXN0T3B0aW9ucz17fV0gLSBPcHRpb25zIGZvciB0aGUgSFRUUC9IVFRQUyByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBIVFRQL0hUVFBTIHJlc3BvbnNlXHJcbiAqIG9iamVjdCB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmZXRjaCh1cmwsIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgX2dldFByb3RvY29sTW9kdWxlKHVybClcclxuICAgICAgLmdldCh1cmwsIHJlcXVlc3RPcHRpb25zLCAocmVzcG9uc2UpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIXJlc3BvbnNlRGF0YSkge1xyXG4gICAgICAgICAgICByZWplY3QoJ05vdGhpbmcgd2FzIGZldGNoZWQgZnJvbSB0aGUgVVJMLicpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZW5kcyBhIFBPU1QgcmVxdWVzdCB0byB0aGUgc3BlY2lmaWVkIFVSTCB3aXRoIHRoZSBwcm92aWRlZCBKU09OIGJvZHkgdXNpbmdcclxuICogZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBzZW5kIHRoZSBQT1NUIHJlcXVlc3QgdG8uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbYm9keT17fV0gLSBUaGUgSlNPTiBib2R5IHRvIGluY2x1ZGUgaW4gdGhlIFBPU1QgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3QodXJsLCBib2R5ID0ge30sIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KGJvZHkpO1xyXG5cclxuICAgIC8vIFNldCBkZWZhdWx0IGhlYWRlcnMgYW5kIG1lcmdlIHdpdGggcmVxdWVzdE9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSBPYmplY3QuYXNzaWduKFxyXG4gICAgICB7XHJcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXHJcbiAgICAgICAgaGVhZGVyczoge1xyXG4gICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcclxuICAgICAgICAgICdDb250ZW50LUxlbmd0aCc6IGRhdGEubGVuZ3RoXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICByZXF1ZXN0T3B0aW9uc1xyXG4gICAgKTtcclxuXHJcbiAgICBjb25zdCByZXF1ZXN0ID0gX2dldFByb3RvY29sTW9kdWxlKHVybClcclxuICAgICAgLnJlcXVlc3QodXJsLCBvcHRpb25zLCAocmVzcG9uc2UpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZFxyXG4gICAgICAgIHJlc3BvbnNlLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgICByZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAvLyBXcml0ZSB0aGUgcmVxdWVzdCBib2R5IGFuZCBlbmQgdGhlIHJlcXVlc3RcclxuICAgIHJlcXVlc3Qud3JpdGUoZGF0YSk7XHJcbiAgICByZXF1ZXN0LmVuZCgpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgYmFzZWQgb24gdGhlIHByb3ZpZGVkIFVSTC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9nZXRQcm90b2NvbE1vZHVsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBkZXRlcm1pbmUgdGhlIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgKGh0dHAgb3IgaHR0cHMpLlxyXG4gKi9cclxuZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlKHVybCkge1xyXG4gIHJldHVybiB1cmwuc3RhcnRzV2l0aCgnaHR0cHMnKSA/IGh0dHBzIDogaHR0cDtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGZldGNoLFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQSBjdXN0b20gZXJyb3IgY2xhc3MgZm9yIGhhbmRsaW5nIGV4cG9ydC1yZWxhdGVkIGVycm9ycy4gRXh0ZW5kcyB0aGUgbmF0aXZlXHJcbiAqIGBFcnJvcmAgY2xhc3MgdG8gaW5jbHVkZSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgbGlrZSBzdGF0dXMgY29kZSBhbmQgc3RhY2tcclxuICogdHJhY2UgZGV0YWlscy5cclxuICovXHJcbmNsYXNzIEV4cG9ydEVycm9yIGV4dGVuZHMgRXJyb3Ige1xyXG4gIC8qKlxyXG4gICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIFRoZSBlcnJvciBtZXNzYWdlIHRvIGJlIGRpc3BsYXllZC5cclxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzQ29kZSAtIE9wdGlvbmFsIEhUVFAgc3RhdHVzIGNvZGUgYXNzb2NpYXRlZFxyXG4gICAqIHdpdGggdGhlIGVycm9yIChlLmcuLCA0MDAsIDUwMCkuXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSwgc3RhdHVzQ29kZSkge1xyXG4gICAgc3VwZXIoKTtcclxuXHJcbiAgICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gICAgdGhpcy5zdGFja01lc3NhZ2UgPSBtZXNzYWdlO1xyXG5cclxuICAgIGlmIChzdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IHN0YXR1c0NvZGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXRzIG9yIHVwZGF0ZXMgdGhlIEhUVFAgc3RhdHVzIGNvZGUgZm9yIHRoZSBlcnJvci5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gVGhlIEhUVFAgc3RhdHVzIGNvZGUgdG8gYXNzaWduIHRvIHRoZSBlcnJvci5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtFeHBvcnRFcnJvcn0gVGhlIHVwZGF0ZWQgaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAgY2xhc3MuXHJcbiAgICovXHJcbiAgc2V0U3RhdHVzKHN0YXR1c0NvZGUpIHtcclxuICAgIHRoaXMuc3RhdHVzQ29kZSA9IHN0YXR1c0NvZGU7XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBTZXRzIGFkZGl0aW9uYWwgZXJyb3IgZGV0YWlscyBiYXNlZCBvbiBhbiBleGlzdGluZyBlcnJvciBvYmplY3QuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIEFuIGVycm9yIG9iamVjdCBjb250YWluaW5nIGRldGFpbHMgdG8gcG9wdWxhdGVcclxuICAgKiB0aGUgYEV4cG9ydEVycm9yYCBpbnN0YW5jZS5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtFeHBvcnRFcnJvcn0gVGhlIHVwZGF0ZWQgaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAgY2xhc3MuXHJcbiAgICovXHJcbiAgc2V0RXJyb3IoZXJyb3IpIHtcclxuICAgIHRoaXMuZXJyb3IgPSBlcnJvcjtcclxuXHJcbiAgICBpZiAoZXJyb3IubmFtZSkge1xyXG4gICAgICB0aGlzLm5hbWUgPSBlcnJvci5uYW1lO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChlcnJvci5zdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XHJcbiAgICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcclxuICAgICAgdGhpcy5zdGFjayA9IGVycm9yLnN0YWNrO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgRXhwb3J0RXJyb3I7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgY2FjaGUgbWFuYWdlciBpcyByZXNwb25zaWJsZSBmb3IgaGFuZGxpbmcgYW5kIG1hbmFnaW5nXHJcbiAqIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYWxvbmcgd2l0aCBpdHMgZGVwZW5kZW5jaWVzLiBJdCBlbnN1cmVzIHRoYXQgdGhlc2VcclxuICogcmVzb3VyY2VzIGFyZSBzdG9yZWQgYW5kIHJldHJpZXZlZCBlZmZpY2llbnRseSB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZVxyXG4gKiBhbmQgcmVkdWNlIHJlZHVuZGFudCBuZXR3b3JrIHJlcXVlc3RzLiBUaGUgY2FjaGUgaXMgc3RvcmVkIGluIHRoZSBgLmNhY2hlYFxyXG4gKiBkaXJlY3RvcnkgYnkgZGVmYXVsdCwgd2hpY2ggc2VydmVzIGFzIGEgZGVkaWNhdGVkIGZvbGRlciBmb3Iga2VlcGluZyBjYWNoZWRcclxuICogZmlsZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IEh0dHBzUHJveHlBZ2VudCB9IGZyb20gJ2h0dHBzLXByb3h5LWFnZW50JztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGZldGNoIH0gZnJvbSAnLi9mZXRjaC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGluaXRpYWwgY2FjaGUgdGVtcGxhdGVcclxuY29uc3QgY2FjaGUgPSB7XHJcbiAgY2RuVXJsOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICBhY3RpdmVNYW5pZmVzdDoge30sXHJcbiAgc291cmNlczogJycsXHJcbiAgaGNWZXJzaW9uOiAnJ1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyB0aGUgY2FjaGUgZm9yIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLCB1cGRhdGVzIHRoZSBjYWNoZSBpZiBuZWVkZWQsXHJcbiAqIGFuZCBsb2FkcyB0aGUgc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjaGVja0FuZFVwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgc2VydmVyLnByb3h5YCBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGUoXHJcbiAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgc2VydmVyUHJveHlPcHRpb25zXHJcbikge1xyXG4gIHRyeSB7XHJcbiAgICBsZXQgZmV0Y2hlZE1vZHVsZXM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjYWNoZSBwYXRoXHJcbiAgICBjb25zdCBjYWNoZVBhdGggPSBnZXRDYWNoZVBhdGgoKTtcclxuXHJcbiAgICAvLyBQcmVwYXJlIHBhdGhzIHRvIG1hbmlmZXN0IGFuZCBzb3VyY2VzIGZyb20gdGhlIGNhY2hlIGZvbGRlclxyXG4gICAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgICBjb25zdCBzb3VyY2VQYXRoID0gam9pbihjYWNoZVBhdGgsICdzb3VyY2VzLmpzJyk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBjYWNoZSBkZXN0aW5hdGlvbiBpZiBpdCBkb2Vzbid0IGV4aXN0IGFscmVhZHlcclxuICAgICFleGlzdHNTeW5jKGNhY2hlUGF0aCkgJiYgbWtkaXJTeW5jKGNhY2hlUGF0aCwgeyByZWN1cnNpdmU6IHRydWUgfSk7XHJcblxyXG4gICAgLy8gRmV0Y2ggYWxsIHRoZSBzY3JpcHRzIGVpdGhlciBpZiB0aGUgYG1hbmlmZXN0Lmpzb25gIGRvZXMgbm90IGV4aXN0XHJcbiAgICAvLyBvciBpZiB0aGUgYGZvcmNlRmV0Y2hgIG9wdGlvbiBpcyBlbmFibGVkXHJcbiAgICBpZiAoIWV4aXN0c1N5bmMobWFuaWZlc3RQYXRoKSB8fCBoaWdoY2hhcnRzT3B0aW9ucy5mb3JjZUZldGNoKSB7XHJcbiAgICAgIGxvZygzLCAnW2NhY2hlXSBGZXRjaGluZyBhbmQgY2FjaGluZyBIaWdoY2hhcnRzIGRlcGVuZGVuY2llcy4nKTtcclxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCBfdXBkYXRlQ2FjaGUoXHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgIHNvdXJjZVBhdGhcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XHJcblxyXG4gICAgICAvLyBSZWFkIHRoZSBtYW5pZmVzdCBKU09OXHJcbiAgICAgIGNvbnN0IG1hbmlmZXN0ID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMobWFuaWZlc3RQYXRoKSwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBtb2R1bGVzIGlzIGFuIGFycmF5LCBpZiBzbywgd2UgcmV3cml0ZSBpdCB0byBhIG1hcCB0byBtYWtlXHJcbiAgICAgIC8vIGl0IGVhc2llciB0byByZXNvbHZlIG1vZHVsZXMuXHJcbiAgICAgIGlmIChtYW5pZmVzdC5tb2R1bGVzICYmIEFycmF5LmlzQXJyYXkobWFuaWZlc3QubW9kdWxlcykpIHtcclxuICAgICAgICBjb25zdCBtb2R1bGVNYXAgPSB7fTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzLmZvckVhY2goKG0pID0+IChtb2R1bGVNYXBbbV0gPSAxKSk7XHJcbiAgICAgICAgbWFuaWZlc3QubW9kdWxlcyA9IG1vZHVsZU1hcDtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSBhY3R1YWwgbnVtYmVyIG9mIHNjcmlwdHMgdG8gYmUgZmV0Y2hlZFxyXG4gICAgICBjb25zdCB7IGNvcmVTY3JpcHRzLCBtb2R1bGVTY3JpcHRzLCBpbmRpY2F0b3JTY3JpcHRzIH0gPVxyXG4gICAgICAgIGhpZ2hjaGFydHNPcHRpb25zO1xyXG4gICAgICBjb25zdCBudW1iZXJPZk1vZHVsZXMgPVxyXG4gICAgICAgIGNvcmVTY3JpcHRzLmxlbmd0aCArIG1vZHVsZVNjcmlwdHMubGVuZ3RoICsgaW5kaWNhdG9yU2NyaXB0cy5sZW5ndGg7XHJcblxyXG4gICAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgaGlnaGNoYXJ0cyBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAgIC8vIElmIHRoZXJlIGFyZSBjaGFuZ2VzLCBmZXRjaCByZXF1ZXN0ZWQgbW9kdWxlcyBhbmQgcHJvZHVjdHMsXHJcbiAgICAgIC8vIGFuZCBiYWtlIHRoZW0gaW50byBhIGdpYW50IGJsb2IuIFNhdmUgdGhlIGJsb2IuXHJcbiAgICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgICdbY2FjaGVdIEEgSGlnaGNoYXJ0cyB2ZXJzaW9uIG1pc21hdGNoIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC4nXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgICAgfSBlbHNlIGlmIChcclxuICAgICAgICBPYmplY3Qua2V5cyhtYW5pZmVzdC5tb2R1bGVzIHx8IHt9KS5sZW5ndGggIT09IG51bWJlck9mTW9kdWxlc1xyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gVGhlIGNhY2hlIGFuZCB0aGUgcmVxdWVzdGVkIG1vZHVsZXMgZG8gbm90IG1hdGNoLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENoZWNrIGVhY2ggbW9kdWxlLCBpZiBhbnl0aGluZyBpcyBtaXNzaW5nIHJlZmV0Y2ggZXZlcnl0aGluZ1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSAobW9kdWxlU2NyaXB0cyB8fCBbXSkuc29tZSgobW9kdWxlTmFtZSkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFtYW5pZmVzdC5tb2R1bGVzW21vZHVsZU5hbWVdKSB7XHJcbiAgICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICAgIGBbY2FjaGVdIFRoZSAke21vZHVsZU5hbWV9IGlzIG1pc3NpbmcgaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLmBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFVwZGF0ZSBjYWNoZSBpZiBuZWVkZWRcclxuICAgICAgaWYgKHJlcXVlc3RVcGRhdGUpIHtcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICAgIGhpZ2hjaGFydHNPcHRpb25zLFxyXG4gICAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDMsICdbY2FjaGVdIERlcGVuZGVuY3kgY2FjaGUgaXMgdXAgdG8gZGF0ZSwgcHJvY2VlZGluZy4nKTtcclxuXHJcbiAgICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xyXG4gICAgICAgIGNhY2hlLnNvdXJjZXMgPSByZWFkRmlsZVN5bmMoc291cmNlUGF0aCwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IGN1cnJlbnQgbW9kdWxlcyBtYXBcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XHJcblxyXG4gICAgICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICAgICAgY2FjaGUuaGNWZXJzaW9uID0gZXh0cmFjdFZlcnNpb24oY2FjaGUuc291cmNlcyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcclxuICAgIC8vIGluIGEgc2xpZ2h0bHkgZGlmZmVyZW50IGZvcm1hdFxyXG4gICAgYXdhaXQgX3NhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHNPcHRpb25zLCBmZXRjaGVkTW9kdWxlcyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gQ291bGQgbm90IGNvbmZpZ3VyZSBjYWNoZSBhbmQgY3JlYXRlIG9yIHVwZGF0ZSB0aGUgY29uZmlnIG1hbmlmZXN0LicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgdmVyc2lvbiBvZiBIaWdoY2hhcnRzIGZyb20gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0SGlnaGNoYXJ0c1ZlcnNpb25cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNhY2hlZCBIaWdoY2hhcnRzIHZlcnNpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSB7XHJcbiAgcmV0dXJuIGNhY2hlLmhjVmVyc2lvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBpbiB0aGUgYXBwbGllZCBjb25maWd1cmF0aW9uIGFuZCBjaGVja3NcclxuICogdGhlIGNhY2hlIGZvciB0aGUgbmV3IHZlcnNpb24uXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IG5ld1ZlcnNpb24gLSBUaGUgbmV3IEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uKG5ld1ZlcnNpb24pIHtcclxuICAvLyBVcGRhdGUgdG8gdGhlIG5ldyB2ZXJzaW9uXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgaGlnaGNoYXJ0czoge1xyXG4gICAgICB2ZXJzaW9uOiBuZXdWZXJzaW9uXHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIENoZWNrIGlmIGNhY2hlIG5lZWRzIHRvIGJlIHVwZGF0ZWRcclxuICBhd2FpdCBjaGVja0FuZFVwZGF0ZUNhY2hlKG9wdGlvbnMuaGlnaGNoYXJ0cywgb3B0aW9ucy5zZXJ2ZXIucHJveHkpO1xyXG59XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgSGlnaGNoYXJ0cyB2ZXJzaW9uIGZyb20gdGhlIGNhY2hlJ3Mgc291cmNlcyBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHRyYWN0VmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2FjaGVTb3VyY2VzIC0gVGhlIGNhY2hlIHNvdXJjZXMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgZXh0cmFjdGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0VmVyc2lvbihjYWNoZVNvdXJjZXMpIHtcclxuICByZXR1cm4gY2FjaGVTb3VyY2VzXHJcbiAgICAuc3Vic3RyaW5nKDAsIGNhY2hlU291cmNlcy5pbmRleE9mKCcqLycpKVxyXG4gICAgLnJlcGxhY2UoJy8qJywgJycpXHJcbiAgICAucmVwbGFjZSgnKi8nLCAnJylcclxuICAgIC5yZXBsYWNlKC9cXG4vZywgJycpXHJcbiAgICAudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgdGhlIEhpZ2hjaGFydHMgbW9kdWxlIG5hbWUgYmFzZWQgb24gdGhlIHNjcmlwdFBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0UGF0aCAtIFRoZSBwYXRoIG9mIHRoZSBzY3JpcHQgZnJvbSB3aGljaCB0aGUgbW9kdWxlXHJcbiAqIG5hbWUgd2lsbCBiZSBleHRyYWN0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgbW9kdWxlIG5hbWUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0UGF0aCkge1xyXG4gIHJldHVybiBzY3JpcHRQYXRoLnJlcGxhY2UoXHJcbiAgICAvKC4qKVxcL3woLiopbW9kdWxlc1xcL3xzdG9ja1xcLyguKilpbmRpY2F0b3JzXFwvfG1hcHNcXC8oLiopbW9kdWxlc1xcLy9naSxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBjYWNoZSBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY2FjaGUgb2JqZWN0IGNvbnRhaW5pbmcgdmFyaW91cyBjYWNoZWQgZGF0YS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZSgpIHtcclxuICByZXR1cm4gY2FjaGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBjYWNoZSBwYXRoIGZvciBIaWdoY2hhcnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBhYnNvbHV0ZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnkgZm9yIEhpZ2hjaGFydHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoKCkge1xyXG4gIHJldHVybiBnZXRBYnNvbHV0ZVBhdGgoZ2V0T3B0aW9ucygpLmhpZ2hjaGFydHMuY2FjaGVQYXRoLCAndXRmOCcpOyAvLyAjNTYyXHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGEgc2luZ2xlIHNjcmlwdCBhbmQgdXBkYXRlcyB0aGUgYGZldGNoZWRNb2R1bGVzYCBhY2NvcmRpbmdseS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHQgLSBBIHBhdGggdG8gc2NyaXB0IHRvIGdldC5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gQWRkaXRpb25hbCBvcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnRcclxuICogdG8gdXNlIGZvciBhIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtzaG91bGRUaHJvd0Vycm9yPWZhbHNlXSAtIEEgZmxhZyB0byBpbmRpY2F0ZSBpZiB0aGUgZXJyb3JcclxuICogc2hvdWxkIGJlIHRocm93bi4gVGhpcyBzaG91bGQgYmUgdXNlZCBvbmx5IGZvciB0aGUgY29yZSBzY3JpcHRzLiBUaGUgZGVmYXVsdFxyXG4gKiB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgdGV4dCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgZmV0Y2hlZCBzY3JpcHQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhIHByb2JsZW1cclxuICogd2l0aCBmZXRjaGluZyB0aGUgc2NyaXB0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChcclxuICBzY3JpcHQsXHJcbiAgcmVxdWVzdE9wdGlvbnMsXHJcbiAgZmV0Y2hlZE1vZHVsZXMsXHJcbiAgc2hvdWxkVGhyb3dFcnJvciA9IGZhbHNlXHJcbikge1xyXG4gIC8vIEdldCByaWQgb2YgdGhlIC5qcyBmcm9tIHRoZSBjdXN0b20gc3RyaW5nc1xyXG4gIGlmIChzY3JpcHQuZW5kc1dpdGgoJy5qcycpKSB7XHJcbiAgICBzY3JpcHQgPSBzY3JpcHQuc3Vic3RyaW5nKDAsIHNjcmlwdC5sZW5ndGggLSAzKTtcclxuICB9XHJcbiAgbG9nKDQsIGBbY2FjaGVdIEZldGNoaW5nIHNjcmlwdCAtICR7c2NyaXB0fS5qc2ApO1xyXG5cclxuICAvLyBGZXRjaCB0aGUgc2NyaXB0XHJcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHtzY3JpcHR9LmpzYCwgcmVxdWVzdE9wdGlvbnMpO1xyXG5cclxuICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAgaWYgKHJlc3BvbnNlLnN0YXR1c0NvZGUgPT09IDIwMCAmJiB0eXBlb2YgcmVzcG9uc2UudGV4dCA9PSAnc3RyaW5nJykge1xyXG4gICAgaWYgKGZldGNoZWRNb2R1bGVzKSB7XHJcbiAgICAgIGNvbnN0IG1vZHVsZU5hbWUgPSBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHQpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlc1ttb2R1bGVOYW1lXSA9IDE7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gcmVzcG9uc2UudGV4dDtcclxuICB9XHJcblxyXG4gIC8vIEJhc2VkIG9uIHRoZSBgc2hvdWxkVGhyb3dFcnJvcmAgZmxhZywgZGVjaWRlIGhvdyB0byBzZXJ2ZSBlcnJvciBtZXNzYWdlXHJcbiAgaWYgKHNob3VsZFRocm93RXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbiAoc3RhdHVzIGNvZGU6ICR7cmVzcG9uc2Uuc3RhdHVzQ29kZX0pLmAsXHJcbiAgICAgIDQwNFxyXG4gICAgKS5zZXRFcnJvcihyZXNwb25zZSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxvZyhcclxuICAgICAgMixcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNhdmVzIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCBmZXRjaGVkIG1vZHVsZXMgdG8gdGhlIGNhY2hlIG1hbmlmZXN0XHJcbiAqIGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NhdmVDb25maWdUb01hbmlmZXN0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2ZldGNoZWRNb2R1bGVzPXt9XSAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzID0ge30pIHtcclxuICBjb25zdCBuZXdNYW5pZmVzdCA9IHtcclxuICAgIHZlcnNpb246IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24sXHJcbiAgICBtb2R1bGVzOiBmZXRjaGVkTW9kdWxlc1xyXG4gIH07XHJcblxyXG4gIC8vIFVwZGF0ZSBjYWNoZSBvYmplY3Qgd2l0aCB0aGUgY3VycmVudCBtb2R1bGVzXHJcbiAgY2FjaGUuYWN0aXZlTWFuaWZlc3QgPSBuZXdNYW5pZmVzdDtcclxuXHJcbiAgbG9nKDMsICdbY2FjaGVdIFdyaXRpbmcgYSBuZXcgbWFuaWZlc3QuJyk7XHJcbiAgdHJ5IHtcclxuICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgIGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdtYW5pZmVzdC5qc29uJyksXHJcbiAgICAgIEpTT04uc3RyaW5naWZ5KG5ld01hbmlmZXN0KSxcclxuICAgICAgJ3V0ZjgnXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIEVycm9yIHdyaXRpbmcgdGhlIGNhY2hlIG1hbmlmZXN0LicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBIaWdoY2hhcnRzIGBzY3JpcHRzYCBhbmQgYGN1c3RvbVNjcmlwdHNgIGZyb20gdGhlIGdpdmVuIENETnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2ZldGNoU2NyaXB0c1xyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjb3JlU2NyaXB0cyAtIEhpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBtb2R1bGVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBtb2R1bGVzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBjdXN0b21TY3JpcHRzIC0gQ3VzdG9tIHNjcmlwdCBwYXRocyB0byBmZXRjaCAoZnVsbFxyXG4gKiBVUkxzKS5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZmV0Y2hlZCBzY3JpcHRzXHJcbiAqIGNvbnRlbnQgam9pbmVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIHdoaWxlXHJcbiAqIHNldHRpbmcgYW4gSFRUUCBBZ2VudCBmb3IgcHJveHkuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzKFxyXG4gIGNvcmVTY3JpcHRzLFxyXG4gIG1vZHVsZVNjcmlwdHMsXHJcbiAgY3VzdG9tU2NyaXB0cyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgZmV0Y2hlZE1vZHVsZXNcclxuKSB7XHJcbiAgLy8gQ29uZmlndXJlIHByb3h5IGlmIGV4aXN0c1xyXG4gIGxldCBwcm94eUFnZW50O1xyXG4gIGNvbnN0IHByb3h5SG9zdCA9IHNlcnZlclByb3h5T3B0aW9ucy5ob3N0O1xyXG4gIGNvbnN0IHByb3h5UG9ydCA9IHNlcnZlclByb3h5T3B0aW9ucy5wb3J0O1xyXG5cclxuICAvLyBUcnkgdG8gY3JlYXRlIGEgUHJveHkgQWdlbnRcclxuICBpZiAocHJveHlIb3N0ICYmIHByb3h5UG9ydCkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgcHJveHlBZ2VudCA9IG5ldyBIdHRwc1Byb3h5QWdlbnQoe1xyXG4gICAgICAgIGhvc3Q6IHByb3h5SG9zdCxcclxuICAgICAgICBwb3J0OiBwcm94eVBvcnRcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tjYWNoZV0gQ291bGQgbm90IGNyZWF0ZSBhIFByb3h5IEFnZW50LicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gSWYgZXhpc3RzLCBhZGQgcHJveHkgYWdlbnQgdG8gcmVxdWVzdCBvcHRpb25zXHJcbiAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSBwcm94eUFnZW50XHJcbiAgICA/IHtcclxuICAgICAgICBhZ2VudDogcHJveHlBZ2VudCxcclxuICAgICAgICB0aW1lb3V0OiBzZXJ2ZXJQcm94eU9wdGlvbnMudGltZW91dFxyXG4gICAgICB9XHJcbiAgICA6IHt9O1xyXG5cclxuICBjb25zdCBhbGxGZXRjaFByb21pc2VzID0gW1xyXG4gICAgLi4uY29yZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcywgdHJ1ZSlcclxuICAgICksXHJcbiAgICAuLi5tb2R1bGVTY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMpXHJcbiAgICApLFxyXG4gICAgLi4uY3VzdG9tU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMpXHJcbiAgICApXHJcbiAgXTtcclxuXHJcbiAgY29uc3QgZmV0Y2hlZFNjcmlwdHMgPSBhd2FpdCBQcm9taXNlLmFsbChhbGxGZXRjaFByb21pc2VzKTtcclxuICByZXR1cm4gZmV0Y2hlZFNjcmlwdHMuam9pbignO1xcbicpO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgbG9jYWwgY2FjaGUgd2l0aCBIaWdoY2hhcnRzIHNjcmlwdHMgYW5kIHRoZWlyIHZlcnNpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF91cGRhdGVDYWNoZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaGlnaGNoYXJ0c09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgaGlnaGNoYXJ0c2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzb3VyY2VQYXRoIC0gVGhlIHBhdGggdG8gdGhlIHNvdXJjZSBmaWxlIGluIHRoZSBjYWNoZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0IHJlcHJlc2VudGluZ1xyXG4gKiB0aGUgZmV0Y2hlZCBtb2R1bGVzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gaXNzdWUgdXBkYXRpbmdcclxuICogdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfdXBkYXRlQ2FjaGUoaGlnaGNoYXJ0c09wdGlvbnMsIHNlcnZlclByb3h5T3B0aW9ucywgc291cmNlUGF0aCkge1xyXG4gIC8vIEdldCBIaWdoY2hhcnRzIHZlcnNpb24gZm9yIHNjcmlwdHNcclxuICBjb25zdCBoY1ZlcnNpb24gPVxyXG4gICAgaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbiA9PT0gJ2xhdGVzdCdcclxuICAgICAgPyBudWxsXHJcbiAgICAgIDogYCR7aGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbn1gO1xyXG5cclxuICAvLyBHZXQgdGhlIENETiB1cmwgZm9yIHNjcmlwdHNcclxuICBjb25zdCBjZG5VcmwgPSBoaWdoY2hhcnRzT3B0aW9ucy5jZG5VcmwgfHwgY2FjaGUuY2RuVXJsO1xyXG5cclxuICB0cnkge1xyXG4gICAgY29uc3QgZmV0Y2hlZE1vZHVsZXMgPSB7fTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbY2FjaGVdIFVwZGF0aW5nIGNhY2hlIHZlcnNpb24gdG8gSGlnaGNoYXJ0czogJHtoY1ZlcnNpb24gfHwgJ2xhdGVzdCd9LmBcclxuICAgICk7XHJcblxyXG4gICAgY2FjaGUuc291cmNlcyA9IGF3YWl0IF9mZXRjaFNjcmlwdHMoXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5jb3JlU2NyaXB0cy5tYXAoKGMpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb24gPyBgJHtjZG5Vcmx9LyR7aGNWZXJzaW9ufS8ke2N9YCA6IGAke2NkblVybH0vJHtjfWBcclxuICAgICAgICApXHJcbiAgICAgIF0sXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5tb2R1bGVTY3JpcHRzLm1hcCgobSkgPT5cclxuICAgICAgICAgIG0gPT09ICdtYXAnXHJcbiAgICAgICAgICAgID8gaGNWZXJzaW9uXHJcbiAgICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L21hcHMvJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbWFwcy9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgIDogaGNWZXJzaW9uXHJcbiAgICAgICAgICAgICAgPyBgJHtjZG5Vcmx9LyR7aGNWZXJzaW9ufS9tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgICAgOiBgJHtjZG5Vcmx9L21vZHVsZXMvJHttfWBcclxuICAgICAgICApLFxyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmluZGljYXRvclNjcmlwdHMubWFwKChpKSA9PlxyXG4gICAgICAgICAgaGNWZXJzaW9uXHJcbiAgICAgICAgICAgID8gYCR7Y2RuVXJsfS9zdG9jay8ke2hjVmVyc2lvbn0vaW5kaWNhdG9ycy8ke2l9YFxyXG4gICAgICAgICAgICA6IGAke2NkblVybH0vc3RvY2svaW5kaWNhdG9ycy8ke2l9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgaGlnaGNoYXJ0c09wdGlvbnMuY3VzdG9tU2NyaXB0cyxcclxuICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICBmZXRjaGVkTW9kdWxlc1xyXG4gICAgKTtcclxuXHJcbiAgICAvLyBFeHRyYWN0IGFuZCBzYXZlIHZlcnNpb24gb2YgY3VycmVudGx5IHVzZWQgSGlnaGNoYXJ0c1xyXG4gICAgY2FjaGUuaGNWZXJzaW9uID0gZXh0cmFjdFZlcnNpb24oY2FjaGUuc291cmNlcyk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgZmV0Y2hlZCBtb2R1bGVzIGludG8gY2FjaGVzJyBzb3VyY2UgSlNPTlxyXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcclxuICAgIHJldHVybiBmZXRjaGVkTW9kdWxlcztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBVbmFibGUgdG8gdXBkYXRlIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgY2hlY2tBbmRVcGRhdGVDYWNoZSxcclxuICBnZXRIaWdoY2hhcnRzVmVyc2lvbixcclxuICB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbixcclxuICBleHRyYWN0VmVyc2lvbixcclxuICBleHRyYWN0TW9kdWxlTmFtZSxcclxuICBnZXRDYWNoZSxcclxuICBnZXRDYWNoZVBhdGhcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1ldGhvZHMgZm9yIGluaXRpYWxpemluZyBIaWdoY2hhcnRzIHdpdGggY3VzdG9taXplZFxyXG4gKiBhbmltYXRpb24gc2V0dGluZ3MgYW5kIHRyaWdnZXJpbmcgdGhlIGNyZWF0aW9uIG9mIEhpZ2hjaGFydHMgY2hhcnRzIHdpdGhcclxuICogZXhwb3J0LXNwZWNpZmljIGNvbmZpZ3VyYXRpb25zIGluIHRoZSBwYWdlIGNvbnRleHQuIFN1cHBvcnRzIGR5bmFtaWMgb3B0aW9uXHJcbiAqIG1lcmdpbmcsIGN1c3RvbSBsb2dpYyBpbmplY3Rpb24sIGFuZCBjb250cm9sIG92ZXIgcmVuZGVyaW5nIGJlaGF2aW9ycy4gVXNlZFxyXG4gKiBieSB0aGUgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqL1xyXG5cclxuLyogZXNsaW50LWRpc2FibGUgbm8tdW5kZWYgKi9cclxuXHJcbi8qKlxyXG4gKiBTZXR0aW5nIHRoZSBgSGlnaGNoYXJ0cy5hbmltT2JqZWN0YCBmdW5jdGlvbi4gQ2FsbGVkIHdoZW4gaW5pdGluZyB0aGUgcGFnZS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0c1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0cygpIHtcclxuICBIaWdoY2hhcnRzLmFuaW1PYmplY3QgPSBmdW5jdGlvbiAoKSB7XHJcbiAgICByZXR1cm4geyBkdXJhdGlvbjogMCB9O1xyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIHRoZSBhY3R1YWwgSGlnaGNoYXJ0cyBjaGFydCBvbiBhIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY3JlYXRlQ2hhcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUNoYXJ0KGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEdldCByZXF1aXJlZCBmdW5jdGlvbnNcclxuICBjb25zdCB7IGdldE9wdGlvbnMsIHNldE9wdGlvbnMsIG1lcmdlLCB3cmFwIH0gPSBIaWdoY2hhcnRzO1xyXG5cclxuICAvLyBDcmVhdGUgYSBzZXBhcmF0ZSBvYmplY3QgZm9yIGEgcG90ZW50aWFsIGBzZXRPcHRpb25zYCB1c2FnZXMgaW4gb3JkZXJcclxuICAvLyB0byBwcmV2ZW50IGZyb20gcG9sbHV0aW5nIG90aGVyIGV4cG9ydHMgdGhhdCBjYW4gaGFwcGVuIG9uIHRoZSBzYW1lIHBhZ2VcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSBtZXJnZShmYWxzZSwge30sIGdldE9wdGlvbnMoKSk7XHJcblxyXG4gIC8vIE5PVEU6IElzIHRoaXMgdXNlZCBmb3IgYW55dGhpbmcgdXNlZnVsP1xyXG4gIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gZmFsc2U7XHJcbiAgd3JhcChIaWdoY2hhcnRzLkNoYXJ0LnByb3RvdHlwZSwgJ2luaXQnLCBmdW5jdGlvbiAocHJvY2VlZCwgdXNlck9wdGlvbnMsIGNiKSB7XHJcbiAgICAvLyBPdmVycmlkZSB0aGUgYHVzZXJPcHRpb25zYCB3aXRoIGltYWdlIGZyaWVuZGx5IG9wdGlvbnNcclxuICAgIHVzZXJPcHRpb25zID0gbWVyZ2UodXNlck9wdGlvbnMsIHtcclxuICAgICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgfSxcclxuICAgICAgcGxvdE9wdGlvbnM6IHtcclxuICAgICAgICBzZXJpZXM6IHtcclxuICAgICAgICAgIGxhYmVsOiB7XHJcbiAgICAgICAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICAvKiBFeHBlY3RzIHRvb2x0aXAgaW4gdGhlIGB1c2VyT3B0aW9uc2Agd2hlbiBgZm9yRXhwb3J0YCBpcyB0cnVlLlxyXG4gICAgICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL2hpZ2hjaGFydHMvYmxvYi8zYWQ0MzBhMzUzYjgwNTZiOWU3NjRhYTRlNWNkNjgyOGFhNDc5ZGIyL2pzL3BhcnRzL0NoYXJ0LmpzI0wyNDFcclxuICAgICAgICAqL1xyXG4gICAgICB0b29sdGlwOiB7fVxyXG4gICAgfSk7XHJcblxyXG4gICAgKHVzZXJPcHRpb25zLnNlcmllcyB8fCBbXSkuZm9yRWFjaChmdW5jdGlvbiAoc2VyaWVzKSB7XHJcbiAgICAgIHNlcmllcy5hbmltYXRpb24gPSBmYWxzZTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEFkZCBmbGFnIHRvIGtub3cgaWYgY2hhcnQgcmVuZGVyIGhhcyBiZWVuIGNhbGxlZC5cclxuICAgIGlmICghd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlcikge1xyXG4gICAgICB3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyID0gSGlnaGNoYXJ0cy5hZGRFdmVudCh0aGlzLCAncmVuZGVyJywgKCkgPT4ge1xyXG4gICAgICAgIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gdHJ1ZTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbdXNlck9wdGlvbnMsIGNiXSk7XHJcbiAgfSk7XHJcblxyXG4gIHdyYXAoSGlnaGNoYXJ0cy5TZXJpZXMucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCBjaGFydCwgb3B0aW9ucykge1xyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbY2hhcnQsIG9wdGlvbnNdKTtcclxuICB9KTtcclxuXHJcbiAgLy8gU29tZSBtYW5kYXRvcnkgYWRkaXRpb25hbCBgY2hhcnRgIGFuZCBgZXhwb3J0aW5nYCBvcHRpb25zXHJcbiAgY29uc3QgYWRkaXRpb25hbE9wdGlvbnMgPSB7XHJcbiAgICBjaGFydDoge1xyXG4gICAgICAvLyBCeSBkZWZhdWx0IGFuaW1hdGlvbiBpcyBkaXNhYmxlZFxyXG4gICAgICBhbmltYXRpb246IGZhbHNlLFxyXG4gICAgICAvLyBHZXQgdGhlIHJpZ2h0IHNpemUgdmFsdWVzXHJcbiAgICAgIGhlaWdodDogZXhwb3J0T3B0aW9ucy5oZWlnaHQsXHJcbiAgICAgIHdpZHRoOiBleHBvcnRPcHRpb25zLndpZHRoXHJcbiAgICB9LFxyXG4gICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgIC8vIE5vIG5lZWQgZm9yIHRoZSBleHBvcnRpbmcgYnV0dG9uXHJcbiAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IHRoZSBpbnB1dCB0byBleHBvcnQgZnJvbSB0aGUgYGluc3RyYCBvcHRpb25cclxuICBjb25zdCB1c2VyT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy5pbnN0cn1gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvblxyXG4gIGNvbnN0IHRoZW1lT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy5nbG9iYWxPcHRpb25zfWApKCk7XHJcblxyXG4gIC8vIE1lcmdlIHRoZSBmb2xsb3dpbmcgb3B0aW9ucyBvYmplY3RzIHRvIGNyZWF0ZSBmaW5hbCBvcHRpb25zXHJcbiAgY29uc3QgZmluYWxPcHRpb25zID0gbWVyZ2UoXHJcbiAgICBmYWxzZSxcclxuICAgIHRoZW1lT3B0aW9ucyxcclxuICAgIHVzZXJPcHRpb25zLFxyXG4gICAgLy8gUGxhY2VkIGl0IGhlcmUgaW5zdGVhZCBpbiB0aGUgaW5pdCBiZWNhdXNlIG9mIHRoZSBzaXplIGlzc3Vlc1xyXG4gICAgYWRkaXRpb25hbE9wdGlvbnNcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gIGNvbnN0IGZpbmFsQ2FsbGJhY2sgPSBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2tcclxuICAgID8gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2t9YCkoKVxyXG4gICAgOiBudWxsO1xyXG5cclxuICAvLyBUcmlnZ2VyIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSB7XHJcbiAgICBuZXcgRnVuY3Rpb24oJ29wdGlvbnMnLCBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkodXNlck9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBnbG9iYWwgb3B0aW9ucyBpZiBleGlzdFxyXG4gIGlmIChnbG9iYWxPcHRpb25zKSB7XHJcbiAgICBzZXRPcHRpb25zKGdsb2JhbE9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsbCB0aGUgY2hhcnQgY3JlYXRpb25cclxuICBIaWdoY2hhcnRzW2V4cG9ydE9wdGlvbnMuY29uc3RyXSgnY29udGFpbmVyJywgZmluYWxPcHRpb25zLCBmaW5hbENhbGxiYWNrKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBjdXJyZW50IGdsb2JhbCBvcHRpb25zXHJcbiAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIENsZWFyIGl0IGp1c3QgaW4gY2FzZSAoZS5nLiB0aGUgYHNldE9wdGlvbnNgIHdhcyB1c2VkIGluIHRoZSBgY3VzdG9tQ29kZWApXHJcbiAgZm9yIChjb25zdCBwcm9wIGluIGRlZmF1bHRPcHRpb25zKSB7XHJcbiAgICBpZiAodHlwZW9mIGRlZmF1bHRPcHRpb25zW3Byb3BdICE9PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgIGRlbGV0ZSBkZWZhdWx0T3B0aW9uc1twcm9wXTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgZGVmYXVsdCBvcHRpb25zIGJhY2tcclxuICBzZXRPcHRpb25zKEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaik7XHJcblxyXG4gIC8vIEVtcHR5IHRoZSBjdXN0b20gZ2xvYmFsIG9wdGlvbnMgb2JqZWN0XHJcbiAgSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqID0ge307XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzZXR1cEhpZ2hjaGFydHMsXHJcbiAgY3JlYXRlQ2hhcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgbWFuYWdpbmcgUHVwcGV0ZWVyIGJyb3dzZXJcclxuICogaW5zdGFuY2UsIGNyZWF0aW5nIGFuZCBjbGVhcmluZyBwYWdlcywgaW5qZWN0aW5nIGN1c3RvbSByZXNvdXJjZXMsXHJcbiAqIGFuZCBzZXR0aW5nIHVwIEhpZ2hjaGFydHMgZm9yIHNlcnZlci1zaWRlIHJlbmRlcmluZy4gVGhlIG1vZHVsZSBlbnN1cmVzXHJcbiAqIHRoYXQgcmVzb3VyY2VzIGFyZSBjb3JyZWN0bHkgbWFuYWdlZCBhbmQgY2FuIGhhbmRsZSBmYWlsdXJlcyBkdXJpbmdcclxuICogb3BlcmF0aW9ucyBsaWtlIGxhdW5jaGluZyB0aGUgYnJvd3NlciBvciBjcmVhdGluZyBuZXcgcGFnZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgcHVwcGV0ZWVyIGZyb20gJ3B1cHBldGVlcic7XHJcblxyXG5pbXBvcnQgeyBnZXRDYWNoZVBhdGggfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgc2V0dXBIaWdoY2hhcnRzIH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgcGFnZXNcclxuY29uc3QgdGVtcGxhdGUgPSByZWFkRmlsZVN5bmMoXHJcbiAgam9pbihfX2Rpcm5hbWUsICd0ZW1wbGF0ZXMnLCAndGVtcGxhdGUuaHRtbCcpLFxyXG4gICd1dGY4J1xyXG4pO1xyXG5cclxuLy8gVG8gc2F2ZSB0aGUgYnJvd3NlclxyXG5sZXQgYnJvd3NlciA9IG51bGw7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBleGlzdGluZyBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJyb3dzZXJcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEJyb3dzZXIoKSB7XHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBObyB2YWxpZCBicm93c2VyIGhhcyBiZWVuIGNyZWF0ZWQuJywgNTAwKTtcclxuICB9XHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVCcm93c2VyXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyXHJcbiAqIGxhdW5jaC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyXHJcbiAqIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBtYXggcmV0cmllcyB0byBvcGVuXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhcmUgcmVhY2hlZCwgb3IgaWYgbm8gYnJvd3NlciBpbnN0YW5jZSBpcyBmb3VuZCBhZnRlclxyXG4gKiByZXRyaWVzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncykge1xyXG4gIC8vIEdldCBgZGVidWdgIGFuZCBgb3RoZXJgIG9wdGlvbnNcclxuICBjb25zdCB7IGRlYnVnLCBvdGhlciB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZW5hYmxlOiBlbmFibGVkRGVidWcsIC4uLmRlYnVnT3B0aW9ucyB9ID0gZGVidWc7XHJcblxyXG4gIC8vIExhdW5jaCBvcHRpb25zIGZvciB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGNvbnN0IGxhdW5jaE9wdGlvbnMgPSB7XHJcbiAgICBoZWFkbGVzczogb3RoZXIuYnJvd3NlclNoZWxsTW9kZSA/ICdzaGVsbCcgOiB0cnVlLFxyXG4gICAgdXNlckRhdGFEaXI6ICd0bXAnLFxyXG4gICAgYXJnczogcHVwcGV0ZWVyQXJncyB8fCBbXSxcclxuICAgIGhhbmRsZVNJR0lOVDogZmFsc2UsXHJcbiAgICBoYW5kbGVTSUdURVJNOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR0hVUDogZmFsc2UsXHJcbiAgICB3YWl0Rm9ySW5pdGlhbFBhZ2U6IGZhbHNlLFxyXG4gICAgZGVmYXVsdFZpZXdwb3J0OiBudWxsLFxyXG4gICAgLi4uKGVuYWJsZWREZWJ1ZyAmJiBkZWJ1Z09wdGlvbnMpXHJcbiAgfTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlclxyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgLy8gQSBjb3VudGVyIGZvciB0aGUgYnJvd3NlcidzIGxhdW5jaCByZXRyaWVzXHJcbiAgICBsZXQgdHJ5Q291bnQgPSAwO1xyXG5cclxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbYnJvd3Nlcl0gQXR0ZW1wdGluZyB0byBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIExhdW5jaCB0aGUgYnJvd3NlclxyXG4gICAgICAgIGJyb3dzZXIgPSBhd2FpdCBwdXBwZXRlZXIubGF1bmNoKGxhdW5jaE9wdGlvbnMpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuXHJcbiAgICAgICAgICAvLyBXYWl0IGZvciBhIDQgc2Vjb25kcyBiZWZvcmUgdHJ5aW5nIGFnYWluXHJcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIDQwMDApKTtcclxuICAgICAgICAgIGF3YWl0IG9wZW4oKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIGF3YWl0IG9wZW4oKTtcclxuXHJcbiAgICAgIC8vIFNoZWxsIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChsYXVuY2hPcHRpb25zLmhlYWRsZXNzID09PSAnc2hlbGwnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBzaGVsbCBtb2RlLmApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEZWJ1ZyBtb2RlIGluZm9ybVxyXG4gICAgICBpZiAoZW5hYmxlZERlYnVnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1ticm93c2VyXSBNYXhpbXVtIHJldHJpZXMgdG8gb3BlbiBhIGJyb3dzZXIgaW5zdGFuY2UgcmVhY2hlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJywgNTAwKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGJyb3dzZXIgaW5zdGFuY2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyB0aGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgaWYgaXQgaXMgY29ubmVjdGVkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsb3NlQnJvd3NlclxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsb3NlQnJvd3NlcigpIHtcclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciB3aGVuIGNvbm5lY3RlZFxyXG4gIGlmIChicm93c2VyICYmIGJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgfVxyXG4gIGJyb3dzZXIgPSBudWxsO1xyXG4gIGxvZyg0LCAnW2Jyb3dzZXJdIENsb3NlZCB0aGUgYnJvd3Nlci4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBuZXcgUHVwcGV0ZWVyIHBhZ2Ugd2l0aGluIGFuIGV4aXN0aW5nIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqIFRoZSBmdW5jdGlvbiBjcmVhdGVzIGEgbmV3IHBhZ2UsIGRpc2FibGVzIGNhY2hpbmcsIHNldHMgY29udGVudCB1c2luZ1xyXG4gKiB0aGUgYF9zZXRQYWdlQ29udGVudCgpYCwgYW5kIHJldHVybnMgdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gbmV3UGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBgaWRgLFxyXG4gKiBgd29ya0NvdW50YCwgYW5kIGBwYWdlYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG5vIHZhbGlkIGJyb3dzZXJcclxuICogaGFzIGJlZW4gY29ubmVjdGVkIG9yIGlmIGEgcGFnZSBpcyBpbnZhbGlkIG9yIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBuZXdQYWdlKHBvb2xSZXNvdXJjZSkge1xyXG4gIC8vIEVycm9yIGluIGNhc2Ugb2Ygbm8gY29ubmVjdGVkIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIgfHwgIWJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFticm93c2VyXSBCcm93c2VyIGlzIG5vdCB5ZXQgY29ubmVjdGVkLmAsIDUwMCk7XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBwYWdlXHJcbiAgcG9vbFJlc291cmNlLnBhZ2UgPSBhd2FpdCBicm93c2VyLm5ld1BhZ2UoKTtcclxuXHJcbiAgLy8gRGlzYWJsZSBjYWNoZVxyXG4gIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IF9zZXRQYWdlQ29udGVudChwb29sUmVzb3VyY2UucGFnZSk7XHJcblxyXG4gIC8vIFNldCBwYWdlIGV2ZW50c1xyXG4gIF9zZXRQYWdlRXZlbnRzKHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIHBhZ2UgaXMgY29ycmVjdGx5IGNyZWF0ZWRcclxuICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlIHx8IHBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIFRoZSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLicsIDQwMCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLiBMb2dzXHJcbiAqIHRocm93biBlcnJvciBpZiBjbGVhcmluZyBvZiBhIHBhZ2UncyBjb250ZW50IGZhaWxzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIHBvb2wgcmVzb3VyY2UgdGhhdCBjb250YWlucyBwYWdlIGFuZCBpZC5cclxuICogQHBhcmFtIHtib29sZWFufSBbaGFyZFJlc2V0PWZhbHNlXSAtIEEgZmxhZyBpbmRpY2F0aW5nIHRoZSB0eXBlIG9mIGNsZWFyaW5nXHJcbiAqIHRvIGJlIHBlcmZvcm1lZC4gSWYgdHJ1ZSwgbmF2aWdhdGVzIHRvIGBhYm91dDpibGFua2AgYW5kIHJlc2V0cyBjb250ZW50XHJcbiAqIGFuZCBzY3JpcHRzLiBJZiBmYWxzZSwgY2xlYXJzIHRoZSBib2R5IGNvbnRlbnQgYnkgc2V0dGluZyBhIHByZWRlZmluZWQgSFRNTFxyXG4gKiBzdHJ1Y3R1cmUuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0cnVlIHdoZW4gcGFnZVxyXG4gKiBpcyBjb3JyZWN0bHkgY2xlYXJlZCBhbmQgZmFsc2Ugd2hlbiBpdCBpcyBub3QuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlKHBvb2xSZXNvdXJjZSwgaGFyZFJlc2V0ID0gZmFsc2UpIHtcclxuICB0cnkge1xyXG4gICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIGlmIChoYXJkUmVzZXQpIHtcclxuICAgICAgICAvLyBOYXZpZ2F0ZSB0byBgYWJvdXQ6YmxhbmtgXHJcbiAgICAgICAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2UuZ290bygnYWJvdXQ6YmxhbmsnLCB7XHJcbiAgICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBTZXQgdGhlIGNvbnRlbnQgYW5kIGFuZCBzY3JpcHRzIGFnYWluXHJcbiAgICAgICAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBDbGVhciBib2R5IGNvbnRlbnRcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LmlubmVySFRNTCA9XHJcbiAgICAgICAgICAgICc8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+PGRpdiBpZD1cImNvbnRhaW5lclwiPjwvZGl2PjwvZGl2Pic7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgMixcclxuICAgICAgZXJyb3IsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIENvbnRlbnQgb2YgdGhlIHBhZ2UgY291bGQgbm90IGJlIGNsZWFyZWQuYFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgcG9vbFJlc291cmNlLndvcmtDb3VudCA9IGdldE9wdGlvbnMoKS5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgfVxyXG4gIHJldHVybiBmYWxzZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgY3VzdG9tIEpTIGFuZCBDU1MgcmVzb3VyY2VzIHRvIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHJlc291cmNlcyB3aWxsXHJcbiAqIGJlIGFkZGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxBcnJheS48T2JqZWN0Pj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIGFycmF5XHJcbiAqIG9mIGluamVjdGVkIHJlc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheVxyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIC8vIFVzZSByZXNvdXJjZXNcclxuICBjb25zdCByZXNvdXJjZXMgPSBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzO1xyXG4gIGlmIChyZXNvdXJjZXMpIHtcclxuICAgIGNvbnN0IGluamVjdGVkSnMgPSBbXTtcclxuXHJcbiAgICAvLyBMb2FkIGN1c3RvbSBKUyBjb2RlXHJcbiAgICBpZiAocmVzb3VyY2VzLmpzKSB7XHJcbiAgICAgIGluamVjdGVkSnMucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmpzXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExvYWQgc2NyaXB0cyBmcm9tIGFsbCBjdXN0b20gZmlsZXNcclxuICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICAgIGNvbnN0IGlzTG9jYWwgPSBmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IGZhbHNlIDogdHJ1ZTtcclxuXHJcbiAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICBpbmplY3RlZEpzLnB1c2goXHJcbiAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgID8ge1xyXG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChmaWxlKSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGZvciAoY29uc3QganNSZXNvdXJjZSBvZiBpbmplY3RlZEpzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhqc1Jlc291cmNlKSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIFRoZSBKUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpbmplY3RlZEpzLmxlbmd0aCA9IDA7XHJcblxyXG4gICAgLy8gTG9hZCBDU1NcclxuICAgIGNvbnN0IGluamVjdGVkQ3NzID0gW107XHJcbiAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgcGF0aDogZ2V0QWJzb2x1dGVQYXRoKGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoY3NzUmVzb3VyY2UpKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICAgYFticm93c2VyXSBUaGUgQ1NTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaW5qZWN0ZWRDc3MubGVuZ3RoID0gMDtcclxuICAgIH1cclxuICB9XHJcbiAgcmV0dXJuIGluamVjdGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIG91dCBhbGwgc3RhdGUgc2V0IG9uIHRoZSBwYWdlIHdpdGggYGFkZFNjcmlwdFRhZ2AgYW5kIGBhZGRTdHlsZVRhZ2AuXHJcbiAqIFJlbW92ZXMgaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS5cclxuICogQWRkaXRpb25hbGx5LCBpdCBkZXN0cm95cyBwcmV2aW91c2x5IGV4aXN0aW5nIGNoYXJ0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtBcnJheS48T2JqZWN0Pn0gaW5qZWN0ZWRSZXNvdXJjZXMgLSBBcnJheSBvZiBpbmplY3RlZCByZXNvdXJjZXNcclxuICogdG8gYmUgY2xlYXJlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICB0cnkge1xyXG4gICAgZm9yIChjb25zdCByZXNvdXJjZSBvZiBpbmplY3RlZFJlc291cmNlcykge1xyXG4gICAgICBhd2FpdCByZXNvdXJjZS5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzIGFmdGVyIGV4cG9ydCBpcyBkb25lIGFuZCByZXNldCBhbGwgQ1NTIGFuZCBzY3JpcHQgdGFnc1xyXG4gICAgYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCB3aGVuIGRvaW5nIFNWRyBleHBvcnRzXHJcbiAgICAgIGlmICh0eXBlb2YgSGlnaGNoYXJ0cyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcclxuXHJcbiAgICAgICAgLy8gQ2hlY2sgaW4gYW55IGFscmVhZHkgZXhpc3RpbmcgY2hhcnRzXHJcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XHJcbiAgICAgICAgICAvLyBEZXN0cm95IG9sZCBjaGFydHNcclxuICAgICAgICAgIGZvciAoY29uc3Qgb2xkQ2hhcnQgb2Ygb2xkQ2hhcnRzKSB7XHJcbiAgICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcclxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICAgIEhpZ2hjaGFydHMuY2hhcnRzLnNoaWZ0KCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLnNjcmlwdHNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLCAuLi5zdHlsZXNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc3R5bGUnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5saW5rc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdsaW5rJyk7XHJcblxyXG4gICAgICAvLyBSZW1vdmUgdGFnc1xyXG4gICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgW1xyXG4gICAgICAgIC4uLnNjcmlwdHNUb1JlbW92ZSxcclxuICAgICAgICAuLi5zdHlsZXNUb1JlbW92ZSxcclxuICAgICAgICAuLi5saW5rc1RvUmVtb3ZlXHJcbiAgICAgIF0pIHtcclxuICAgICAgICBlbGVtZW50LnJlbW92ZSgpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciBwYWdlJ3MgcmVzb3VyY2VzLmApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvbnRlbnQgZm9yIGEgUHVwcGV0ZWVyIFBhZ2UgdXNpbmcgYSBwcmVkZWZpbmVkIHRlbXBsYXRlXHJcbiAqIGFuZCBhZGRpdGlvbmFsIHNjcmlwdHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VDb250ZW50XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgY29udGVudFxyXG4gKiBpcyBiZWluZyBzZXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnQocGFnZSkge1xyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBwYWdlIGNvbnRlbnRcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gIC8vIEFkZCBhbGwgcmVnaXN0ZXJlZCBIaWdjaGFydHMgc2NyaXB0cywgcXVpdGUgZGVtYW5kaW5nXHJcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBqb2luKGdldENhY2hlUGF0aCgpLCAnc291cmNlcy5qcycpIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGluaXRpYWwgYGFuaW1PYmplY3RgIGZvciBIaWdoY2hhcnRzXHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IGV2ZW50cyAobGlrZSBgcGFnZWVycm9yYCBhbmQgYGNvbnNvbGVgKSBmb3IgYSBQdXBwZXRlZXIgUGFnZSBpbiBvcmRlclxyXG4gKiB0byBjYXRjaCBhbmQgZGlzcGxheSBlcnJvcnMgYW5kIGNvbnNvbGUgbG9ncyBmcm9tIHRoZSB3aW5kb3cgY29udGV4dC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCB0aGUgbGlzdGVuZXJzXHJcbiAqIGFyZSBiZWluZyBzZXQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc2V0UGFnZUV2ZW50cyhwYWdlKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgcGFnZWVycm9yYCBsaXN0ZW5lclxyXG4gIHBhZ2Uub24oJ3BhZ2VlcnJvcicsIGFzeW5jICgpID0+IHtcclxuICAgIC8vIEl0IHdvdWxkIHNlZW0gbGlrZSB0aGlzIG1heSBmaXJlIGF0IHRoZSBzYW1lIHRpbWUgb3Igc2hvcnRseSBiZWZvcmVcclxuICAgIC8vIGEgcGFnZSBpcyBjbG9zZWQuXHJcbiAgICBpZiAocGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgY29uc29sZWAgbGlzdGVuZXIsIGlmIG5lZWRlZFxyXG4gIGlmIChkZWJ1Zy5lbmFibGUgJiYgZGVidWcubGlzdGVuVG9Db25zb2xlKSB7XHJcbiAgICBwYWdlLm9uKCdjb25zb2xlJywgKG1lc3NhZ2UpID0+IHtcclxuICAgICAgY29uc29sZS5sb2coYFtkZWJ1Z10gJHttZXNzYWdlLnRleHQoKX1gKTtcclxuICAgIH0pO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldEJyb3dzZXIsXHJcbiAgY3JlYXRlQnJvd3NlcixcclxuICBjbG9zZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSxcclxuICBjbGVhclBhZ2UsXHJcbiAgYWRkUGFnZVJlc291cmNlcyxcclxuICBjbGVhclBhZ2VSZXNvdXJjZXNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogVGhlIENTUyB0byBiZSB1c2VkIG9uIHRoZSBleHBvcnRlZCBwYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgQ1NTIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoKSA9PiBgXHJcblxyXG5odG1sLCBib2R5IHtcclxuICBtYXJnaW46IDA7XHJcbiAgcGFkZGluZzogMDtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG59XHJcblxyXG4jdGFibGUtZGl2LCAjc2xpZGVycywgI2RhdGF0YWJsZSwgI2NvbnRyb2xzLCAubGQtcm93IHtcclxuICBkaXNwbGF5OiBub25lO1xyXG4gIGhlaWdodDogMDtcclxufVxyXG5cclxuI2NoYXJ0LWNvbnRhaW5lciB7XHJcbiAgYm94LXNpemluZzogYm9yZGVyLWJveDtcclxuICBtYXJnaW46IDA7XHJcbiAgb3ZlcmZsb3c6IGF1dG87XHJcbiAgZm9udC1zaXplOiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyID4gZmlndXJlLCBkaXYge1xyXG4gIG1hcmdpbi10b3A6IDAgIWltcG9ydGFudDtcclxuICBtYXJnaW4tYm90dG9tOiAwICFpbXBvcnRhbnQ7XHJcbn1cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNzc1RlbXBsYXRlIGZyb20gJy4vY3NzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBUaGUgU1ZHIHRlbXBsYXRlIHRvIHVzZSB3aGVuIGxvYWRpbmcgU1ZHIGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIGlucHV0IGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBTVkcgdGVtcGxhdGUuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoc3ZnKSA9PiBgXHJcbjwhRE9DVFlQRSBodG1sPlxyXG48aHRtbCBsYW5nPSdlbi1VUyc+XHJcbiAgPGhlYWQ+XHJcbiAgICA8bWV0YSBodHRwLWVxdWl2PVwiQ29udGVudC1UeXBlXCIgY29udGVudD1cInRleHQvaHRtbDsgY2hhcnNldD11dGYtOFwiPlxyXG4gICAgPHRpdGxlPkhpZ2hjaGFydHMgRXhwb3J0PC90aXRsZT5cclxuICA8L2hlYWQ+XHJcbiAgPHN0eWxlPlxyXG4gICAgJHtjc3NUZW1wbGF0ZSgpfVxyXG4gIDwvc3R5bGU+XHJcbiAgPGJvZHk+XHJcbiAgICA8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+XHJcbiAgICAgICR7c3ZnfVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgaGFuZGxlcyBjaGFydCBleHBvcnQgZnVuY3Rpb25hbGl0eSB1c2luZyBQdXBwZXRlZXIuXHJcbiAqIEl0IHN1cHBvcnRzIGV4cG9ydGluZyBjaGFydHMgYXMgU1ZHLCBQTkcsIEpQRUcsIGFuZCBQREYgZm9ybWF0cy4gVGhlIG1vZHVsZVxyXG4gKiBtYW5hZ2VzIHBhZ2UgcmVzb3VyY2VzLCBzZXRzIHVwIHRoZSBleHBvcnQgZW52aXJvbm1lbnQsIGFuZCBwcm9jZXNzZXMgY2hhcnRcclxuICogY29uZmlndXJhdGlvbnMgb3IgU1ZHIGlucHV0cyBmb3IgcmVuZGVyaW5nLiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2VcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGFkZFBhZ2VSZXNvdXJjZXMsIGNsZWFyUGFnZVJlc291cmNlcyB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IGNyZWF0ZUNoYXJ0IH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IHN2Z1RlbXBsYXRlIGZyb20gJy4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8KHN0cmluZ3xCdWZmZXJ8RXhwb3J0RXJyb3IpPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXNcclxuICogdG8gdGhlIGV4cG9ydGVkIGRhdGEgb3IgcmVqZWN0aW5nIHdpdGggYW4gYEV4cG9ydEVycm9yYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGV4cG9ydCB0byBhbiB1bnN1cHBvcnRlZFxyXG4gKiBvdXRwdXQgZm9ybWF0IG9jY3Vycy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwdXBwZXRlZXJFeHBvcnQocGFnZSwgZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5IChhZGRpdGlvbmFsIEpTIGFuZCBDU1MpXHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxldCBpc1NWRyA9IGZhbHNlO1xyXG5cclxuICAgIC8vIERlY2lkZSBvbiB0aGUgZXhwb3J0IG1ldGhvZFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnKSB7XHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgU1ZHIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gSWYgdGhlIGB0eXBlYCBpcyBhbHNvIFNWRywgcmV0dXJuIHRoZSBpbnB1dFxyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAgIHJldHVybiBleHBvcnRPcHRpb25zLnN2ZztcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gTWFyayBhcyBTVkcgZXhwb3J0IGZvciB0aGUgbGF0ZXIgc2l6ZSBjb3JyZWN0aW9uc1xyXG4gICAgICBpc1NWRyA9IHRydWU7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2Uuc2V0Q29udGVudChzdmdUZW1wbGF0ZShleHBvcnRPcHRpb25zLnN2ZyksIHtcclxuICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xyXG4gICAgICB9KTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgSlNPTiBjb25maWcuJyk7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGV4cG9ydFxyXG4gICAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKGNyZWF0ZUNoYXJ0LCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXBzIHRyYWNrIG9mIGFsbCByZXNvdXJjZXMgYWRkZWQgb24gdGhlIHBhZ2Ugd2l0aCBhZGRYWFhUYWcuIGV0Y1xyXG4gICAgLy8gSXQncyBWSVRBTCB0aGF0IGFsbCBhZGRlZCByZXNvdXJjZXMgZW5kcyB1cCBoZXJlIHNvIHdlIGNhbiBjbGVhciB0aGluZ3NcclxuICAgIC8vIG91dCB3aGVuIGRvaW5nIGEgbmV3IGV4cG9ydCBpbiB0aGUgc2FtZSBwYWdlIVxyXG4gICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgLi4uKGF3YWl0IGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZWFsIGNoYXJ0IHNpemUgYW5kIHNldCB0aGUgem9vbSBhY2NvcmRpbmdseVxyXG4gICAgY29uc3Qgc2l6ZSA9IGlzU1ZHXHJcbiAgICAgID8gYXdhaXQgcGFnZS5ldmFsdWF0ZSgoc2NhbGUpID0+IHtcclxuICAgICAgICAgIGNvbnN0IHN2Z0VsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFxyXG4gICAgICAgICAgICAnI2NoYXJ0LWNvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZSdcclxuICAgICAgICAgICk7XHJcblxyXG4gICAgICAgICAgLy8gR2V0IHRoZSB2YWx1ZXMgY29ycmVjdGx5IHNjYWxlZFxyXG4gICAgICAgICAgY29uc3QgY2hhcnRIZWlnaHQgPSBzdmdFbGVtZW50LmhlaWdodC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcbiAgICAgICAgICBjb25zdCBjaGFydFdpZHRoID0gc3ZnRWxlbWVudC53aWR0aC5iYXNlVmFsLnZhbHVlICogc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gSW4gY2FzZSBvZiBTVkcgdGhlIHpvb20gbXVzdCBiZSBzZXQgZGlyZWN0bHkgZm9yIGJvZHkgYXMgc2NhbGVcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUubWFyZ2luID0gJzBweCc7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSwgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKSlcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG5cclxuICAgICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXNcclxuICAgICAgICAgIC8vIG9mIGV4cG9ydHMuIFJlc2V0IHRoZSB6b29tIGZvciBvdGhlciBleHBvcnRzIHRoYW4gdG8gU1ZHc1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY2xpcCByZWdpb24gZm9yIHRoZSBwYWdlXHJcbiAgICBjb25zdCB7IHgsIHkgfSA9IGF3YWl0IF9nZXRDbGlwUmVnaW9uKHBhZ2UpO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgaGVpZ2h0YCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0SGVpZ2h0ID0gTWF0aC5hYnMoXHJcbiAgICAgIE1hdGguY2VpbChzaXplLmNoYXJ0SGVpZ2h0IHx8IGV4cG9ydE9wdGlvbnMuaGVpZ2h0KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgZmluYWwgYHdpZHRoYCBmb3Igdmlld3BvcnRcclxuICAgIGNvbnN0IHZpZXdwb3J0V2lkdGggPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRXaWR0aCB8fCBleHBvcnRPcHRpb25zLndpZHRoKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGZpbmFsIHZpZXdwb3J0IG5vdyB0aGF0IHdlIGhhdmUgdGhlIHJlYWwgaGVpZ2h0XHJcbiAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcclxuICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgIGRldmljZVNjYWxlRmFjdG9yOiBpc1NWRyA/IDEgOiBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpXHJcbiAgICB9KTtcclxuXHJcbiAgICBsZXQgcmVzdWx0O1xyXG4gICAgLy8gUmFzdGVyaXphdGlvbiBwcm9jZXNzXHJcbiAgICBzd2l0Y2ggKGV4cG9ydE9wdGlvbnMudHlwZSkge1xyXG4gICAgICBjYXNlICdzdmcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVTVkcocGFnZSk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BuZyc6XHJcbiAgICAgIGNhc2UgJ2pwZWcnOlxyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IF9jcmVhdGVJbWFnZShcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnR5cGUsXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgICB4LFxyXG4gICAgICAgICAgICB5XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJlYWs7XHJcbiAgICAgIGNhc2UgJ3BkZic6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVBERihcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgZGVmYXVsdDpcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdDogJHtleHBvcnRPcHRpb25zLnR5cGV9LmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENsZWFyIHByZXZpb3VzbHkgaW5qZWN0ZWQgSlMgYW5kIENTUyByZXNvdXJjZXNcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gcmVzdWx0O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50XHJcbiAqIHdpdGggdGhlICdjaGFydC1jb250YWluZXInIGlkLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgeGAsIGB5YCwgYHdpZHRoYCwgYW5kIGBoZWlnaHRgIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZ2V0Q2xpcFJlZ2lvbihwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoJyNjaGFydC1jb250YWluZXInLCAoZWxlbWVudCkgPT4ge1xyXG4gICAgY29uc3QgeyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH0gPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgeCxcclxuICAgICAgeSxcclxuICAgICAgd2lkdGgsXHJcbiAgICAgIGhlaWdodDogTWF0aC50cnVuYyhoZWlnaHQgPiAxID8gaGVpZ2h0IDogNTAwKVxyXG4gICAgfTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gU1ZHIGJ5IGV2YWx1YXRpbmcgdGhlIGBvdXRlckhUTUxgIG9mIHRoZSBmaXJzdCAnc3ZnJyBlbGVtZW50XHJcbiAqIGluc2lkZSBhbiBlbGVtZW50IHdpdGggdGhlIGlkICdjb250YWluZXInLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVTVkdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBTVkcgc3RyaW5nLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVNWRyhwYWdlKSB7XHJcbiAgcmV0dXJuIHBhZ2UuJGV2YWwoXHJcbiAgICAnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsXHJcbiAgICAoZWxlbWVudCkgPT4gZWxlbWVudC5vdXRlckhUTUxcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBzY3JlZW5zaG90YCBmdW5jdGlvbmFsaXR5IHdpdGhcclxuICogc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZUltYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEltYWdlIHR5cGUuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjbGlwIC0gQ2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGltYWdlIGJ1ZmZlclxyXG4gKiBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgIGZvciB0aW1lb3V0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZUltYWdlKHBhZ2UsIHR5cGUsIGNsaXAsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgcmV0dXJuIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xyXG4gICAgICB0eXBlLFxyXG4gICAgICBjbGlwLFxyXG4gICAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICAgIGZ1bGxQYWdlOiBmYWxzZSxcclxuICAgICAgb3B0aW1pemVGb3JTcGVlZDogdHJ1ZSxcclxuICAgICAgY2FwdHVyZUJleW9uZFZpZXdwb3J0OiB0cnVlLFxyXG4gICAgICAuLi4odHlwZSAhPT0gJ3BuZycgPyB7IHF1YWxpdHk6IDgwIH0gOiB7fSksXHJcbiAgICAgIC8vIEFsd2F5cyByZW5kZXIgb24gYSB0cmFuc3BhcmVudCBwYWdlIGlmIHRoZSBleHBlY3RlZCB0eXBlIGZvcm1hdCBpcyBQTkdcclxuICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZycgLy8gIzQ0NywgIzQ2M1xyXG4gICAgfSksXHJcbiAgICBuZXcgUHJvbWlzZSgoX3Jlc29sdmUsIHJlamVjdCkgPT5cclxuICAgICAgc2V0VGltZW91dChcclxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEV4cG9ydEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnLCA0MDgpKSxcclxuICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgICAgIClcclxuICAgIClcclxuICBdKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQREYgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBgcGRmYCBmdW5jdGlvbmFsaXR5IHdpdGggc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVBERlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtudW1iZXJ9IGhlaWdodCAtIFBERiBoZWlnaHQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aCAtIFBERiB3aWR0aC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBQREYgYnVmZmVyLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2NyZWF0ZVBERihwYWdlLCBoZWlnaHQsIHdpZHRoLCByYXN0ZXJpemF0aW9uVGltZW91dCkge1xyXG4gIGF3YWl0IHBhZ2UuZW11bGF0ZU1lZGlhVHlwZSgnc2NyZWVuJyk7XHJcbiAgcmV0dXJuIHBhZ2UucGRmKHtcclxuICAgIC8vIFRoaXMgd2lsbCByZW1vdmUgYW4gZXh0cmEgZW1wdHkgcGFnZSBpbiBQREYgZXhwb3J0c1xyXG4gICAgaGVpZ2h0OiBoZWlnaHQgKyAxLFxyXG4gICAgd2lkdGgsXHJcbiAgICBlbmNvZGluZzogJ2Jhc2U2NCcsXHJcbiAgICB0aW1lb3V0OiByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBwdXBwZXRlZXJFeHBvcnRcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGEgd29ya2VyIHBvb2wgaW1wbGVtZW50YXRpb24gZm9yIG1hbmFnaW5nXHJcbiAqIHRoZSBicm93c2VyIGluc3RhbmNlIGFuZCBwYWdlcywgc3BlY2lmaWNhbGx5IGRlc2lnbmVkIGZvciB1c2Ugd2l0aFxyXG4gKiB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBvcHRpbWl6ZXMgcmVzb3VyY2VzIHVzYWdlIGFuZCBwZXJmb3JtYW5jZVxyXG4gKiBieSBtYWludGFpbmluZyBhIHBvb2wgb2Ygd29ya2VycyB0aGF0IGNhbiBoYW5kbGUgY29uY3VycmVudCBleHBvcnQgdGFza3NcclxuICogdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgY3JlYXRlQnJvd3NlciwgY2xvc2VCcm93c2VyLCBuZXdQYWdlLCBjbGVhclBhZ2UgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBwdXBwZXRlZXJFeHBvcnQgfSBmcm9tICcuL2V4cG9ydC5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXROZXdEYXRlVGltZSwgbWVhc3VyZVRpbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgcG9vbCBpbnN0YW5jZVxyXG5sZXQgcG9vbCA9IG51bGw7XHJcblxyXG4vLyBQb29sIHN0YXRpc3RpY3NcclxuY29uc3QgcG9vbFN0YXRzID0ge1xyXG4gIGV4cG9ydHNBdHRlbXB0ZWQ6IDAsXHJcbiAgZXhwb3J0c1BlcmZvcm1lZDogMCxcclxuICBleHBvcnRzRHJvcHBlZDogMCxcclxuICBleHBvcnRzRnJvbVN2ZzogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnM6IDAsXHJcbiAgZXhwb3J0c0Zyb21TdmdBdHRlbXB0czogMCxcclxuICBleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0czogMCxcclxuICB0aW1lU3BlbnQ6IDAsXHJcbiAgdGltZVNwZW50QXZlcmFnZTogMFxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNldHRpbmcgdXAgd29ya2VyIHJlc291cmNlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0UG9vbFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gZW5kaW5nIHRoZSBmdW5jdGlvblxyXG4gKiBleGVjdXRpb24gd2hlbiBhbiBhbHJlYWR5IGluaXRpYWxpemVkIHBvb2wgb2YgcmVzb3VyY2VzIGlzIGZvdW5kLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbFxyXG4gKiBvZiB3b3JrZXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRQb29sKHBvb2xPcHRpb25zLCBwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBwdXBwZXRlZXIgYXJndW1lbnRzXHJcbiAgYXdhaXQgY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBJbml0aWFsaXppbmcgcG9vbCB3aXRoIHdvcmtlcnM6IG1pbiAke3Bvb2xPcHRpb25zLm1pbldvcmtlcnN9LCBtYXggJHtwb29sT3B0aW9ucy5tYXhXb3JrZXJzfS5gXHJcbiAgICApO1xyXG5cclxuICAgIGlmIChwb29sKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgICdbcG9vbF0gQWxyZWFkeSBpbml0aWFsaXplZCwgcGxlYXNlIGtpbGwgaXQgYmVmb3JlIGNyZWF0aW5nIGEgbmV3IG9uZS4nXHJcbiAgICAgICk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBLZWVwIGFuIGV5ZSBvbiBhIGNvcnJlY3QgbWluIGFuZCBtYXggd29ya2VycyBudW1iZXJcclxuICAgIGlmIChwb29sT3B0aW9ucy5taW5Xb3JrZXJzID4gcG9vbE9wdGlvbnMubWF4V29ya2Vycykge1xyXG4gICAgICBwb29sT3B0aW9ucy5taW5Xb3JrZXJzID0gcG9vbE9wdGlvbnMubWF4V29ya2VycztcclxuICAgIH1cclxuXHJcbiAgICAvLyBDcmVhdGUgYSBwb29sIGFsb25nIHdpdGggYSBtaW5pbWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIHBvb2wgPSBuZXcgUG9vbCh7XHJcbiAgICAgIC8vIEdldCB0aGUgYGNyZWF0ZWAsIGB2YWxpZGF0ZWAsIGFuZCBgZGVzdHJveWAgZnVuY3Rpb25zXHJcbiAgICAgIC4uLl9mYWN0b3J5KHBvb2xPcHRpb25zKSxcclxuICAgICAgbWluOiBwb29sT3B0aW9ucy5taW5Xb3JrZXJzLFxyXG4gICAgICBtYXg6IHBvb2xPcHRpb25zLm1heFdvcmtlcnMsXHJcbiAgICAgIGFjcXVpcmVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5hY3F1aXJlVGltZW91dCxcclxuICAgICAgY3JlYXRlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlVGltZW91dCxcclxuICAgICAgZGVzdHJveVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmRlc3Ryb3lUaW1lb3V0LFxyXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuaWRsZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXM6IHBvb2xPcHRpb25zLmNyZWF0ZVJldHJ5SW50ZXJ2YWwsXHJcbiAgICAgIHJlYXBJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMucmVhcGVySW50ZXJ2YWwsXHJcbiAgICAgIHByb3BhZ2F0ZUNyZWF0ZUVycm9yOiBmYWxzZVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGV2ZW50c1xyXG4gICAgcG9vbC5vbigncmVsZWFzZScsIGFzeW5jIChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBDbGVhciBwYWdlXHJcbiAgICAgIGNvbnN0IGNsZWFyU3RhdHVzID0gYXdhaXQgY2xlYXJQYWdlKHJlc291cmNlLCBmYWxzZSk7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gUmVsZWFzaW5nIGEgd29ya2VyLiBDbGVhciBwYWdlIHN0YXR1czogJHtjbGVhclN0YXR1c30uYFxyXG4gICAgICApO1xyXG4gICAgfSk7XHJcblxyXG4gICAgcG9vbC5vbignZGVzdHJveVN1Y2Nlc3MnLCAoX2V2ZW50SWQsIHJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gRGVzdHJveWVkIGEgd29ya2VyIHN1Y2Nlc3NmdWxseS5gXHJcbiAgICAgICk7XHJcbiAgICAgIHJlc291cmNlLnBhZ2UgPSBudWxsO1xyXG4gICAgfSk7XHJcblxyXG4gICAgY29uc3QgaW5pdGlhbFJlc291cmNlcyA9IFtdO1xyXG4gICAgLy8gQ3JlYXRlIGFuIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwb29sT3B0aW9ucy5taW5Xb3JrZXJzOyBpKyspIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcbiAgICAgICAgaW5pdGlhbFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSBhbiBpbml0aWFsIHJlc291cmNlLicpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHJlc291cmNlKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGggPyBgIHdpdGggJHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aH0gaW5pdGlhbCByZXNvdXJjZXMgd2FpdGluZy5gIDogJy4nfWBcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1twb29sXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBjcmVhdGUgdGhlIHBvb2wgb2Ygd29ya2Vycy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFRlcm1pbmF0ZXMgYWxsIHdvcmtlcnMgaW4gdGhlIHBvb2wsIGRlc3Ryb3lzIHRoZSBwb29sLCBhbmQgY2xvc2VzIHRoZSBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGtpbGxQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIGFsbCB3b3JrZXJzIGFyZVxyXG4gKiB0ZXJtaW5hdGVkLCB0aGUgcG9vbCBpcyBkZXN0cm95ZWQsIGFuZCB0aGUgYnJvd3NlciBpcyBzdWNjZXNzZnVsbHkgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGtpbGxQb29sKCkge1xyXG4gIGxvZygzLCAnW3Bvb2xdIEtpbGxpbmcgcG9vbCB3aXRoIGFsbCB3b3JrZXJzIGFuZCBjbG9zaW5nIGJyb3dzZXIuJyk7XHJcblxyXG4gIC8vIElmIHN0aWxsIGFsaXZlLCBkZXN0cm95IHRoZSBwb29sIG9mIHBhZ2VzIGJlZm9yZSBjbG9zaW5nIGEgYnJvd3NlclxyXG4gIGlmIChwb29sKSB7XHJcbiAgICAvLyBGcmVlIHVwIG5vdCByZWxlYXNlZCB3b3JrZXJzXHJcbiAgICBmb3IgKGNvbnN0IHdvcmtlciBvZiBwb29sLnVzZWQpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlci5yZXNvdXJjZSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSB0aGUgcG9vbCBpZiBpdCBpcyBzdGlsbCBhdmFpbGFibGVcclxuICAgIGlmICghcG9vbC5kZXN0cm95ZWQpIHtcclxuICAgICAgYXdhaXQgcG9vbC5kZXN0cm95KCk7XHJcbiAgICAgIGxvZyg0LCAnW3Bvb2xdIERlc3Ryb3llZCB0aGUgcG9vbCBvZiByZXNvdXJjZXMuJyk7XHJcbiAgICB9XHJcbiAgICBwb29sID0gbnVsbDtcclxuICB9XHJcblxyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgYXdhaXQgY2xvc2VCcm93c2VyKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcm9jZXNzZXMgdGhlIGV4cG9ydCB3b3JrIHVzaW5nIGEgd29ya2VyIGZyb20gdGhlIHBvb2wuIEFjcXVpcmVzIGEgd29ya2VyXHJcbiAqIGhhbmRsZSBmcm9tIHRoZSBwb29sLCBwZXJmb3JtcyB0aGUgZXhwb3J0IHVzaW5nIHB1cHBldGVlciwgYW5kIHJlbGVhc2VzXHJcbiAqIHRoZSB3b3JrZXIgaGFuZGxlIGJhY2sgdG8gdGhlIHBvb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFdvcmtcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGV4cG9ydCByZXN1bHRcclxuICogYW5kIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0V29yayhvcHRpb25zKSB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIHN0YXJ0aW5nIHRvIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gQW4gZXhwb3J0IGF0dGVtcHQgY291bnRlZFxyXG4gICAgKytwb29sU3RhdHMuZXhwb3J0c0F0dGVtcHRlZDtcclxuXHJcbiAgICAvLyBEaXNwbGF5IHRoZSBwb29sIGluZm9ybWF0aW9uIGlmIG5lZWRlZFxyXG4gICAgaWYgKG9wdGlvbnMucG9vbC5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgZ2V0UG9vbEluZm8oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpbiBjYXNlIG9mIGxhY2tpbmcgdGhlIHBvb2wgaW5zdGFuY2VcclxuICAgIGlmICghcG9vbCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1twb29sXSBXb3JrIHJlY2VpdmVkLCBidXQgcG9vbCBoYXMgbm90IGJlZW4gc3RhcnRlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRoZSBhY3F1aXJlIGNvdW50ZXJcclxuICAgIGNvbnN0IGFjcXVpcmVDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBUcnkgdG8gYWNxdWlyZSB0aGUgd29ya2VyIGFsb25nIHdpdGggdGhlIGlkLCB3b3JrcyBjb3VudCBhbmQgcGFnZVxyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuXHJcbiAgICAgIC8vIEFjcXVpcmUgYSBwb29sIHJlc291cmNlXHJcbiAgICAgIHdvcmtlckhhbmRsZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcblxyXG4gICAgICAvLyBDaGVjayB0aGUgcGFnZSBhY3F1aXJlIHRpbWVcclxuICAgICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDUsXHJcbiAgICAgICAgICBgW2JlbmNobWFya10gJHtvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJyd9YCxcclxuICAgICAgICAgIGBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlIHRvb2sgJHthY3F1aXJlQ291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgfUVycm9yIGVuY291bnRlcmVkIHdoZW4gYWNxdWlyaW5nIGFuIGF2YWlsYWJsZSBlbnRyeTogJHthY3F1aXJlQ291bnRlcigpfW1zLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyZWQgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgIGlmICghd29ya2VySGFuZGxlLnBhZ2UpIHtcclxuICAgICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1twb29sXSBSZXNvbHZlZCB3b3JrZXIgcGFnZSBpcyBpbnZhbGlkOiB0aGUgcG9vbCBzZXR1cCBpcyB3b25reS4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgdGhlIHN0YXJ0IHRpbWVcclxuICAgIGNvbnN0IHdvcmtTdGFydCA9IGdldE5ld0RhdGVUaW1lKCk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICA0LFxyXG4gICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7d29ya2VySGFuZGxlLmlkfV0gLSBTdGFydGluZyB3b3JrIG9uIHRoaXMgcG9vbCBlbnRyeS5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFN0YXJ0IG1lYXN1cmluZyBleHBvcnQgdGltZVxyXG4gICAgY29uc3QgZXhwb3J0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnQgb24gYSBwdXBwZXRlZXIgbGV2ZWxcclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHB1cHBldGVlckV4cG9ydChcclxuICAgICAgd29ya2VySGFuZGxlLnBhZ2UsXHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICApO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIGl0J3MgYW4gZXJyb3JcclxuICAgIGlmIChyZXN1bHQgaW5zdGFuY2VvZiBFcnJvcikge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJZiB0aGVyZSdzIGEgcmFzdGVyaXphdGlvbiB0aW1lb3V0LCB3ZSB3YW50IG5lZWQgdG8gZmx1c2ggdGhlIHBhZ2UuXHJcbiAgICAgIC8vIFRoaXMgaXMgYmVjYXVzZSB0aGUgcGFnZSBtYXkgYmUgaW4gYSBzdGF0ZSB3aGVyZSBpdCdzIHdhaXRpbmcgZm9yXHJcbiAgICAgIC8vIHRoZSBzY3JlZW5zaG90IHRvIGZpbmlzaCBldmVuIHRob3VnaCB0aGUgdGltZW91dCBoYXMgb2NjdXJlZC5cclxuICAgICAgLy8gV2hpY2ggb2YgY291cnNlIGNhdXNlcyBhIGxvdCBvZiBpc3N1ZXMgd2l0aCB0aGUgZXZlbnQgc3lzdGVtLFxyXG4gICAgICAvLyBhbmQgcGFnZSBjb25zaXN0ZW5jeS5cclxuICAgICAgLy9cclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gT25seSBwYWdlLnNjcmVlbnNob3Qgd2lsbCB0aHJvdyB0aGlzLCB0aW1lb3V0cyBmb3IgUERGJ3MgYXJlXHJcbiAgICAgIC8vIGhhbmRsZWQgYnkgdGhlIHBhZ2UucGRmIGZ1bmN0aW9uIGl0c2VsZi5cclxuICAgICAgLy9cclxuICAgICAgLy8gLi4ueWVzLCB0aGlzIGlzIHVnbHkuXHJcbiAgICAgIGlmIChyZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpIHtcclxuICAgICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICAgIHdvcmtlckhhbmRsZS53b3JrQ291bnQgPSBvcHRpb25zLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICAgICAgICB3b3JrZXJIYW5kbGUucGFnZSA9IG51bGw7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChcclxuICAgICAgICByZXN1bHQubmFtZSA9PT0gJ1RpbWVvdXRFcnJvcicgfHxcclxuICAgICAgICByZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCdcclxuICAgICAgKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICAgIH1SYXN0ZXJpemF0aW9uIHRpbWVvdXQ6IHlvdXIgY2hhcnQgbWF5IGJlIHRvbyBjb21wbGV4IG9yIGxhcmdlLCBhbmQgZmFpbGVkIHRvIHJlbmRlciB3aXRoaW4gdGhlIGFsbG90dGVkIHRpbWUuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfUVycm9yIGVuY291bnRlcmVkIGR1cmluZyBleHBvcnQ6ICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApLnNldEVycm9yKHJlc3VsdCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayB0aGUgUHVwcGV0ZWVyIGV4cG9ydCB0aW1lXHJcbiAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA1LFxyXG4gICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgIGBFeHBvcnRpbmcgYSBjaGFydCBzdWNlc3NmdWxseSB0b29rICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXHJcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXHJcbiAgICBjb25zdCB3b3JrRW5kID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuICAgIGNvbnN0IGV4cG9ydFRpbWUgPSB3b3JrRW5kIC0gd29ya1N0YXJ0O1xyXG5cclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnQgKz0gZXhwb3J0VGltZTtcclxuICAgIHBvb2xTdGF0cy50aW1lU3BlbnRBdmVyYWdlID1cclxuICAgICAgcG9vbFN0YXRzLnRpbWVTcGVudCAvICsrcG9vbFN0YXRzLmV4cG9ydHNQZXJmb3JtZWQ7XHJcblxyXG4gICAgbG9nKDQsIGBbcG9vbF0gV29yayBjb21wbGV0ZWQgaW4gJHtleHBvcnRUaW1lfW1zLmApO1xyXG5cclxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gdGhlIHJlc3VsdFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzdWx0LFxyXG4gICAgICBvcHRpb25zXHJcbiAgICB9O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzRHJvcHBlZDtcclxuXHJcbiAgICBpZiAod29ya2VySGFuZGxlKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG4gICAgfVxyXG5cclxuICAgIHRocm93IGVycm9yO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZSBpZiBpbml0aWFsaXplZCwgb3IgbnVsbFxyXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sKCkge1xyXG4gIHJldHVybiBwb29sO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgc3RhdGlzdGljIG9mIGEgcG9vbCBpbnN0YWNlIGFib3V0IGV4cG9ydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sU3RhdHNcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGN1cnJlbnQgcG9vbCBzdGF0aXN0aWNzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xTdGF0cygpIHtcclxuICByZXR1cm4gcG9vbFN0YXRzO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXHJcbiAqIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZSByZXF1ZXN0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBQb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTigpIHtcclxuICByZXR1cm4ge1xyXG4gICAgbWluOiBwb29sLm1pbixcclxuICAgIG1heDogcG9vbC5tYXgsXHJcbiAgICB1c2VkOiBwb29sLm51bVVzZWQoKSxcclxuICAgIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXHJcbiAgICBhbGxDcmVhdGVkOiBwb29sLm51bVVzZWQoKSArIHBvb2wubnVtRnJlZSgpLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXM6IHBvb2wubnVtUGVuZGluZ0NyZWF0ZXMoKSxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9uczogcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSxcclxuICAgIHBlbmRpbmdEZXN0cm95czogcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoLFxyXG4gICAgYWJzb2x1dGVBbGw6XHJcbiAgICAgIHBvb2wubnVtVXNlZCgpICtcclxuICAgICAgcG9vbC5udW1GcmVlKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSArXHJcbiAgICAgIHBvb2wucGVuZGluZ0Rlc3Ryb3lzLmxlbmd0aFxyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cclxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXHJcbiAqIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mbygpIHtcclxuICBjb25zdCB7XHJcbiAgICBtaW4sXHJcbiAgICBtYXgsXHJcbiAgICB1c2VkLFxyXG4gICAgYXZhaWxhYmxlLFxyXG4gICAgYWxsQ3JlYXRlZCxcclxuICAgIHBlbmRpbmdBY3F1aXJlcyxcclxuICAgIHBlbmRpbmdDcmVhdGVzLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzLFxyXG4gICAgYWJzb2x1dGVBbGxcclxuICB9ID0gZ2V0UG9vbEluZm9KU09OKCk7XHJcblxyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21pbn0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWF4fS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHVzZWQgcmVzb3VyY2VzOiAke3VzZWR9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgZnJlZSByZXNvdXJjZXM6ICR7YXZhaWxhYmxlfS5gKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCBjcmVhdGVkICh1c2VkIGFuZCBmcmVlKSByZXNvdXJjZXM6ICR7YWxsQ3JlYXRlZH0uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBhY3F1aXJlZDogJHtwZW5kaW5nQWNxdWlyZXN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgY3JlYXRlZDogJHtwZW5kaW5nQ3JlYXRlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSB2YWxpZGF0ZWQ6ICR7cGVuZGluZ1ZhbGlkYXRpb25zfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGRlc3Ryb3llZDogJHtwZW5kaW5nRGVzdHJveXN9LmBcclxuICApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIHJlc291cmNlczogJHthYnNvbHV0ZUFsbH0uYCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGYWN0b3J5IGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhbiBvYmplY3Qgd2l0aCBgY3JlYXRlYCwgYHZhbGlkYXRlYCxcclxuICogYW5kIGBkZXN0cm95YCBmdW5jdGlvbnMgZm9yIHRoZSBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZhY3RvcnlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgXHJcbiAqIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmFjdG9yeShwb29sT3B0aW9ucykge1xyXG4gIHJldHVybiB7XHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiBjcmVhdGVcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3RcclxuICAgICAqIGNvbnRhaW5pbmcgdGhlIHdvcmtlciBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIGluaXRpYWxcclxuICAgICAqIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGFuIGVycm9yIGR1cmluZ1xyXG4gICAgICogdGhlIGNyZWF0aW9uIG9mIHRoZSBuZXcgcGFnZS5cclxuICAgICAqL1xyXG4gICAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgIC8vIEluaXQgdGhlIHJlc291cmNlIHdpdGggdW5pcXVlIGlkIGFuZCB3b3JrIGNvdW50XHJcbiAgICAgIGNvbnN0IHBvb2xSZXNvdXJjZSA9IHtcclxuICAgICAgICBpZDogdXVpZCgpLFxyXG4gICAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sT3B0aW9ucy53b3JrTGltaXQgLyAyKSlcclxuICAgICAgfTtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gU3RhcnQgbWVhc3VyaW5nIGEgcGFnZSBjcmVhdGlvbiB0aW1lXHJcbiAgICAgICAgY29uc3Qgc3RhcnREYXRlID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICAgICAgLy8gQ3JlYXRlIGEgbmV3IHBhZ2VcclxuICAgICAgICBhd2FpdCBuZXdQYWdlKHBvb2xSZXNvdXJjZSk7XHJcblxyXG4gICAgICAgIC8vIE1lYXN1cmUgdGhlIHRpbWUgb2YgZnVsbCBjcmVhdGlvbiBhbmQgY29uZmlndXJhdGlvbiBvZiBhIHBhZ2VcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIsIHRvb2sgJHtcclxuICAgICAgICAgICAgZ2V0TmV3RGF0ZVRpbWUoKSAtIHN0YXJ0RGF0ZVxyXG4gICAgICAgICAgfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBSZXR1cm4gcmVhZHkgcG9vbCByZXNvdXJjZVxyXG4gICAgICAgIHJldHVybiBwb29sUmVzb3VyY2U7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIEVycm9yIGVuY291bnRlcmVkIHdoZW4gY3JlYXRpbmcgYSBuZXcgcGFnZS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIFZhbGlkYXRlcyBhIHdvcmtlciBwYWdlIGluIHRoZSBleHBvcnQgcG9vbCwgY2hlY2tpbmcgaWYgaXQgaGFzIGV4Y2VlZGVkXHJcbiAgICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiB2YWxpZGF0ZVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgaWYgdGhlIHdvcmtlclxyXG4gICAgICogaXMgdmFsaWQgYW5kIHdpdGhpbiB0aGUgd29yayBsaW1pdDsgb3RoZXJ3aXNlLCB0byBmYWxzZS5cclxuICAgICAqL1xyXG4gICAgdmFsaWRhdGU6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSW4gY2VydGFpbiBjYXNlcyBhY3F1aXJpbmcgdGhyb3dzIGEgVGFyZ2V0Q2xvc2VFcnJvciwgd2hpY2ggbWF5XHJcbiAgICAgIC8vIGJlIGNhdXNlZCBieSB0d28gdGhpbmdzOlxyXG4gICAgICAvLyAtIFRoZSBwYWdlIGlzIGNsb3NlZCBhbmQgYXR0ZW1wdGVkIHRvIGJlIHJldXNlZC5cclxuICAgICAgLy8gLSBMb3N0IGNvbnRhY3Qgd2l0aCB0aGUgYnJvd3Nlci5cclxuICAgICAgLy9cclxuICAgICAgLy8gV2hhdCB3ZSdyZSBzZWVpbmcgaW4gbG9ncyBpcyB0aGF0IHN1Y2Nlc3NpdmUgZXhwb3J0cyB0eXBpY2FsbHlcclxuICAgICAgLy8gc3VjY2VlZHMsIGFuZCB0aGUgc2VydmVyIHJlY292ZXJzLCBpbmRpY2F0aW5nIHRoYXQgaXQncyBsaWtlbHlcclxuICAgICAgLy8gdGhlIGZpcnN0IGNhc2UuIFRoaXMgaXMgYW4gYXR0ZW1wdCBhdCBhbGxpZXZhdGluZyB0aGUgaXNzdWUgYnlcclxuICAgICAgLy8gc2ltcGx5IG5vdCB2YWxpZGF0aW5nIHRoZSB3b3JrZXIgaWYgdGhlIHBhZ2UgaXMgbnVsbCBvciBjbG9zZWQuXHJcbiAgICAgIC8vXHJcbiAgICAgIC8vIFRoZSBhY3R1YWwgcmVzdWx0IGZyb20gd2hlbiB0aGlzIGhhcHBlbmVkLCB3YXMgdGhhdCBhIHdvcmtlciB3b3VsZFxyXG4gICAgICAvLyBiZSBjb21wbGV0ZWx5IGxvY2tlZCwgc3RvcHBpbmcgaXQgZnJvbSBiZWluZyBhY3F1aXJlZCB1bnRpbFxyXG4gICAgICAvLyBpdHMgd29yayBjb3VudCByZWFjaGVkIHRoZSBsaW1pdC5cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgdmFsaWRcclxuICAgICAgaWYgKCFwb29sUmVzb3VyY2UucGFnZSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAobm8gdmFsaWQgcGFnZSBpcyBmb3VuZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHBhZ2VgIGlzIGNsb3NlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBWYWxpZGF0aW9uIGZhaWxlZCAocGFnZSBpcyBjbG9zZWQgb3IgaW52YWxpZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYG1haW5GcmFtZWAgaXMgZGV0YWNoZWRcclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlLm1haW5GcmFtZSgpLmRldGFjaGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlJ3MgZnJhbWUgaXMgZGV0YWNoZWQpLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGB3b3JrTGltaXRgIGlzIGV4Y2VlZGVkXHJcbiAgICAgIGlmIChcclxuICAgICAgICBwb29sT3B0aW9ucy53b3JrTGltaXQgJiZcclxuICAgICAgICArK3Bvb2xSZXNvdXJjZS53b3JrQ291bnQgPiBwb29sT3B0aW9ucy53b3JrTGltaXRcclxuICAgICAgKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChleGNlZWRlZCB0aGUgJHtwb29sT3B0aW9ucy53b3JrTGltaXR9IHdvcmtzIHBlciByZXNvdXJjZSBsaW1pdCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBUaGUgYHBvb2xSZXNvdXJjZWAgaXMgdmFsaWRhdGVkXHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIERlc3Ryb3lzIGEgd29ya2VyIGVudHJ5IGluIHRoZSBleHBvcnQgcG9vbCwgY2xvc2luZyBpdHMgYXNzb2NpYXRlZCBwYWdlLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGRlc3Ryb3lcclxuICAgICAqXHJcbiAgICAgKiBAcGFyYW0ge09iamVjdH0gcG9vbFJlc291cmNlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICAgKiB0aGUgd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAgICovXHJcbiAgICBkZXN0cm95OiBhc3luYyAocG9vbFJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIERlc3Ryb3lpbmcgYSB3b3JrZXIuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgaWYgKHBvb2xSZXNvdXJjZS5wYWdlICYmICFwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFJlbW92ZSBhbGwgYXR0YWNoZWQgZXZlbnQgbGlzdGVuZXJzIGZyb20gdGhlIHJlc291cmNlXHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ3BhZ2VlcnJvcicpO1xyXG4gICAgICAgICAgcG9vbFJlc291cmNlLnBhZ2UucmVtb3ZlQWxsTGlzdGVuZXJzKCdjb25zb2xlJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2ZyYW1lZGV0YWNoZWQnKTtcclxuXHJcbiAgICAgICAgICAvLyBXZSBuZWVkIHRvIHdhaXQgYXJvdW5kIGZvciB0aGlzXHJcbiAgICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5jbG9zZSgpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFBhZ2UgY291bGQgbm90IGJlIGNsb3NlZCB1cG9uIGRlc3Ryb3lpbmcuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH07XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBpbml0UG9vbCxcclxuICBraWxsUG9vbCxcclxuICBwb3N0V29yayxcclxuICBnZXRQb29sLFxyXG4gIGdldFBvb2xTdGF0cyxcclxuICBnZXRQb29sSW5mbyxcclxuICBnZXRQb29sSW5mb0pTT05cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFVzZWQgdG8gc2FuaXRpemUgdGhlIHN0cmluZ3MgY29taW5nIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVcclxuICogdG8gcHJldmVudCBYU1MgYXR0YWNrcyAod2l0aCB0aGUgRE9NUHVyaWZ5IGxpYnJhcnkpLlxyXG4gKi9cclxuXHJcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcclxuaW1wb3J0IHsgSlNET00gfSBmcm9tICdqc2RvbSc7XHJcblxyXG4vKipcclxuICogU2FuaXRpemVzIGEgZ2l2ZW4gSFRNTCBzdHJpbmcgYnkgcmVtb3ZpbmcgPHNjcmlwdD4gdGFncy4gVGhpcyBmdW5jdGlvbiB1c2VzXHJcbiAqIGEgcmVndWxhciBleHByZXNzaW9uIHRvIGZpbmQgYW5kIHJlbW92ZSBhbGwgb2NjdXJyZW5jZXMgb2YgPHNjcmlwdD48L3NjcmlwdD5cclxuICogdGFncyBhbmQgYW55IGNvbnRlbnQgd2l0aGluIHRoZW0uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzYW5pdGl6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgSFRNTCBzdHJpbmcgdG8gYmUgc2FuaXRpemVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2FuaXRpemVkIEhUTUwgc3RyaW5nLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplKGlucHV0KSB7XHJcbiAgLy8gR2V0IHRoZSB2aXJ0dWFsIERPTVxyXG4gIGNvbnN0IHdpbmRvdyA9IG5ldyBKU0RPTSgnJykud2luZG93O1xyXG5cclxuICAvLyBDcmVhdGUgYSBwdXJpZnlpbmcgaW5zdGFuY2VcclxuICBjb25zdCBwdXJpZnkgPSBET01QdXJpZnkod2luZG93KTtcclxuXHJcbiAgLy8gUmV0dXJuIHNhbml0aXplZCBpbnB1dFxyXG4gIHJldHVybiBwdXJpZnkuc2FuaXRpemUoaW5wdXQsIHsgQUREX1RBR1M6IFsnZm9yZWlnbk9iamVjdCddIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2FuaXRpemVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyB0byBwcmVwYXJlIGZvciB0aGUgZXhwb3J0aW5nIGNoYXJ0c1xyXG4gKiBpbnRvIHZhcmlvdXMgaW1hZ2Ugb3V0cHV0IGZvcm1hdHMgc3VjaCBhcyBKUEVHLCBQTkcsIFBERiwgYW5kIFNWR3MuXHJcbiAqIEl0IHN1cHBvcnRzIHNpbmdsZSBhbmQgYmF0Y2ggZXhwb3J0IG9wZXJhdGlvbnMgYW5kIGFsbG93cyBjdXN0b21pemF0aW9uXHJcbiAqIHRocm91Z2ggb3B0aW9ucyBwYXNzZWQgZnJvbSBjb25maWd1cmF0aW9ucyBvciBBUElzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGlzQWxsb3dlZENvbmZpZywgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldFBvb2xTdGF0cywga2lsbFBvb2wsIHBvc3RXb3JrIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2FuaXRpemUgfSBmcm9tICcuL3Nhbml0aXplLmpzJztcclxuaW1wb3J0IHtcclxuICBmaXhDb25zdHIsXHJcbiAgZml4T3V0ZmlsZSxcclxuICBmaXhUeXBlLFxyXG4gIGdldEFic29sdXRlUGF0aCxcclxuICBnZXRCYXNlNjQsXHJcbiAgaXNPYmplY3QsXHJcbiAgcm91bmROdW1iZXIsXHJcbiAgd3JhcEFyb3VuZFxyXG59IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBnbG9iYWwgZmxhZyBmb3IgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb25cclxubGV0IGFsbG93Q29kZUV4ZWN1dGlvbiA9IGZhbHNlO1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIHNpbmdsZSBleHBvcnQgcHJvY2VzcyBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG9wdGlvbnMgYW5kIHNhdmVzXHJcbiAqIHRoZSByZXN1bHRpbmcgaW1hZ2UgdG8gdGhlIHByb3ZpZGVkIG91dHB1dCBmaWxlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHNpbmdsZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBUaGUgb2JqZWN0IG11c3QgY29udGFpbiBhdCBsZWFzdCBvbmVcclxuICogb2YgdGhlIGZvbGxvd2luZyBgZXhwb3J0YCBwcm9wZXJ0aWVzOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYFxyXG4gKiB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgc2luZ2xlIGV4cG9ydFxyXG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIHNpbmdsZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaW5nbGVFeHBvcnQob3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBleHBvcnQgbWFrZXMgc2Vuc2VcclxuICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLmV4cG9ydCkge1xyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnRcclxuICAgIGF3YWl0IHN0YXJ0RXhwb3J0KFxyXG4gICAgICB7IGV4cG9ydDogb3B0aW9ucy5leHBvcnQsIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljIH0sXHJcbiAgICAgIGFzeW5jIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICBvdXRmaWxlIHx8IGBjaGFydC4ke3R5cGV9YCxcclxuICAgICAgICAgICAgICB0eXBlICE9PSAnc3ZnJyA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JykgOiBkYXRhLnJlc3VsdFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAgICdbY2hhcnRdIEVycm9yIHdoaWxlIHNhdmluZyBhIGNoYXJ0LicsXHJcbiAgICAgICAgICAgIDUwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBLaWxsIHBvb2wgYW5kIGNsb3NlIGJyb3dzZXIgYWZ0ZXIgZmluaXNoaW5nIHNpbmdsZSBleHBvcnRcclxuICAgICAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG4gICAgICB9XHJcbiAgICApO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIE5vIGV4cGVjdGVkIGBleHBvcnRgIG9wdGlvbnMgd2VyZSBmb3VuZC4gUGxlYXNlIHByb3ZpZGUgb25lIG9mIHRoZSBmb2xsb3dpbmcgb3B0aW9uczogYGluZmlsZWAsIGBpbnN0cmAsIGBvcHRpb25zYCwgb3IgYHN2Z2AgdG8gZ2VuZXJhdGUgYSB2YWxpZCBpbWFnZS4nLFxyXG4gICAgICA0MDBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgYmF0Y2ggZXhwb3J0IHByb2Nlc3MgZm9yIG11bHRpcGxlIGNoYXJ0cyBiYXNlZCBvbiBpbmZvcm1hdGlvblxyXG4gKiBwcm92aWRlZCBpbiB0aGUgYGJhdGNoYCBvcHRpb24uIFRoZSBgYmF0Y2hgIGlzIGEgc3RyaW5nIGluIHRoZSBmb2xsb3dpbmdcclxuICogZm9ybWF0OiBcImluZmlsZTEuanNvbj1vdXRmaWxlMS5wbmc7aW5maWxlMi5qc29uPW91dGZpbGUyLnBuZzsuLi5cIi4gUmVzdWx0c1xyXG4gKiBhcmUgc2F2ZWQgdG8gdGhlIHNwZWNpZmllZCBvdXRwdXQgZmlsZXMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYmF0Y2hFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgYG9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3NcclxuICogZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhbiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGVcclxuICogc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSXQgbXVzdCBjb250YWluIHRoZSBgYmF0Y2hgIG9wdGlvbiBmcm9tXHJcbiAqIHRoZSBgZXhwb3J0YCBzZWN0aW9uIHRvIGdlbmVyYXRlIHZhbGlkIGltYWdlcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGJhdGNoIGV4cG9ydFxyXG4gKiBwcm9jZXNzZXMgYXJlIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogYW55IG9mIHRoZSBiYXRjaCBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBiYXRjaEV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0ICYmIG9wdGlvbnMuZXhwb3J0LmJhdGNoKSB7XHJcbiAgICAvLyBBbiBhcnJheSBmb3IgY29sbGVjdGluZyBiYXRjaCBleHBvcnRzXHJcbiAgICBjb25zdCBiYXRjaEZ1bmN0aW9ucyA9IFtdO1xyXG5cclxuICAgIC8vIFNwbGl0IGFuZCBwYWlyIHRoZSBgYmF0Y2hgIGFyZ3VtZW50c1xyXG4gICAgZm9yIChsZXQgcGFpciBvZiBvcHRpb25zLmV4cG9ydC5iYXRjaC5zcGxpdCgnOycpIHx8IFtdKSB7XHJcbiAgICAgIHBhaXIgPSBwYWlyLnNwbGl0KCc9Jyk7XHJcbiAgICAgIGlmIChwYWlyLmxlbmd0aCA9PT0gMikge1xyXG4gICAgICAgIGJhdGNoRnVuY3Rpb25zLnB1c2goXHJcbiAgICAgICAgICBzdGFydEV4cG9ydChcclxuICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgICAgICAgICAgLi4ub3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgICAgICAgICBpbmZpbGU6IHBhaXJbMF0sXHJcbiAgICAgICAgICAgICAgICBvdXRmaWxlOiBwYWlyWzFdXHJcbiAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICBjdXN0b21Mb2dpYzogb3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAoZXJyb3IsIGRhdGEpID0+IHtcclxuICAgICAgICAgICAgICAvLyBFeGl0IHByb2Nlc3Mgd2hlbiBlcnJvciBleGlzdHNcclxuICAgICAgICAgICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgLy8gR2V0IHRoZSBgYjY0YCwgYG91dGZpbGVgLCBhbmQgYHR5cGVgIGZvciBhIGNoYXJ0XHJcbiAgICAgICAgICAgICAgY29uc3QgeyBiNjQsIG91dGZpbGUsIHR5cGUgfSA9IGRhdGEub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgICAgICAgICAgIC8vIFNhdmUgdGhlIHJlc3VsdFxyXG4gICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBpZiAoYjY0KSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgQmFzZTY0IHN0cmluZyB0byBhIHR4dCBmaWxlXHJcbiAgICAgICAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgICAgICAgYCR7b3V0ZmlsZS5zcGxpdCgnLicpLnNoaWZ0KCkgfHwgJ2NoYXJ0J30udHh0YCxcclxuICAgICAgICAgICAgICAgICAgICBnZXRCYXNlNjQoZGF0YS5yZXN1bHQsIHR5cGUpXHJcbiAgICAgICAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAvLyBBcyBhIGNvcnJlY3QgaW1hZ2UgZm9ybWF0XHJcbiAgICAgICAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgICAgICAgb3V0ZmlsZSxcclxuICAgICAgICAgICAgICAgICAgICB0eXBlICE9PSAnc3ZnJ1xyXG4gICAgICAgICAgICAgICAgICAgICAgPyBCdWZmZXIuZnJvbShkYXRhLnJlc3VsdCwgJ2Jhc2U2NCcpXHJcbiAgICAgICAgICAgICAgICAgICAgICA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIClcclxuICAgICAgICApO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGxvZygyLCAnW2NoYXJ0XSBObyBjb3JyZWN0IHBhaXIgZm91bmQgZm9yIHRoZSBiYXRjaCBleHBvcnQuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBBd2FpdCBhbGwgZXhwb3J0cyBhcmUgZG9uZVxyXG4gICAgY29uc3QgYmF0Y2hSZXN1bHRzID0gYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKGJhdGNoRnVuY3Rpb25zKTtcclxuXHJcbiAgICAvLyBLaWxsIHBvb2wgYW5kIGNsb3NlIGJyb3dzZXIgYWZ0ZXIgZmluaXNoaW5nIGJhdGNoIGV4cG9ydFxyXG4gICAgYXdhaXQga2lsbFBvb2woKTtcclxuXHJcbiAgICAvLyBMb2cgZXJyb3JzIGlmIGZvdW5kXHJcbiAgICBiYXRjaFJlc3VsdHMuZm9yRWFjaCgocmVzdWx0LCBpbmRleCkgPT4ge1xyXG4gICAgICAvLyBMb2cgdGhlIGVycm9yIHdpdGggc3RhY2sgYWJvdXQgdGhlIHNwZWNpZmljIGJhdGNoIGV4cG9ydFxyXG4gICAgICBpZiAocmVzdWx0LnJlYXNvbikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICByZXN1bHQucmVhc29uLFxyXG4gICAgICAgICAgYFtjaGFydF0gQmF0Y2ggZXhwb3J0IG51bWJlciAke2luZGV4ICsgMX0gY291bGQgbm90IGJlIGNvcnJlY3RseSBjb21wbGV0ZWQuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIE5vIGV4cGVjdGVkIGBleHBvcnRgIG9wdGlvbnMgd2VyZSBmb3VuZC4gUGxlYXNlIHByb3ZpZGUgdGhlIGBiYXRjaGAgb3B0aW9uIHRvIGdlbmVyYXRlIHZhbGlkIGltYWdlcy4nLFxyXG4gICAgICA0MDBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIGV4cG9ydCBwcm9jZXNzLiBUaGUgYGltYWdlT3B0aW9uc2AgcGFyYW1ldGVyIGlzIGFuIG9iamVjdCB0aGF0XHJcbiAqIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW5cclxuICogYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIElmIHBhcnRpYWxcclxuICogb3B0aW9ucyBhcmUgcHJvdmlkZWQsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBgZW5kQ2FsbGJhY2tgIGZ1bmN0aW9uIGlzIGludm9rZWQgdXBvbiB0aGUgY29tcGxldGlvbiBvZiB0aGUgZXhwb3J0LFxyXG4gKiBlaXRoZXIgc3VjY2Vzc2Z1bGx5IG9yIHdpdGggYW4gZXJyb3IuIFRoZSBgZXJyb3JgIG9iamVjdCBpcyBwcm92aWRlZFxyXG4gKiBhcyB0aGUgZmlyc3QgYXJndW1lbnQsIGFuZCB0aGUgYGRhdGFgIG9iamVjdCBpcyB0aGUgc2Vjb25kLCBjb250YWluaW5nXHJcbiAqIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YCBwcm9wZXJ0eVxyXG4gKiBhbmQgdGhlIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGluIHRoZSBgb3B0aW9uc2AgcHJvcGVydHkuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGltYWdlT3B0aW9ucyAtIFRoZSBgaW1hZ2VPcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZFxyXG4gKiBpbmNsdWRlIHNldHRpbmdzIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW5cclxuICogYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIElmIHRoZSBwcm92aWRlZFxyXG4gKiBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlcyB3aWxsIGJlIG1lcmdlZCB3aXRoIHRoZSBjdXJyZW50IGdsb2JhbFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkIHVwb25cclxuICogZmluYWxpemluZyB0aGUgZXhwb3J0IHByb2Nlc3Mgb3IgdXBvbiBlbmNvdW50ZXJpbmcgYW4gZXJyb3IuIFRoZSBmaXJzdFxyXG4gKiBhcmd1bWVudCBpcyB0aGUgYGVycm9yYCBvYmplY3QsIGFuZCB0aGUgc2Vjb25kIGFyZ3VtZW50IGlzIHRoZSBgZGF0YWAgb2JqZWN0LFxyXG4gKiB3aGljaCBpbmNsdWRlcyB0aGUgQmFzZTY0IHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCBpbiB0aGUgYHJlc3VsdGBcclxuICogcHJvcGVydHkgYW5kIHRoZSBmdWxsIHNldCBvZiBvcHRpb25zIGluIHRoZSBgb3B0aW9uc2AgcHJvcGVydHkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBUaGlzIGZ1bmN0aW9uIGRvZXMgbm90IHJldHVybiBhIHZhbHVlIGRpcmVjdGx5LlxyXG4gKiBJbnN0ZWFkLCBpdCBjb21tdW5pY2F0ZXMgcmVzdWx0cyB2aWEgdGhlIGBlbmRDYWxsYmFja2AuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhIHByb2JsZW0gd2l0aFxyXG4gKiBwcm9jZXNzaW5nIGlucHV0IG9mIGFueSB0eXBlLiBUaGUgZXJyb3IgaXMgcGFzc2VkIGludG8gdGhlIGBlbmRDYWxsYmFja2BcclxuICogZnVuY3Rpb24gYW5kIHByb2Nlc3NlZCB0aGVyZS5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydEV4cG9ydChpbWFnZU9wdGlvbnMsIGVuZENhbGxiYWNrKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHByb3ZpZGVkIG9wdGlvbnMgYXJlIGluIGFuIG9iamVjdFxyXG4gICAgaWYgKCFpc09iamVjdChpbWFnZU9wdGlvbnMpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NoYXJ0XSBJbmNvcnJlY3QgdmFsdWUgb2YgdGhlIHByb3ZpZGVkIGBpbWFnZU9wdGlvbnNgLiBOZWVkcyB0byBiZSBhbiBvYmplY3QuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBNZXJnZSBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gdGhlIGNvcHkgb2YgdGhlIGluc3RhbmNlIG9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKFxyXG4gICAgICB7XHJcbiAgICAgICAgZXhwb3J0OiBpbWFnZU9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljOiBpbWFnZU9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICAgfSxcclxuICAgICAgdHJ1ZVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGBleHBvcnRgIG9wdGlvbnNcclxuICAgIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAvLyBTdGFydGluZyBleHBvcnRpbmcgcHJvY2VzcyBtZXNzYWdlXHJcbiAgICBsb2coNCwgJ1tjaGFydF0gU3RhcnRpbmcgdGhlIGV4cG9ydGluZyBwcm9jZXNzLicpO1xyXG5cclxuICAgIC8vIEV4cG9ydCB1c2luZyBvcHRpb25zIGZyb20gdGhlIGZpbGUgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluZmlsZSAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBhIGZpbGUgaW5wdXQuJyk7XHJcblxyXG4gICAgICBsZXQgZmlsZUNvbnRlbnQ7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gVHJ5IHRvIHJlYWQgdGhlIGZpbGUgdG8gZ2V0IHRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb25cclxuICAgICAgICBmaWxlQ29udGVudCA9IHJlYWRGaWxlU3luYyhcclxuICAgICAgICAgIGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zLmluZmlsZSksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEVycm9yIGxvYWRpbmcgY29udGVudCBmcm9tIGEgZmlsZSBpbnB1dC4nLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBmaWxlJ3MgZXh0ZW5zaW9uXHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLmluZmlsZS5lbmRzV2l0aCgnLnN2ZycpKSB7XHJcbiAgICAgICAgLy8gU2V0IHRvIHRoZSBgc3ZnYCBvcHRpb25cclxuICAgICAgICBleHBvcnRPcHRpb25zLnN2ZyA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2UgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuanNvbicpKSB7XHJcbiAgICAgICAgLy8gU2V0IHRvIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgPSBmaWxlQ29udGVudDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW2NoYXJ0XSBJbmNvcnJlY3QgdmFsdWUgb2YgdGhlIGBpbmZpbGVgIG9wdGlvbi4nLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEV4cG9ydCB1c2luZyBTVkcgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLnN2ZyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBhbiBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBhdHRlbXB0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21TdmdBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIGFuIFNWRyBzdHJpbmdcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21TdmcoXHJcbiAgICAgICAgc2FuaXRpemUoZXhwb3J0T3B0aW9ucy5zdmcpLCAvLyAjMjA5XHJcbiAgICAgICAgb3B0aW9uc1xyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gU1ZHIGV4cG9ydHMgY291bnRlclxyXG4gICAgICArK2dldFBvb2xTdGF0cygpLmV4cG9ydHNGcm9tU3ZnO1xyXG5cclxuICAgICAgLy8gUGFzcyBTVkcgZXhwb3J0IHJlc3VsdCB0byB0aGUgZW5kIGNhbGxiYWNrXHJcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhudWxsLCByZXN1bHQpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEV4cG9ydCB1c2luZyBvcHRpb25zIGFzIGFuIGlucHV0XHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy5pbnN0ciAhPT0gbnVsbCB8fCBleHBvcnRPcHRpb25zLm9wdGlvbnMgIT09IG51bGwpIHtcclxuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gb3B0aW9ucyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBhdHRlbXB0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHM7XHJcblxyXG4gICAgICAvLyBFeHBvcnQgZnJvbSBvcHRpb25zXHJcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IF9leHBvcnRGcm9tT3B0aW9ucyhcclxuICAgICAgICBleHBvcnRPcHRpb25zLmluc3RyIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyxcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGV4cG9ydHMgY291bnRlclxyXG4gICAgICArK2dldFBvb2xTdGF0cygpLmV4cG9ydHNGcm9tT3B0aW9ucztcclxuXHJcbiAgICAgIC8vIFBhc3Mgb3B0aW9ucyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTm8gaW5wdXQgc3BlY2lmaWVkLCBwYXNzIGFuIGVycm9yIG1lc3NhZ2UgdG8gdGhlIGNhbGxiYWNrXHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgIG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW2NoYXJ0XSBObyB2YWxpZCBpbnB1dCBzcGVjaWZpZWQuIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMgaXMgY29ycmVjdGx5IHNldDogJ2luZmlsZScsICdpbnN0cicsICdvcHRpb25zJywgb3IgJ3N2ZycuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKVxyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYW5kIHJldHVybnMgdGhlIGN1cnJlbnQgc3RhdHVzIG9mIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUaGUgdmFsdWUgb2YgdGhlIGdsb2JhbCBgYWxsb3dDb2RlRXhlY3V0aW9uYCBvcHRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWxsb3dDb2RlRXhlY3V0aW9uKCkge1xyXG4gIHJldHVybiBhbGxvd0NvZGVFeGVjdXRpb247XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBib29sZWFuIHZhbHVlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdmFsdWUgLSBUaGUgYm9vbGVhbiB2YWx1ZSB0byBiZSBhc3NpZ25lZCB0byB0aGUgZ2xvYmFsXHJcbiAqIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb24odmFsdWUpIHtcclxuICBhbGxvd0NvZGVFeGVjdXRpb24gPSB2YWx1ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEV4cG9ydHMgZnJvbSBhbiBTVkcgYmFzZWQgaW5wdXQgd2l0aCB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZXhwb3J0RnJvbVN2Z1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBTVkcgYmFzZWQgaW5wdXQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgY29tcGxldGUgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHVua25vd24+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhIHJlc3VsdCBvZiB0aGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBub3QgYSBjb3JyZWN0IFNWR1xyXG4gKiBpbnB1dC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiBpdCBpcyBTVkdcclxuICBpZiAoXHJcbiAgICB0eXBlb2YgaW5wdXRUb0V4cG9ydCA9PT0gJ3N0cmluZycgJiZcclxuICAgIChpbnB1dFRvRXhwb3J0LmluZGV4T2YoJzxzdmcnKSA+PSAwIHx8IGlucHV0VG9FeHBvcnQuaW5kZXhPZignPD94bWwnKSA+PSAwKVxyXG4gICkge1xyXG4gICAgbG9nKDQsICdbY2hhcnRdIFBhcnNpbmcgaW5wdXQgYXMgU1ZHLicpO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZXhwb3J0IGlucHV0IGFzIFNWR1xyXG4gICAgb3B0aW9ucy5leHBvcnQuc3ZnID0gaW5wdXRUb0V4cG9ydDtcclxuXHJcbiAgICAvLyBSZXNldCB0aGUgcmVzdCBvZiB0aGUgZXhwb3J0IGlucHV0IG9wdGlvbnNcclxuICAgIG9wdGlvbnMuZXhwb3J0Lmluc3RyID0gbnVsbDtcclxuICAgIG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnMgPSBudWxsO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYW4gU1ZHIHN0cmluZyBhcyBhbiBleHBvcnQgaW5wdXRcclxuICAgIHJldHVybiBfcHJlcGFyZUV4cG9ydChvcHRpb25zKTtcclxuICB9IGVsc2Uge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2hhcnRdIE5vdCBhIGNvcnJlY3QgU1ZHIGlucHV0LicsIDQwMCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIG9wdGlvbnMgYmFzZWQgaW5wdXQgd2l0aCB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZXhwb3J0RnJvbU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0VG9FeHBvcnQgLSBUaGUgb3B0aW9ucyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3RcclxuICogY2hhcnQgb3B0aW9ucyBpbnB1dC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9ucyhpbnB1dFRvRXhwb3J0LCBvcHRpb25zKSB7XHJcbiAgbG9nKDQsICdbY2hhcnRdIFBhcnNpbmcgaW5wdXQgZnJvbSBvcHRpb25zLicpO1xyXG5cclxuICAvLyBUcnkgdG8gY2hlY2ssIHZhbGlkYXRlIGFuZCBwYXJzZSB0byBzdHJpbmdpZmllZCBvcHRpb25zXHJcbiAgY29uc3Qgc3RyaW5naWZpZWRPcHRpb25zID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgaW5wdXRUb0V4cG9ydCxcclxuICAgIHRydWUsXHJcbiAgICBvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICk7XHJcblxyXG4gIC8vIENoZWNrIGlmIGEgY29ycmVjdCBzdHJpbmdpZmllZCBvcHRpb25zXHJcbiAgaWYgKFxyXG4gICAgc3RyaW5naWZpZWRPcHRpb25zID09PSBudWxsIHx8XHJcbiAgICB0eXBlb2Ygc3RyaW5naWZpZWRPcHRpb25zICE9PSAnc3RyaW5nJyB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5zdGFydHNXaXRoKCd7JykgfHxcclxuICAgICFzdHJpbmdpZmllZE9wdGlvbnMuZW5kc1dpdGgoJ30nKVxyXG4gICkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NoYXJ0XSBJbnZhbGlkIGNvbmZpZ3VyYXRpb24gcHJvdmlkZWQgLSBPbmx5IG9wdGlvbnMgY29uZmlndXJhdGlvbnMgYW5kIFNWRyBhcmUgYWxsb3dlZCBmb3IgdGhpcyBzZXJ2ZXIuIElmIHRoaXMgaXMgeW91ciBzZXJ2ZXIsIEphdmFTY3JpcHQgY3VzdG9tIGNvZGUgY2FuIGJlIGVuYWJsZWQgYnkgc3RhcnRpbmcgdGhlIHNlcnZlciB3aXRoIHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBvcHRpb25zIHNldCB0byB0cnVlLicsXHJcbiAgICAgIDQwM1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgZXhwb3J0IGlucHV0IGFzIGEgc3RyaW5naWZpZWQgY2hhcnQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0Lmluc3RyID0gc3RyaW5naWZpZWRPcHRpb25zO1xyXG5cclxuICAvLyBSZXNldCB0aGUgcmVzdCBvZiB0aGUgZXhwb3J0IGlucHV0IG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydC5zdmcgPSBudWxsO1xyXG5cclxuICAvLyBDYWxsIHRoZSBmdW5jdGlvbiB3aXRoIGEgc3RyaW5naWZpZWQgY2hhcnQgb3B0aW9uc1xyXG4gIHJldHVybiBfcHJlcGFyZUV4cG9ydChvcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZ1bmN0aW9uIGZvciBmaW5hbGl6aW5nIG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb25zIGJlZm9yZSBleHBvcnQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpIHtcclxuICBjb25zdCB7IGV4cG9ydDogZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWM6IGN1c3RvbUxvZ2ljT3B0aW9ucyB9ID0gb3B0aW9ucztcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYHR5cGVgIG9wdGlvblxyXG4gIGV4cG9ydE9wdGlvbnMudHlwZSA9IGZpeFR5cGUoZXhwb3J0T3B0aW9ucy50eXBlLCBleHBvcnRPcHRpb25zLm91dGZpbGUpO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5vdXRmaWxlID0gZml4T3V0ZmlsZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjb25zdHJgIG9wdGlvblxyXG4gIGV4cG9ydE9wdGlvbnMuY29uc3RyID0gZml4Q29uc3RyKGV4cG9ydE9wdGlvbnMuY29uc3RyKTtcclxuXHJcbiAgLy8gTm90aWZ5IGFib3V0IHRoZSBjdXN0b20gbG9naWMgdXNhZ2Ugc3RhdHVzXHJcbiAgbG9nKFxyXG4gICAgMyxcclxuICAgIGBbY2hhcnRdIFRoZSBjdXN0b20gbG9naWMgaXMgJHtjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uID8gJ2FsbG93ZWQnIDogJ2Rpc2FsbG93ZWQnfS5gXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgKGBjdXN0b21Db2RlYCwgYGNhbGxiYWNrYCwgYHJlc291cmNlc2ApXHJcbiAgX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICAgIGV4cG9ydE9wdGlvbnMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBhbmQgYHNjYWxlYCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQgPSB7XHJcbiAgICAuLi5leHBvcnRPcHRpb25zLFxyXG4gICAgLi4uX2ZpbmRDaGFydFNpemUoZXhwb3J0T3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBQb3N0IHRoZSB3b3JrIHRvIHRoZSBwb29sXHJcbiAgcmV0dXJuIHBvc3RXb3JrKG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyB0aGUgYGhlaWdodGAsIGB3aWR0aGAgYW5kIGBzY2FsZWAgZm9yIGNoYXJ0IGV4cG9ydHMgYmFzZWRcclxuICogb24gdGhlIHByb3ZpZGVkIGV4cG9ydCBvcHRpb25zLlxyXG4gKlxyXG4gKiBUaGUgZnVuY3Rpb24gcHJpb3JpdGl6ZXMgdmFsdWVzIGluIHRoZSBmb2xsb3dpbmcgb3JkZXI6XHJcbiAqIDEuIFRoZSBgaGVpZ2h0YCwgYHdpZHRoYCwgYHNjYWxlYCBmcm9tIHRoZSBgZXhwb3J0T3B0aW9uc2AuXHJcbiAqIDIuIE9wdGlvbnMgZnJvbSB0aGUgY2hhcnQgY29uZmlndXJhdGlvbiAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDMuIE9wdGlvbnMgZnJvbSB0aGUgZ2xvYmFsIG9wdGlvbnMgKGZyb20gYGV4cG9ydGluZ2AgYW5kIGBjaGFydGApLlxyXG4gKiA0LiBPcHRpb25zIGZyb20gdGhlIHRoZW1lIG9wdGlvbnMgKGZyb20gYGV4cG9ydGluZ2AgYW5kIGBjaGFydGAgc2VjdGlvbnMpLlxyXG4gKiA1LiBGYWxsYmFjayBkZWZhdWx0IHZhbHVlcyAoYGhlaWdodCA9IDQwMGAsIGB3aWR0aCA9IDYwMGAsIGBzY2FsZSA9IDFgKS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9maW5kQ2hhcnRTaXplXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIG9iamVjdCBjb250YWluaW5nIGNhbGN1bGF0ZWQgYGhlaWdodGAsIGB3aWR0aGBcclxuICogYW5kIGBzY2FsZWAgdmFsdWVzIGZvciB0aGUgY2hhcnQgZXhwb3J0LlxyXG4gKi9cclxuZnVuY3Rpb24gX2ZpbmRDaGFydFNpemUoZXhwb3J0T3B0aW9ucykge1xyXG4gIC8vIENoZWNrIHRoZSBgb3B0aW9uc2AgYW5kIGBpbnN0cmAgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiBvcHRpb25zQ2hhcnQsIGV4cG9ydGluZzogb3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGV4cG9ydE9wdGlvbnMub3B0aW9ucyB8fCBpc0FsbG93ZWRDb25maWcoZXhwb3J0T3B0aW9ucy5pbnN0cikgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgZ2xvYmFsT3B0aW9uc2AgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiBnbG9iYWxPcHRpb25zQ2hhcnQsIGV4cG9ydGluZzogZ2xvYmFsT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnMpIHx8IGZhbHNlO1xyXG5cclxuICAvLyBDaGVjayB0aGUgYHRoZW1lT3B0aW9uc2AgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiB0aGVtZU9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiB0aGVtZU9wdGlvbnNFeHBvcnRpbmcgfSA9XHJcbiAgICBpc0FsbG93ZWRDb25maWcoZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnMpIHx8IGZhbHNlO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgc2NhbGVgIHZhbHVlOlxyXG4gIC8vIC0gSXQgY2Fubm90IGJlIGxvd2VyIHRoYW4gMC4xXHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgaGlnaGVyIHRoYW4gNS4wXHJcbiAgLy8gLSBJdCBtdXN0IGJlIHJvdW5kZWQgdG8gMiBkZWNpbWFsIHBsYWNlcyAoZS5nLiAwLjIzMjM0IC0+IDAuMjMpXHJcbiAgY29uc3Qgc2NhbGUgPSByb3VuZE51bWJlcihcclxuICAgIE1hdGgubWF4KFxyXG4gICAgICAwLjEsXHJcbiAgICAgIE1hdGgubWluKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc2NhbGUgfHxcclxuICAgICAgICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5kZWZhdWx0U2NhbGUgfHxcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgNS4wXHJcbiAgICAgIClcclxuICAgICksXHJcbiAgICAyXHJcbiAgKTtcclxuXHJcbiAgLy8gRmluZCB0aGUgYGhlaWdodGAgdmFsdWVcclxuICBjb25zdCBoZWlnaHQgPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5oZWlnaHQgfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8uaGVpZ2h0IHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRIZWlnaHQgfHxcclxuICAgIDQwMDtcclxuXHJcbiAgLy8gRmluZCB0aGUgYHdpZHRoYCB2YWx1ZVxyXG4gIGNvbnN0IHdpZHRoID1cclxuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py53aWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRXaWR0aCB8fFxyXG4gICAgNjAwO1xyXG5cclxuICAvLyBHYXRoZXIgYGhlaWdodGAsIGB3aWR0aGAgYW5kIGBzY2FsZWAgaW5mb3JtYXRpb24gaW4gb25lIG9iamVjdFxyXG4gIGNvbnN0IHNpemUgPSB7IGhlaWdodCwgd2lkdGgsIHNjYWxlIH07XHJcblxyXG4gIC8vIEdldCByaWQgb2YgcG90ZW50aWFsIGBweGAgYW5kIGAlYFxyXG4gIGZvciAobGV0IFtwYXJhbSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHNpemUpKSB7XHJcbiAgICBzaXplW3BhcmFtXSA9XHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgPyArdmFsdWUucmVwbGFjZSgvcHh8JS9naSwgJycpIDogdmFsdWU7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIHNpemUgb2JqZWN0XHJcbiAgcmV0dXJuIHNpemU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleGVjdXRpb24gb2YgY3VzdG9tIGxvZ2ljIG9wdGlvbnMsIGluY2x1ZGluZyBsb2FkaW5nIGByZXNvdXJjZXNgLFxyXG4gKiBgY3VzdG9tQ29kZWAsIGFuZCBgY2FsbGJhY2tgLiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLCBpdCBwcm9jZXNzZXNcclxuICogdGhlIGN1c3RvbSBsb2dpYyBvcHRpb25zIGFjY29yZGluZ2x5LiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBub3QgYWxsb3dlZCxcclxuICogaXQgZGlzYWJsZXMgdGhlIHVzYWdlIG9mIHJlc291cmNlcywgY3VzdG9tIGNvZGUgYW5kIGNhbGxiYWNrLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY29kZSBleGVjdXRpb25cclxuICogaXMgbm90IGFsbG93ZWQgYnV0IGN1c3RvbSBsb2dpYyBvcHRpb25zIGFyZSBzdGlsbCBwcm92aWRlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVDdXN0b21Mb2dpYyhjdXN0b21Mb2dpY09wdGlvbnMsIGFsbG93Q29kZUV4ZWN1dGlvbikge1xyXG4gIC8vIEluIGNhc2Ugb2YgYWxsb3dpbmcgY29kZSBleGVjdXRpb25cclxuICBpZiAoYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgcmVzb3VyY2VzYCBvcHRpb25cclxuICAgIGlmICh0eXBlb2YgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gQ3VzdG9tIHN0cmluZ2lmaWVkIHJlc291cmNlc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmICghY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIExvYWQgdGhlIGRlZmF1bHQgb25lXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9IF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKCdyZXNvdXJjZXMuanNvbicpLCAndXRmOCcpLFxyXG4gICAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgIHRydWVcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZygyLCAnW2NoYXJ0XSBVbmFibGUgdG8gbG9hZCB0aGUgZGVmYXVsdCBgcmVzb3VyY2VzLmpzb25gIGZpbGUuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBUcnkgdG8gbG9hZCBjdXN0b20gY29kZSBhbmQgd3JhcCBhcm91bmQgaXQgaW4gYSBzZWxmIGludm9raW5nIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY3VzdG9tQ29kZWAgY2Fubm90IGJlIGxvYWRlZC4nKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUHJvY2VzcyB0aGUgYGNhbGxiYWNrYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGNhbGxiYWNrIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IHdyYXBBcm91bmQoXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW2NoYXJ0XSBUaGUgYGNhbGxiYWNrYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjdXN0b21Db2RlYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBjdXN0b21Db2RlYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjYWxsYmFja2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjaykpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGNhbGxiYWNrYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGByZXNvdXJjZXNgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgcmVzb3VyY2VzYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIElmIHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBmbGFnIGlzIHNldCB0byBmYWxzZSwgd2Ugc2hvdWxkIHJlZnVzZVxyXG4gICAgLy8gdGhlIHVzYWdlIG9mIHRoZSBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYCwgYW5kIGBjdXN0b21Db2RlYCBvcHRpb25zLlxyXG4gICAgLy8gQWRkaXRpb25hbGx5LCB0aGUgd29ya2VyIHdpbGwgcmVmdXNlIHRvIHJ1biBhcmJpdHJhcnkgSmF2YVNjcmlwdC5cclxuICAgIGlmIChcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGVcclxuICAgICkge1xyXG4gICAgICAvLyBSZXNldCBhbGwgY3VzdG9tIGNvZGUgb3B0aW9uc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gbnVsbDtcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBudWxsO1xyXG5cclxuICAgICAgLy8gU2VuZCBhIG1lc3NhZ2Ugc2F5aW5nIHRoYXQgdGhlIGV4cG9ydGVyIGRvZXMgbm90IHN1cHBvcnQgdGhlc2Ugc2V0dGluZ3NcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJywgYW5kICdjdXN0b21Db2RlJyBvcHRpb25zIGhhdmUgYmVlbiBkaXNhYmxlZCBmb3IgdGhpcyBzZXJ2ZXIuYCxcclxuICAgICAgICA0MDNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIGFuZCB2YWxpZGF0ZXMgcmVzb3VyY2VzIGZyb20gdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3IgZXhwb3J0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8c3RyaW5nfG51bGwpfSBbcmVzb3VyY2VzPW51bGxdIC0gVGhlIHJlc291cmNlcyB0byBiZSBoYW5kbGVkLlxyXG4gKiBDYW4gYmUgZWl0aGVyIGEgSlNPTiBvYmplY3QsIHN0cmluZ2lmaWVkIEpTT04sIGEgcGF0aCB0byBhIEpTT04gZmlsZSxcclxuICogb3IgbnVsbC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYG51bGxgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8bnVsbCl9IFRoZSBoYW5kbGVkIHJlc291cmNlcyBvciBudWxsIGlmIG5vIHZhbGlkIHJlc291cmNlc1xyXG4gKiBhcmUgZm91bmQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gIHJlc291cmNlcyA9IG51bGwsXHJcbiAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4pIHtcclxuICAvLyBMaXN0IG9mIGFsbG93ZWQgc2VjdGlvbnMgaW4gdGhlIHJlc291cmNlcyBKU09OXHJcbiAgY29uc3QgYWxsb3dlZFByb3BzID0gWydqcycsICdjc3MnLCAnZmlsZXMnXTtcclxuXHJcbiAgbGV0IGhhbmRsZWRSZXNvdXJjZXMgPSByZXNvdXJjZXM7XHJcbiAgbGV0IGNvcnJlY3RSZXNvdXJjZXMgPSBmYWxzZTtcclxuXHJcbiAgLy8gVHJ5IHRvIGxvYWQgcmVzb3VyY2VzIGZyb20gYSBmaWxlXHJcbiAgaWYgKGFsbG93RmlsZVJlc291cmNlcyAmJiByZXNvdXJjZXMuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChyZXNvdXJjZXMpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCB7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0FsbG93ZWRDb25maWcocmVzb3VyY2VzLCBmYWxzZSwgYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgICAvLyBHZXQgcmlkIG9mIHRoZSBmaWxlcyBzZWN0aW9uXHJcbiAgICBpZiAoaGFuZGxlZFJlc291cmNlcyAmJiAhYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRmlsdGVyIGZyb20gdW5uZWNlc3NhcnkgcHJvcGVydGllc1xyXG4gIGZvciAoY29uc3QgcHJvcE5hbWUgaW4gaGFuZGxlZFJlc291cmNlcykge1xyXG4gICAgaWYgKCFhbGxvd2VkUHJvcHMuaW5jbHVkZXMocHJvcE5hbWUpKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzW3Byb3BOYW1lXTtcclxuICAgIH0gZWxzZSBpZiAoIWNvcnJlY3RSZXNvdXJjZXMpIHtcclxuICAgICAgY29ycmVjdFJlc291cmNlcyA9IHRydWU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBhdCBsZWFzdCBvbmUgb2YgYWxsb3dlZCBwcm9wZXJ0aWVzIGlzIHByZXNlbnRcclxuICBpZiAoIWNvcnJlY3RSZXNvdXJjZXMpIHtcclxuICAgIHJldHVybiBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gSGFuZGxlIGZpbGVzIHNlY3Rpb25cclxuICBpZiAoaGFuZGxlZFJlc291cmNlcy5maWxlcykge1xyXG4gICAgaGFuZGxlZFJlc291cmNlcy5maWxlcyA9IGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMubWFwKChpdGVtKSA9PiBpdGVtLnRyaW0oKSk7XHJcbiAgICBpZiAoIWhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgfHwgaGFuZGxlZFJlc291cmNlcy5maWxlcy5sZW5ndGggPD0gMCkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiByZXNvdXJjZXNcclxuICByZXR1cm4gaGFuZGxlZFJlc291cmNlcztcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGxvYWRpbmcgYW5kIHZhbGlkYXRpb24gb2YgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2BcclxuICogaW4gdGhlIGV4cG9ydCBvcHRpb25zLiBJZiB0aGUgb3B0aW9uIGlzIGEgc3RyaW5nIGFuZCByZWZlcmVuY2VzIGEgSlNPTiBmaWxlXHJcbiAqICh3aGVuIHRoZSBgYWxsb3dGaWxlUmVzb3VyY2VzYCBpcyB0cnVlKSwgaXQgcmVhZHMgYW5kIHBhcnNlcyB0aGUgZmlsZS5cclxuICogT3RoZXJ3aXNlLCBpdCBhdHRlbXB0cyB0byBwYXJzZSB0aGUgc3RyaW5nIG9yIG9iamVjdCBhcyBKU09OLiBJZiBhbnkgZXJyb3JzXHJcbiAqIG9jY3VyIGR1cmluZyB0aGlzIHByb2Nlc3MsIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuIElmIHRoZXJlIGlzIGFuIGVycm9yXHJcbiAqIGxvYWRpbmcgb3IgcGFyc2luZyB0aGUgYGdsb2JhbE9wdGlvbnNgIG9yIGB0aGVtZU9wdGlvbnNgLCB0aGUgZXJyb3IgaXMgbG9nZ2VkXHJcbiAqIGFuZCB0aGUgb3B0aW9uIGlzIHNldCB0byBudWxsLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUdsb2JhbEFuZFRoZW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGxvYWRpbmdcclxuICogcmVzb3VyY2VzIGZyb20gZmlsZXMgaXMgYWxsb3dlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWUoXHJcbiAgZXhwb3J0T3B0aW9ucyxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIENoZWNrIHRoZSBgZ2xvYmFsT3B0aW9uc2AgYW5kIGB0aGVtZU9wdGlvbnNgIG9wdGlvbnNcclxuICBbJ2dsb2JhbE9wdGlvbnMnLCAndGhlbWVPcHRpb25zJ10uZm9yRWFjaCgob3B0aW9uc05hbWUpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBvcHRpb24gZXhpc3RzXHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSkge1xyXG4gICAgICAgIC8vIENoZWNrIGlmIGl0IGlzIGEgc3RyaW5nIGFuZCBhIGZpbGUgbmFtZSB3aXRoIHRoZSBgLmpzb25gIGV4dGVuc2lvblxyXG4gICAgICAgIGlmIChcclxuICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyAmJlxyXG4gICAgICAgICAgdHlwZW9mIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0uZW5kc1dpdGgoJy5qc29uJylcclxuICAgICAgICApIHtcclxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSBmaWxlIGNvbnRlbnQgY2FuIGJlIGEgY29uZmlnLCBhbmQgc2F2ZSBpdCBhcyBhIHN0cmluZ1xyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0pLCAndXRmOCcpLFxyXG4gICAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSB2YWx1ZSBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0sXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGVycm9yLFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSBcXGAke29wdGlvbnNOYW1lfVxcYCBjYW5ub3QgYmUgbG9hZGVkLmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IG51bGw7XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGlzIHRoZSBgZ2xvYmFsT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnMpKSB7XHJcbiAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3B0aW9uIGZvdW5kLicpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGB0aGVtZU9wdGlvbnNgIHByZXNlbnRcclxuICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnMpKSB7XHJcbiAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgdGhlbWVPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIGdldEFsbG93Q29kZUV4ZWN1dGlvbixcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBpbnRlcnZhbHNcclxuICogYW5kIHRpbWVvdXRzIGluIGEgY2VudHJhbGl6ZWQgbWFubmVyLiBJdCBtYWludGFpbnMgYSByZWdpc3RyeSBvZiBhbGwgYWN0aXZlXHJcbiAqIHRpbWVycyBhbmQgYWxsb3dzIGZvciB0aGVpciBlZmZpY2llbnQgY2xlYW51cCB3aGVuIG5lZWRlZC4gVGhpcyBjYW4gYmUgdXNlZnVsXHJcbiAqIGluIGFwcGxpY2F0aW9ucyB3aGVyZSBwcm9wZXIgcmVzb3VyY2UgbWFuYWdlbWVudCBhbmQgY2xlYW4gc2h1dGRvd24gb2YgdGltZXJzXHJcbiAqIGFyZSBjcml0aWNhbCB0byBhdm9pZCBtZW1vcnkgbGVha3Mgb3IgdW5pbnRlbmRlZCBiZWhhdmlvci5cclxuICovXHJcblxyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG4vLyBBcnJheSB0aGF0IGNvbnRhaW5zIGlkcyBvZiBhbGwgb25nb2luZyBpbnRlcnZhbHMgYW5kIHRpbWVvdXRzXHJcbmNvbnN0IHRpbWVySWRzID0gW107XHJcblxyXG4vKipcclxuICogQWRkcyBpZCBvZiB0aGUgYHNldEludGVydmFsYCBvciBgc2V0VGltZW91dGAgYW5kIHRvIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gYWRkVGltZXJcclxuICpcclxuICogQHBhcmFtIHtOb2RlSlMuVGltZW91dH0gaWQgLSBJZCBvZiBhbiBpbnRlcnZhbCBvciBhIHRpbWVvdXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gYWRkVGltZXIoaWQpIHtcclxuICB0aW1lcklkcy5wdXNoKGlkKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbGwgb2Ygb25nb2luZyBpbnRlcnZhbHMgYW5kIHRpbWVvdXRzIGJ5IGlkcyBnYXRoZXJlZFxyXG4gKiBpbiB0aGUgYHRpbWVySWRzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIGNsZWFyQWxsVGltZXJzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnMoKSB7XHJcbiAgbG9nKDQsIGBbdGltZXJdIENsZWFyaW5nIGFsbCByZWdpc3RlcmVkIGludGVydmFscyBhbmQgdGltZW91dHMuYCk7XHJcbiAgZm9yIChjb25zdCBpZCBvZiB0aW1lcklkcykge1xyXG4gICAgY2xlYXJJbnRlcnZhbChpZCk7XHJcbiAgICBjbGVhclRpbWVvdXQoaWQpO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGFkZFRpbWVyLFxyXG4gIGNsZWFyQWxsVGltZXJzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtaWRkbGV3YXJlIGZ1bmN0aW9ucyBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZXNcclxuICogYW5kIGhhbmRsaW5nIGVycm9yIHJlc3BvbnNlcyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2dXaXRoU3RhY2sgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIGxvZ2dpbmcgZXJyb3JzIHdpdGggc3RhY2sgdHJhY2UgYW5kIGhhbmRsaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbG9nRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9IFRoZSBjYWxsIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24gd2l0aFxyXG4gKiB0aGUgcGFzc2VkIGVycm9yLlxyXG4gKi9cclxuZnVuY3Rpb24gbG9nRXJyb3JNaWRkbGV3YXJlKGVycm9yLCByZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIC8vIERpc3BsYXkgdGhlIGVycm9yIHdpdGggc3RhY2sgaW4gYSBjb3JyZWN0IGZvcm1hdFxyXG4gIGxvZ1dpdGhTdGFjaygxLCBlcnJvcik7XHJcblxyXG4gIC8vIERlbGV0ZSB0aGUgc3RhY2sgZm9yIHRoZSBlbnZpcm9ubWVudCBvdGhlciB0aGFuIHRoZSBkZXZlbG9wbWVudFxyXG4gIGlmIChnZXRPcHRpb25zKCkub3RoZXIubm9kZUVudiAhPT0gJ2RldmVsb3BtZW50Jykge1xyXG4gICAgZGVsZXRlIGVycm9yLnN0YWNrO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsbCB0aGUgYHJldHVybkVycm9yTWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gIHJldHVybiBuZXh0KGVycm9yKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHJldHVybmluZyBlcnJvciByZXNwb25zZS5cclxuICpcclxuICogQGZ1bmN0aW9uIHJldHVybkVycm9yTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKi9cclxuZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlKGVycm9yLCByZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIC8vIEdhdGhlciBhbGwgcmVxdWllZCBpbmZvcm1hdGlvbiBmb3IgdGhlIHJlc3BvbnNlXHJcbiAgY29uc3QgeyBtZXNzYWdlLCBzdGFjayB9ID0gZXJyb3I7XHJcblxyXG4gIC8vIFVzZSB0aGUgZXJyb3IncyBzdGF0dXMgY29kZSBvciB0aGUgZGVmYXVsdCA0MDBcclxuICBjb25zdCBzdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZSB8fCA0MDA7XHJcblxyXG4gIC8vIFNldCBhbmQgcmV0dXJuIHJlc3BvbnNlXHJcbiAgcmVzcG9uc2Uuc3RhdHVzKHN0YXR1c0NvZGUpLmpzb24oeyBzdGF0dXNDb2RlLCBtZXNzYWdlLCBzdGFjayB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGVycm9yIG1pZGRsZXdhcmVzIHRvIHRoZSBwYXNzZWQgZXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gZXJyb3JNaWRkbGV3YXJlKGFwcCkge1xyXG4gIC8vIEFkZCBsb2cgZXJyb3IgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UobG9nRXJyb3JNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHNldCBzdGF0dXMgYW5kIHJldHVybiBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShyZXR1cm5FcnJvck1pZGRsZXdhcmUpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtaWRkbGV3YXJlIGZ1bmN0aW9ucyBmb3IgY29uZmlndXJpbmcgYW5kIGVuYWJsaW5nIHJhdGVcclxuICogbGltaXRpbmcgaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgcmF0ZUxpbWl0IGZyb20gJ2V4cHJlc3MtcmF0ZS1saW1pdCc7XHJcblxyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgZW5hYmxpbmcgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc3BlY2lmaWVkIEV4cHJlc3MgYXBwLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHJhdGVMaW1pdGluZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0XHJcbiAqIHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgcmF0ZUxpbWl0aW5nT3B0aW9ucykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBDaGVjayBpZiB0aGUgcmF0ZSBsaW1pdGluZyBpcyBlbmFibGVkIGFuZCB0aGUgYXBwIGV4aXN0c1xyXG4gICAgaWYgKGFwcCAmJiByYXRlTGltaXRpbmdPcHRpb25zLmVuYWJsZSkge1xyXG4gICAgICBjb25zdCBtZXNzYWdlID1cclxuICAgICAgICAnVG9vIG1hbnkgcmVxdWVzdHMsIHlvdSBoYXZlIGJlZW4gcmF0ZSBsaW1pdGVkLiBQbGVhc2UgdHJ5IGFnYWluIGxhdGVyLic7XHJcblxyXG4gICAgICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICAgIGNvbnN0IHJhdGVPcHRpb25zID0ge1xyXG4gICAgICAgIHdpbmRvdzogcmF0ZUxpbWl0aW5nT3B0aW9ucy53aW5kb3cgfHwgMSxcclxuICAgICAgICBtYXhSZXF1ZXN0czogcmF0ZUxpbWl0aW5nT3B0aW9ucy5tYXhSZXF1ZXN0cyB8fCAzMCxcclxuICAgICAgICBkZWxheTogcmF0ZUxpbWl0aW5nT3B0aW9ucy5kZWxheSB8fCAwLFxyXG4gICAgICAgIHRydXN0UHJveHk6IHJhdGVMaW1pdGluZ09wdGlvbnMudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgICAgICBza2lwS2V5OiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBLZXkgfHwgbnVsbCxcclxuICAgICAgICBza2lwVG9rZW46IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcFRva2VuIHx8IG51bGxcclxuICAgICAgfTtcclxuXHJcbiAgICAgIC8vIFNldCBpZiBiZWhpbmQgYSBwcm94eVxyXG4gICAgICBpZiAocmF0ZU9wdGlvbnMudHJ1c3RQcm94eSkge1xyXG4gICAgICAgIGFwcC5lbmFibGUoJ3RydXN0IHByb3h5Jyk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENyZWF0ZSBhIGxpbWl0ZXJcclxuICAgICAgY29uc3QgbGltaXRlciA9IHJhdGVMaW1pdCh7XHJcbiAgICAgICAgLy8gVGltZSBmcmFtZSBmb3Igd2hpY2ggcmVxdWVzdHMgYXJlIGNoZWNrZWQgYW5kIHJlbWVtYmVyZWRcclxuICAgICAgICB3aW5kb3dNczogcmF0ZU9wdGlvbnMud2luZG93ICogNjAgKiAxMDAwLFxyXG4gICAgICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciBgd2luZG93TXNgXHJcbiAgICAgICAgbGltaXQ6IHJhdGVPcHRpb25zLm1heFJlcXVlc3RzLFxyXG4gICAgICAgIC8vIERpc2FibGUgZGVsYXlpbmcsIGZ1bGwgc3BlZWQgdW50aWwgdGhlIG1heCBsaW1pdCBpcyByZWFjaGVkXHJcbiAgICAgICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICAgICAgaGFuZGxlcjogKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5mb3JtYXQoe1xyXG4gICAgICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZCh7IG1lc3NhZ2UgfSk7XHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIGRlZmF1bHQ6ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKG1lc3NhZ2UpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICB9LFxyXG4gICAgICAgIHNraXA6IChyZXF1ZXN0KSA9PiB7XHJcbiAgICAgICAgICAvLyBBbGxvdyBieXBhc3NpbmcgdGhlIGxpbWl0ZXIgaWYgYSB2YWxpZCBrZXkvdG9rZW4gaGFzIGJlZW4gc2VudFxyXG4gICAgICAgICAgaWYgKFxyXG4gICAgICAgICAgICByYXRlT3B0aW9ucy5za2lwS2V5ICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBUb2tlbiAhPT0gbnVsbCAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmtleSA9PT0gcmF0ZU9wdGlvbnMuc2tpcEtleSAmJlxyXG4gICAgICAgICAgICByZXF1ZXN0LnF1ZXJ5LmFjY2Vzc190b2tlbiA9PT0gcmF0ZU9wdGlvbnMuc2tpcFRva2VuXHJcbiAgICAgICAgICApIHtcclxuICAgICAgICAgICAgbG9nKDQsICdbcmF0ZSBsaW1pdGluZ10gU2tpcHBpbmcgcmF0ZSBsaW1pdGVyLicpO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgLy8gVXNlIGEgbGltaXRlciBhcyBhIG1pZGRsZXdhcmVcclxuICAgICAgYXBwLnVzZShsaW1pdGVyKTtcclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcmF0ZSBsaW1pdGluZ10gRW5hYmxlZCByYXRlIGxpbWl0aW5nIHdpdGggJHtyYXRlT3B0aW9ucy5tYXhSZXF1ZXN0c30gcmVxdWVzdHMgcGVyICR7cmF0ZU9wdGlvbnMud2luZG93fSBtaW51dGUgZm9yIGVhY2ggSVAsIHRydXN0aW5nIHByb3h5OiAke3JhdGVPcHRpb25zLnRydXN0UHJveHl9LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3JhdGUgbGltaXRpbmddIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHNldCB0aGUgcmF0ZSBsaW1pdGluZyBvcHRpb25zLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBQcm92aWRlcyBtaWRkbGV3YXJlIGZ1bmN0aW9ucyBmb3IgdmFsaWRhdGluZyBpbmNvbWluZyBIVFRQIHJlcXVlc3RzXHJcbiAqIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uIFRoaXMgbW9kdWxlIGVuc3VyZXMgdGhhdCByZXF1ZXN0cyBjb250YWluXHJcbiAqIGFwcHJvcHJpYXRlIGNvbnRlbnQgdHlwZXMgYW5kIHZhbGlkIHJlcXVlc3QgYm9kaWVzLCBpbmNsdWRpbmcgcHJvcGVyIEpTT05cclxuICogc3RydWN0dXJlcyBhbmQgY2hhcnQgZGF0YSBmb3IgZXhwb3J0cy4gSXQgY2hlY2tzIGZvciBwb3RlbnRpYWwgaXNzdWVzIHN1Y2hcclxuICogYXMgbWlzc2luZyBvciBtYWxmb3JtZWQgZGF0YSwgcHJpdmF0ZSByYW5nZSBVUkxzIGluIFNWRyBwYXlsb2FkcywgYW5kIGFsbG93c1xyXG4gKiBmb3IgZmxleGlibGUgb3B0aW9ucyB2YWxpZGF0aW9uLiBUaGUgbWlkZGxld2FyZSBsb2dzIGRldGFpbGVkIGluZm9ybWF0aW9uXHJcbiAqIGFuZCBoYW5kbGVzIGVycm9ycyByZWxhdGVkIHRvIGluY29ycmVjdCBwYXlsb2FkcywgY2hhcnQgZGF0YSwgYW5kIHByaXZhdGUgVVJMXHJcbiAqIHVzYWdlLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7IGdldEFsbG93Q29kZUV4ZWN1dGlvbiB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaXNPYmplY3RFbXB0eSwgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIGNvbnRlbnQtdHlwZSBoZWFkZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9IFRoZSBjYWxsIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY29udGVudC10eXBlXHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gY29udGVudFR5cGVNaWRkbGV3YXJlKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgY29udGVudCB0eXBlIGhlYWRlclxyXG4gICAgY29uc3QgY29udGVudFR5cGUgPSByZXF1ZXN0LmhlYWRlcnNbJ2NvbnRlbnQtdHlwZSddIHx8ICcnO1xyXG5cclxuICAgIC8vIEFsbG93IG9ubHkgSlNPTiwgVVJMLWVuY29kZWQgYW5kIGZvcm0gZGF0YSB3aXRob3V0IGZpbGVzIHR5cGVzIG9mIGRhdGFcclxuICAgIGlmIChcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdhcHBsaWNhdGlvbi9qc29uJykgJiZcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ211bHRpcGFydC9mb3JtLWRhdGEnKVxyXG4gICAgKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3ZhbGlkYXRpb25dIENvbnRlbnQtVHlwZSBtdXN0IGJlIGFwcGxpY2F0aW9uL2pzb24sIGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCwgb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YS4nLFxyXG4gICAgICAgIDQxNVxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENhbGwgdGhlIGByZXF1ZXN0Qm9keU1pZGRsZXdhcmVgIG1pZGRsZXdhcmVcclxuICAgIHJldHVybiBuZXh0KCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciB2YWxpZGF0aW5nIHRoZSByZXF1ZXN0J3MgYm9keS5cclxuICpcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RCb2R5TWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBib2R5IGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGNoYXJ0IGRhdGEgZnJvbSB0aGUgYm9keVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGluIGNhc2Ugb2YgdGhlIHByaXZhdGUgcmFuZ2VcclxuICogdXJsIGVycm9yLlxyXG4gKi9cclxuZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgcmVxdWVzdCBib2R5XHJcbiAgICBjb25zdCBib2R5ID0gcmVxdWVzdC5ib2R5O1xyXG5cclxuICAgIC8vIENyZWF0ZSBhIHVuaXF1ZSBJRCBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSB1dWlkKCk7XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgaXMgbm8gY29ycmVjdCBib2R5XHJcbiAgICBpZiAoIWJvZHkgfHwgaXNPYmplY3RFbXB0eShib2R5KSkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIGVtcHR5LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBib2R5IGlzIHJlcXVpcmVkLiBQbGVhc2UgZW5zdXJlIHRoYXQgeW91ciBDb250ZW50LVR5cGUgaGVhZGVyIGlzIGNvcnJlY3QuIEFjY2VwdGVkIHR5cGVzIGFyZSAnYXBwbGljYXRpb24vanNvbicgYW5kICdtdWx0aXBhcnQvZm9ybS1kYXRhJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIG9wdGlvbiBmb3IgdGhlIHNlcnZlclxyXG4gICAgY29uc3QgYWxsb3dDb2RlRXhlY3V0aW9uID0gZ2V0QWxsb3dDb2RlRXhlY3V0aW9uKCk7XHJcblxyXG4gICAgLy8gRmluZCBhIGNvcnJlY3QgY2hhcnQgb3B0aW9uc1xyXG4gICAgY29uc3QgaW5zdHIgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgIC8vIFVzZSBvbmUgb2YgdGhlIGJlbG93XHJcbiAgICAgIGJvZHkuaW5zdHIgfHwgYm9keS5vcHRpb25zIHx8IGJvZHkuaW5maWxlIHx8IGJvZHkuZGF0YSxcclxuICAgICAgLy8gU3RyaW5naWZ5IG9wdGlvbnNcclxuICAgICAgdHJ1ZSxcclxuICAgICAgLy8gQWxsb3cgb3IgZGlzYWxsb3cgZnVuY3Rpb25zXHJcbiAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGNoYXJ0IGRhdGFcclxuICAgIGlmIChpbnN0ciA9PT0gbnVsbCAmJiAhYm9keS5zdmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fCByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcGF5bG9hZCBpcyBtaXNzaW5nIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3IgZXhwb3J0OiAke0pTT04uc3RyaW5naWZ5KGJvZHkpfS5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBbdmFsaWRhdGlvbl0gTm8gY29ycmVjdCBjaGFydCBkYXRhIGZvdW5kLiBFbnN1cmUgdGhhdCB5b3UgYXJlIHVzaW5nIGVpdGhlciBhcHBsaWNhdGlvbi9qc29uIG9yIG11bHRpcGFydC9mb3JtLWRhdGEgaGVhZGVycy4gSWYgc2VuZGluZyBKU09OLCBtYWtlIHN1cmUgdGhlIGNoYXJ0IGRhdGEgaXMgaW4gdGhlICdpbmZpbGUnLCAnb3B0aW9ucycsIG9yICdkYXRhJyBhdHRyaWJ1dGUuIElmIHNlbmRpbmcgU1ZHLCBlbnN1cmUgaXQgaXMgaW4gdGhlICdzdmcnIGF0dHJpYnV0ZS5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRlc3Qgb2YgeGxpbms6aHJlZiBlbGVtZW50cyBmcm9tIHBheWxvYWQncyBTVkcgZmFpbHNcclxuICAgIGlmIChib2R5LnN2ZyAmJiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKGJvZHkuc3ZnKSkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBbdmFsaWRhdGlvbl0gU1ZHIHBvdGVudGlhbGx5IGNvbnRhaW4gYXQgbGVhc3Qgb25lIGZvcmJpZGRlbiBVUkwgaW4gJ3hsaW5rOmhyZWYnIGVsZW1lbnQuIFBsZWFzZSByZXZpZXcgdGhlIFNWRyBjb250ZW50IGFuZCBlbnN1cmUgdGhhdCBhbGwgcmVmZXJlbmNlZCBVUkxzIGNvbXBseSB3aXRoIHNlY3VyaXR5IHBvbGljaWVzLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IG9wdGlvbnMgYW5kIHN0b3JlIHBhcnNlZCBzdHJ1Y3R1cmUgaW4gdGhlIHJlcXVlc3RcclxuICAgIHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucyA9IHtcclxuICAgICAgLy8gU2V0IHRoZSBjcmVhdGVkIElEIGFzIGEgYHJlcXVlc3RJZGAgcHJvcGVydHkgaW4gdGhlIG9wdGlvbnNcclxuICAgICAgcmVxdWVzdElkLFxyXG4gICAgICBleHBvcnQ6IHtcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICBzdmc6IGJvZHkuc3ZnLFxyXG4gICAgICAgIG91dGZpbGU6XHJcbiAgICAgICAgICBib2R5Lm91dGZpbGUgfHxcclxuICAgICAgICAgIGAke3JlcXVlc3QucGFyYW1zLmZpbGVuYW1lIHx8ICdjaGFydCd9LiR7Ym9keS50eXBlIHx8ICdwbmcnfWAsXHJcbiAgICAgICAgdHlwZTogYm9keS50eXBlLFxyXG4gICAgICAgIGNvbnN0cjogYm9keS5jb25zdHIsXHJcbiAgICAgICAgYjY0OiBib2R5LmI2NCxcclxuICAgICAgICBub0Rvd25sb2FkOiBib2R5Lm5vRG93bmxvYWQsXHJcbiAgICAgICAgaGVpZ2h0OiBib2R5LmhlaWdodCxcclxuICAgICAgICB3aWR0aDogYm9keS53aWR0aCxcclxuICAgICAgICBzY2FsZTogYm9keS5zY2FsZSxcclxuICAgICAgICBnbG9iYWxPcHRpb25zOiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICBib2R5Lmdsb2JhbE9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKSxcclxuICAgICAgICB0aGVtZU9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkudGhlbWVPcHRpb25zLFxyXG4gICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgIClcclxuICAgICAgfSxcclxuICAgICAgY3VzdG9tTG9naWM6IHtcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzOiBmYWxzZSxcclxuICAgICAgICBjdXN0b21Db2RlOiBib2R5LmN1c3RvbUNvZGUsXHJcbiAgICAgICAgY2FsbGJhY2s6IGJvZHkuY2FsbGJhY2ssXHJcbiAgICAgICAgcmVzb3VyY2VzOiBpc0FsbG93ZWRDb25maWcoYm9keS5yZXNvdXJjZXMsIHRydWUsIGFsbG93Q29kZUV4ZWN1dGlvbilcclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBuZXh0IG1pZGRsZXdhcmVcclxuICAgIHJldHVybiBuZXh0KCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVzIHRvIHRoZSBwYXNzZWQgZXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmFsaWRhdGlvbk1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGNvbnRlbnQgdHlwZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCBjb250ZW50VHlwZU1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgcmVxdWVzdCBib2R5IHJlcXVlc3QgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgYXBwLnBvc3QoWycvJywgJy86ZmlsZW5hbWUnXSwgcmVxdWVzdEJvZHlNaWRkbGV3YXJlKTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyB0aGUgZXhwb3J0IHJvdXRlcyBhbmQgbG9naWMgZm9yIGhhbmRsaW5nIGNoYXJ0IGV4cG9ydFxyXG4gKiByZXF1ZXN0cyBpbiBhbiBFeHByZXNzIHNlcnZlci4gVGhpcyBtb2R1bGUgcHJvY2Vzc2VzIGluY29taW5nIHJlcXVlc3RzXHJcbiAqIHRvIGV4cG9ydCBjaGFydHMgaW4gdmFyaW91cyBmb3JtYXRzIChlLmcuIEpQRUcsIFBORywgUERGLCBTVkcpLiBJdCBpbnRlZ3JhdGVzXHJcbiAqIHdpdGggSGlnaGNoYXJ0cycgY29yZSBmdW5jdGlvbmFsaXRpZXMgYW5kIHN1cHBvcnRzIGJvdGggaW1tZWRpYXRlIGRvd25sb2FkXHJcbiAqIHJlc3BvbnNlcyBhbmQgQmFzZTY0LWVuY29kZWQgY29udGVudCByZXR1cm5zLiBUaGUgY29kZSBhbHNvIGZlYXR1cmVzXHJcbiAqIGJlbmNobWFya2luZyBmb3IgcGVyZm9ybWFuY2UgbW9uaXRvcmluZy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBzdGFydEV4cG9ydCB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0QmFzZTY0LCBtZWFzdXJlVGltZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xyXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XHJcbiAgcG5nOiAnaW1hZ2UvcG5nJyxcclxuICBqcGVnOiAnaW1hZ2UvanBlZycsXHJcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcclxuICBwZGY6ICdhcHBsaWNhdGlvbi9wZGYnLFxyXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXHJcbn07XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgZXhwb3J0IHJlcXVlc3RzIGZyb20gdGhlIGNsaWVudC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiByZXF1ZXN0RXhwb3J0KHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0IGNvdW50aW5nIHRpbWUgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgcmVxdWVzdENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIEluIGNhc2UgdGhlIGNvbm5lY3Rpb24gaXMgY2xvc2VkLCBmb3JjZSB0byBhYm9ydCBmdXJ0aGVyIGFjdGlvbnNcclxuICAgIGxldCBjb25uZWN0aW9uQWJvcnRlZCA9IGZhbHNlO1xyXG4gICAgcmVxdWVzdC5zb2NrZXQub24oJ2Nsb3NlJywgKGhhZEVycm9ycykgPT4ge1xyXG4gICAgICBpZiAoaGFkRXJyb3JzKSB7XHJcbiAgICAgICAgY29ubmVjdGlvbkFib3J0ZWQgPSB0cnVlO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIG9wdGlvbnMgcHJldmlvdXNseSB2YWxpZGF0ZWQgaW4gdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gICAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGlkXHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSByZXF1ZXN0T3B0aW9ucy5yZXF1ZXN0SWQ7XHJcblxyXG4gICAgLy8gSW5mbyBhYm91dCBhbiBpbmNvbWluZyByZXF1ZXN0IHdpdGggY29ycmVjdCBkYXRhXHJcbiAgICBsb2coNCwgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBHb3QgYW4gaW5jb21pbmcgSFRUUCByZXF1ZXN0LmApO1xyXG5cclxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQocmVxdWVzdE9wdGlvbnMsIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAvLyBSZW1vdmUgdGhlIGNsb3NlIGV2ZW50IGZyb20gdGhlIHNvY2tldFxyXG4gICAgICByZXF1ZXN0LnNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2Nsb3NlJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkLCBkbyBub3RoaW5nXHJcbiAgICAgIGlmIChjb25uZWN0aW9uQWJvcnRlZCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSBjbGllbnQgY2xvc2VkIHRoZSBjb25uZWN0aW9uIGJlZm9yZSB0aGUgY2hhcnQgZmluaXNoZWQgcHJvY2Vzc2luZy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGVycm9yLCBsb2cgaXQgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGRhdGEgaXMgbWlzc2luZywgbG9nIHRoZSBtZXNzYWdlIGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmICghZGF0YSB8fCAhZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBSZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fFxyXG4gICAgICAgICAgICByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCByZXN1bHQgaXMgJHtkYXRhLnJlc3VsdH0uYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVW5leHBlY3RlZCByZXR1cm4gb2YgdGhlIGV4cG9ydCByZXN1bHQgZnJvbSB0aGUgY2hhcnQgZ2VuZXJhdGlvbi4gUGxlYXNlIGNoZWNrIHlvdXIgcmVxdWVzdCBkYXRhLmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBSZXR1cm4gdGhlIHJlc3VsdCBpbiBhbiBhcHByb3ByaWF0ZSBmb3JtYXRcclxuICAgICAgaWYgKGRhdGEucmVzdWx0KSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzIHRvb2sgJHtyZXF1ZXN0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIGB0eXBlYCwgYGI2NGAsIGBub0Rvd25sb2FkYCwgYW5kIGBvdXRmaWxlYCBmcm9tIG9wdGlvbnNcclxuICAgICAgICBjb25zdCB7IHR5cGUsIGI2NCwgbm9Eb3dubG9hZCwgb3V0ZmlsZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgLy8gSWYgb25seSBCYXNlNjQgaXMgcmVxdWlyZWQsIHJldHVybiBpdFxyXG4gICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gU2V0IGNvcnJlY3QgY29udGVudCB0eXBlXHJcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVyKCdDb250ZW50LVR5cGUnLCByZXZlcnNlZE1pbWVbdHlwZV0gfHwgJ2ltYWdlL3BuZycpO1xyXG5cclxuICAgICAgICAvLyBEZWNpZGUgd2hldGhlciB0byBkb3dubG9hZCBvciBub3QgY2hhcnQgZmlsZVxyXG4gICAgICAgIGlmICghbm9Eb3dubG9hZCkge1xyXG4gICAgICAgICAgcmVzcG9uc2UuYXR0YWNobWVudChvdXRmaWxlKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIElmIFNWRywgcmV0dXJuIHBsYWluIGNvbnRlbnQsIG90aGVyd2lzZSBhIGI2NCBzdHJpbmcgZnJvbSBhIGJ1ZmZlclxyXG4gICAgICAgIHJldHVybiB0eXBlID09PSAnc3ZnJ1xyXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGRhdGEucmVzdWx0KVxyXG4gICAgICAgICAgOiByZXNwb25zZS5zZW5kKEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JykpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBleHBvcnRgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4cG9ydFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGV4cG9ydFJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3RcclxuICAgKiBlbmRwb2ludC5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLycsIHJlcXVlc3RFeHBvcnQpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvOmZpbGVuYW1lJyAtIEEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgd2l0aFxyXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLzpmaWxlbmFtZScsIHJlcXVlc3RFeHBvcnQpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHNlcnZlciBoZWFsdGggbW9uaXRvcmluZywgaW5jbHVkaW5nXHJcbiAqIHVwdGltZSwgc3VjY2VzcyByYXRlcywgYW5kIG90aGVyIHNlcnZlciBzdGF0aXN0aWNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldFBvb2xTdGF0cywgZ2V0UG9vbEluZm9KU09OIH0gZnJvbSAnLi4vLi4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGFkZFRpbWVyIH0gZnJvbSAnLi4vLi4vdGltZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIGdldE5ld0RhdGVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLy8gU2V0IHRoZSBzdGFydCBkYXRlIG9mIHRoZSBzZXJ2ZXJcclxuY29uc3Qgc2VydmVyU3RhcnRUaW1lID0gbmV3IERhdGUoKTtcclxuXHJcbi8vIEdldCB0aGUgYHBhY2thZ2UuanNvbmAgY29udGVudFxyXG5jb25zdCBwYWNrYWdlRmlsZSA9IEpTT04ucGFyc2UoXHJcbiAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuKTtcclxuXHJcbi8vIEFuIGFycmF5IGZvciBzdWNjZXNzIHJhdGUgcmF0aW9zXHJcbmNvbnN0IHN1Y2Nlc3NSYXRlcyA9IFtdO1xyXG5cclxuLy8gUmVjb3JkIGV2ZXJ5IG1pbnV0ZVxyXG5jb25zdCByZWNvcmRJbnRlcnZhbCA9IDYwICogMTAwMDtcclxuXHJcbi8vIDMwIG1pbnV0ZXNcclxuY29uc3Qgd2luZG93U2l6ZSA9IDMwO1xyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgbW92aW5nIGF2ZXJhZ2UgaW5kaWNhdG9yIGJhc2VkIG9uIHRoZSBkYXRhIGZyb20gdGhlIGBzdWNjZXNzUmF0ZXNgXHJcbiAqIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2VcclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gQSBtb3ZpbmcgYXZlcmFnZSBmb3Igc3VjY2VzcyByYXRpbyBvZiB0aGUgc2VydmVyIGV4cG9ydHMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpIHtcclxuICByZXR1cm4gc3VjY2Vzc1JhdGVzLnJlZHVjZSgoYSwgYikgPT4gYSArIGIsIDApIC8gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyB0aGUgaW50ZXJ2YWwgcmVzcG9uc2libGUgZm9yIGNhbGN1bGF0aW5nIGN1cnJlbnQgc3VjY2VzcyByYXRlIHJhdGlvXHJcbiAqIGFuZCBjb2xsZWN0cyByZWNvcmRzIHRvIHRoZSBgc3VjY2Vzc1JhdGVzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtOb2RlSlMuVGltZW91dH0gSWQgb2YgYW4gaW50ZXJ2YWwuXHJcbiAqL1xyXG5mdW5jdGlvbiBfc3RhcnRTdWNjZXNzUmF0ZSgpIHtcclxuICByZXR1cm4gc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgIGNvbnN0IHN1Y2Nlc3NSYXRpbyA9XHJcbiAgICAgIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQgPT09IDBcclxuICAgICAgICA/IDFcclxuICAgICAgICA6IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDA7XHJcblxyXG4gICAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcclxuICAgIGlmIChzdWNjZXNzUmF0ZXMubGVuZ3RoID4gd2luZG93U2l6ZSkge1xyXG4gICAgICBzdWNjZXNzUmF0ZXMuc2hpZnQoKTtcclxuICAgIH1cclxuICB9LCByZWNvcmRJbnRlcnZhbCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgaGVhbHRoYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBoZWFsdGhSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBoZWFsdGhSb3V0ZXMoYXBwKSB7XHJcbiAgLy8gU3RhcnQgcHJvY2Vzc2luZyBzdWNjZXNzIHJhdGUgcmF0aW8gaW50ZXJ2YWwgYW5kIHNhdmUgaXRzIGlkIHRvIHRoZSBhcnJheVxyXG4gIC8vIGZvciB0aGUgZ3JhY2VmdWwgY2xlYXJpbmcgb24gc2h1dGRvd24gd2l0aCBpbmplY3RlZCBgYWRkVGltZXJgIGZ1bnRpb25cclxuICBhZGRUaW1lcihfc3RhcnRTdWNjZXNzUmF0ZSgpKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgR0VUICcvaGVhbHRoJyAtIEEgcm91dGUgZm9yIGdldHRpbmcgdGhlIGJhc2ljIHN0YXRzIG9mIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldCgnL2hlYWx0aCcsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbaGVhbHRoXSBSZXR1cm5pbmcgc2VydmVyIGhlYWx0aC4nKTtcclxuXHJcbiAgICAgIGNvbnN0IHN0YXRzID0gZ2V0UG9vbFN0YXRzKCk7XHJcbiAgICAgIGNvbnN0IHBlcmlvZCA9IHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbiAgICAgIGNvbnN0IG1vdmluZ0F2ZXJhZ2UgPSBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpO1xyXG5cclxuICAgICAgLy8gU2VuZCB0aGUgc2VydmVyJ3Mgc3RhdGlzdGljc1xyXG4gICAgICByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAvLyBTdGF0dXMgYW5kIHRpbWVzXHJcbiAgICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICAgIGJvb3RUaW1lOiBzZXJ2ZXJTdGFydFRpbWUsXHJcbiAgICAgICAgdXB0aW1lOiBgJHtNYXRoLmZsb29yKChnZXROZXdEYXRlVGltZSgpIC0gc2VydmVyU3RhcnRUaW1lLmdldFRpbWUoKSkgLyAxMDAwIC8gNjApfSBtaW51dGVzYCxcclxuXHJcbiAgICAgICAgLy8gVmVyc2lvbnNcclxuICAgICAgICBzZXJ2ZXJWZXJzaW9uOiBwYWNrYWdlRmlsZS52ZXJzaW9uLFxyXG4gICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG5cclxuICAgICAgICAvLyBFeHBvcnRzXHJcbiAgICAgICAgYXZlcmFnZUV4cG9ydFRpbWU6IHN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UsXHJcbiAgICAgICAgYXR0ZW1wdGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCxcclxuICAgICAgICBwZXJmb3JtZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzUGVyZm9ybWVkLFxyXG4gICAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNEcm9wcGVkLFxyXG4gICAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCAvIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQpICogMTAwLFxyXG5cclxuICAgICAgICAvLyBQb29sXHJcbiAgICAgICAgcG9vbDogZ2V0UG9vbEluZm9KU09OKCksXHJcblxyXG4gICAgICAgIC8vIE1vdmluZyBhdmVyYWdlXHJcbiAgICAgICAgcGVyaW9kLFxyXG4gICAgICAgIG1vdmluZ0F2ZXJhZ2UsXHJcbiAgICAgICAgbWVzc2FnZTpcclxuICAgICAgICAgIGlzTmFOKG1vdmluZ0F2ZXJhZ2UpIHx8ICFzdWNjZXNzUmF0ZXMubGVuZ3RoXHJcbiAgICAgICAgICAgID8gJ1RvbyBlYXJseSB0byByZXBvcnQuIE5vIGV4cG9ydHMgbWFkZSB5ZXQuIFBsZWFzZSBjaGVjayBiYWNrIHNvb24uJ1xyXG4gICAgICAgICAgICA6IGBMYXN0ICR7cGVyaW9kfSBtaW51dGVzIGhhZCBhIHN1Y2Nlc3MgcmF0ZSBvZiAke21vdmluZ0F2ZXJhZ2UudG9GaXhlZCgyKX0lLmAsXHJcblxyXG4gICAgICAgIC8vIFNWRyBhbmQgSlNPTiBleHBvcnRzXHJcbiAgICAgICAgc3ZnRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21TdmcsXHJcbiAgICAgICAganNvbkV4cG9ydHM6IHN0YXRzLmV4cG9ydHNGcm9tT3B0aW9ucyxcclxuICAgICAgICBzdmdFeHBvcnRzQXR0ZW1wdHM6IHN0YXRzLmV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMsXHJcbiAgICAgICAganNvbkV4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHNcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmluZyB0aGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyXHJcbiAqIHdoZW4gZW5hYmxlZC5cclxuICovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB1aWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdWlSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB1aVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy8nIC0gQSByb3V0ZSBmb3IgYSBVSSB3aGVuIGVuYWJsZWQgb24gdGhlIGV4cG9ydCBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLmdldChnZXRPcHRpb25zKCkudWkucm91dGUgfHwgJy8nLCAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3VpXSBSZXR1cm5pbmcgVUkgZm9yIHRoZSBleHBvcnQuJyk7XHJcblxyXG4gICAgICByZXNwb25zZS5zZW5kRmlsZShqb2luKF9fZGlybmFtZSwgJ3B1YmxpYycsICdpbmRleC5odG1sJyksIHtcclxuICAgICAgICBhY2NlcHRSYW5nZXM6IGZhbHNlXHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHVwZGF0aW5nIHRoZSBIaWdoY2hhcnRzIHZlcnNpb25cclxuICogb24gdGhlIHNlcnZlciwgd2l0aCBhdXRoZW50aWNhdGlvbiBhbmQgdmFsaWRhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiwgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuLi8uLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB2ZXJzaW9uX2NoYW5nZWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHZlcnNpb25DaGFuZ2VSb3V0ZXMoYXBwKSB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnL3ZlcnNpb25fY2hhbmdlLzpuZXdWZXJzaW9uJyAtIEEgcm91dGUgZm9yIGNoYW5naW5nXHJcbiAgICogdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBvbiB0aGUgc2VydmVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nLCBhc3luYyAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3ZlcnNpb25dIENoYW5naW5nIEhpZ2hjaGFydHMgdmVyc2lvbi4nKTtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZGlyZWN0bHkgZnJvbSBlbnZzXHJcbiAgICAgIGNvbnN0IGFkbWluVG9rZW4gPSBlbnZzLkhJR0hDSEFSVFNfQURNSU5fVE9LRU47XHJcblxyXG4gICAgICAvLyBDaGVjayB0aGUgZXhpc3RlbmNlIG9mIHRoZSB0b2tlblxyXG4gICAgICBpZiAoIWFkbWluVG9rZW4gfHwgIWFkbWluVG9rZW4ubGVuZ3RoKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1t2ZXJzaW9uXSBUaGUgc2VydmVyIGlzIG5vdCBjb25maWd1cmVkIHRvIHBlcmZvcm0gcnVuLXRpbWUgdmVyc2lvbiBjaGFuZ2VzOiBISUdIQ0hBUlRTX0FETUlOX1RPS0VOIGlzIG5vdCBzZXQuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZnJvbSB0aGUgaGMtYXV0aCBoZWFkZXJcclxuICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXHJcbiAgICAgIGlmICghdG9rZW4gfHwgdG9rZW4gIT09IGFkbWluVG9rZW4pIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIEludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdmVyc2lvbnNcclxuICAgICAgbGV0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG4gICAgICBpZiAobmV3VmVyc2lvbikge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAvLyBVcGRhdGUgdmVyc2lvblxyXG4gICAgICAgICAgYXdhaXQgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbik7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgYFt2ZXJzaW9uXSBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgIDQwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbdmVyc2lvbl0gTm8gbmV3IHZlcnNpb24gc3VwcGxpZWQuJywgNDAwKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSB0aGF0IHNldHMgdXAgYW5kIG1hbmFnZXMgSFRUUCBhbmQgSFRUUFMgc2VydmVyc1xyXG4gKiBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci4gSXQgaGFuZGxlcyBzZXJ2ZXIgaW5pdGlhbGl6YXRpb24sXHJcbiAqIGNvbmZpZ3VyYXRpb24sIGVycm9yIGhhbmRsaW5nLCBtaWRkbGV3YXJlIHNldHVwLCByb3V0ZSBkZWZpbml0aW9uLCBhbmQgcmF0ZVxyXG4gKiBsaW1pdGluZy4gVGhlIG1vZHVsZSBleHBvcnRzIGZ1bmN0aW9ucyB0byBzdGFydCwgc3RvcCwgYW5kIG1hbmFnZSBzZXJ2ZXJcclxuICogaW5zdGFuY2VzLCBhcyB3ZWxsIGFzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBkZWZpbmluZyByb3V0ZXMgYW5kIGF0dGFjaGluZ1xyXG4gKiBtaWRkbGV3YXJlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBjb3JzIGZyb20gJ2NvcnMnO1xyXG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcbmltcG9ydCBtdWx0ZXIgZnJvbSAnbXVsdGVyJztcclxuXHJcbmltcG9ydCB7IHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IGVycm9yTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL2Vycm9yLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdGluZ01pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy9yYXRlTGltaXRpbmcuanMnO1xyXG5pbXBvcnQgdmFsaWRhdGlvbk1pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzJztcclxuXHJcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcclxuaW1wb3J0IGhlYWx0aFJvdXRlcyBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xyXG5pbXBvcnQgdmVyc2lvbkNoYW5nZVJvdXRlcyBmcm9tICcuL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcclxuY29uc3QgYWN0aXZlU2VydmVycyA9IG5ldyBNYXAoKTtcclxuXHJcbi8vIENyZWF0ZSBleHByZXNzIGFwcFxyXG5jb25zdCBhcHAgPSBleHByZXNzKCk7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIEhUVFAgYW5kL29yIEhUVFBTIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi5cclxuICogVGhlIGBzZXJ2ZXJPcHRpb25zYCBvYmplY3QgY29udGFpbnMgc2VydmVyLXJlbGF0ZWQgcHJvcGVydGllcyAocmVmZXJcclxuICogdG8gdGhlIGBzZXJ2ZXJgIHNlY3Rpb24gaW4gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgZGV0YWlscykuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRTZXJ2ZXJcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlck9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIHRoZSBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBkZWZhdWx0XHJcbiAqIHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGVpdGhlclxyXG4gKiBub3QgZW5hYmxlZCBvciBubyB2YWxpZCBFeHByZXNzIGFwcCBpcyBmb3VuZCwgc2lnbmFsaW5nIHRoZSBlbmQgb2YgdGhlXHJcbiAqIGZ1bmN0aW9uJ3MgZXhlY3V0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIHNlcnZlciBjYW5ub3RcclxuICogYmUgY29uZmlndXJlZCBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFNlcnZlcihzZXJ2ZXJPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgc2VydmVyOiBzZXJ2ZXJPcHRpb25zXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBVc2UgdmFsaWRhdGVkIG9wdGlvbnNcclxuICAgIHNlcnZlck9wdGlvbnMgPSBvcHRpb25zLnNlcnZlcjtcclxuXHJcbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXHJcbiAgICBpZiAoIXNlcnZlck9wdGlvbnMuZW5hYmxlIHx8ICFhcHApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbc2VydmVyXSBTZXJ2ZXIgY2Fubm90IGJlIHN0YXJ0ZWQgKG5vdCBlbmFibGVkIG9yIG5vIGNvcnJlY3QgRXhwcmVzcyBhcHAgZm91bmQpLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVG9vIGJpZyBsaW1pdHMgbGVhZCB0byB0aW1lb3V0cyBpbiB0aGUgZXhwb3J0IHByb2Nlc3Mgd2hlblxyXG4gICAgLy8gdGhlIHJhc3Rlcml6YXRpb24gdGltZW91dCBpcyBzZXQgdG9vIGxvd1xyXG4gICAgY29uc3QgdXBsb2FkTGltaXRCeXRlcyA9IHNlcnZlck9wdGlvbnMudXBsb2FkTGltaXQgKiAxMDI0ICogMTAyNDtcclxuXHJcbiAgICAvLyBNZW1vcnkgc3RvcmFnZSBmb3IgbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHN0b3JhZ2UgPSBtdWx0ZXIubWVtb3J5U3RvcmFnZSgpO1xyXG5cclxuICAgIC8vIEVuYWJsZSBwYXJzaW5nIG9mIGZvcm0gZGF0YSAoZmlsZXMpIHdpdGggbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHVwbG9hZCA9IG11bHRlcih7XHJcbiAgICAgIHN0b3JhZ2UsXHJcbiAgICAgIGxpbWl0czoge1xyXG4gICAgICAgIGZpZWxkU2l6ZTogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXHJcbiAgICBhcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XHJcblxyXG4gICAgLy8gRW5hYmxlIENPUlMgc3VwcG9ydFxyXG4gICAgYXBwLnVzZShcclxuICAgICAgY29ycyh7XHJcbiAgICAgICAgbWV0aG9kczogWydQT1NUJywgJ0dFVCcsICdPUFRJT05TJ11cclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0dGluZyBhIGxvdCBvZiBgUmFuZ2VOb3RTYXRpc2ZpYWJsZUVycm9yYCBleGNlcHRpb25zIChldmVuIHRob3VnaCB0aGlzXHJcbiAgICAvLyBpcyBhIGRlcHJlY2F0ZWQgb3B0aW9ucywgbGV0J3MgdHJ5IHRvIHNldCBpdCB0byBmYWxzZSlcclxuICAgIGFwcC51c2UoKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgIHJlc3BvbnNlLnNldCgnQWNjZXB0LVJhbmdlcycsICdub25lJyk7XHJcbiAgICAgIG5leHQoKTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgSlNPTiBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLmpzb24oe1xyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgVVJMLWVuY29kZWQgZm9ybSBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLnVybGVuY29kZWQoe1xyXG4gICAgICAgIGV4dGVuZGVkOiB0cnVlLFxyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xyXG4gICAgYXBwLnVzZSh1cGxvYWQubm9uZSgpKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgc3RhdGljIGZvbGRlcidzIHJvdXRlXHJcbiAgICBhcHAudXNlKGV4cHJlc3Muc3RhdGljKGpvaW4oX19kaXJuYW1lLCAncHVibGljJykpKTtcclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5zc2wuZm9yY2UpIHtcclxuICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFApXHJcbiAgICAgIGNvbnN0IGh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApO1xyXG5cclxuICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIC8vIExpc3RlblxyXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnBvcnQsIHNlcnZlck9wdGlvbnMuaG9zdCwgKCkgPT4ge1xyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMucG9ydCwgaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnBvcnR9LmBcclxuICAgICAgICApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgICBpZiAoc2VydmVyT3B0aW9ucy5zc2wuZW5hYmxlKSB7XHJcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cclxuICAgICAgbGV0IGtleSwgY2VydDtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XHJcbiAgICAgICAga2V5ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmtleScpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wgY2VydGlmaWNhdGVcclxuICAgICAgICBjZXJ0ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmNydCcpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFVuYWJsZSB0byBsb2FkIGtleS9jZXJ0aWZpY2F0ZSBmcm9tIHRoZSAnJHtzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChrZXkgJiYgY2VydCkge1xyXG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcclxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xyXG5cclxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgICAgX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAvLyBMaXN0ZW5cclxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyT3B0aW9ucy5zc2wucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUFMgc2VydmVyXHJcbiAgICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAzLFxyXG4gICAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQUyBzZXJ2ZXIgb24gJHtzZXJ2ZXJPcHRpb25zLmhvc3R9OiR7c2VydmVyT3B0aW9ucy5zc2wucG9ydH0uYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgc2VydmVyT3B0aW9ucy5yYXRlTGltaXRpbmcpO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgdmFsaWRhdGlvbiBoYW5kbGVyXHJcbiAgICB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGV4cG9ydFJvdXRlcyhhcHApO1xyXG4gICAgaGVhbHRoUm91dGVzKGFwcCk7XHJcbiAgICB1aVJvdXRlcyhhcHApO1xyXG4gICAgdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgY2VudHJhbGl6ZWQgZXJyb3IgaGFuZGxlclxyXG4gICAgZXJyb3JNaWRkbGV3YXJlKGFwcCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tzZXJ2ZXJdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHN0YXJ0IHRoZSBzZXJ2ZXIuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbG9zZXMgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzKCkge1xyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBzZXJ2ZXJzIHdvcmtpbmdcclxuICBpZiAoYWN0aXZlU2VydmVycy5zaXplID4gMCkge1xyXG4gICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zaW5nIGFsbCBzZXJ2ZXJzLmApO1xyXG5cclxuICAgIC8vIENsb3NlIGVhY2ggb25lIG9mIHNlcnZlcnNcclxuICAgIGZvciAoY29uc3QgW3BvcnQsIHNlcnZlcl0gb2YgYWN0aXZlU2VydmVycykge1xyXG4gICAgICBzZXJ2ZXIuY2xvc2UoKCkgPT4ge1xyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuZGVsZXRlKHBvcnQpO1xyXG4gICAgICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2VkIHNlcnZlciBvbiBwb3J0OiAke3BvcnR9LmApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0U2VydmVyc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPE9iamVjdD59IFNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFNlcnZlcnMoKSB7XHJcbiAgcmV0dXJuIGFjdGl2ZVNlcnZlcnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRFeHByZXNzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRFeHByZXNzKCkge1xyXG4gIHJldHVybiBleHByZXNzO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEFwcFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RXhwcmVzc30gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEFwcCgpIHtcclxuICByZXR1cm4gYXBwO1xyXG59XHJcblxyXG4vKipcclxuICogRW5hYmxlIHJhdGUgbGltaXRpbmcgZm9yIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVSYXRlTGltaXRpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHJhdGVMaW1pdGluZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0XHJcbiAqIG9mIHRoZSBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXNcclxuICogd2lsbCBkZWZhdWx0IHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZyhyYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgIHNlcnZlcjoge1xyXG4gICAgICByYXRlTGltaXRpbmc6IHJhdGVMaW1pdGluZ09wdGlvbnNcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnNcclxuICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgb3B0aW9ucy5zZXJ2ZXIucmF0ZUxpbWl0aW5nT3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBcHBseSBtaWRkbGV3YXJlKHMpIHRvIGEgc3BlY2lmaWMgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIHVzZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLnVzZShwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIEdFVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5nZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBQT1NUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHBvc3RcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAucG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgdG8gdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KGh0dHAuU2VydmVyfGh0dHBzLlNlcnZlcil9IHNlcnZlciAtIFRoZSBIVFRQL0hUVFBTIHNlcnZlciBpbnN0YW5jZS5cclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKHNlcnZlcikge1xyXG4gIHNlcnZlci5vbignY2xpZW50RXJyb3InLCAoZXJyb3IsIHNvY2tldCkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAxLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtzZXJ2ZXJdIENsaWVudCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfSwgZGVzdHJveWluZyBzb2NrZXQuYFxyXG4gICAgKTtcclxuICAgIHNvY2tldC5kZXN0cm95KCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNlcnZlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Nvbm5lY3Rpb24nLCAoc29ja2V0KSA9PiB7XHJcbiAgICBzb2NrZXQub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNvY2tldCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzdGFydFNlcnZlcixcclxuICBjbG9zZVNlcnZlcnMsXHJcbiAgZ2V0U2VydmVycyxcclxuICBnZXRFeHByZXNzLFxyXG4gIGdldEFwcCxcclxuICBlbmFibGVSYXRlTGltaXRpbmcsXHJcbiAgdXNlLFxyXG4gIGdldCxcclxuICBwb3N0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBIYW5kbGVzIGdyYWNlZnVsIHNodXRkb3duIG9mIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIsIGVuc3VyaW5nXHJcbiAqIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlcyBzdWNoIGFzIGJyb3dzZXIsIHBhZ2VzLCBzZXJ2ZXJzLCBhbmQgdGltZXJzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgY2xlYXJBbGxUaW1lcnMgfSBmcm9tICcuL3RpbWVyLmpzJztcclxuXHJcbmltcG9ydCB7IGNsb3NlU2VydmVycyB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogUGVyZm9ybXMgY2xlYW51cCBvcGVyYXRpb25zIHRvIGVuc3VyZSBhIGdyYWNlZnVsIHNodXRkb3duIG9mIHRoZSBwcm9jZXNzLlxyXG4gKiBUaGlzIGluY2x1ZGVzIGNsZWFyaW5nIGFsbCByZWdpc3RlcmVkIHRpbWVvdXRzL2ludGVydmFscywgY2xvc2luZyBhY3RpdmVcclxuICogc2VydmVycywgdGVybWluYXRpbmcgcmVzb3VyY2VzIChwYWdlcykgb2YgdGhlIHBvb2wsIHBvb2wgaXRzZWxmLCBhbmQgY2xvc2luZ1xyXG4gKiB0aGUgYnJvd3Nlci5cclxuICpcclxuICogQGZ1bmN0aW9uIHNodXRkb3duQ2xlYW5VcFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2V4aXRDb2RlPTBdIC0gVGhlIGV4aXQgY29kZSB0byB1c2Ugd2l0aCBgcHJvY2Vzcy5leGl0KClgLlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgMGAuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwKGV4aXRDb2RlID0gMCkge1xyXG4gIC8vIEF3YWl0IGZyZWVpbmcgYWxsIHJlc291cmNlc1xyXG4gIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChbXHJcbiAgICAvLyBDbGVhciBhbGwgb25nb2luZyBpbnRlcnZhbHNcclxuICAgIGNsZWFyQWxsVGltZXJzKCksXHJcblxyXG4gICAgLy8gR2V0IGF2YWlsYWJsZSBzZXJ2ZXIgaW5zdGFuY2VzIChIVFRQL0hUVFBTKSBhbmQgY2xvc2UgdGhlbVxyXG4gICAgY2xvc2VTZXJ2ZXJzKCksXHJcblxyXG4gICAgLy8gQ2xvc2UgYW4gYWN0aXZlIHBvb2wgYWxvbmcgd2l0aCBpdHMgd29ya2VycyBhbmQgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICAgIGtpbGxQb29sKClcclxuICBdKTtcclxuXHJcbiAgLy8gRXhpdCBwcm9jZXNzIHdpdGggYSBjb3JyZWN0IGNvZGVcclxuICBwcm9jZXNzLmV4aXQoZXhpdENvZGUpO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2h1dGRvd25DbGVhblVwXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBDb3JlIG1vZHVsZSBmb3IgaW5pdGlhbGl6aW5nIGFuZCBtYW5hZ2luZyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnRcclxuICogU2VydmVyLiBQcm92aWRlcyBmdW5jdGlvbmFsaXRpZXMgZm9yIGNvbmZpZ3VyaW5nIGV4cG9ydHMsIHNldHRpbmcgdXAgc2VydmVyXHJcbiAqIG9wZXJhdGlvbnMsIGxvZ2dpbmcsIHNjcmlwdHMgY2FjaGluZywgcmVzb3VyY2UgcG9vbGluZywgYW5kIGdyYWNlZnVsIHByb2Nlc3NcclxuICogY2xlYW51cC5cclxuICovXHJcblxyXG5pbXBvcnQgJ2NvbG9ycyc7XHJcblxyXG5pbXBvcnQgeyBjaGVja0FuZFVwZGF0ZUNhY2hlIH0gZnJvbSAnLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7XHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59IGZyb20gJy4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCB1cGRhdGVPcHRpb25zLCBtYXBUb05ld09wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbFxyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2h1dGRvd25DbGVhblVwIH0gZnJvbSAnLi9yZXNvdXJjZVJlbGVhc2UuanMnO1xyXG5cclxuaW1wb3J0IHNlcnZlciBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiB0aGUgY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHJlc291cmNlIHBvb2wgb2NjdXIgZHVyaW5nIHRoaXNcclxuICogc3RhZ2UuXHJcbiAqXHJcbiAqIFRoaXMgZnVuY3Rpb24gbXVzdCBiZSBjYWxsZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXRcclxuICogdXAgYSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW5pdE9wdGlvbnMgLSBUaGUgYGluaXRPcHRpb25zYCBvYmplY3QsIHdoaWNoIG1heVxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nXHJcbiAqIHZhbHVlcyB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdEV4cG9ydChpbml0T3B0aW9ucykge1xyXG4gIC8vIEluaXQgYW5kIHVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyhpbml0T3B0aW9ucyk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcblxyXG4gIC8vIEluaXQgdGhlIHBvb2xcclxuICBhd2FpdCBpbml0UG9vbChvcHRpb25zLnBvb2wsIG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoZXMgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MsIGVuc3VyaW5nIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlc1xyXG4gKiBhbmQgdGVybWluYXRpb24gb24gZXhpdCBzaWduYWxzLiBIYW5kbGVzICdleGl0JywgJ1NJR0lOVCcsICdTSUdURVJNJ1xyXG4gKiBhbmQgJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnNcclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LmApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0lOVCdcclxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHVEVSTSdcclxuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0hVUCdcclxuICBwcm9jZXNzLm9uKCdTSUdIVVAnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAndW5jYXVnaHRFeGNlcHRpb24nXHJcbiAgcHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCBhc3luYyAoZXJyb3IsIG5hbWUpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIC4uLnNlcnZlcixcclxuXHJcbiAgLy8gT3B0aW9uc1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgdXBkYXRlT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcblxyXG4gIC8vIEV4cG9ydGluZ1xyXG4gIGluaXRFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG5cclxuICAvLyBSZWxlYXNlXHJcbiAga2lsbFBvb2wsXHJcbiAgc2h1dGRvd25DbGVhblVwLFxyXG5cclxuICAvLyBMb2dzXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbDogZnVuY3Rpb24gKGxldmVsKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICBsZXZlbFxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgc2V0TG9nTGV2ZWwob3B0aW9ucy5sb2dnaW5nLmxldmVsKTtcclxuICB9LFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nOiBmdW5jdGlvbiAodG9Db25zb2xlKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICB0b0NvbnNvbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUNvbnNvbGVMb2dnaW5nKG9wdGlvbnMubG9nZ2luZy50b0NvbnNvbGUpO1xyXG4gIH0sXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmc6IGZ1bmN0aW9uIChkZXN0LCBmaWxlLCB0b0ZpbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGRlc3QsXHJcbiAgICAgICAgZmlsZSxcclxuICAgICAgICB0b0ZpbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUZpbGVMb2dnaW5nKFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZGVzdCxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLmZpbGUsXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy50b0ZpbGVcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG4iXSwibmFtZXMiOlsiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJiYXNlVVJJIiwiZGVlcENvcHkiLCJvYmpBcnIiLCJvYmpBcnJDb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwiT2JqZWN0IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiZml4Q29uc3RyIiwiY29uc3RyIiwiZml4ZWRDb25zdHIiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJpbmNsdWRlcyIsImZpeE91dGZpbGUiLCJ0eXBlIiwib3V0ZmlsZSIsImdldEFic29sdXRlUGF0aCIsInNwbGl0Iiwic2hpZnQiLCJmaXhUeXBlIiwibWltZVR5cGVzIiwiZm9ybWF0cyIsInZhbHVlcyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsInBhdGgiLCJpc0Fic29sdXRlIiwiam9pbiIsImdldEJhc2U2NCIsImlucHV0IiwiQnVmZmVyIiwiZnJvbSIsInRvU3RyaW5nIiwiZ2V0TmV3RGF0ZSIsIkRhdGUiLCJ0cmltIiwiZ2V0TmV3RGF0ZVRpbWUiLCJnZXRUaW1lIiwiaXNPYmplY3QiLCJpdGVtIiwiaXNPYmplY3RFbXB0eSIsImtleXMiLCJsZW5ndGgiLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwic29tZSIsInBhdHRlcm4iLCJ0ZXN0IiwibWVhc3VyZVRpbWUiLCJzdGFydCIsInByb2Nlc3MiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJyb3VuZE51bWJlciIsInZhbHVlIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsIk1hdGgiLCJwb3ciLCJyb3VuZCIsIndyYXBBcm91bmQiLCJjdXN0b21Db2RlIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiaXNDYWxsYmFjayIsImVuZHNXaXRoIiwicmVhZEZpbGVTeW5jIiwic3RhcnRzV2l0aCIsImNvbG9ycyIsImxvZ2dpbmciLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsInBhdGhUb0xvZyIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibG9nIiwiYXJncyIsIm5ld0xldmVsIiwidGV4dHMiLCJsZXZlbCIsInByZWZpeCIsIl9sb2dUb0ZpbGUiLCJjb25zb2xlIiwiYXBwbHkiLCJ1bmRlZmluZWQiLCJjb25jYXQiLCJsb2dXaXRoU3RhY2siLCJlcnJvciIsImN1c3RvbU1lc3NhZ2UiLCJtYWluTWVzc2FnZSIsIm1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInB1c2giLCJpbml0TG9nZ2luZyIsImxvZ2dpbmdPcHRpb25zIiwiZGVzdCIsImZpbGUiLCJzZXRMb2dMZXZlbCIsImVuYWJsZUNvbnNvbGVMb2dnaW5nIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJpc0ludGVnZXIiLCJleGlzdHNTeW5jIiwibWtkaXJTeW5jIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiX2luaXRPcHRpb25zIiwiZ2V0T3B0aW9ucyIsImdldENvcHkiLCJ1cGRhdGVPcHRpb25zIiwibmV3T3B0aW9ucyIsIl9tZXJnZU9wdGlvbnMiLCJtYXBUb05ld09wdGlvbnMiLCJvbGRPcHRpb25zIiwiZW50cmllcyIsInByb3BlcnRpZXNDaGFpbiIsInJlZHVjZSIsIm9iaiIsInByb3AiLCJpbmRleCIsImlzQWxsb3dlZENvbmZpZyIsImFsbG93RnVuY3Rpb25zIiwib2JqZWN0Q29uZmlnIiwiZXZhbCIsIkpTT04iLCJzdHJpbmdpZmllZE9wdGlvbnMiLCJfb3B0aW9uc1N0cmluZ2lmeSIsInBhcnNlZE9wdGlvbnMiLCJfIiwibmFtZSIsIm9yaWdpbmFsT3B0aW9ucyIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJFcnJvciIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwiX2dldFByb3RvY29sTW9kdWxlIiwiZ2V0IiwicmVzcG9uc2UiLCJyZXNwb25zZURhdGEiLCJvbiIsImNodW5rIiwidGV4dCIsImh0dHBzIiwiaHR0cCIsIkV4cG9ydEVycm9yIiwiY29uc3RydWN0b3IiLCJzdGF0dXNDb2RlIiwic3VwZXIiLCJ0aGlzIiwic2V0U3RhdHVzIiwic2V0RXJyb3IiLCJjYWNoZSIsImFjdGl2ZU1hbmlmZXN0Iiwic291cmNlcyIsImhjVmVyc2lvbiIsImNoZWNrQW5kVXBkYXRlQ2FjaGUiLCJoaWdoY2hhcnRzT3B0aW9ucyIsInNlcnZlclByb3h5T3B0aW9ucyIsImZldGNoZWRNb2R1bGVzIiwiZ2V0Q2FjaGVQYXRoIiwibWFuaWZlc3RQYXRoIiwic291cmNlUGF0aCIsInJlY3Vyc2l2ZSIsIl91cGRhdGVDYWNoZSIsInJlcXVlc3RVcGRhdGUiLCJtYW5pZmVzdCIsIm1vZHVsZXMiLCJtb2R1bGVNYXAiLCJtIiwibnVtYmVyT2ZNb2R1bGVzIiwibW9kdWxlTmFtZSIsImV4dHJhY3RWZXJzaW9uIiwiX3NhdmVDb25maWdUb01hbmlmZXN0IiwiZ2V0SGlnaGNoYXJ0c1ZlcnNpb24iLCJ1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiIsIm5ld1ZlcnNpb24iLCJjYWNoZVNvdXJjZXMiLCJpbmRleE9mIiwiZXh0cmFjdE1vZHVsZU5hbWUiLCJzY3JpcHRQYXRoIiwiX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdCIsInNjcmlwdCIsInNob3VsZFRocm93RXJyb3IiLCJuZXdNYW5pZmVzdCIsIndyaXRlRmlsZVN5bmMiLCJfZmV0Y2hTY3JpcHRzIiwicHJveHlBZ2VudCIsInByb3h5SG9zdCIsInByb3h5UG9ydCIsIkh0dHBzUHJveHlBZ2VudCIsImFnZW50IiwiYWxsRmV0Y2hQcm9taXNlcyIsImFsbCIsImMiLCJpIiwic2V0dXBIaWdoY2hhcnRzIiwiSGlnaGNoYXJ0cyIsImFuaW1PYmplY3QiLCJkdXJhdGlvbiIsImNyZWF0ZUNoYXJ0IiwiZXhwb3J0T3B0aW9ucyIsImN1c3RvbUxvZ2ljT3B0aW9ucyIsInNldE9wdGlvbnMiLCJtZXJnZSIsIndyYXAiLCJzZXRPcHRpb25zT2JqIiwiaXNSZW5kZXJDb21wbGV0ZSIsIkNoYXJ0IiwicHJvY2VlZCIsInVzZXJPcHRpb25zIiwiY2IiLCJleHBvcnRpbmciLCJlbmFibGVkIiwicGxvdE9wdGlvbnMiLCJzZXJpZXMiLCJsYWJlbCIsInRvb2x0aXAiLCJhbmltYXRpb24iLCJvbkhpZ2hjaGFydHNSZW5kZXIiLCJhZGRFdmVudCIsIlNlcmllcyIsImNoYXJ0IiwiYWRkaXRpb25hbE9wdGlvbnMiLCJGdW5jdGlvbiIsImZpbmFsT3B0aW9ucyIsImZpbmFsQ2FsbGJhY2siLCJkZWZhdWx0T3B0aW9ucyIsInRlbXBsYXRlIiwiYnJvd3NlciIsImNyZWF0ZUJyb3dzZXIiLCJwdXBwZXRlZXJBcmdzIiwiZW5hYmxlZERlYnVnIiwiZGVidWdPcHRpb25zIiwibGF1bmNoT3B0aW9ucyIsInVzZXJEYXRhRGlyIiwiaGFuZGxlU0lHSU5UIiwiaGFuZGxlU0lHVEVSTSIsImhhbmRsZVNJR0hVUCIsIndhaXRGb3JJbml0aWFsUGFnZSIsImRlZmF1bHRWaWV3cG9ydCIsInRyeUNvdW50Iiwib3BlbiIsImxhdW5jaCIsInNldFRpbWVvdXQiLCJjbG9zZUJyb3dzZXIiLCJjb25uZWN0ZWQiLCJjbG9zZSIsIm5ld1BhZ2UiLCJwb29sUmVzb3VyY2UiLCJwYWdlIiwic2V0Q2FjaGVFbmFibGVkIiwiX3NldFBhZ2VDb250ZW50IiwiX3NldFBhZ2VFdmVudHMiLCJpc0Nsb3NlZCIsImNsZWFyUGFnZSIsImhhcmRSZXNldCIsImdvdG8iLCJ3YWl0VW50aWwiLCJldmFsdWF0ZSIsImJvZHkiLCJpbm5lckhUTUwiLCJpZCIsIndvcmtDb3VudCIsImFkZFBhZ2VSZXNvdXJjZXMiLCJpbmplY3RlZFJlc291cmNlcyIsImluamVjdGVkSnMiLCJqcyIsImNvbnRlbnQiLCJmaWxlcyIsImlzTG9jYWwiLCJqc1Jlc291cmNlIiwiYWRkU2NyaXB0VGFnIiwiaW5qZWN0ZWRDc3MiLCJjc3MiLCJjc3NJbXBvcnRzIiwibWF0Y2giLCJjc3NJbXBvcnRQYXRoIiwiY3NzUmVzb3VyY2UiLCJhZGRTdHlsZVRhZyIsImNsZWFyUGFnZVJlc291cmNlcyIsInJlc291cmNlIiwiZGlzcG9zZSIsIm9sZENoYXJ0cyIsImNoYXJ0cyIsIm9sZENoYXJ0IiwiZGVzdHJveSIsInNjcmlwdHNUb1JlbW92ZSIsImdldEVsZW1lbnRzQnlUYWdOYW1lIiwic3R5bGVzVG9SZW1vdmUiLCJsaW5rc1RvUmVtb3ZlIiwiZWxlbWVudCIsInJlbW92ZSIsInNldENvbnRlbnQiLCJjc3NUZW1wbGF0ZSIsInN2Z1RlbXBsYXRlIiwicHVwcGV0ZWVyRXhwb3J0IiwiaXNTVkciLCJzaXplIiwic3ZnRWxlbWVudCIsInF1ZXJ5U2VsZWN0b3IiLCJjaGFydEhlaWdodCIsImJhc2VWYWwiLCJjaGFydFdpZHRoIiwic3R5bGUiLCJ6b29tIiwibWFyZ2luIiwieCIsInkiLCJfZ2V0Q2xpcFJlZ2lvbiIsInZpZXdwb3J0SGVpZ2h0IiwiYWJzIiwiY2VpbCIsInZpZXdwb3J0V2lkdGgiLCJyZXN1bHQiLCJzZXRWaWV3cG9ydCIsImRldmljZVNjYWxlRmFjdG9yIiwiX2NyZWF0ZVNWRyIsIl9jcmVhdGVJbWFnZSIsIl9jcmVhdGVQREYiLCIkZXZhbCIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsInRydW5jIiwib3V0ZXJIVE1MIiwiY2xpcCIsInJhY2UiLCJzY3JlZW5zaG90IiwiZW5jb2RpbmciLCJmdWxsUGFnZSIsIm9wdGltaXplRm9yU3BlZWQiLCJjYXB0dXJlQmV5b25kVmlld3BvcnQiLCJxdWFsaXR5Iiwib21pdEJhY2tncm91bmQiLCJfcmVzb2x2ZSIsImVtdWxhdGVNZWRpYVR5cGUiLCJwZGYiLCJwb29sU3RhdHMiLCJleHBvcnRzQXR0ZW1wdGVkIiwiZXhwb3J0c1BlcmZvcm1lZCIsImV4cG9ydHNEcm9wcGVkIiwiZXhwb3J0c0Zyb21TdmciLCJleHBvcnRzRnJvbU9wdGlvbnMiLCJleHBvcnRzRnJvbVN2Z0F0dGVtcHRzIiwiZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHMiLCJ0aW1lU3BlbnQiLCJ0aW1lU3BlbnRBdmVyYWdlIiwiaW5pdFBvb2wiLCJwb29sT3B0aW9ucyIsIlBvb2wiLCJfZmFjdG9yeSIsImFjcXVpcmVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlVGltZW91dE1pbGxpcyIsImRlc3Ryb3lUaW1lb3V0TWlsbGlzIiwiaWRsZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzIiwicmVhcEludGVydmFsTWlsbGlzIiwicHJvcGFnYXRlQ3JlYXRlRXJyb3IiLCJjbGVhclN0YXR1cyIsIl9ldmVudElkIiwiaW5pdGlhbFJlc291cmNlcyIsImFjcXVpcmUiLCJwcm9taXNlIiwicmVsZWFzZSIsImtpbGxQb29sIiwid29ya2VyIiwidXNlZCIsImRlc3Ryb3llZCIsInBvc3RXb3JrIiwid29ya2VySGFuZGxlIiwiZ2V0UG9vbEluZm8iLCJhY3F1aXJlQ291bnRlciIsInJlcXVlc3RJZCIsIndvcmtTdGFydCIsImV4cG9ydENvdW50ZXIiLCJleHBvcnRUaW1lIiwiZ2V0UG9vbFN0YXRzIiwiZ2V0UG9vbEluZm9KU09OIiwibnVtVXNlZCIsImF2YWlsYWJsZSIsIm51bUZyZWUiLCJhbGxDcmVhdGVkIiwicGVuZGluZ0FjcXVpcmVzIiwibnVtUGVuZGluZ0FjcXVpcmVzIiwicGVuZGluZ0NyZWF0ZXMiLCJudW1QZW5kaW5nQ3JlYXRlcyIsInBlbmRpbmdWYWxpZGF0aW9ucyIsIm51bVBlbmRpbmdWYWxpZGF0aW9ucyIsInBlbmRpbmdEZXN0cm95cyIsImFic29sdXRlQWxsIiwiY3JlYXRlIiwidXVpZCIsInJhbmRvbSIsInN0YXJ0RGF0ZSIsInZhbGlkYXRlIiwibWFpbkZyYW1lIiwiZGV0YWNoZWQiLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJzYW5pdGl6ZSIsIkpTRE9NIiwiRE9NUHVyaWZ5IiwiQUREX1RBR1MiLCJzaW5nbGVFeHBvcnQiLCJzdGFydEV4cG9ydCIsImRhdGEiLCJiYXRjaEV4cG9ydCIsImJhdGNoRnVuY3Rpb25zIiwicGFpciIsImJhdGNoUmVzdWx0cyIsImFsbFNldHRsZWQiLCJyZWFzb24iLCJpbWFnZU9wdGlvbnMiLCJlbmRDYWxsYmFjayIsImZpbGVDb250ZW50IiwiX2V4cG9ydEZyb21TdmciLCJfZXhwb3J0RnJvbU9wdGlvbnMiLCJnZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJzZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJpbnB1dFRvRXhwb3J0IiwiX3ByZXBhcmVFeHBvcnQiLCJfaGFuZGxlQ3VzdG9tTG9naWMiLCJfaGFuZGxlR2xvYmFsQW5kVGhlbWUiLCJfZmluZENoYXJ0U2l6ZSIsIm9wdGlvbnNDaGFydCIsIm9wdGlvbnNFeHBvcnRpbmciLCJnbG9iYWxPcHRpb25zQ2hhcnQiLCJnbG9iYWxPcHRpb25zRXhwb3J0aW5nIiwidGhlbWVPcHRpb25zQ2hhcnQiLCJ0aGVtZU9wdGlvbnNFeHBvcnRpbmciLCJzb3VyY2VIZWlnaHQiLCJzb3VyY2VXaWR0aCIsInBhcmFtIiwiX2hhbmRsZVJlc291cmNlcyIsImFsbG93ZWRQcm9wcyIsImhhbmRsZWRSZXNvdXJjZXMiLCJjb3JyZWN0UmVzb3VyY2VzIiwicHJvcE5hbWUiLCJvcHRpb25zTmFtZSIsInRpbWVySWRzIiwiYWRkVGltZXIiLCJjbGVhckFsbFRpbWVycyIsImNsZWFySW50ZXJ2YWwiLCJjbGVhclRpbWVvdXQiLCJsb2dFcnJvck1pZGRsZXdhcmUiLCJyZXF1ZXN0IiwibmV4dCIsInJldHVybkVycm9yTWlkZGxld2FyZSIsInN0YXR1cyIsImpzb24iLCJlcnJvck1pZGRsZXdhcmUiLCJhcHAiLCJ1c2UiLCJyYXRlTGltaXRpbmdNaWRkbGV3YXJlIiwicmF0ZUxpbWl0aW5nT3B0aW9ucyIsInJhdGVPcHRpb25zIiwibGltaXRlciIsInJhdGVMaW1pdCIsIndpbmRvd01zIiwibGltaXQiLCJkZWxheU1zIiwiaGFuZGxlciIsImZvcm1hdCIsInNlbmQiLCJkZWZhdWx0Iiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwiY29udGVudFR5cGVNaWRkbGV3YXJlIiwiY29udGVudFR5cGUiLCJoZWFkZXJzIiwicmVxdWVzdEJvZHlNaWRkbGV3YXJlIiwiY29ubmVjdGlvbiIsInJlbW90ZUFkZHJlc3MiLCJ2YWxpZGF0ZWRPcHRpb25zIiwicGFyYW1zIiwiZmlsZW5hbWUiLCJ2YWxpZGF0aW9uTWlkZGxld2FyZSIsInBvc3QiLCJyZXZlcnNlZE1pbWUiLCJwbmciLCJqcGVnIiwiZ2lmIiwicmVxdWVzdEV4cG9ydCIsInJlcXVlc3RDb3VudGVyIiwiY29ubmVjdGlvbkFib3J0ZWQiLCJzb2NrZXQiLCJoYWRFcnJvcnMiLCJoZWFkZXIiLCJhdHRhY2htZW50IiwiZXhwb3J0Um91dGVzIiwic2VydmVyU3RhcnRUaW1lIiwicGFja2FnZUZpbGUiLCJzdWNjZXNzUmF0ZXMiLCJyZWNvcmRJbnRlcnZhbCIsIndpbmRvd1NpemUiLCJfY2FsY3VsYXRlTW92aW5nQXZlcmFnZSIsImEiLCJiIiwiX3N0YXJ0U3VjY2Vzc1JhdGUiLCJzZXRJbnRlcnZhbCIsInN0YXRzIiwic3VjY2Vzc1JhdGlvIiwiaGVhbHRoUm91dGVzIiwicGVyaW9kIiwibW92aW5nQXZlcmFnZSIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJzZXJ2ZXJWZXJzaW9uIiwiaGlnaGNoYXJ0c1ZlcnNpb24iLCJhdmVyYWdlRXhwb3J0VGltZSIsImF0dGVtcHRlZEV4cG9ydHMiLCJwZXJmb3JtZWRFeHBvcnRzIiwiZmFpbGVkRXhwb3J0cyIsInN1Y2Vzc1JhdGlvIiwidG9GaXhlZCIsInN2Z0V4cG9ydHMiLCJqc29uRXhwb3J0cyIsInN2Z0V4cG9ydHNBdHRlbXB0cyIsImpzb25FeHBvcnRzQXR0ZW1wdHMiLCJ1aVJvdXRlcyIsInNlbmRGaWxlIiwiYWNjZXB0UmFuZ2VzIiwidmVyc2lvbkNoYW5nZVJvdXRlcyIsImFkbWluVG9rZW4iLCJ0b2tlbiIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJPcHRpb25zIiwidXBsb2FkTGltaXRCeXRlcyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRTaXplIiwiZGlzYWJsZSIsImNvcnMiLCJtZXRob2RzIiwic2V0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsInN0YXRpYyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyIsImxpc3RlbiIsImNlcnQiLCJodHRwc1NlcnZlciIsImNsb3NlU2VydmVycyIsImRlbGV0ZSIsImdldFNlcnZlcnMiLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwiZW5hYmxlUmF0ZUxpbWl0aW5nIiwibWlkZGxld2FyZXMiLCJzaHV0ZG93bkNsZWFuVXAiLCJleGl0Q29kZSIsImV4aXQiLCJpbml0RXhwb3J0IiwiaW5pdE9wdGlvbnMiLCJfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMiLCJjb2RlIl0sIm1hcHBpbmdzIjoid2xCQTJCTyxNQUFNQSxZQUFZQyxJQUFh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 +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?path.normalize(e):path.resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIGpvaW4sIG5vcm1hbGl6ZSwgcmVzb2x2ZSB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBkaXJlY3RvcnkgcGF0aFxyXG5leHBvcnQgY29uc3QgX19kaXJuYW1lID0gZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuLi8uJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFuZCBzdGFuZGFyZGl6ZXMgdGV4dCBieSByZXBsYWNpbmcgbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZVxyXG4gKiBjaGFyYWN0ZXJzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIHRyaW1taW5nIGFueSBsZWFkaW5nIG9yIHRyYWlsaW5nXHJcbiAqIHdoaXRlc3BhY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhclRleHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyB0aGUgJy9cXHNcXHMrL2cnIFJlZ0V4cC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtyZXBsYWNlcj0nICddIC0gVGhlIHN0cmluZyB1c2VkIHRvIHJlcGxhY2UgbXVsdGlwbGVcclxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgJyAnIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyVGV4dCh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpIHtcclxuICByZXR1cm4gdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBkZWVwQ29weVxyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8QXJyYXkpfSBvYmpBcnIgLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fEFycmF5KX0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDb3B5KG9iakFycikge1xyXG4gIC8vIElmIHRoZSBgb2JqQXJyYCBpcyBudWxsIG9yIG5vdCBvZiB0aGUgYG9iamVjdGAgdHlwZSwgcmV0dXJuIGl0XHJcbiAgaWYgKG9iakFyciA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqQXJyICE9PSAnb2JqZWN0Jykge1xyXG4gICAgcmV0dXJuIG9iakFycjtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZWl0aGVyIGEgbmV3IGFycmF5IG9yIGEgbmV3IG9iamVjdFxyXG4gIGNvbnN0IG9iakFyckNvcHkgPSBBcnJheS5pc0FycmF5KG9iakFycikgPyBbXSA6IHt9O1xyXG5cclxuICAvLyBSZWN1cnNpdmVseSBjb3B5IGVhY2ggcHJvcGVydHlcclxuICBmb3IgKGNvbnN0IGtleSBpbiBvYmpBcnIpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqQXJyLCBrZXkpKSB7XHJcbiAgICAgIG9iakFyckNvcHlba2V5XSA9IGRlZXBDb3B5KG9iakFycltrZXldKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY29waWVkIG9iamVjdFxyXG4gIHJldHVybiBvYmpBcnJDb3B5O1xyXG59XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZXhwQmFja29mZlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBmdW5jdGlvbiB0byBiZSByZXRyaWVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYDBgLlxyXG4gKiBAcGFyYW0gey4uLnVua25vd259IGFyZ3MgLSBBcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSByZXN1bHRcclxuICogb2YgdGhlIGZ1bmN0aW9uIGlmIHN1Y2Nlc3NmdWwuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCBpZiB0aGUgbWF4aW11bSBudW1iZXIgb2YgYXR0ZW1wdHNcclxuICogaXMgcmVhY2hlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleHBCYWNrb2ZmKGZuLCBhdHRlbXB0ID0gMCwgLi4uYXJncykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBUcnkgdG8gY2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIHJldHVybiBhd2FpdCBmbiguLi5hcmdzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gQ2FsY3VsYXRlIGRlbGF5IGluIG1zXHJcbiAgICBjb25zdCBkZWxheUluTXMgPSAyICoqIGF0dGVtcHQgKiAxMDAwO1xyXG5cclxuICAgIC8vIElmIHRoZSBhdHRlbXB0IGV4Y2VlZHMgdGhlIG1heGltdW0gYXR0ZW1wdHMgb2YgcmVwZWF0LCB0aHJvdyBhbiBlcnJvclxyXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXYWl0IGdpdmVuIGFtb3VudCBvZiB0aW1lXHJcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIGRlbGF5SW5NcykpO1xyXG5cclxuICAgIC8vLyBUTyBETzogQ29ycmVjdFxyXG4gICAgLy8gLy8gSW5mb3JtYXRpb24gYWJvdXQgdGhlIHJlc291cmNlIHRpbWVvdXRcclxuICAgIC8vIGxvZyhcclxuICAgIC8vICAgMyxcclxuICAgIC8vICAgYFt1dGlsc10gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIG9mIElEOiAke2FyZ3NbMF19LmBcclxuICAgIC8vICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRqdXN0cyB0aGUgY29uc3RydWN0b3IgbmFtZSBieSB0cmFuc2Zvcm1pbmcgYW5kIG5vcm1hbGl6aW5nIGl0IGJhc2VkXHJcbiAqIG9uIGNvbW1vbiBjaGFydCB0eXBlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeENvbnN0clxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY29uc3RyIC0gVGhlIG9yaWdpbmFsIGNvbnN0cnVjdG9yIG5hbWUgdG8gYmUgZml4ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IgbmFtZSwgb3IgJ2NoYXJ0JyBpZiB0aGUgaW5wdXRcclxuICogaXMgbm90IHJlY29nbml6ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4Q29uc3RyKGNvbnN0cikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBGaXggdGhlIGNvbnN0cnVjdG9yIGJ5IGxvd2VyaW5nIGNhc2luZ1xyXG4gICAgY29uc3QgZml4ZWRDb25zdHIgPSBgJHtjb25zdHIudG9Mb3dlckNhc2UoKS5yZXBsYWNlKCdjaGFydCcsICcnKX1DaGFydGA7XHJcblxyXG4gICAgLy8gSGFuZGxlIHRoZSBjYXNlIHdoZXJlIHRoZSByZXN1bHQgaXMganVzdCAnQ2hhcnQnXHJcbiAgICBpZiAoZml4ZWRDb25zdHIgPT09ICdDaGFydCcpIHtcclxuICAgICAgZml4ZWRDb25zdHIudG9Mb3dlckNhc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZXR1cm4gdGhlIGNvcnJlY3RlZCBjb25zdHJ1Y3Rvciwgb3RoZXJ3aXNlIGRlZmF1bHQgdG8gJ2NoYXJ0J1xyXG4gICAgcmV0dXJuIFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10uaW5jbHVkZXMoXHJcbiAgICAgIGZpeGVkQ29uc3RyXHJcbiAgICApXHJcbiAgICAgID8gZml4ZWRDb25zdHJcclxuICAgICAgOiAnY2hhcnQnO1xyXG4gIH0gY2F0Y2gge1xyXG4gICAgLy8gRGVmYXVsdCB0byAnY2hhcnQnIGluIGNhc2Ugb2YgYW55IGVycm9yXHJcbiAgICByZXR1cm4gJ2NoYXJ0JztcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGaXhlcyB0aGUgb3V0ZmlsZSBiYXNlZCBvbiBwcm92aWRlZCB0eXBlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZml4T3V0ZmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSBvcmlnaW5hbCBleHBvcnQgdHlwZS5cclxuICogQHBhcmFtIHtzdHJpbmd9IG91dGZpbGUgLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgb3V0ZmlsZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhPdXRmaWxlKHR5cGUsIG91dGZpbGUpIHtcclxuICAvLyBHZXQgdGhlIGZpbGUgbmFtZSBmcm9tIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgY29uc3QgZmlsZU5hbWUgPSBnZXRBYnNvbHV0ZVBhdGgob3V0ZmlsZSB8fCAnY2hhcnQnKVxyXG4gICAgLnNwbGl0KCcuJylcclxuICAgIC5zaGlmdCgpO1xyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IG91dGZpbGVcclxuICByZXR1cm4gYCR7ZmlsZU5hbWV9LiR7dHlwZX1gO1xyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIGV4cG9ydCB0eXBlIGJhc2VkIG9uIE1JTUUgdHlwZXMgYW5kIGZpbGUgZXh0ZW5zaW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeFR5cGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbb3V0ZmlsZT1udWxsXSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYG51bGxgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeFR5cGUodHlwZSwgb3V0ZmlsZSA9IG51bGwpIHtcclxuICAvLyBNSU1FIHR5cGVzXHJcbiAgY29uc3QgbWltZVR5cGVzID0ge1xyXG4gICAgJ2ltYWdlL3BuZyc6ICdwbmcnLFxyXG4gICAgJ2ltYWdlL2pwZWcnOiAnanBlZycsXHJcbiAgICAnYXBwbGljYXRpb24vcGRmJzogJ3BkZicsXHJcbiAgICAnaW1hZ2Uvc3ZnK3htbCc6ICdzdmcnXHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IGZvcm1hdHNcclxuICBjb25zdCBmb3JtYXRzID0gT2JqZWN0LnZhbHVlcyhtaW1lVHlwZXMpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICAvLyBTdXBwb3J0IHRoZSBKUEcgdHlwZVxyXG4gICAgaWYgKG91dFR5cGUgPT09ICdqcGcnKSB7XHJcbiAgICAgIHR5cGUgPSAnanBlZyc7XHJcbiAgICB9IGVsc2UgaWYgKGZvcm1hdHMuaW5jbHVkZXMob3V0VHlwZSkgJiYgdHlwZSAhPT0gb3V0VHlwZSkge1xyXG4gICAgICB0eXBlID0gb3V0VHlwZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGNvcnJlY3QgdHlwZVxyXG4gIHJldHVybiBtaW1lVHlwZXNbdHlwZV0gfHwgZm9ybWF0cy5maW5kKCh0KSA9PiB0ID09PSB0eXBlKSB8fCAncG5nJztcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gcGF0aCBpcyByZWxhdGl2ZSBvciBhYnNvbHV0ZSBhbmQgcmV0dXJucyB0aGUgY29ycmVjdGVkLFxyXG4gKiBhYnNvbHV0ZSBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBYnNvbHV0ZVBhdGhcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byBiZSBjaGVja2VkIG9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgYWJzb2x1dGUgcGF0aC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBYnNvbHV0ZVBhdGgocGF0aCkge1xyXG4gIHJldHVybiBpc0Fic29sdXRlKHBhdGgpID8gbm9ybWFsaXplKHBhdGgpIDogcmVzb2x2ZShwYXRoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGlucHV0IGRhdGEgdG8gYSBCYXNlNjQgc3RyaW5nIGJhc2VkIG9uIHRoZSBleHBvcnQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJhc2U2NFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgaW5wdXQgdG8gYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZvcm1hdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBCYXNlNjQgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRCYXNlNjQoaW5wdXQsIHR5cGUpIHtcclxuICAvLyBGb3IgcGRmIGFuZCBzdmcgdHlwZXMgdGhlIGlucHV0IG11c3QgYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZyb20gYSBidWZmZXJcclxuICBpZiAodHlwZSA9PT0gJ3BkZicgfHwgdHlwZSA9PSAnc3ZnJykge1xyXG4gICAgcmV0dXJuIEJ1ZmZlci5mcm9tKGlucHV0LCAndXRmOCcpLnRvU3RyaW5nKCdiYXNlNjQnKTtcclxuICB9XHJcblxyXG4gIC8vIEZvciBwbmcgYW5kIGpwZWcgaW5wdXQgaXMgYWxyZWFkeSBhIEJhc2U2NCBzdHJpbmdcclxuICByZXR1cm4gaW5wdXQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHN0cmluZ2lmaWVkIGRhdGUgd2l0aG91dCB0aGUgR01UIHRleHQgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3RGF0ZSgpIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIHJldHVybiBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBzdG9yZWQgdGltZSB2YWx1ZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlVGltZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGVUaW1lKCkge1xyXG4gIHJldHVybiBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gaXRlbSBpcyBhbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgaXRlbSBpcyBhbiBvYmplY3QsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdChpdGVtKSB7XHJcbiAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChpdGVtKSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIG9iamVjdCBpcyBlbXB0eS5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW0gLSBUaGUgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdEVtcHR5KGl0ZW0pIHtcclxuICByZXR1cm4gKFxyXG4gICAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgICAhQXJyYXkuaXNBcnJheShpdGVtKSAmJlxyXG4gICAgaXRlbSAhPT0gbnVsbCAmJlxyXG4gICAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kLCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChpdGVtKSB7XHJcbiAgY29uc3QgcmVnZXhQYXR0ZXJucyA9IFtcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT9sb2NhbGhvc3RcXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEwXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMjdcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE3MlxcLigxWzYtOV18MlswLTldfDNbMC0xXSlcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xOTJcXC4xNjhcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiL1xyXG4gIF07XHJcblxyXG4gIHJldHVybiByZWdleFBhdHRlcm5zLnNvbWUoKHBhdHRlcm4pID0+IHBhdHRlcm4udGVzdChpdGVtKSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVdGlsaXR5IHRvIG1lYXN1cmUgZWxhcHNlZCB0aW1lIHVzaW5nIHRoZSBOb2RlLmpzIGBwcm9jZXNzLmhydGltZSgpYCBtZXRob2QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtZWFzdXJlVGltZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IEEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBlbGFwc2VkIHRpbWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1lYXN1cmVUaW1lKCkge1xyXG4gIGNvbnN0IHN0YXJ0ID0gcHJvY2Vzcy5ocnRpbWUuYmlnaW50KCk7XHJcbiAgcmV0dXJuICgpID0+IE51bWJlcihwcm9jZXNzLmhydGltZS5iaWdpbnQoKSAtIHN0YXJ0KSAvIDEwMDAwMDA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSb3VuZHMgYSBudW1iZXIgdG8gdGhlIHNwZWNpZmllZCBwcmVjaXNpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByb3VuZE51bWJlclxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBwcmVjaXNpb24gLSBUaGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIHRvIHJvdW5kIHRvLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgcm91bmRlZCBudW1iZXIuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcm91bmROdW1iZXIodmFsdWUsIHByZWNpc2lvbiA9IDEpIHtcclxuICBjb25zdCBtdWx0aXBsaWVyID0gTWF0aC5wb3coMTAsIHByZWNpc2lvbiB8fCAwKTtcclxuICByZXR1cm4gTWF0aC5yb3VuZCgrdmFsdWUgKiBtdWx0aXBsaWVyKSAvIG11bHRpcGxpZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBhIHZhbHVlIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHRvQm9vbGVhblxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSBib29sZWFuIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dCB2YWx1ZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB0b0Jvb2xlYW4oaXRlbSkge1xyXG4gIHJldHVybiBbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTicsICcwJywgJyddLmluY2x1ZGVzKGl0ZW0pXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6ICEhaXRlbTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFdyYXBzIGN1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgaXQgc2FmZWx5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gd3JhcEFyb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tQ29kZSAtIFRoZSBjdXN0b20gY29kZSB0byBiZSB3cmFwcGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEZsYWcgdG8gYWxsb3cgbG9hZGluZyBjb2RlIGZyb20gYSBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtpc0NhbGxiYWNrPWZhbHNlXSAtIEZsYWcgdGhhdCBpbmRpY2F0ZXMgdGhlIHJldHVybmVkIGNvZGVcclxuICogbXVzdCBiZSBpbiBhIGNhbGxiYWNrIGZvcm1hdC5cclxuICpcclxuICogQHJldHVybnMgeyhzdHJpbmd8bnVsbCl9IFRoZSB3cmFwcGVkIGN1c3RvbSBjb2RlIG9yIG51bGwgaWYgd3JhcHBpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gd3JhcEFyb3VuZChjdXN0b21Db2RlLCBhbGxvd0ZpbGVSZXNvdXJjZXMsIGlzQ2FsbGJhY2sgPSBmYWxzZSkge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICAvLyBMb2FkIGEgZmlsZSBpZiB0aGUgZmlsZSByZXNvdXJjZXMgYXJlIGFsbG93ZWRcclxuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICAgID8gd3JhcEFyb3VuZChcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChjdXN0b21Db2RlKSwgJ3V0ZjgnKSxcclxuICAgICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgICBpc0NhbGxiYWNrXHJcbiAgICAgICAgICApXHJcbiAgICAgICAgOiBudWxsO1xyXG4gICAgfSBlbHNlIGlmIChcclxuICAgICAgIWlzQ2FsbGJhY2sgJiZcclxuICAgICAgKGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24oKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbiAoKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpID0+JykpXHJcbiAgICApIHtcclxuICAgICAgLy8gVHJlYXQgYSBmdW5jdGlvbiBhcyBhIHNlbGYtaW52b2tpbmcgZXhwcmVzc2lvblxyXG4gICAgICByZXR1cm4gYCgke2N1c3RvbUNvZGV9KSgpYDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBPciByZXR1cm4gYXMgYSBzdHJpbmdpZmllZCBjb2RlXHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGRlZXBDb3B5LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4Q29uc3RyLFxyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIGdldE5ld0RhdGUsXHJcbiAgZ2V0TmV3RGF0ZVRpbWUsXHJcbiAgaXNPYmplY3QsXHJcbiAgaXNPYmplY3RFbXB0eSxcclxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxyXG4gIG1lYXN1cmVUaW1lLFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHRvQm9vbGVhbixcclxuICB3cmFwQXJvdW5kXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSBmb3IgbWFuYWdpbmcgbG9nZ2luZyBmdW5jdGlvbmFsaXR5IHdpdGggY3VzdG9taXphYmxlXHJcbiAqIGxvZyBsZXZlbHMsIGNvbnNvbGUgYW5kIGZpbGUgbG9nZ2luZyBvcHRpb25zLCBhbmQgZXJyb3IgaGFuZGxpbmcgc3VwcG9ydC5cclxuICogVGhlIG1vZHVsZSBhbHNvIGVuc3VyZXMgdGhhdCBmaWxlLWJhc2VkIGxvZ3MgYXJlIHN0b3JlZCBpbiBhIHN0cnVjdHVyZWRcclxuICogZGlyZWN0b3J5LCBjcmVhdGluZyB0aGUgbmVjZXNzYXJ5IHBhdGhzIGF1dG9tYXRpY2FsbHkgaWYgdGhleSBkbyBub3QgZXhpc3QuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYXBwZW5kRmlsZSwgZXhpc3RzU3luYywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGgsIGdldE5ld0RhdGUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmNvbnN0IGxvZ2dpbmcgPSB7XHJcbiAgLy8gRmxhZ3MgZm9yIGxvZ2dpbmcgc3RhdHVzXHJcbiAgdG9Db25zb2xlOiB0cnVlLFxyXG4gIHRvRmlsZTogZmFsc2UsXHJcbiAgcGF0aENyZWF0ZWQ6IGZhbHNlLFxyXG4gIC8vIEZ1bGwgcGF0aCB0byB0aGUgbG9nIGZpbGVcclxuICBwYXRoVG9Mb2c6ICcnLFxyXG4gIC8vIExvZyBsZXZlbHNcclxuICBsZXZlbHNEZXNjOiBbXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnZXJyb3InLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzBdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3dhcm5pbmcnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzFdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ25vdGljZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMl1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAndmVyYm9zZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbM11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnYmVuY2htYXJrJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1s0XVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGEgbWVzc2FnZSB3aXRoIGEgc3BlY2lmaWVkIGxvZyBsZXZlbC4gQWNjZXB0cyBhIHZhcmlhYmxlIG51bWJlclxyXG4gKiBvZiBhcmd1bWVudHMuIFRoZSBhcmd1bWVudHMgYWZ0ZXIgdGhlIGBsZXZlbGAgYXJlIHBhc3NlZCB0byBgY29uc29sZS5sb2dgXHJcbiAqIGFuZC9vciB1c2VkIHRvIGNvbnN0cnVjdCBhbmQgYXBwZW5kIG1lc3NhZ2VzIHRvIGEgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dcclxuICpcclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQW4gYXJyYXkgb2YgYXJndW1lbnRzIHdoZXJlIHRoZSBmaXJzdCBpcyB0aGUgbG9nXHJcbiAqIGxldmVsIGFuZCB0aGUgcmVtYWluaW5nIGFyZSBzdHJpbmdzIHVzZWQgdG8gYnVpbGQgdGhlIGxvZyBtZXNzYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nKC4uLmFyZ3MpIHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbHNEZXNjLCBsZXZlbCB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlIG9yIGlzIGl0IGEgYmVuY2htYXJrIGxvZ1xyXG4gIGlmIChcclxuICAgIG5ld0xldmVsICE9PSA1ICYmXHJcbiAgICAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKVxyXG4gICkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdCh0ZXh0cylcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIGFsb25nIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ1dpdGhTdGFja1xyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbG9nIGxldmVsLlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QgY29udGFpbmluZyB0aGUgc3RhY2sgdHJhY2UuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21NZXNzYWdlIC0gQW4gb3B0aW9uYWwgY3VzdG9tIG1lc3NhZ2UgdG8gYmUgaW5jbHVkZWRcclxuICogaW4gdGhlIGxvZyBhbG9uZ3NpZGUgdGhlIGVycm9yLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nV2l0aFN0YWNrKG5ld0xldmVsLCBlcnJvciwgY3VzdG9tTWVzc2FnZSkge1xyXG4gIC8vIEdldCB0aGUgbWFpbiBtZXNzYWdlXHJcbiAgY29uc3QgbWFpbk1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlIHx8IChlcnJvciAmJiBlcnJvci5tZXNzYWdlKSB8fCAnJztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQWRkIHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXHJcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID0gZXJyb3IgJiYgZXJyb3Iuc3RhY2s7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2UsIGlmIGV4aXN0c1xyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlXTtcclxuICBpZiAoc3RhY2tNZXNzYWdlKSB7XHJcbiAgICB0ZXh0cy5wdXNoKCdcXG4nLCBzdGFja01lc3NhZ2UpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcclxuICAgICAgICB0ZXh0cy5zaGlmdCgpW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAuLi50ZXh0c1xyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpbml0TG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgbG9nZ2luZ2Agb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpbml0TG9nZ2luZyhsb2dnaW5nT3B0aW9ucykge1xyXG4gIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGBsb2dnaW5nT3B0aW9uc2Agb2JqZWN0XHJcbiAgY29uc3QgeyBsZXZlbCwgZGVzdCwgZmlsZSwgdG9Db25zb2xlLCB0b0ZpbGUgfSA9IGxvZ2dpbmdPcHRpb25zO1xyXG5cclxuICAvLyBSZXNldCBmbGFncyB0byB0aGUgZGVmYXVsdCB2YWx1ZXNcclxuICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgbG9nZ2luZy5wYXRoVG9Mb2cgPSAnJztcclxuXHJcbiAgLy8gU2V0IHRoZSBsb2dnaW5nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobGV2ZWwpO1xyXG5cclxuICAvLyBTZXQgdGhlIGNvbnNvbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nKHRvQ29uc29sZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgZmlsZSBsb2dnaW5nXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGxvZyBsZXZlbCB0byB0aGUgc3BlY2lmaWVkIHZhbHVlLiBMb2cgbGV2ZWxzIGFyZSAoYDBgID0gbm8gbG9nZ2luZyxcclxuICogYDFgID0gZXJyb3IsIGAyYCA9IHdhcm5pbmcsIGAzYCA9IG5vdGljZSwgYDRgID0gdmVyYm9zZSwgb3IgYDVgID0gYmVuY2htYXJrKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldExvZ0xldmVsXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsb2cgbGV2ZWwgdG8gYmUgc2V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldExvZ0xldmVsKGxldmVsKSB7XHJcbiAgaWYgKFxyXG4gICAgTnVtYmVyLmlzSW50ZWdlcihsZXZlbCkgJiZcclxuICAgIGxldmVsID49IDAgJiZcclxuICAgIGxldmVsIDw9IGxvZ2dpbmcubGV2ZWxzRGVzYy5sZW5ndGhcclxuICApIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgbGV2ZWxgIG9wdGlvblxyXG4gICAgbG9nZ2luZy5sZXZlbCA9IGxldmVsO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgY29uc29sZSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB0b0NvbnNvbGUgLSBUaGUgZmxhZyBmb3Igc2V0dGluZyB0aGUgbG9nZ2luZyB0byB0aGUgY29uc29sZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpIHtcclxuICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYHRvQ29uc29sZWAgb3B0aW9uXHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSAhIXRvQ29uc29sZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUgbmFtZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBkZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggd2hlcmUgdGhlIGxvZyBmaWxlIHNob3VsZFxyXG4gKiBiZSBzYXZlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGZpbGUgLSBUaGUgbmFtZSBvZiB0aGUgbG9nIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9GaWxlIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2dnaW5nIHNob3VsZFxyXG4gKiBiZSBkaXJlY3RlZCB0byBhIGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0ZpbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9GaWxlID0gISF0b0ZpbGU7XHJcblxyXG4gIC8vIFNldCB0aGUgYGRlc3RgIGFuZCBgZmlsZWAgb3B0aW9ucyBvbmx5IGlmIHRoZSBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZFxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ID0gZGVzdCB8fCAnJztcclxuICAgIGxvZ2dpbmcuZmlsZSA9IGZpbGUgfHwgJyc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyB0aGUgcHJvdmlkZWQgdGV4dHMgdG8gYSBmaWxlLCBpZiBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZC4gSXQgY3JlYXRlc1xyXG4gKiB0aGUgbmVjZXNzYXJ5IGRpcmVjdG9yeSBzdHJ1Y3R1cmUgaWYgbm90IGFscmVhZHkgY3JlYXRlZCBhbmQgYXBwZW5kc1xyXG4gKiB0aGUgY29udGVudCwgaW5jbHVkaW5nIGFuIG9wdGlvbmFsIHByZWZpeCwgdG8gdGhlIHNwZWNpZmllZCBsb2cgZmlsZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2dUb0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBBbiBvcHRpb25hbCBwcmVmaXggdG8gYmUgYWRkZWQgdG8gZWFjaCBsb2cgZW50cnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpIHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpICYmXHJcbiAgICAgIG1rZGlyU3luYyhnZXRBYnNvbHV0ZVBhdGgobG9nZ2luZy5kZXN0KSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBmdWxsIHBhdGhcclxuICAgIGxvZ2dpbmcucGF0aFRvTG9nID0gZ2V0QWJzb2x1dGVQYXRoKGpvaW4obG9nZ2luZy5kZXN0LCBsb2dnaW5nLmZpbGUpKTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yICYmIGxvZ2dpbmcudG9GaWxlICYmIGxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29uZmlndXJhdGlvbiBtYW5hZ2VtZW50IG1vZHVsZSBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci5cclxuICogUHJvdmlkZXMgZGVmYXVsdCBjb25maWd1cmF0aW9ucyB0aGF0IHN1cHBvcnQgZW52aXJvbm1lbnQgdmFyaWFibGVzLCBDTElcclxuICogYXJndW1lbnRzLCBhbmQgaW50ZXJhY3RpdmUgcHJvbXB0cyBmb3IgY3VzdG9taXphdGlvbiBvZiBvcHRpb25zIGFuZCBmZWF0dXJlcy5cclxuICogQWRkaXRpb25hbGx5LCBpdCBtYXBzIGxlZ2FjeSBvcHRpb25zIHRvIG1vZGVybiBzdHJ1Y3R1cmVzLCBnZW5lcmF0ZXMgbmVzdGVkXHJcbiAqIGFyZ3VtZW50IG1hcHBpbmdzLCBhbmQgZGlzcGxheXMgQ0xJIHVzYWdlIGluZm9ybWF0aW9uLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBhbGwgYXZhaWxhYmxlIG9wdGlvbnMsIG9yZ2FuaXplZFxyXG4gKiBieSBzZWN0aW9ucy5cclxuICpcclxuICogVGhpcyBvYmplY3QgaW5jbHVkZXM6XHJcbiAqIC0gRGVmYXVsdCB2YWx1ZXMgZm9yIGVhY2ggb3B0aW9uXHJcbiAqIC0gRGF0YSB0eXBlcyBmb3IgdmFsaWRhdGlvblxyXG4gKiAtIE5hbWVzIG9mIGNvcnJlc3BvbmRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIC0gRGVzY3JpcHRpb25zIG9mIGVhY2ggcHJvcGVydHlcclxuICogLSBJbmZvcm1hdGlvbiB1c2VkIGZvciBwcm9tcHRzIGluIGludGVyYWN0aXZlIGNvbmZpZ3VyYXRpb25cclxuICogLSBbT3B0aW9uYWxdIENvcnJlc3BvbmRpbmcgQ0xJIGFyZ3VtZW50IG5hbWVzIGZvciBDTEkgdXNhZ2VcclxuICogLSBbT3B0aW9uYWxdIExlZ2FjeSBuYW1lcyBmcm9tIHRoZSBwcmV2aW91cyBQaGFudG9tSlMtYmFzZWQgc2VydmVyXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IHtcclxuICAgIGFyZ3M6IHtcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnLS1hbGxvdy1ydW5uaW5nLWluc2VjdXJlLWNvbnRlbnQnLFxyXG4gICAgICAgICctLWFzaC1uby1udWRnZXMnLFxyXG4gICAgICAgICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICAgICAgICctLWJsb2NrLW5ldy13ZWItY29udGVudHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYWNjZWxlcmF0ZWQtMmQtY2FudmFzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtbmV0d29ya2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLXRpbWVyLXRocm90dGxpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZGluZy1vY2NsdWRlZC13aW5kb3dzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJyZWFrcGFkJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNoZWNrZXItaW1hZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jbGllbnQtc2lkZS1waGlzaGluZy1kZXRlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LWV4dGVuc2lvbnMtd2l0aC1iYWNrZ3JvdW5kLXBhZ2VzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICAgICAgICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAgICAgICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZmVhdHVyZXM9Q2FsY3VsYXRlTmF0aXZlV2luT2NjbHVzaW9uLEludGVyZXN0RmVlZENvbnRlbnRTdWdnZXN0aW9ucyxXZWJPVFAnLFxyXG4gICAgICAgICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWlwYy1mbG9vZGluZy1wcm90ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWxvZ2dpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtbm90aWZpY2F0aW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1vZmZlci1zdG9yZS11bm1hc2tlZC13YWxsZXQtY2FyZHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtcG9wdXAtYmxvY2tpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJpbnQtcHJldmlldycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcm9tcHQtb24tcmVwb3N0JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXJlbmRlcmVyLWJhY2tncm91bmRpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2VhcmNoLWVuZ2luZS1jaG9pY2Utc2NyZWVuJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlc3Npb24tY3Jhc2hlZC1idWJibGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2l0ZS1pc29sYXRpb24tdHJpYWxzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICAgICAgICctLWRpc2FibGUtc3luYycsXHJcbiAgICAgICAgJy0tZW5hYmxlLXVuc2FmZS13ZWJncHUnLFxyXG4gICAgICAgICctLWhpZGUtY3Jhc2gtcmVzdG9yZS1idWJibGUnLFxyXG4gICAgICAgICctLWhpZGUtc2Nyb2xsYmFycycsXHJcbiAgICAgICAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgICAgICAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgICAgICAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAgICAgICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICAgICAgICctLW5vLXBpbmdzJyxcclxuICAgICAgICAnLS1uby1zYW5kYm94JyxcclxuICAgICAgICAnLS1uby1zdGFydHVwLXdpbmRvdycsXHJcbiAgICAgICAgJy0tbm8tenlnb3RlJyxcclxuICAgICAgICAnLS1wYXNzd29yZC1zdG9yZT1iYXNpYycsXHJcbiAgICAgICAgJy0tcHJvY2Vzcy1wZXItdGFiJyxcclxuICAgICAgICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuICAgICAgXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ1BVUFBFVEVFUl9BUkdTJyxcclxuICAgICAgY2xpTmFtZTogJ3B1cHBldGVlckFyZ3MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FycmF5IG9mIFB1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfVkVSU0lPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2RuVXJsOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NETl9VUkwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0NETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGbGFnIHRvIHJlZmV0Y2ggc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdG9yeSBwYXRoIGZvciBjYWNoZWQgSGlnaGNoYXJ0cyBzY3JpcHRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DT1JFX1NDUklQVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtb2R1bGVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ3N0b2NrJyxcclxuICAgICAgICAnbWFwJyxcclxuICAgICAgICAnZ2FudHQnLFxyXG4gICAgICAgICdleHBvcnRpbmcnLFxyXG4gICAgICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAgICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgICAgIC8vICdhbm5vdGF0aW9ucy1hZHZhbmNlZCcsXHJcbiAgICAgICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAgICAgJ2Jvb3N0JyxcclxuICAgICAgICAnZGF0YScsXHJcbiAgICAgICAgJ2RhdGEtdG9vbHMnLFxyXG4gICAgICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICAgICAnc3RhdGljLXNjYWxlJyxcclxuICAgICAgICAnYnJva2VuLWF4aXMnLFxyXG4gICAgICAgICdoZWF0bWFwJyxcclxuICAgICAgICAndGlsZW1hcCcsXHJcbiAgICAgICAgJ3RpbGVkd2VibWFwJyxcclxuICAgICAgICAndGltZWxpbmUnLFxyXG4gICAgICAgICd0cmVlbWFwJyxcclxuICAgICAgICAndHJlZWdyYXBoJyxcclxuICAgICAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgICAgICdkcmlsbGRvd24nLFxyXG4gICAgICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICAgICAnYnVsbGV0JyxcclxuICAgICAgICAnZnVubmVsJyxcclxuICAgICAgICAnZnVubmVsM2QnLFxyXG4gICAgICAgICdnZW9oZWF0bWFwJyxcclxuICAgICAgICAncHlyYW1pZDNkJyxcclxuICAgICAgICAnbmV0d29ya2dyYXBoJyxcclxuICAgICAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAgICAgJ3BhcmV0bycsXHJcbiAgICAgICAgJ3BhdHRlcm4tZmlsbCcsXHJcbiAgICAgICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAgICAgJ3ByaWNlLWluZGljYXRvcicsXHJcbiAgICAgICAgJ3NhbmtleScsXHJcbiAgICAgICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICAgICAnZGVwZW5kZW5jeS13aGVlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1sYWJlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1vbi1wb2ludCcsXHJcbiAgICAgICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICAgICAnc29uaWZpY2F0aW9uJyxcclxuICAgICAgICAvLyAnc3RvY2stdG9vbHMnLFxyXG4gICAgICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAgICAgJ3N1bmJ1cnN0JyxcclxuICAgICAgICAndmFyaWFibGUtcGllJyxcclxuICAgICAgICAndmFyaXdpZGUnLFxyXG4gICAgICAgICd2ZWN0b3InLFxyXG4gICAgICAgICd2ZW5uJyxcclxuICAgICAgICAnd2luZGJhcmInLFxyXG4gICAgICAgICd3b3JkY2xvdWQnLFxyXG4gICAgICAgICd4cmFuZ2UnLFxyXG4gICAgICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgICAgICdkcmFnLXBhbmVzJyxcclxuICAgICAgICAnZGVidWdnZXInLFxyXG4gICAgICAgICdkdW1iYmVsbCcsXHJcbiAgICAgICAgJ2xvbGxpcG9wJyxcclxuICAgICAgICAnY3lsaW5kZXInLFxyXG4gICAgICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgICAgICdkb3RwbG90JyxcclxuICAgICAgICAnbWFya2VyLWNsdXN0ZXJzJyxcclxuICAgICAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgICAgICdoZWlraW5hc2hpJyxcclxuICAgICAgICAnZmxvd21hcCcsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAnbmF2aWdhdG9yJyxcclxuICAgICAgICAndGV4dHBhdGgnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIG1vZHVsZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbmRpY2F0b3JTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2luZGljYXRvcnMtYWxsJ10sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGluZGljYXRvciBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjMwLjEvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuNDUvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBZGRpdGlvbmFsIGN1c3RvbSBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgICAgc2VwYXJhdG9yOiAnOydcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5GSUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0IGZpbGVuYW1lIHdpdGggdHlwZSwgZm9ybWF0dGVkIGNvcnJlY3RseSBhcyBKU09OIG9yIFNWRycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9JTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdmVycmlkZXMgdGhlIGBpbmZpbGVgIHdpdGggSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgU1ZHIGlucHV0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgb3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWxpYXMgZm9yIHRoZSBgaW5zdHJgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHN2Zzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TVkcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1NWRyBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IHRvIHJlbmRlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJhdGNoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0JBVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0JhdGNoIGpvYiBzdHJpbmcgd2l0aCBpbnB1dC9vdXRwdXQgcGFpcnM6IFwiaW49b3V0O2luPW91dDsuLi5cIicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG91dGZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1VURklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdXRwdXQgZmlsZW5hbWUgd2l0aCB0eXBlLiBDYW4gYmUganBlZywgcG5nLCBwZGYsIG9yIHN2ZyBhbmQgaWdub3JlcyBgdHlwZWAgb3B0aW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdHlwZToge1xyXG4gICAgICB2YWx1ZTogJ3BuZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1RZUEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0ZpbGUgZXhwb3J0IGZvcm1hdC4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3NlbGVjdCcsXHJcbiAgICAgICAgaGludDogJ0RlZmF1bHQ6IHBuZycsXHJcbiAgICAgICAgY2hvaWNlczogWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvbnN0cjoge1xyXG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfQ09OU1RSJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NoYXJ0IGNvbnN0cnVjdG9yLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBjaGFydCcsXHJcbiAgICAgICAgY2hvaWNlczogWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYjY0OiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0I2NCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byB0aGUgY2hhcnQgc2hvdWxkIGJlIHJlY2VpdmVkIGluIEJhc2U2NCBmb3JtYXQgaW5zdGVhZCBvZiBiaW5hcnknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vRG93bmxvYWQ6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfTk9fRE9XTkxPQUQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gaW5jbHVkZSBvciBleGNsdWRlIGF0dGFjaG1lbnQgaGVhZGVycyBpbiB0aGUgcmVzcG9uc2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgd2lkdGg6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfV0lEVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1dpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGVzIGNoYXJ0IHNldHRpbmdzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzY2FsZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TQ0FMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdTY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncy4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdEhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX0hFSUdIVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRXaWR0aDoge1xyXG4gICAgICB2YWx1ZTogNjAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWZhdWx0IHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0U2NhbGU6IHtcclxuICAgICAgdmFsdWU6IDEsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGVmYXVsdCBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldC4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBtaW46IDAuMSxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGdsb2JhbE9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0dMT0JBTF9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCBnbG9iYWwgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdGhlbWVPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9USEVNRV9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCB0aGVtZSBvcHRpb25zIGZvciBIaWdoY2hhcnRzLnNldE9wdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXN0ZXJpemF0aW9uVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMTUwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaWxsaXNlY29uZHMgdG8gd2FpdCBmb3Igd2VicGFnZSByZW5kZXJpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgY3VzdG9tTG9naWM6IHtcclxuICAgIGFsbG93Q29kZUV4ZWN1dGlvbjoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbGxvd3Mgb3IgZGlzYWxsb3dzIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBkdXJpbmcgZXhwb3J0aW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBpbmplY3Rpb24gb2YgZmlsZXN5c3RlbSByZXNvdXJjZXMgKGRpc2FibGVkIGluIHNlcnZlciBtb2RlKScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tQ29kZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DVVNUT01fQ09ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDdXN0b20gY29kZSB0byBleGVjdXRlIGJlZm9yZSBjaGFydCBpbml0aWFsaXphdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24sIGNvZGUgd3JhcHBlZCBpbiBhIGZ1bmN0aW9uLCBvciBhIC5qcyBmaWxlbmFtZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhbGxiYWNrOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0NBTExCQUNLJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0phdmFTY3JpcHQgY29kZSB0byBydW4gZHVyaW5nIGNvbnN0cnVjdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24gb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX1JFU09VUkNFUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBZGRpdGlvbmFsIHJlc291cmNlcyBhcyBKU09OLCBzdHJpbmdpZmllZCBKU09OLCBvciBmaWxlbmFtZSwgY29udGFpbmluZyBmaWxlcywganMsIGFuZCBjc3Mgc2VjdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsb2FkQ29uZmlnOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHJyxcclxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIHdpdGggYSBwcmUtZGVmaW5lZCBjb25maWd1cmF0aW9uIHRvIHVzZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZUNvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1Byb21wdC1iYXNlZCBvcHRpb24gc2V0dGluZywgc2F2ZWQgdG8gYSBwcm92aWRlZCBjb25maWcgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU3RhcnRzIHRoZSBzZXJ2ZXIgd2hlbiB0cnVlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIHZhbHVlOiAnMC4wLjAuMCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hvc3RuYW1lIG9mIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA3ODAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG51bWJlciBmb3IgdGhlIHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdXBsb2FkTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDMsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX1VQTE9BRF9MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSByZXF1ZXN0IGJvZHkgc2l6ZSBpbiBNQicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGlzcGxheXMgb3Igbm90IGFjdGlvbiBkdXJhdGlvbnMgaW4gbWlsbGlzZWNvbmRzIGR1cmluZyBzZXJ2ZXIgcmVxdWVzdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHByb3h5OiB7XHJcbiAgICAgIGhvc3Q6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfSE9TVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5SG9zdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdIb3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcG9ydDoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgb2YgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRpbWVvdXQ6IHtcclxuICAgICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX1RJTUVPVVQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVRpbWVvdXQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJhdGVMaW1pdGluZzoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdlbmFibGVSYXRlTGltaXRpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzZXJ2ZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBtYXhSZXF1ZXN0czoge1xyXG4gICAgICAgIHZhbHVlOiAxMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAncmF0ZUxpbWl0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgcGVyIG1pbnV0ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIHZhbHVlOiAxLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1cnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGltZSB3aW5kb3cgaW4gbWludXRlcyBmb3IgcmF0ZSBsaW1pdGluZycsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGRlbGF5OiB7XHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdEZWxheSBkdXJhdGlvbiBiZXR3ZWVuIHN1Y2Nlc3NpdmUgcmVxdWVzdHMgYmVmb3JlIHJlYWNoaW5nIHRoZSBsaW1pdCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRydXN0UHJveHk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTZXQgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBza2lwS2V5OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnS2V5IHRvIGJ5cGFzcyB0aGUgcmF0ZSBsaW1pdGVyLCB1c2VkIHdpdGggYHNraXBUb2tlbmAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTicsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUb2tlbiB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwS2V5YCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgU1NMIHByb3RvY29sJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgZm9yY2U6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0ZPUkNFJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsRm9yY2UnLFxyXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdzc2xPbmx5JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0ZvcmNlcyB0aGUgc2VydmVyIHRvIHVzZSBIVFRQUyBvbmx5IHdoZW4gdHJ1ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogNDQzLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX1BPUlQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgZm9yIHRoZSBTU0wgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgY2VydFBhdGg6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0NFUlRfUEFUSCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbENlcnRQYXRoJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsUGF0aCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHRoZSBTU0wgY2VydGlmaWNhdGUva2V5IGZpbGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX01JTl9XT1JLRVJTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaW5pbXVtIGFuZCBpbml0aWFsIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG1heFdvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDgsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NQVhfV09SS0VSUycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICd3b3JrZXJzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdvcmtMaW1pdDoge1xyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9XT1JLX0xJTUlUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdOdW1iZXIgb2YgdGFza3MgYSB3b3JrZXIgY2FuIGhhbmRsZSBiZWZvcmUgcmVzdGFydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWNxdWlyZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBhY3F1aXJpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3JlYXRlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGlkbGVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiAzMDAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0lETEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGRlc3Ryb3lpbmcgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWw6IHtcclxuICAgICAgdmFsdWU6IDIwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYmVmb3JlIHJldHJ5aW5nIHJlc291cmNlIGNyZWF0aW9uIG9uIGZhaWx1cmUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0ludGVydmFsIGluIG1pbGxpc2Vjb25kcyB0byBjaGVjayBhbmQgZGVzdHJveSBpZGxlIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAncG9vbEJlbmNobWFya2luZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU2hvd3Mgc3RhdGlzdGljcyBmb3IgdGhlIHBvb2wgb2YgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfTEVWRUwnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0xvZ2dpbmcgdmVyYm9zaXR5IGxldmVsJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIHJvdW5kOiAwLFxyXG4gICAgICAgIG1pbjogMCxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgdmFsdWU6ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdMb2cgZmlsZSBuYW1lLiBSZXF1aXJlcyBgbG9nVG9GaWxlYCBhbmQgYGxvZ0Rlc3RgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Q6IHtcclxuICAgICAgdmFsdWU6ICdsb2cnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfREVTVCcsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgdG8gYmUgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9Db25zb2xlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0NPTlNPTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9Db25zb2xlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGNvbnNvbGUgbG9nZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9GaWxlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGxvZ2dpbmcgdG8gYSBmaWxlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcm91dGU6IHtcclxuICAgICAgdmFsdWU6ICcvJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9ST1VURScsXHJcbiAgICAgIGNsaU5hbWU6ICd1aVJvdXRlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgZW5kcG9pbnQgcm91dGUgZm9yIHRoZSBVSScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgb3RoZXI6IHtcclxuICAgIG5vZGVFbnY6IHtcclxuICAgICAgdmFsdWU6ICdwcm9kdWN0aW9uJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT0RFX0VOVicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIE5vZGUuanMgZW52aXJvbm1lbnQgdHlwZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvUHJvY2Vzc0V4aXRzOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gYXR0YWNoIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbm9Mb2dvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9fTE9HTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGlzcGxheSBvciBza2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIHN0YXJ0dXAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhhcmRSZXNldFBhZ2U6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9IQVJEX1JFU0VUX1BBR0UnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHJlc2V0IHRoZSBwYWdlIGNvbnRlbnQgZW50aXJlbHknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJyb3dzZXJTaGVsbE1vZGU6IHtcclxuICAgICAgdmFsdWU6IHRydWUsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX0JST1dTRVJfU0hFTExfTU9ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBzaGVsbCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGRlYnVnOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlRGVidWcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIHVuZGVybHlpbmcgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaGVhZGxlc3M6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19IRUFETEVTUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byBzZXQgdGhlIGJyb3dzZXIgdG8gcnVuIGluIGhlYWRsZXNzIG1vZGUgZHVyaW5nIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGV2dG9vbHM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19ERVZUT09MUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBEZXZUb29scyBpbiBoZWFkZnVsIG1vZGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0xJU1RFTl9UT19DT05TT0xFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VuYWJsZXMgb3IgZGlzYWJsZXMgbGlzdGVuaW5nIHRvIGNvbnNvbGUgbWVzc2FnZXMgZnJvbSB0aGUgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZHVtcGlvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRFVNUElPJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1JlZGlyZWN0cyBvciBub3QgYnJvd3NlciBzdGRvdXQgYW5kIHN0ZGVyciB0byBwcm9jZXNzLnN0ZG91dCBhbmQgcHJvY2Vzcy5zdGRlcnInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNsb3dNbzoge1xyXG4gICAgICB2YWx1ZTogMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19TTE9XX01PJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWxheXMgUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBtaWxsaXNlY29uZHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlYnVnZ2luZ1BvcnQ6IHtcclxuICAgICAgdmFsdWU6IDkyMjIsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVCVUdHSU5HX1BPUlQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgdXNlZCBmb3IgZGVidWdnaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vLyBQcm9wZXJ0aWVzIG5lc3RpbmcgbGV2ZWwgb2YgYWxsIG9wdGlvbnNcclxuZXhwb3J0IGNvbnN0IG5lc3RlZFByb3BzID0gX2NyZWF0ZU5lc3RlZFByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLy8gUHJvcGVydGllcyBuYW1lcyB0aGF0IHNob3VsZCBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkXHJcbmV4cG9ydCBjb25zdCBhYnNvbHV0ZVByb3BzID0gX2NyZWF0ZUFic29sdXRlUHJvcHMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgZ2VuZXJhdGVzIGEgbWFwcGluZyBvZiBuZXN0ZWQgYXJndW1lbnQgY2hhaW5zIGZyb20gYSBuZXN0ZWRcclxuICogY29uZmlnIG9iamVjdC4gVGhpcyBmdW5jdGlvbiB0cmF2ZXJzZXMgYSBuZXN0ZWQgb2JqZWN0IGFuZCBjcmVhdGVzIGEgbWFwcGluZ1xyXG4gKiB3aGVyZSBlYWNoIGtleSBpcyBhbiBhcmd1bWVudCBuYW1lIChlaXRoZXIgZnJvbSBgY2xpTmFtZWAsIGBsZWdhY3lOYW1lYCxcclxuICogb3IgdGhlIG9yaWdpbmFsIGtleSkgYW5kIGVhY2ggdmFsdWUgaXMgYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBjaGFpblxyXG4gKiBvZiBuZXN0ZWQgcHJvcGVydGllcyBsZWFkaW5nIHRvIHRoYXQgYXJndW1lbnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtuZXN0ZWRQcm9wcz17fV0gLSBUaGUgYWNjdW11bGF0b3Igb2JqZWN0IGZvciBzdG9yaW5nXHJcbiAqIHRoZSByZXN1bHRpbmcgYXJndW1lbnRzIGNoYWlucy4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtwcm9wQ2hhaW49JyddIC0gVGhlIGN1cnJlbnQgY2hhaW4gb2YgbmVzdGVkIHByb3BlcnRpZXMsXHJcbiAqIHVzZWQgaW50ZXJuYWxseSBkdXJpbmcgcmVjdXJzaW9uLiAgVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgc3RyaW5nLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgbWFwcGluZyBhcmd1bWVudCBuYW1lcyB0byB0aGVpciBjb3JyZXNwb25kaW5nXHJcbiAqIG5lc3RlZCBwcm9wZXJ0eSBjaGFpbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHMoY29uZmlnLCBuZXN0ZWRQcm9wcyA9IHt9LCBwcm9wQ2hhaW4gPSAnJykge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHMgb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBfY3JlYXRlTmVzdGVkUHJvcHMoZW50cnksIG5lc3RlZFByb3BzLCBgJHtwcm9wQ2hhaW59LiR7a2V5fWApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIHRoZSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzXHJcbiAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmNsaU5hbWUgfHwga2V5XSA9IGAke3Byb3BDaGFpbn0uJHtrZXl9YC5zdWJzdHJpbmcoMSk7XHJcblxyXG4gICAgICAvLyBTdXBwb3J0IGZvciB0aGUgbGVnYWN5LCBQaGFudG9tSlMgcHJvcGVydGllcyBuYW1lc1xyXG4gICAgICBpZiAoZW50cnkubGVnYWN5TmFtZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgbmVzdGVkUHJvcHNbZW50cnkubGVnYWN5TmFtZV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb2JqZWN0IHdpdGggbmVzdGVkIGFyZ3VtZW50IGNoYWluc1xyXG4gIHJldHVybiBuZXN0ZWRQcm9wcztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdhdGhlcnMgdGhlIG5hbWVzIG9mIHByb3BlcnRpZXMgZnJvbSBhIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRoYXRcclxuICogY2FuIGJlIHRyZWF0ZWQgYXMgYWJzb2x1dGUgcHJvcGVydGllcy4gVGhlc2UgcHJvcGVydGllcyBoYXZlIHZhbHVlcyB0aGF0XHJcbiAqIGFyZSBvYmplY3RzIGFuZCBkbyBub3QgY29udGFpbiBmdXJ0aGVyIG5lc3RlZCBkZXB0aCB3aGVuIG1lcmdpbmcgYW4gb2JqZWN0XHJcbiAqIGNvbnRhaW5pbmcgdGhlc2Ugb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVBYnNvbHV0ZVByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IFthYnNvbHV0ZVByb3BzPVtdXSAtIEFuIGFycmF5IHRvIGNvbGxlY3QgdGhlIG5hbWVzXHJcbiAqIG9mIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPHN0cmluZz59IEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG5hbWVzIG9mIGFic29sdXRlXHJcbiAqIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wcyhjb25maWcsIGFic29sdXRlUHJvcHMgPSBbXSkge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS50eXBlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHNcclxuICAgICAgX2NyZWF0ZUFic29sdXRlUHJvcHMoZW50cnksIGFic29sdXRlUHJvcHMpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSWYgdGhlIG9wdGlvbiBjYW4gYmUgYW4gb2JqZWN0LCBzYXZlIGl0cyB0eXBlIGluIHRoZSBhcnJheVxyXG4gICAgICBpZiAoZW50cnkudHlwZXMuaW5jbHVkZXMoJ09iamVjdCcpKSB7XHJcbiAgICAgICAgYWJzb2x1dGVQcm9wcy5wdXNoKGtleSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBhcnJheSB3aXRoIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzXHJcbiAgcmV0dXJuIGFic29sdXRlUHJvcHM7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBkZWZhdWx0Q29uZmlnLFxyXG4gIG5lc3RlZFByb3BzLFxyXG4gIGFic29sdXRlUHJvcHNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgZmlsZSBpcyByZXNwb25zaWJsZSBmb3IgcGFyc2luZyB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIHdpdGggdGhlICd6b2QnIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkXHJcbiAqIHRvIGJlIHVzZWQgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIGBlbnZzYC4gV2Ugc2hvdWxkIG5vdCB1c2UgdGhlIGBwcm9jZXNzLmVudmBcclxuICogZGlyZWN0bHkgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuXHJcbiAqIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lclxyXG4gKiBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICovXHJcblxyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5cclxuaW1wb3J0IHsgZGVmYXVsdENvbmZpZyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuLy8gTG9hZCAuZW52IGludG8gZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbi8vIE9iamVjdCB3aXRoIGN1c3RvbSB2YWxpZGF0b3JzIGFuZCB0cmFuc2Zvcm1lcnMsIHRvIGF2b2lkIHJlcGV0aXRpb25cclxuLy8gaW4gdGhlIENvbmZpZyBvYmplY3RcclxuY29uc3QgdiA9IHtcclxuICAvLyBTcGxpdHMgc3RyaW5nIHZhbHVlIGludG8gZWxlbWVudHMgaW4gYW4gYXJyYXksIHRyaW1zIGV2ZXJ5IGVsZW1lbnQsIGNoZWNrc1xyXG4gIC8vIGlmIGFuIGFycmF5IGlzIGNvcnJlY3QsIGlmIGl0IGlzIGVtcHR5LCBhbmQgaWYgaXQgaXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgYXJyYXk6IChmaWx0ZXJBcnJheSkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlXHJcbiAgICAgICAgICAuc3BsaXQoJywnKVxyXG4gICAgICAgICAgLm1hcCgodmFsdWUpID0+IHZhbHVlLnRyaW0oKSlcclxuICAgICAgICAgIC5maWx0ZXIoKHZhbHVlKSA9PiBmaWx0ZXJBcnJheS5pbmNsdWRlcyh2YWx1ZSkpXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZS5sZW5ndGggPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3Mgb25seSB0cnVlLCBmYWxzZSBhbmQgY29ycmVjdGx5IHBhcnNlIHRoZSB2YWx1ZSB0byBib29sZWFuXHJcbiAgLy8gb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSB1bmRlZmluZWRcclxuICBib29sZWFuOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbJ3RydWUnLCAnZmFsc2UnLCAnJ10pXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgPT09ICd0cnVlJyA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3MgcGFzc2VkIHZhbHVlcyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsXHJcbiAgLy8gYmUgdW5kZWZpbmVkXHJcbiAgZW51bTogKHZhbHVlcykgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWy4uLnZhbHVlcywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIFRyaW1zIHRoZSBzdHJpbmcgdmFsdWUgYW5kIGNoZWNrcyBpZiBpdCBpcyBlbXB0eSBvciBjb250YWlucyBzdHJpbmdpZmllZFxyXG4gIC8vIHZhbHVlcyBzdWNoIGFzIGZhbHNlLCB1bmRlZmluZWQsIG51bGwsIE5hTiwgaWYgaXQgZG9lcywgcmV0dXJucyB1bmRlZmluZWRcclxuICBzdHJpbmc6ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgIVsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJ10uaW5jbHVkZXModmFsdWUpIHx8XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSBzdHJpbmcgY29udGFpbnMgZm9yYmlkZGVuIHZhbHVlcywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBvc2l0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIHBvc2l0aXZlTnVtOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBwb3NpdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG5vbi1uZWdhdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlXHJcbiAgLy8gd2lsbCBiZSB1bmRlZmluZWRcclxuICBub25OZWdhdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCksXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSB2YWx1ZSBtdXN0IGJlIG51bWVyaWMgYW5kIG5vbi1uZWdhdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKVxyXG59O1xyXG5cclxuZXhwb3J0IGNvbnN0IENvbmZpZyA9IHoub2JqZWN0KHtcclxuICAvLyBwdXBwZXRlZXJcclxuICBQVVBQRVRFRVJfQVJHUzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEhJR0hDSEFSVFNfVkVSU0lPTiBtdXN0IGJlICdsYXRlc3QnLCBhIG1ham9yIHZlcnNpb24sIG9yIGluIHRoZSBmb3JtIFhYLllZLlpaLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfQ0ROX1VSTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHA6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcbiAgSElHSENIQVJUU19GT1JDRV9GRVRDSDogdi5ib29sZWFuKCksXHJcbiAgSElHSENIQVJUU19DQUNIRV9QQVRIOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQURNSU5fVE9LRU46IHYuc3RyaW5nKCksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNvcmVTY3JpcHRzLnZhbHVlKSxcclxuICBISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLm1vZHVsZVNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuaW5kaWNhdG9yU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcbiAgSElHSENIQVJUU19DVVNUT01fU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jdXN0b21TY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuXHJcbiAgLy8gZXhwb3J0XHJcbiAgRVhQT1JUX0lORklMRTogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfSU5TVFI6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1NWRzogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfQkFUQ0g6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09VVEZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RZUEU6IHYuZW51bShbJ2pwZWcnLCAncG5nJywgJ3BkZicsICdzdmcnXSksXHJcbiAgRVhQT1JUX0NPTlNUUjogdi5lbnVtKFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10pLFxyXG4gIEVYUE9SVF9CNjQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9OT19ET1dOTE9BRDogdi5ib29sZWFuKCksXHJcbiAgRVhQT1JUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1dJRFRIOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfR0xPQkFMX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RIRU1FX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG5cclxuICAvLyBjdXN0b21cclxuICBDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT046IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUzogdi5ib29sZWFuKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NVU1RPTV9DT0RFOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19DQUxMQkFDSzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfUkVTT1VSQ0VTOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gc2VydmVyXHJcbiAgU0VSVkVSX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX0hPU1Q6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfVVBMT0FEX0xJTUlUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX0JFTkNITUFSS0lORzogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIHNlcnZlciBwcm94eVxyXG4gIFNFUlZFUl9QUk9YWV9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QUk9YWV9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1BST1hZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gc2VydmVyIHJhdGUgbGltaXRpbmdcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFk6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU46IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlciBzc2xcclxuICBTRVJWRVJfU1NMX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9GT1JDRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1NTTF9DRVJUX1BBVEg6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHBvb2xcclxuICBQT09MX01JTl9XT1JLRVJTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9NQVhfV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfV09SS19MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfREVTVFJPWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfUkVBUEVSX0lOVEVSVkFMOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBsb2dnZXJcclxuICBMT0dHSU5HX0xFVkVMOiB6XHJcbiAgICAuc3RyaW5nKClcclxuICAgIC50cmltKClcclxuICAgIC5yZWZpbmUoXHJcbiAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZSA9PT0gJycgfHxcclxuICAgICAgICAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPD0gNSksXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgTE9HR0lOR19MRVZFTC4gV2Ugb25seSBhY2NlcHQgdmFsdWVzIGZyb20gMCB0byA1IGFzIGxvZ2dpbmcgbGV2ZWxzLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpLFxyXG4gIExPR0dJTkdfRklMRTogdi5zdHJpbmcoKSxcclxuICBMT0dHSU5HX0RFU1Q6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19UT19DT05TT0xFOiB2LmJvb2xlYW4oKSxcclxuICBMT0dHSU5HX1RPX0ZJTEU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyB1aVxyXG4gIFVJX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgVUlfUk9VVEU6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIG90aGVyXHJcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcclxuICBPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfTk9fTE9HTzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcclxuICBPVEhFUl9CUk9XU0VSX1NIRUxMX01PREU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBkZWJ1Z2dlclxyXG4gIERFQlVHX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfSEVBRExFU1M6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RFVlRPT0xTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19MSVNURU5fVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfRFVNUElPOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19TTE9XX01POiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgREVCVUdfREVCVUdHSU5HX1BPUlQ6IHYucG9zaXRpdmVOdW0oKVxyXG59KTtcclxuXHJcbmV4cG9ydCBjb25zdCBlbnZzID0gQ29uZmlnLnBhcnRpYWwoKS5wYXJzZShwcm9jZXNzLmVudik7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBNYW5hZ2VzIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgYnkgbG9hZGluZ1xyXG4gKiBhbmQgbWVyZ2luZyBvcHRpb25zIGZyb20gbXVsdGlwbGUgc291cmNlcywgc3VjaCBhcyBkZWZhdWx0IHNldHRpbmdzLFxyXG4gKiBlbnZpcm9ubWVudCB2YXJpYWJsZXMsIHVzZXItcHJvdmlkZWQgb3B0aW9ucywgYW5kIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqIEVuc3VyZXMgdGhlIGdsb2JhbCBvcHRpb25zIGFyZSB1cC10by1kYXRlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHkgdmFsdWVzLlxyXG4gKiBQcm92aWRlcyBmdW5jdGlvbnMgZm9yIGFjY2Vzc2luZyBhbmQgdXBkYXRpbmcgY29uZmlndXJhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBkZWVwQ29weSwgZ2V0QWJzb2x1dGVQYXRoLCBpc09iamVjdCB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IHsgYWJzb2x1dGVQcm9wcywgbmVzdGVkUHJvcHMsIGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0T3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PXRydWVdIC0gU3BlY2lmaWVzIHdoZXRoZXIgdG8gcmV0dXJuIGEgY29waWVkXHJcbiAqIG9iamVjdCBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgKGB0cnVlYCkgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCBvciBhIHJlZmVyZW5jZVxyXG4gKiB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoZ2V0Q29weSA9IHRydWUpIHtcclxuICByZXR1cm4gZ2V0Q29weSA/IGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpIDogZ2xvYmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXBkYXRlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBuZXcgb3B0aW9ucyB0byBiZVxyXG4gKiBtZXJnZWQgaW50byB0aGUgZ2xvYmFsIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldENvcHk9ZmFsc2VdIC0gRGV0ZXJtaW5lcyB3aGV0aGVyIHRvIG1lcmdlIHRoZSBuZXdcclxuICogb3B0aW9ucyBpbnRvIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IChgdHJ1ZWApIG9yIGRpcmVjdGx5IGludG9cclxuICogdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0LCBlaXRoZXIgdGhlIG1vZGlmaWVkIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9yIGEgbW9kaWZpZWQgY29weSwgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGBnZXRDb3B5YC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVPcHRpb25zKG5ld09wdGlvbnMsIGdldENvcHkgPSBmYWxzZSkge1xyXG4gIC8vIE1lcmdlIG5ldyBvcHRpb25zIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyBvciBpdHMgY29weSBhbmQgcmV0dXJuIHRoZSByZXN1bHRcclxuICByZXR1cm4gX21lcmdlT3B0aW9ucyhnZXRPcHRpb25zKGdldENvcHkpLCBuZXdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggdmFsdWVzIHByb3ZpZGVkIHRocm91Z2ggdGhlIENMSSwga2VlcGluZ1xyXG4gKiB0aGUgcHJpbmNpcGxlIG9mIG9wdGlvbnMgbG9hZCBwcmlvcml0eS4gVGhpcyBmdW5jdGlvbiBhY2NlcHRzIGEgYGNsaUFyZ3NgXHJcbiAqIGFycmF5IGNvbnRhaW5pbmcgYXJndW1lbnRzIGZyb20gdGhlIENMSSwgd2hpY2ggd2lsbCBiZSB2YWxpZGF0ZWQgYW5kIGFwcGxpZWRcclxuICogaWYgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIFRoZSBwcmlvcml0eSBvcmRlciBmb3Igc2V0dGluZyB2YWx1ZXMgaXM6XHJcbiAqXHJcbiAqIDEuIFZhbHVlcyBmcm9tIGEgY3VzdG9tIEpTT04gZmlsZSAobG9hZGVkIGJ5IHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24pLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSB0aGUgY29tbWFuZCBsaW5lIGludGVyZmFjZSAoQ0xJKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldENsaU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQgbGluZSBhcmd1bWVudHMgdXNlZFxyXG4gKiBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHJlZmxlY3RpbmcgdGhlIG1lcmdlZFxyXG4gKiBjb25maWd1cmF0aW9uIGZyb20gc291cmNlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEkuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0Q2xpT3B0aW9ucyhjbGlBcmdzKSB7XHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzICYmIEFycmF5LmlzQXJyYXkoY2xpQXJncykgJiYgY2xpQXJncy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGxvYWRlZCB2aWEgdGhlIGAtLWxvYWRDb25maWdgXHJcbiAgICBjb25zdCBjb25maWdPcHRpb25zID0gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNvbmZpZ09wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNvbmZpZ09wdGlvbnMpO1xyXG5cclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIENMSVxyXG4gICAgY29uc3QgY2xpT3B0aW9ucyA9IF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncyk7XHJcblxyXG4gICAgLy8gVXBkYXRlIGdsb2JhbCBvcHRpb25zIHdpdGggdGhlIHZhbHVlcyBmcm9tIHRoZSBgY2xpT3B0aW9uc2BcclxuICAgIHVwZGF0ZU9wdGlvbnMoY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnZXRPcHRpb25zKGZhbHNlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG9sZE9wdGlvbnMpKSB7XHJcbiAgICAvLyBJdGVyYXRlIG92ZXIgZWFjaCBrZXktdmFsdWUgcGFpciBpbiB0aGUgb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgdGhlcmUgaXMgYSBuZXN0ZWQgbWFwcGluZywgc3BsaXQgaXQgaW50byBhIHByb3BlcnRpZXMgY2hhaW5cclxuICAgICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNba2V5XVxyXG4gICAgICAgID8gbmVzdGVkUHJvcHNba2V5XS5zcGxpdCgnLicpXHJcbiAgICAgICAgOiBbXTtcclxuXHJcbiAgICAgIC8vIElmIGl0IGlzIHRoZSBsYXN0IHByb3BlcnR5IGluIHRoZSBjaGFpbiwgYXNzaWduIHRoZSB2YWx1ZSwgb3RoZXJ3aXNlLFxyXG4gICAgICAvLyBjcmVhdGUgb3IgcmV1c2UgdGhlIG5lc3RlZCBvYmplY3RcclxuICAgICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cclxuICAgICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgICAgbmV3T3B0aW9uc1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgICdbY29uZmlnXSBObyBjb3JyZWN0IG9iamVjdCB3aXRoIG9wdGlvbnMgd2FzIHByb3ZpZGVkLiBSZXR1cm5pbmcgYW4gZW1wdHkgYXJyYXkuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgbmV3LCBzdHJ1Y3R1cmVkIG9wdGlvbnMgb2JqZWN0XHJcbiAgcmV0dXJuIG5ld09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBWYWxpZGF0ZXMsIHBhcnNlcywgYW5kIGNoZWNrcyBpZiB0aGUgcHJvdmlkZWQgY29uZmlnIGlzIGFsbG93ZWQgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0FsbG93ZWRDb25maWdcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBjb25maWcgLSBUaGUgY29uZmlnIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkIGFzIGEgc2V0XHJcbiAqIG9mIG9wdGlvbnMuIE11c3QgYmUgZWl0aGVyIGFuIG9iamVjdCBvciBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBbdG9TdHJpbmc9ZmFsc2VdIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCB2ZXJzaW9uXHJcbiAqIG9mIHRoZSBwYXJzZWQgY29uZmlnLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFthbGxvd0Z1bmN0aW9ucz1mYWxzZV0gLSBXaGV0aGVyIHRvIGFsbG93IGZ1bmN0aW9uc1xyXG4gKiBpbiB0aGUgcGFyc2VkIGNvbmZpZy4gSWYgdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWQuIE90aGVyd2lzZSwgd2hlblxyXG4gKiBhIGZ1bmN0aW9uIGlzIGZvdW5kLCBudWxsIGlzIHJldHVybmVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFZlcnNpb24obm9Mb2dvID0gZmFsc2UpIHtcclxuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGAuZW52YCBvciBmcm9tIGBwYWNrYWdlLmpzb25gXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuICApLnZlcnNpb247XHJcblxyXG4gIC8vIFByaW50IHRleHQgb25seVxyXG4gIGlmIChub0xvZ28pIHtcclxuICAgIGNvbnNvbGUubG9nKGBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259YCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFByaW50IHRoZSBsb2dvXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAnbXNnJywgJ3N0YXJ0dXAubXNnJyksICd1dGY4JykudG9TdHJpbmcoKVxyXG4gICAgICAgIC5ib2xkLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IGJhc2VkIG9uIHRoZSBwcm92aWRlZFxyXG4gKiBjb25maWd1cmF0aW9uLCBzZXR0aW5nIHZhbHVlcyBmcm9tIG5lc3RlZCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5LlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIChkZWZhdWx0cykuXHJcbiAqIDIuIFZhbHVlcyBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcyAoc3BlY2lmaWVkIGluIHRoZSBgLmVudmAgZmlsZSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaW5pdE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogdGhlIGdsb2JhbCBvcHRpb25zLiBJdCBzaG91bGQgaW5jbHVkZSBuZXN0ZWQgcHJvcGVydGllcyB3aXRoIGEgYHZhbHVlYFxyXG4gKiBhbmQgYW4gYGVudkxpbmtgIGZvciBsaW5raW5nIHRvIGVudmlyb25tZW50IHZhcmlhYmxlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGluaXRpYWxpemVkIGdsb2JhbCBvcHRpb25zIG9iamVjdCwgcG9wdWxhdGVkIHdpdGhcclxuICogdmFsdWVzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHlcclxuICogb3JkZXIuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaW5pdE9wdGlvbnMoY29uZmlnKSB7XHJcbiAgLy8gSW5pdCB0aGUgb2JqZWN0IGZvciBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBTdGFydCBpbml0aWFsaXppbmcgdGhlIGBvcHRpb25zYCBvYmplY3QgcmVjdXJzaXZlbHlcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBpdGVtXSBvZiBPYmplY3QuZW50cmllcyhjb25maWcpKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY29ycmVjdCB2YWx1ZSBiYXNlZCBvbiB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHkgb3JkZXJcclxuICAgICAgaWYgKGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gdW5kZWZpbmVkICYmIGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gbnVsbCkge1xyXG4gICAgICAgIC8vIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdmFsdWVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gZW52c1tpdGVtLmVudkxpbmtdO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRoZSB2YWx1ZSBmcm9tIHRoZSBjb25maWcgZmlsZVxyXG4gICAgICAgIG9wdGlvbnNbbmFtZV0gPSBpdGVtLnZhbHVlO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDcmVhdGUgYSBzZWN0aW9uIGluIHRoZSBvcHRpb25zXHJcbiAgICAgIG9wdGlvbnNbbmFtZV0gPSBfaW5pdE9wdGlvbnMoaXRlbSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNyZWF0ZWQgYG9wdGlvbnNgIG9iamVjdFxyXG4gIHJldHVybiBvcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogTWVyZ2VzIHR3byBzZXRzIG9mIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgY29uc2lkZXJpbmcgYWJzb2x1dGUgcHJvcGVydGllcy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9tZXJnZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9yaWdpbmFsT3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTWVyZ2VkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9ucywgbmV3T3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgb3JpZ2luYWxPcHRpb25zYCBhbmQgYG5ld09wdGlvbnNgIGFyZSBjb3JyZWN0IG9iamVjdHNcclxuICBpZiAoaXNPYmplY3Qob3JpZ2luYWxPcHRpb25zKSAmJiBpc09iamVjdChuZXdPcHRpb25zKSkge1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobmV3T3B0aW9ucykpIHtcclxuICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gPVxyXG4gICAgICAgIGlzT2JqZWN0KHZhbHVlKSAmJlxyXG4gICAgICAgICFhYnNvbHV0ZVByb3BzLmluY2x1ZGVzKGtleSkgJiZcclxuICAgICAgICBvcmlnaW5hbE9wdGlvbnNba2V5XSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICA/IF9tZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zW2tleV0sIHZhbHVlKVxyXG4gICAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgICAgOiBvcmlnaW5hbE9wdGlvbnNba2V5XSB8fCBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBvcmlnaW5hbCAobW9kaWZpZWQgb3Igbm90KSBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIHRoZSBwcm92aWRlZCBvcHRpb25zIG9iamVjdCB0byBhIEpTT04tZm9ybWF0dGVkIHN0cmluZ1xyXG4gKiB3aXRoIHRoZSBvcHRpb24gdG8gcHJlc2VydmUgZnVuY3Rpb25zLiBJbiBvcmRlciBmb3IgYSBmdW5jdGlvblxyXG4gKiB0byBiZSBwcmVzZXJ2ZWQsIGl0IG5lZWRzIHRvIGZvbGxvdyB0aGUgZm9ybWF0IGBmdW5jdGlvbiAoLi4uKSB7Li4ufWAuXHJcbiAqIFN1Y2ggYSBmdW5jdGlvbiBjYW4gYWxzbyBiZSBzdHJpbmdpZmllZC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9vcHRpb25zU3RyaW5naWZ5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IHRvIGJlIGNvbnZlcnRlZCB0byBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0Z1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHByZXNlcnZlZFxyXG4gKiBpbiB0aGUgb3V0cHV0LiBPdGhlcndpc2UgYW4gZXJyb3IgaXMgdGhyb3duLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHN0cmluZ2lmeUZ1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHNhdmVkXHJcbiAqIGFzIHN0cmluZ3MuIFRoZSBgYWxsb3dGdW5jdGlvbnNgIG11c3QgYmUgc2V0IHRvIHRydWUgYXMgd2VsbCBmb3IgdGhpcyB0byB0YWtlXHJcbiAqIGFuIGVmZmVjdC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCB3aGVuIGZ1bmN0aW9ucyBhcmUgbm90IGFsbG93ZWQgYnV0IGFyZVxyXG4gKiBmb3VuZCBpbiBwcm92aWRlZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeShvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucywgc3RyaW5naWZ5RnVuY3Rpb25zKSB7XHJcbiAgY29uc3QgcmVwbGFjZXJDYWxsYmFjayA9IChfLCB2YWx1ZSkgPT4ge1xyXG4gICAgLy8gVHJpbSBzdHJpbmcgdmFsdWVzXHJcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xyXG4gICAgICB2YWx1ZSA9IHZhbHVlLnRyaW0oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBJZiB2YWx1ZSBpcyBhIGZ1bmN0aW9uIG9yIHN0cmluZ2lmaWVkIGZ1bmN0aW9uXHJcbiAgICBpZiAoXHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJyB8fFxyXG4gICAgICAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJykgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIElmIGFsbG93RnVuY3Rpb25zIGlzIHNldCB0byB0cnVlLCBwcmVzZXJ2ZSBmdW5jdGlvbnNcclxuICAgICAgaWYgKGFsbG93RnVuY3Rpb25zKSB7XHJcbiAgICAgICAgLy8gQmFzZWQgb24gdGhlIGBzdHJpbmdpZnlGdW5jdGlvbnNgIG9wdGlvbnMsIHNldCBmdW5jdGlvbiB2YWx1ZXNcclxuICAgICAgICByZXR1cm4gc3RyaW5naWZ5RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IC8vIEFzIHN0cmluZ2lmaWVkIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgXCJFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5cImBcclxuICAgICAgICAgIDogLy8gQXMgZnVuY3Rpb25zXHJcbiAgICAgICAgICAgIGBFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5gO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRocm93IGFuIGVycm9yIG90aGVyd2lzZVxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcigpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSW4gYWxsIG90aGVyIGNhc2VzLCBzaW1wbHkgcmV0dXJuIHRoZSB2YWx1ZVxyXG4gICAgcmV0dXJuIHZhbHVlO1xyXG4gIH07XHJcblxyXG4gIC8vIFN0cmluZ2lmeSBvcHRpb25zIGFuZCBpZiByZXF1aXJlZCwgcmVwbGFjZSBzcGVjaWFsIGZ1bmN0aW9ucyBtYXJrc1xyXG4gIHJldHVybiBKU09OLnN0cmluZ2lmeShvcHRpb25zLCByZXBsYWNlckNhbGxiYWNrKS5yZXBsYWNlQWxsKFxyXG4gICAgc3RyaW5naWZ5RnVuY3Rpb25zID8gL1xcXFxcIkVYUF9GVU58RVhQX0ZVTlxcXFxcIi9nIDogL1wiRVhQX0ZVTnxFWFBfRlVOXCIvZyxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgcHJvdmlkZWQgdmlhXHJcbiAqIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gaW4gdGhlIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgdG8gc2VhcmNoXHJcbiAqIGZvciB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncykge1xyXG4gIC8vIEdldCB0aGUgYWxsb3cgZmxhZ3MgZm9yIHRoZSBjdXN0b20gbG9naWMgY2hlY2tcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiwgYWxsb3dGaWxlUmVzb3VyY2VzIH0gPSBnZXRPcHRpb25zKCkuY3VzdG9tTG9naWM7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiB2YWx1ZVxyXG4gIGNvbnN0IGNvbmZpZ0ZpbGVOYW1lID0gY29uZmlnSW5kZXggPiAtMSAmJiBjbGlBcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lICYmIGFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICByZXR1cm4gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7Y29uZmlnRmlsZU5hbWV9IGZpbGUuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gYWRkaXRpb25hbCBvcHRpb25zIHRvIHJldHVyblxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhcnNlcyBjb21tYW5kLWxpbmUgYXJndW1lbnRzIGFuZCBwYWlycyBlYWNoIGFyZ3VtZW50IHdpdGggaXRzIGNvcnJlc3BvbmRpbmdcclxuICogb3B0aW9uIGluIHRoZSBjb25maWd1cmF0aW9uLiBUaGUgdmFsdWVzIGFyZSBzdHJ1Y3R1cmVkIGludG8gYSBuZXN0ZWQgb3B0aW9uc1xyXG4gKiBvYmplY3QsIGJhc2VkIG9uIHByZWRlZmluZWQgbWFwcGluZ3MuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gbmVzdGVkUHJvcHMgLSBBbiBhcnJheSBvZiBuZXN0aW5nIGxldmVsIGZvciBhbGxcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQtbGluZSBhcmd1bWVudHNcclxuICogY29udGFpbmluZyBvcHRpb25zIGFuZCB0aGVpciBhc3NvY2lhdGVkIHZhbHVlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gdXBkYXRlZCBvcHRpb25zIG9iamVjdCB3aGVyZSBlYWNoIG9wdGlvbiBmcm9tXHJcbiAqIHRoZSBjb21tYW5kLWxpbmUgaXMgcGFpcmVkIHdpdGggaXRzIHZhbHVlLCBzdHJ1Y3R1cmVkIGludG8gbmVzdGVkIG9iamVjdHNcclxuICogYXMgZGVmaW5lZC5cclxuICovXHJcbmZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncykge1xyXG4gIC8vIEFuIGVtcHR5IG9iamVjdCB0byBjb2xsZWN0IGFuZCBzdHJ1Y3R1cml6ZSBkYXRhIGZyb20gdGhlIGFyZ3NcclxuICBjb25zdCBjbGlPcHRpb25zID0ge307XHJcblxyXG4gIC8vIEN5Y2xlIHRocm91Z2ggYWxsIENMSSBhcmdzIGFuZCBmaWx0ZXIgdGhlbVxyXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2xpQXJncy5sZW5ndGg7IGkrKykge1xyXG4gICAgY29uc3Qgb3B0aW9uID0gY2xpQXJnc1tpXS5yZXBsYWNlKC8tL2csICcnKTtcclxuXHJcbiAgICAvLyBGaW5kIHRoZSByaWdodCBwbGFjZSBmb3IgcHJvcGVydHkncyB2YWx1ZVxyXG4gICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZFByb3BzW29wdGlvbl0uc3BsaXQoJy4nKVxyXG4gICAgICA6IFtdO1xyXG5cclxuICAgIC8vIENyZWF0ZSBvcHRpb25zIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIENMSSBmb3IgbGF0ZXIgcGFyc2luZyBhbmQgbWVyZ2luZ1xyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZSgob2JqLCBwcm9wLCBpbmRleCkgPT4ge1xyXG4gICAgICBpZiAocHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4KSB7XHJcbiAgICAgICAgY29uc3QgdmFsdWUgPSBjbGlBcmdzWysraV07XHJcbiAgICAgICAgaWYgKCF2YWx1ZSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NvbmZpZ10gTWlzc2luZyB2YWx1ZSBmb3IgdGhlIENMSSAnLS0ke29wdGlvbn0nIGFyZ3VtZW50LiBVc2luZyB0aGUgZGVmYXVsdCB2YWx1ZS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgICBvYmpbcHJvcF0gPSB2YWx1ZSB8fCBudWxsO1xyXG4gICAgICB9IGVsc2UgaWYgKG9ialtwcm9wXSA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgb2JqW3Byb3BdID0ge307XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcclxuICAgIH0sIGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHBhcnNlZCBDTEkgb3B0aW9uc1xyXG4gIHJldHVybiBjbGlPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgdHJhdmVyc2VzIHRoZSBvcHRpb25zIG9iamVjdCB0byBwcmludCB0aGUgdXNhZ2UgaW5mb3JtYXRpb25cclxuICogZm9yIGVhY2ggb3B0aW9uIGNhdGVnb3J5IGFuZCBpbmRpdmlkdWFsIG9wdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBDTEkgb3B0aW9ucy4gSXQgbWF5XHJcbiAqIGluY2x1ZGUgbmVzdGVkIGNhdGVnb3JpZXMgYW5kIGluZGl2aWR1YWwgb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9ucykge1xyXG4gIGZvciAoY29uc3QgW25hbWUsIG9wdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcclxuICAgIC8vIElmIHRoZSBjdXJyZW50IGVudHJ5IGlzIGEgY2F0ZWdvcnkgYW5kIG5vdCBhIGxlYWYgb3B0aW9uLCByZWN1cnNlIGludG8gaXRcclxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9wdGlvbiwgJ3ZhbHVlJykpIHtcclxuICAgICAgX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb24pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gUHJlcGFyZSBkZXNjcmlwdGlvblxyXG4gICAgICBjb25zdCBkZXNjTmFtZSA9IGAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9YDtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdmFsdWVcclxuICAgICAgbGV0IG9wdGlvblZhbHVlID0gb3B0aW9uLnZhbHVlO1xyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGFycmF5IG9mIHN0cmluZ3NcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nW10nKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID1cclxuICAgICAgICAgICdbJyArIG9wdGlvblZhbHVlLm1hcCgoaXRlbSkgPT4gYCcke2l0ZW19J2ApLmpvaW4oJywgJykgKyAnXSc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhIHN0cmluZ1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmcnKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID0gYCcke29wdGlvblZhbHVlfSdgO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgIGRlc2NOYW1lLmdyZWVuLFxyXG4gICAgICAgIGAkeygnPCcgKyBvcHRpb24udHlwZXMuam9pbignfCcpICsgJz4nKS55ZWxsb3d9YCxcclxuICAgICAgICBgJHtTdHJpbmcob3B0aW9uVmFsdWUpLmJvbGR9YC5ibHVlLFxyXG4gICAgICAgIGAtICR7b3B0aW9uLmRlc2NyaXB0aW9ufS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIHNldENsaU9wdGlvbnMsXHJcbiAgbWFwVG9OZXdPcHRpb25zLFxyXG4gIGlzQWxsb3dlZENvbmZpZyxcclxuICBwcmludExpY2Vuc2UsXHJcbiAgcHJpbnRVc2FnZSxcclxuICBwcmludFZlcnNpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhUVFAgdXRpbGl0eSBtb2R1bGUgZm9yIGZldGNoaW5nIGFuZCBwb3N0aW5nIGRhdGEuIFN1cHBvcnRzIGJvdGhcclxuICogSFRUUCBhbmQgSFRUUFMgcHJvdG9jb2xzLCBwcm92aWRpbmcgbWV0aG9kcyB0byBtYWtlIEdFVCBhbmQgUE9TVCByZXF1ZXN0c1xyXG4gKiB3aXRoIGN1c3RvbWl6YWJsZSBvcHRpb25zLiBJbmNsdWRlcyBwcm90b2NvbCBkZXRlcm1pbmF0aW9uIGJhc2VkIG9uIFVSTFxyXG4gKiBhbmQgYXVnbWVudHMgcmVzcG9uc2Ugb2JqZWN0cyB3aXRoIGEgJ3RleHQnIHByb3BlcnR5IGZvciBlYXNpZXIgZGF0YSBhY2Nlc3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBkYXRhIGZyb20gdGhlIHNwZWNpZmllZCBVUkwgdXNpbmcgZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZmV0Y2hcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZmV0Y2ggZGF0YSBmcm9tLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZldGNoKHVybCwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAuZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIGlmICghcmVzcG9uc2VEYXRhKSB7XHJcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgUE9TVCByZXF1ZXN0IHRvIHRoZSBzcGVjaWZpZWQgVVJMIHdpdGggdGhlIHByb3ZpZGVkIEpTT04gYm9keSB1c2luZ1xyXG4gKiBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIHNlbmQgdGhlIFBPU1QgcmVxdWVzdCB0by5cclxuICogQHBhcmFtIHtPYmplY3R9IFtib2R5PXt9XSAtIFRoZSBKU09OIGJvZHkgdG8gaW5jbHVkZSBpbiB0aGUgUE9TVCByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcG9zdCh1cmwsIGJvZHkgPSB7fSwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoYm9keSk7XHJcblxyXG4gICAgLy8gU2V0IGRlZmF1bHQgaGVhZGVycyBhbmQgbWVyZ2Ugd2l0aCByZXF1ZXN0T3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oXHJcbiAgICAgIHtcclxuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcclxuICAgICAgICBoZWFkZXJzOiB7XHJcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxyXG4gICAgICAgICAgJ0NvbnRlbnQtTGVuZ3RoJzogZGF0YS5sZW5ndGhcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zXHJcbiAgICApO1xyXG5cclxuICAgIGNvbnN0IHJlcXVlc3QgPSBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAucmVxdWVzdCh1cmwsIG9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgIC8vIFdyaXRlIHRoZSByZXF1ZXN0IGJvZHkgYW5kIGVuZCB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC53cml0ZShkYXRhKTtcclxuICAgIHJlcXVlc3QuZW5kKCk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgVVJMLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGRldGVybWluZSB0aGUgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSAoaHR0cCBvciBodHRwcykuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKSB7XHJcbiAgcmV0dXJuIHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZmV0Y2gsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBlcnJvciBjbGFzcyBmb3IgaGFuZGxpbmcgZXhwb3J0LXJlbGF0ZWQgZXJyb3JzLiBFeHRlbmRzIHRoZSBuYXRpdmVcclxuICogYEVycm9yYCBjbGFzcyB0byBpbmNsdWRlIGFkZGl0aW9uYWwgcHJvcGVydGllcyBsaWtlIHN0YXR1cyBjb2RlIGFuZCBzdGFja1xyXG4gKiB0cmFjZSBkZXRhaWxzLlxyXG4gKi9cclxuY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gVGhlIGVycm9yIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkLlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gT3B0aW9uYWwgSFRUUCBzdGF0dXMgY29kZSBhc3NvY2lhdGVkXHJcbiAgICogd2l0aCB0aGUgZXJyb3IgKGUuZy4sIDQwMCwgNTAwKS5cclxuICAgKi9cclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXNDb2RlKSB7XHJcbiAgICBzdXBlcigpO1xyXG5cclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcblxyXG4gICAgaWYgKHN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgb3IgdXBkYXRlcyB0aGUgSFRUUCBzdGF0dXMgY29kZSBmb3IgdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBUaGUgSFRUUCBzdGF0dXMgY29kZSB0byBhc3NpZ24gdG8gdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgYWRkaXRpb25hbCBlcnJvciBkZXRhaWxzIGJhc2VkIG9uIGFuIGV4aXN0aW5nIGVycm9yIG9iamVjdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gQW4gZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgZGV0YWlscyB0byBwb3B1bGF0ZVxyXG4gICAqIHRoZSBgRXhwb3J0RXJyb3JgIGluc3RhbmNlLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG5cclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoZSBjYWNoZSBtYW5hZ2VyIGlzIHJlc3BvbnNpYmxlIGZvciBoYW5kbGluZyBhbmQgbWFuYWdpbmdcclxuICogdGhlIEhpZ2hjaGFydHMgbGlicmFyeSBhbG9uZyB3aXRoIGl0cyBkZXBlbmRlbmNpZXMuIEl0IGVuc3VyZXMgdGhhdCB0aGVzZVxyXG4gKiByZXNvdXJjZXMgYXJlIHN0b3JlZCBhbmQgcmV0cmlldmVkIGVmZmljaWVudGx5IHRvIG9wdGltaXplIHBlcmZvcm1hbmNlXHJcbiAqIGFuZCByZWR1Y2UgcmVkdW5kYW50IG5ldHdvcmsgcmVxdWVzdHMuIFRoZSBjYWNoZSBpcyBzdG9yZWQgaW4gdGhlIGAuY2FjaGVgXHJcbiAqIGRpcmVjdG9yeSBieSBkZWZhdWx0LCB3aGljaCBzZXJ2ZXMgYXMgYSBkZWRpY2F0ZWQgZm9sZGVyIGZvciBrZWVwaW5nIGNhY2hlZFxyXG4gKiBmaWxlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnNcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNhY2hlIHBhdGhcclxuICAgIGNvbnN0IGNhY2hlUGF0aCA9IGdldENhY2hlUGF0aCgpO1xyXG5cclxuICAgIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgICBjb25zdCBtYW5pZmVzdFBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKTtcclxuICAgIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ3NvdXJjZXMuanMnKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcclxuXHJcbiAgICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIHRoZSBgbWFuaWZlc3QuanNvbmAgZG9lcyBub3QgZXhpc3RcclxuICAgIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICAgIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGhpZ2hjaGFydHNPcHRpb25zLmZvcmNlRmV0Y2gpIHtcclxuICAgICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAgIC8vIFJlYWQgdGhlIG1hbmlmZXN0IEpTT05cclxuICAgICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpLCAndXRmOCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG1vZHVsZXMgaXMgYW4gYXJyYXksIGlmIHNvLCB3ZSByZXdyaXRlIGl0IHRvIGEgbWFwIHRvIG1ha2VcclxuICAgICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIGFjdHVhbCBudW1iZXIgb2Ygc2NyaXB0cyB0byBiZSBmZXRjaGVkXHJcbiAgICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9XHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnM7XHJcbiAgICAgIGNvbnN0IG51bWJlck9mTW9kdWxlcyA9XHJcbiAgICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcclxuICAgICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24pIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAgIE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzXHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgX3VwZGF0ZUNhY2hlKFxyXG4gICAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgICBzb3VyY2VQYXRoXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xyXG5cclxuICAgICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEZpbmFsbHksIHNhdmUgdGhlIG5ldyBtYW5pZmVzdCwgd2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjdXJyZW50IGNvbmZpZ1xyXG4gICAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgICBhd2FpdCBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY29uZmlndXJlIGNhY2hlIGFuZCBjcmVhdGUgb3IgdXBkYXRlIHRoZSBjb25maWcgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIFVwZGF0ZSB0byB0aGUgbmV3IHZlcnNpb25cclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICBoaWdoY2hhcnRzOiB7XHJcbiAgICAgIHZlcnNpb246IG5ld1ZlcnNpb25cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgsICd1dGY4Jyk7IC8vICM1NjJcclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBgZmV0Y2hlZE1vZHVsZXNgIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxyXG4gKiB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxyXG4gKiBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuIFRoZSBkZWZhdWx0XHJcbiAqIHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbVxyXG4gKiB3aXRoIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxyXG4gIHNjcmlwdCxcclxuICByZXF1ZXN0T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlcyxcclxuICBzaG91bGRUaHJvd0Vycm9yID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgLmpzIGZyb20gdGhlIGN1c3RvbSBzdHJpbmdzXHJcbiAgaWYgKHNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgIHNjcmlwdCA9IHNjcmlwdC5zdWJzdHJpbmcoMCwgc2NyaXB0Lmxlbmd0aCAtIDMpO1xyXG4gIH1cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gIH1cclxuXHJcbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYCxcclxuICAgICAgNDA0XHJcbiAgICApLnNldEVycm9yKHJlc3BvbnNlKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uLmBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3RcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbZmV0Y2hlZE1vZHVsZXM9e31dIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzT3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMgPSB7fSkge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzXHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihnZXRDYWNoZVBhdGgoKSwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgYHNjcmlwdHNgIGFuZCBgY3VzdG9tU2NyaXB0c2AgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNvcmVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBjb3JlIHNjcmlwdHMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG1vZHVsZVNjcmlwdHMgLSBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGN1c3RvbVNjcmlwdHMgLSBDdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsXHJcbiAqIFVSTHMpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmZXRjaGVkIHNjcmlwdHNcclxuICogY29udGVudCBqb2luZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogc2V0dGluZyBhbiBIVFRQIEFnZW50IGZvciBwcm94eS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHMoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pIHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gc2VydmVyUHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gc2VydmVyUHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiBleGlzdHMsIGFkZCBwcm94eSBhZ2VudCB0byByZXF1ZXN0IG9wdGlvbnNcclxuICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHByb3h5QWdlbnRcclxuICAgID8ge1xyXG4gICAgICAgIGFnZW50OiBwcm94eUFnZW50LFxyXG4gICAgICAgIHRpbWVvdXQ6IHNlcnZlclByb3h5T3B0aW9ucy50aW1lb3V0XHJcbiAgICAgIH1cclxuICAgIDoge307XHJcblxyXG4gIGNvbnN0IGFsbEZldGNoUHJvbWlzZXMgPSBbXHJcbiAgICAuLi5jb3JlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcylcclxuICAgICksXHJcbiAgICAuLi5jdXN0b21TY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucylcclxuICAgIClcclxuICBdO1xyXG5cclxuICBjb25zdCBmZXRjaGVkU2NyaXB0cyA9IGF3YWl0IFByb21pc2UuYWxsKGFsbEZldGNoUHJvbWlzZXMpO1xyXG4gIHJldHVybiBmZXRjaGVkU2NyaXB0cy5qb2luKCc7XFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgcmVwcmVzZW50aW5nXHJcbiAqIHRoZSBmZXRjaGVkIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF91cGRhdGVDYWNoZShoaWdoY2hhcnRzT3B0aW9ucywgc2VydmVyUHJveHlPcHRpb25zLCBzb3VyY2VQYXRoKSB7XHJcbiAgLy8gR2V0IEhpZ2hjaGFydHMgdmVyc2lvbiBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGhjVmVyc2lvbiA9XHJcbiAgICBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uID09PSAnbGF0ZXN0J1xyXG4gICAgICA/IG51bGxcclxuICAgICAgOiBgJHtoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9ufWA7XHJcblxyXG4gIC8vIEdldCB0aGUgQ0ROIHVybCBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGNkblVybCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVybCB8fCBjYWNoZS5jZG5Vcmw7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5zb3VyY2VzID0gYXdhaXQgX2ZldGNoU2NyaXB0cyhcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoYykgPT5cclxuICAgICAgICAgIGhjVmVyc2lvbiA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259LyR7Y31gIDogYCR7Y2RuVXJsfS8ke2N9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vbWFwcy8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tYXBzL21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgOiBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICksXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGkpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb25cclxuICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L3N0b2NrLyR7aGNWZXJzaW9ufS9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9zdG9jay9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldEhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIGV4dHJhY3RWZXJzaW9uLFxyXG4gIGV4dHJhY3RNb2R1bGVOYW1lLFxyXG4gIGdldENhY2hlLFxyXG4gIGdldENhY2hlUGF0aFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWV0aG9kcyBmb3IgaW5pdGlhbGl6aW5nIEhpZ2hjaGFydHMgd2l0aCBjdXN0b21pemVkXHJcbiAqIGFuaW1hdGlvbiBzZXR0aW5ncyBhbmQgdHJpZ2dlcmluZyB0aGUgY3JlYXRpb24gb2YgSGlnaGNoYXJ0cyBjaGFydHMgd2l0aFxyXG4gKiBleHBvcnQtc3BlY2lmaWMgY29uZmlndXJhdGlvbnMgaW4gdGhlIHBhZ2UgY29udGV4dC4gU3VwcG9ydHMgZHluYW1pYyBvcHRpb25cclxuICogbWVyZ2luZywgY3VzdG9tIGxvZ2ljIGluamVjdGlvbiwgYW5kIGNvbnRyb2wgb3ZlciByZW5kZXJpbmcgYmVoYXZpb3JzLiBVc2VkXHJcbiAqIGJ5IHRoZSBQdXBwZXRlZXIgcGFnZS5cclxuICovXHJcblxyXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xyXG5cclxuLyoqXHJcbiAqIFNldHRpbmcgdGhlIGBIaWdoY2hhcnRzLmFuaW1PYmplY3RgIGZ1bmN0aW9uLiBDYWxsZWQgd2hlbiBpbml0aW5nIHRoZSBwYWdlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xyXG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcclxuICAgIHJldHVybiB7IGR1cmF0aW9uOiAwIH07XHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBIaWdoY2hhcnRzIGNoYXJ0IG9uIGEgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVDaGFydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQ2hhcnQoZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgc2V0T3B0aW9ucywgbWVyZ2UsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHRoZSBgdXNlck9wdGlvbnNgIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB0aGUgYHVzZXJPcHRpb25zYCB3aGVuIGBmb3JFeHBvcnRgIGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBTb21lIG1hbmRhdG9yeSBhZGRpdGlvbmFsIGBjaGFydGAgYW5kIGBleHBvcnRpbmdgIG9wdGlvbnNcclxuICBjb25zdCBhZGRpdGlvbmFsT3B0aW9ucyA9IHtcclxuICAgIGNoYXJ0OiB7XHJcbiAgICAgIC8vIEJ5IGRlZmF1bHQgYW5pbWF0aW9uIGlzIGRpc2FibGVkXHJcbiAgICAgIGFuaW1hdGlvbjogZmFsc2UsXHJcbiAgICAgIC8vIEdldCB0aGUgcmlnaHQgc2l6ZSB2YWx1ZXNcclxuICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcclxuICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmluc3RyfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYHRoZW1lT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgdGhlbWVPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9uc31gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb25cclxuICBjb25zdCBnbG9iYWxPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gTWVyZ2UgdGhlIGZvbGxvd2luZyBvcHRpb25zIG9iamVjdHMgdG8gY3JlYXRlIGZpbmFsIG9wdGlvbnNcclxuICBjb25zdCBmaW5hbE9wdGlvbnMgPSBtZXJnZShcclxuICAgIGZhbHNlLFxyXG4gICAgdGhlbWVPcHRpb25zLFxyXG4gICAgdXNlck9wdGlvbnMsXHJcbiAgICAvLyBQbGFjZWQgaXQgaGVyZSBpbnN0ZWFkIGluIHRoZSBpbml0IGJlY2F1c2Ugb2YgdGhlIHNpemUgaXNzdWVzXHJcbiAgICBhZGRpdGlvbmFsT3B0aW9uc1xyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjYWxsYmFja2Agb3B0aW9uXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke2N1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja31gKSgpXHJcbiAgICA6IG51bGw7XHJcblxyXG4gIC8vIFRyaWdnZXIgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbignb3B0aW9ucycsIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbZXhwb3J0T3B0aW9ucy5jb25zdHJdKCdjb250YWluZXInLCBmaW5hbE9wdGlvbnMsIGZpbmFsQ2FsbGJhY2spO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBgc2V0T3B0aW9uc2Agd2FzIHVzZWQgaW4gdGhlIGBjdXN0b21Db2RlYClcclxuICBmb3IgKGNvbnN0IHByb3AgaW4gZGVmYXVsdE9wdGlvbnMpIHtcclxuICAgIGlmICh0eXBlb2YgZGVmYXVsdE9wdGlvbnNbcHJvcF0gIT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgZGVsZXRlIGRlZmF1bHRPcHRpb25zW3Byb3BdO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBkZWZhdWx0IG9wdGlvbnMgYmFja1xyXG4gIHNldE9wdGlvbnMoSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqKTtcclxuXHJcbiAgLy8gRW1wdHkgdGhlIGN1c3RvbSBnbG9iYWwgb3B0aW9ucyBvYmplY3RcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSB7fTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNldHVwSGlnaGNoYXJ0cyxcclxuICBjcmVhdGVDaGFydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZSwgY3JlYXRpbmcgYW5kIGNsZWFyaW5nIHBhZ2VzLCBpbmplY3RpbmcgY3VzdG9tIHJlc291cmNlcyxcclxuICogYW5kIHNldHRpbmcgdXAgSGlnaGNoYXJ0cyBmb3Igc2VydmVyLXNpZGUgcmVuZGVyaW5nLiBUaGUgbW9kdWxlIGVuc3VyZXNcclxuICogdGhhdCByZXNvdXJjZXMgYXJlIGNvcnJlY3RseSBtYW5hZ2VkIGFuZCBjYW4gaGFuZGxlIGZhaWx1cmVzIGR1cmluZ1xyXG4gKiBvcGVyYXRpb25zIGxpa2UgbGF1bmNoaW5nIHRoZSBicm93c2VyIG9yIGNyZWF0aW5nIG5ldyBwYWdlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciBwYWdlc1xyXG5jb25zdCB0ZW1wbGF0ZSA9IHJlYWRGaWxlU3luYyhcclxuICBqb2luKF9fZGlybmFtZSwgJ3RlbXBsYXRlcycsICd0ZW1wbGF0ZS5odG1sJyksXHJcbiAgJ3V0ZjgnXHJcbik7XHJcblxyXG4vLyBUbyBzYXZlIHRoZSBicm93c2VyXHJcbmxldCBicm93c2VyID0gbnVsbDtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QnJvd3NlclxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBubyB2YWxpZCBicm93c2VyXHJcbiAqIGhhcyBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QnJvd3NlcigpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nLCA1MDApO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXJcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgY3JlYXRlZCBQdXBwZXRlZXJcclxuICogYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG1heCByZXRyaWVzIHRvIG9wZW5cclxuICogYSBicm93c2VyIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyXHJcbiAqIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2AgYW5kIGBvdGhlcmAgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWdPcHRpb25zIH0gPSBkZWJ1ZztcclxuXHJcbiAgLy8gTGF1bmNoIG9wdGlvbnMgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgY29uc3QgbGF1bmNoT3B0aW9ucyA9IHtcclxuICAgIGhlYWRsZXNzOiBvdGhlci5icm93c2VyU2hlbGxNb2RlID8gJ3NoZWxsJyA6IHRydWUsXHJcbiAgICB1c2VyRGF0YURpcjogJ3RtcCcsXHJcbiAgICBhcmdzOiBwdXBwZXRlZXJBcmdzIHx8IFtdLFxyXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHSFVQOiBmYWxzZSxcclxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXHJcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXHJcbiAgICAuLi4oZW5hYmxlZERlYnVnICYmIGRlYnVnT3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICAvLyBBIGNvdW50ZXIgZm9yIHRoZSBicm93c2VyJ3MgbGF1bmNoIHJldHJpZXNcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFticm93c2VyXSBBdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeSAkeysrdHJ5Q291bnR9KS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gTGF1bmNoIHRoZSBicm93c2VyXHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgJ1ticm93c2VyXSBGYWlsZWQgdG8gbGF1bmNoIGEgYnJvd3NlciBpbnN0YW5jZS4nXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0cnkgdG8gbGF1bmNoIGJyb3dzZXIgdW50aWwgcmVhY2hpbmcgbWF4IGF0dGVtcHRzXHJcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcclxuICAgICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIFJldHJ5IHRvIG9wZW4gYSBicm93c2VyICgke3RyeUNvdW50fSBvdXQgb2YgMjUpLmApO1xyXG5cclxuICAgICAgICAgIC8vIFdhaXQgZm9yIGEgNCBzZWNvbmRzIGJlZm9yZSB0cnlpbmcgYWdhaW5cclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG5cclxuICAgICAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGxhdW5jaE9wdGlvbnMuaGVhZGxlc3MgPT09ICdzaGVsbCcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIHNoZWxsIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERlYnVnIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChlbmFibGVkRGVidWcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIGRlYnVnIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2Jyb3dzZXJdIE1heGltdW0gcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlciBpbnN0YW5jZSByZWFjaGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nLCA1MDApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xvc2VCcm93c2VyXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2VCcm93c2VyKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIgJiYgYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgYnJvd3NlciA9IG51bGw7XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgcGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHRoZSBgX3NldFBhZ2VDb250ZW50KClgLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBuZXdQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIGBpZGAsXHJcbiAqIGB3b3JrQ291bnRgLCBhbmQgYHBhZ2VgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjb25uZWN0ZWQgb3IgaWYgYSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG5ld1BhZ2UocG9vbFJlc291cmNlKSB7XHJcbiAgLy8gRXJyb3IgaW4gY2FzZSBvZiBubyBjb25uZWN0ZWQgYnJvd3NlclxyXG4gIGlmICghYnJvd3NlciB8fCAhYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW2Jyb3dzZXJdIEJyb3dzZXIgaXMgbm90IHlldCBjb25uZWN0ZWQuYCwgNTAwKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBwb29sUmVzb3VyY2UucGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBEaXNhYmxlIGNhY2hlXHJcbiAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXHJcbiAgX3NldFBhZ2VFdmVudHMocG9vbFJlc291cmNlLnBhZ2UpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgcGFnZSBpcyBjb3JyZWN0bHkgY3JlYXRlZFxyXG4gIGlmICghcG9vbFJlc291cmNlLnBhZ2UgfHwgcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgdGhlIGNvbnRlbnQgb2YgYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG1vZGUuIExvZ3NcclxuICogdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIG9mIGEgcGFnZSdzIGNvbnRlbnQgZmFpbHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIHBhZ2UgYW5kIGlkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtoYXJkUmVzZXQ9ZmFsc2VdIC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gYGFib3V0OmJsYW5rYCBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgd2hlbiBwYWdlXHJcbiAqIGlzIGNvcnJlY3RseSBjbGVhcmVkIGFuZCBmYWxzZSB3aGVuIGl0IGlzIG5vdC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2UocG9vbFJlc291cmNlLCBoYXJkUmVzZXQgPSBmYWxzZSkge1xyXG4gIHRyeSB7XHJcbiAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGBhYm91dDpibGFua2BcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHtcclxuICAgICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBfc2V0UGFnZUNvbnRlbnQocG9vbFJlc291cmNlLnBhZ2UpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gQ29udGVudCBvZiB0aGUgcGFnZSBjb3VsZCBub3QgYmUgY2xlYXJlZC5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICBwb29sUmVzb3VyY2Uud29ya0NvdW50ID0gZ2V0T3B0aW9ucygpLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICB9XHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyBjdXN0b20gSlMgYW5kIENTUyByZXNvdXJjZXMgdG8gYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYWRkUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9IGZpbGUuc3RhcnRzV2l0aCgnaHR0cCcpID8gZmFsc2UgOiB0cnVlO1xyXG5cclxuICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gc2NyaXB0IGZyb20gcmVzb3VyY2VzJyBmaWxlc1xyXG4gICAgICAgIGluamVjdGVkSnMucHVzaChcclxuICAgICAgICAgIGlzTG9jYWxcclxuICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICBjb250ZW50OiByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGZpbGUpLCAndXRmOCcpXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA6IHtcclxuICAgICAgICAgICAgICAgIHVybDogZmlsZVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgZm9yIChjb25zdCBqc1Jlc291cmNlIG9mIGluamVjdGVkSnMpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU2NyaXB0VGFnKGpzUmVzb3VyY2UpKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gVGhlIEpTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGluamVjdGVkSnMubGVuZ3RoID0gMDtcclxuXHJcbiAgICAvLyBMb2FkIENTU1xyXG4gICAgY29uc3QgaW5qZWN0ZWRDc3MgPSBbXTtcclxuICAgIGlmIChyZXNvdXJjZXMuY3NzKSB7XHJcbiAgICAgIGxldCBjc3NJbXBvcnRzID0gcmVzb3VyY2VzLmNzcy5tYXRjaCgvQGltcG9ydFxccyooW147XSopOy9nKTtcclxuICAgICAgaWYgKGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAvLyBIYW5kbGUgY3NzIHNlY3Rpb25cclxuICAgICAgICBmb3IgKGxldCBjc3NJbXBvcnRQYXRoIG9mIGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoKSB7XHJcbiAgICAgICAgICAgIGNzc0ltcG9ydFBhdGggPSBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ3VybCgnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgnQGltcG9ydCcsICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvJy9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvOy8sICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cXCkvZywgJycpXHJcbiAgICAgICAgICAgICAgLnRyaW0oKTtcclxuXHJcbiAgICAgICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBjc3MgZnJvbSByZXNvdXJjZXNcclxuICAgICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGguc3RhcnRzV2l0aCgnaHR0cCcpKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICBwYXRoOiBnZXRBYnNvbHV0ZVBhdGgoY3NzSW1wb3J0UGF0aClcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVGhlIHJlc3Qgb2YgdGhlIENTUyBzZWN0aW9uIHdpbGwgYmUgY29udGVudCBieSBub3dcclxuICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmNzcy5yZXBsYWNlKC9AaW1wb3J0XFxzKihbXjtdKik7L2csICcnKSB8fCAnICdcclxuICAgICAgfSk7XHJcblxyXG4gICAgICBmb3IgKGNvbnN0IGNzc1Jlc291cmNlIG9mIGluamVjdGVkQ3NzKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyhjc3NSZXNvdXJjZSkpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICBgW2Jyb3dzZXJdIFRoZSBDU1MgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBpbmplY3RlZENzcy5sZW5ndGggPSAwO1xyXG4gICAgfVxyXG4gIH1cclxuICByZXR1cm4gaW5qZWN0ZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgb3V0IGFsbCBzdGF0ZSBzZXQgb24gdGhlIHBhZ2Ugd2l0aCBgYWRkU2NyaXB0VGFnYCBhbmQgYGFkZFN0eWxlVGFnYC5cclxuICogUmVtb3ZlcyBpbmplY3RlZCByZXNvdXJjZXMgYW5kIHJlc2V0cyBDU1MgYW5kIHNjcmlwdCB0YWdzIG9uIHRoZSBwYWdlLlxyXG4gKiBBZGRpdGlvbmFsbHksIGl0IGRlc3Ryb3lzIHByZXZpb3VzbHkgZXhpc3RpbmcgY2hhcnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgZnJvbSB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxPYmplY3Q+fSBpbmplY3RlZFJlc291cmNlcyAtIEFycmF5IG9mIGluamVjdGVkIHJlc291cmNlc1xyXG4gKiB0byBiZSBjbGVhcmVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcykge1xyXG4gIHRyeSB7XHJcbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlc291cmNlLmRpc3Bvc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IG9sZCBjaGFydHMgYWZ0ZXIgZXhwb3J0IGlzIGRvbmUgYW5kIHJlc2V0IGFsbCBDU1MgYW5kIHNjcmlwdCB0YWdzXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgLy8gV2UgYXJlIG5vdCBndWFyYW50ZWVkIHRoYXQgSGlnaGNoYXJ0cyBpcyBsb2FkZWQsIHdoZW4gZG9pbmcgU1ZHIGV4cG9ydHNcclxuICAgICAgaWYgKHR5cGVvZiBIaWdoY2hhcnRzICE9PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgIGNvbnN0IG9sZENoYXJ0cyA9IEhpZ2hjaGFydHMuY2hhcnRzO1xyXG5cclxuICAgICAgICAvLyBDaGVjayBpbiBhbnkgYWxyZWFkeSBleGlzdGluZyBjaGFydHNcclxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShvbGRDaGFydHMpICYmIG9sZENoYXJ0cy5sZW5ndGgpIHtcclxuICAgICAgICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0c1xyXG4gICAgICAgICAgZm9yIChjb25zdCBvbGRDaGFydCBvZiBvbGRDaGFydHMpIHtcclxuICAgICAgICAgICAgb2xkQ2hhcnQgJiYgb2xkQ2hhcnQuZGVzdHJveSgpO1xyXG4gICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgICAgSGlnaGNoYXJ0cy5jaGFydHMuc2hpZnQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gQ291bGQgbm90IGNsZWFyIHBhZ2UncyByZXNvdXJjZXMuYCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgY29udGVudCBmb3IgYSBQdXBwZXRlZXIgUGFnZSB1c2luZyBhIHByZWRlZmluZWQgdGVtcGxhdGVcclxuICogYW5kIGFkZGl0aW9uYWwgc2NyaXB0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBjb250ZW50XHJcbiAqIGlzIGJlaW5nIHNldC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zZXRQYWdlQ29udGVudChwYWdlKSB7XHJcbiAgLy8gU2V0IHRoZSBpbml0aWFsIHBhZ2UgY29udGVudFxyXG4gIGF3YWl0IHBhZ2Uuc2V0Q29udGVudCh0ZW1wbGF0ZSwgeyB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJyB9KTtcclxuXHJcbiAgLy8gQWRkIGFsbCByZWdpc3RlcmVkIEhpZ2NoYXJ0cyBzY3JpcHRzLCBxdWl0ZSBkZW1hbmRpbmdcclxuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdzb3VyY2VzLmpzJykgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBgYW5pbU9iamVjdGAgZm9yIEhpZ2hjaGFydHNcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKHNldHVwSGlnaGNoYXJ0cyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgZXZlbnRzIChsaWtlIGBwYWdlZXJyb3JgIGFuZCBgY29uc29sZWApIGZvciBhIFB1cHBldGVlciBQYWdlIGluIG9yZGVyXHJcbiAqIHRvIGNhdGNoIGFuZCBkaXNwbGF5IGVycm9ycyBhbmQgY29uc29sZSBsb2dzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VFdmVudHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBsaXN0ZW5lcnNcclxuICogYXJlIGJlaW5nIHNldC5cclxuICovXHJcbmZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzKHBhZ2UpIHtcclxuICAvLyBHZXQgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBkZWJ1ZyB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBTZXQgdGhlIGBwYWdlZXJyb3JgIGxpc3RlbmVyXHJcbiAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKCkgPT4ge1xyXG4gICAgLy8gSXQgd291bGQgc2VlbSBsaWtlIHRoaXMgbWF5IGZpcmUgYXQgdGhlIHNhbWUgdGltZSBvciBzaG9ydGx5IGJlZm9yZVxyXG4gICAgLy8gYSBwYWdlIGlzIGNsb3NlZC5cclxuICAgIGlmIChwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGBjb25zb2xlYCBsaXN0ZW5lciwgaWYgbmVlZGVkXHJcbiAgaWYgKGRlYnVnLmVuYWJsZSAmJiBkZWJ1Zy5saXN0ZW5Ub0NvbnNvbGUpIHtcclxuICAgIHBhZ2Uub24oJ2NvbnNvbGUnLCAobWVzc2FnZSkgPT4ge1xyXG4gICAgICBjb25zb2xlLmxvZyhgW2RlYnVnXSAke21lc3NhZ2UudGV4dCgpfWApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0QnJvd3NlcixcclxuICBjcmVhdGVCcm93c2VyLFxyXG4gIGNsb3NlQnJvd3NlcixcclxuICBuZXdQYWdlLFxyXG4gIGNsZWFyUGFnZSxcclxuICBhZGRQYWdlUmVzb3VyY2VzLFxyXG4gIGNsZWFyUGFnZVJlc291cmNlc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgQ1NTIHRvIGJlIHVzZWQgb24gdGhlIGV4cG9ydGVkIHBhZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBDU1MgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0ICgpID0+IGBcclxuXHJcbmh0bWwsIGJvZHkge1xyXG4gIG1hcmdpbjogMDtcclxuICBwYWRkaW5nOiAwO1xyXG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XHJcbn1cclxuXHJcbiN0YWJsZS1kaXYsICNzbGlkZXJzLCAjZGF0YXRhYmxlLCAjY29udHJvbHMsIC5sZC1yb3cge1xyXG4gIGRpc3BsYXk6IG5vbmU7XHJcbiAgaGVpZ2h0OiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyIHtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG4gIG1hcmdpbjogMDtcclxuICBvdmVyZmxvdzogYXV0bztcclxuICBmb250LXNpemU6IDA7XHJcbn1cclxuXHJcbiNjaGFydC1jb250YWluZXIgPiBmaWd1cmUsIGRpdiB7XHJcbiAgbWFyZ2luLXRvcDogMCAhaW1wb3J0YW50O1xyXG4gIG1hcmdpbi1ib3R0b206IDAgIWltcG9ydGFudDtcclxufVxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY3NzVGVtcGxhdGUgZnJvbSAnLi9jc3MuanMnO1xyXG5cclxuLyoqXHJcbiAqIFRoZSBTVkcgdGVtcGxhdGUgdG8gdXNlIHdoZW4gbG9hZGluZyBTVkcgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHN2ZyAtIFRoZSBTVkcgaW5wdXQgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIFNWRyB0ZW1wbGF0ZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChzdmcpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNoYXJ0cyBFeHBvcnQ8L3RpdGxlPlxyXG4gIDwvaGVhZD5cclxuICA8c3R5bGU+XHJcbiAgICAke2Nzc1RlbXBsYXRlKCl9XHJcbiAgPC9zdHlsZT5cclxuICA8Ym9keT5cclxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cclxuICAgICAgJHtzdmd9XHJcbiAgICA8L2Rpdj5cclxuICA8L2JvZHk+XHJcbjwvaHRtbD5cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBoYW5kbGVzIGNoYXJ0IGV4cG9ydCBmdW5jdGlvbmFsaXR5IHVzaW5nIFB1cHBldGVlci5cclxuICogSXQgc3VwcG9ydHMgZXhwb3J0aW5nIGNoYXJ0cyBhcyBTVkcsIFBORywgSlBFRywgYW5kIFBERiBmb3JtYXRzLiBUaGUgbW9kdWxlXHJcbiAqIG1hbmFnZXMgcGFnZSByZXNvdXJjZXMsIHNldHMgdXAgdGhlIGV4cG9ydCBlbnZpcm9ubWVudCwgYW5kIHByb2Nlc3NlcyBjaGFydFxyXG4gKiBjb25maWd1cmF0aW9ucyBvciBTVkcgaW5wdXRzIGZvciByZW5kZXJpbmcuIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZVxyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYWRkUGFnZVJlc291cmNlcywgY2xlYXJQYWdlUmVzb3VyY2VzIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgY3JlYXRlQ2hhcnQgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgc3ZnVGVtcGxhdGUgZnJvbSAnLi4vdGVtcGxhdGVzL3N2Z0V4cG9ydC9zdmdFeHBvcnQuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2UgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTwoc3RyaW5nfEJ1ZmZlcnxFeHBvcnRFcnJvcik+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlc1xyXG4gKiB0byB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgZXhwb3J0IHRvIGFuIHVuc3VwcG9ydGVkXHJcbiAqIG91dHB1dCBmb3JtYXQgb2NjdXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydChwYWdlLCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXkgKGFkZGl0aW9uYWwgSlMgYW5kIENTUylcclxuICBjb25zdCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICB0cnkge1xyXG4gICAgbGV0IGlzU1ZHID0gZmFsc2U7XHJcblxyXG4gICAgLy8gRGVjaWRlIG9uIHRoZSBleHBvcnQgbWV0aG9kXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy5zdmcpIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgYHR5cGVgIGlzIGFsc28gU1ZHLCByZXR1cm4gdGhlIGlucHV0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGV4cG9ydE9wdGlvbnMuc3ZnO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBNYXJrIGFzIFNWRyBleHBvcnQgZm9yIHRoZSBsYXRlciBzaXplIGNvcnJlY3Rpb25zXHJcbiAgICAgIGlzU1ZHID0gdHJ1ZTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRcclxuICAgICAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKGV4cG9ydE9wdGlvbnMuc3ZnKSwge1xyXG4gICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgIH0pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBKU09OIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoY3JlYXRlQ2hhcnQsIGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXHJcbiAgICAvLyBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAuLi4oYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBjdXN0b21Mb2dpY09wdGlvbnMpKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZSBhbmQgc2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XHJcbiAgICBjb25zdCBzaXplID0gaXNTVkdcclxuICAgICAgPyBhd2FpdCBwYWdlLmV2YWx1YXRlKChzY2FsZSkgPT4ge1xyXG4gICAgICAgICAgY29uc3Qgc3ZnRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXHJcbiAgICAgICAgICAgICcjY2hhcnQtY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJ1xyXG4gICAgICAgICAgKTtcclxuXHJcbiAgICAgICAgICAvLyBHZXQgdGhlIHZhbHVlcyBjb3JyZWN0bHkgc2NhbGVkXHJcbiAgICAgICAgICBjb25zdCBjaGFydEhlaWdodCA9IHN2Z0VsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0V2lkdGggPSBzdmdFbGVtZW50LndpZHRoLmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBJbiBjYXNlIG9mIFNWRyB0aGUgem9vbSBtdXN0IGJlIHNldCBkaXJlY3RseSBmb3IgYm9keSBhcyBzY2FsZVxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBTZXQgdGhlIG1hcmdpbiB0byAwcHhcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcclxuXHJcbiAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICBjaGFydEhlaWdodCxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aFxyXG4gICAgICAgICAgfTtcclxuICAgICAgICB9LCBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpKVxyXG4gICAgICA6IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBjb25zdCB7IGNoYXJ0SGVpZ2h0LCBjaGFydFdpZHRoIH0gPSB3aW5kb3cuSGlnaGNoYXJ0cy5jaGFydHNbMF07XHJcblxyXG4gICAgICAgICAgLy8gTm8gbmVlZCBmb3Igc3VjaCBzY2FsZSBtYW5pcHVsYXRpb24gaW4gY2FzZSBvZiBvdGhlciB0eXBlc1xyXG4gICAgICAgICAgLy8gb2YgZXhwb3J0cy4gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjbGlwIHJlZ2lvbiBmb3IgdGhlIHBhZ2VcclxuICAgIGNvbnN0IHsgeCwgeSB9ID0gYXdhaXQgX2dldENsaXBSZWdpb24ocGFnZSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGBoZWlnaHRgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRIZWlnaHQgfHwgZXhwb3J0T3B0aW9ucy5oZWlnaHQpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgd2lkdGhgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRXaWR0aCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcclxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgIH0pO1xyXG5cclxuICAgIGxldCByZXN1bHQ7XHJcbiAgICAvLyBSYXN0ZXJpemF0aW9uIHByb2Nlc3NcclxuICAgIHN3aXRjaCAoZXhwb3J0T3B0aW9ucy50eXBlKSB7XHJcbiAgICAgIGNhc2UgJ3N2Zyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVNWRyhwYWdlKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncG5nJzpcclxuICAgICAgY2FzZSAnanBlZyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZUltYWdlKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMudHlwZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIHgsXHJcbiAgICAgICAgICAgIHlcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncGRmJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlUERGKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBkZWZhdWx0OlxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBVbnN1cHBvcnRlZCBvdXRwdXQgZm9ybWF0OiAke2V4cG9ydE9wdGlvbnMudHlwZX0uYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2xlYXIgcHJldmlvdXNseSBpbmplY3RlZCBKUyBhbmQgQ1NTIHJlc291cmNlc1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMgb2YgdGhlIHNwZWNpZmllZCBwYWdlIGVsZW1lbnRcclxuICogd2l0aCB0aGUgJ2NoYXJ0LWNvbnRhaW5lcicgaWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2dldENsaXBSZWdpb25cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGB4YCwgYHlgLCBgd2lkdGhgLCBhbmQgYGhlaWdodGAgcHJvcGVydGllcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XHJcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB4LFxyXG4gICAgICB5LFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXHJcbiAgICB9O1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBTVkcgYnkgZXZhbHVhdGluZyB0aGUgYG91dGVySFRNTGAgb2YgdGhlIGZpcnN0ICdzdmcnIGVsZW1lbnRcclxuICogaW5zaWRlIGFuIGVsZW1lbnQgd2l0aCB0aGUgaWQgJ2NvbnRhaW5lcicuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVNWR1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFNWRyBzdHJpbmcuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlU1ZHKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbChcclxuICAgICcjY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJyxcclxuICAgIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIGltYWdlIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgYHNjcmVlbnNob3RgIGZ1bmN0aW9uYWxpdHkgd2l0aFxyXG4gKiBzcGVjaWZpZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlSW1hZ2VcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gSW1hZ2UgdHlwZS5cclxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgaW1hZ2UgYnVmZmVyXHJcbiAqIG9yIHJlamVjdGluZyB3aXRoIGFuIGBFeHBvcnRFcnJvcmAgZm9yIHRpbWVvdXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlSW1hZ2UocGFnZSwgdHlwZSwgY2xpcCwgcmFzdGVyaXphdGlvblRpbWVvdXQpIHtcclxuICByZXR1cm4gUHJvbWlzZS5yYWNlKFtcclxuICAgIHBhZ2Uuc2NyZWVuc2hvdCh7XHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGNsaXAsXHJcbiAgICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgICAgZnVsbFBhZ2U6IGZhbHNlLFxyXG4gICAgICBvcHRpbWl6ZUZvclNwZWVkOiB0cnVlLFxyXG4gICAgICBjYXB0dXJlQmV5b25kVmlld3BvcnQ6IHRydWUsXHJcbiAgICAgIC4uLih0eXBlICE9PSAncG5nJyA/IHsgcXVhbGl0eTogODAgfSA6IHt9KSxcclxuICAgICAgLy8gQWx3YXlzIHJlbmRlciBvbiBhIHRyYW5zcGFyZW50IHBhZ2UgaWYgdGhlIGV4cGVjdGVkIHR5cGUgZm9ybWF0IGlzIFBOR1xyXG4gICAgICBvbWl0QmFja2dyb3VuZDogdHlwZSA9PSAncG5nJyAvLyAjNDQ3LCAjNDYzXHJcbiAgICB9KSxcclxuICAgIG5ldyBQcm9taXNlKChfcmVzb2x2ZSwgcmVqZWN0KSA9PlxyXG4gICAgICBzZXRUaW1lb3V0KFxyXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcsIDQwOCkpLFxyXG4gICAgICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICAgICAgKVxyXG4gICAgKVxyXG4gIF0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIFBERiB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBwZGZgIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlUERGXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0IC0gUERGIGhlaWdodC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoIC0gUERGIHdpZHRoLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFBERiBidWZmZXIuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlUERGKHBhZ2UsIGhlaWdodCwgd2lkdGgsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgYXdhaXQgcGFnZS5lbXVsYXRlTWVkaWFUeXBlKCdzY3JlZW4nKTtcclxuICByZXR1cm4gcGFnZS5wZGYoe1xyXG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXHJcbiAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICB3aWR0aCxcclxuICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgIHRpbWVvdXQ6IHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHB1cHBldGVlckV4cG9ydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgYSB3b3JrZXIgcG9vbCBpbXBsZW1lbnRhdGlvbiBmb3IgbWFuYWdpbmdcclxuICogdGhlIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHBhZ2VzLCBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIHVzZSB3aXRoXHJcbiAqIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuIEl0IG9wdGltaXplcyByZXNvdXJjZXMgdXNhZ2UgYW5kIHBlcmZvcm1hbmNlXHJcbiAqIGJ5IG1haW50YWluaW5nIGEgcG9vbCBvZiB3b3JrZXJzIHRoYXQgY2FuIGhhbmRsZSBjb25jdXJyZW50IGV4cG9ydCB0YXNrc1xyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgUG9vbCB9IGZyb20gJ3Rhcm4nO1xyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBjcmVhdGVCcm93c2VyLCBjbG9zZUJyb3dzZXIsIG5ld1BhZ2UsIGNsZWFyUGFnZSB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IHB1cHBldGVlckV4cG9ydCB9IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldE5ld0RhdGVUaW1lLCBtZWFzdXJlVGltZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBwb29sIGluc3RhbmNlXHJcbmxldCBwb29sID0gbnVsbDtcclxuXHJcbi8vIFBvb2wgc3RhdGlzdGljc1xyXG5jb25zdCBwb29sU3RhdHMgPSB7XHJcbiAgZXhwb3J0c0F0dGVtcHRlZDogMCxcclxuICBleHBvcnRzUGVyZm9ybWVkOiAwLFxyXG4gIGV4cG9ydHNEcm9wcGVkOiAwLFxyXG4gIGV4cG9ydHNGcm9tU3ZnOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uczogMCxcclxuICBleHBvcnRzRnJvbVN2Z0F0dGVtcHRzOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzOiAwLFxyXG4gIHRpbWVTcGVudDogMCxcclxuICB0aW1lU3BlbnRBdmVyYWdlOiAwXHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwb29sIHdpdGggdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24sIGNyZWF0aW5nXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhbmQgc2V0dGluZyB1cCB3b3JrZXIgcmVzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGluaXRQb29sXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlclxyXG4gKiBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBlbmRpbmcgdGhlIGZ1bmN0aW9uXHJcbiAqIGV4ZWN1dGlvbiB3aGVuIGFuIGFscmVhZHkgaW5pdGlhbGl6ZWQgcG9vbCBvZiByZXNvdXJjZXMgaXMgZm91bmQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY3JlYXRlIHRoZSBwb29sXHJcbiAqIG9mIHdvcmtlcnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdFBvb2wocG9vbE9wdGlvbnMsIHB1cHBldGVlckFyZ3MpIHtcclxuICAvLyBDcmVhdGUgYSBicm93c2VyIGluc3RhbmNlIHdpdGggdGhlIHB1cHBldGVlciBhcmd1bWVudHNcclxuICBhd2FpdCBjcmVhdGVCcm93c2VyKHB1cHBldGVlckFyZ3MpO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIEluaXRpYWxpemluZyBwb29sIHdpdGggd29ya2VyczogbWluICR7cG9vbE9wdGlvbnMubWluV29ya2Vyc30sIG1heCAke3Bvb2xPcHRpb25zLm1heFdvcmtlcnN9LmBcclxuICAgICk7XHJcblxyXG4gICAgaWYgKHBvb2wpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXAgYW4gZXllIG9uIGEgY29ycmVjdCBtaW4gYW5kIG1heCB3b3JrZXJzIG51bWJlclxyXG4gICAgaWYgKHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzKSB7XHJcbiAgICAgIHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPSBwb29sT3B0aW9ucy5tYXhXb3JrZXJzO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENyZWF0ZSBhIHBvb2wgYWxvbmcgd2l0aCBhIG1pbmltYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgcG9vbCA9IG5ldyBQb29sKHtcclxuICAgICAgLy8gR2V0IHRoZSBgY3JlYXRlYCwgYHZhbGlkYXRlYCwgYW5kIGBkZXN0cm95YCBmdW5jdGlvbnNcclxuICAgICAgLi4uX2ZhY3RvcnkocG9vbE9wdGlvbnMpLFxyXG4gICAgICBtaW46IHBvb2xPcHRpb25zLm1pbldvcmtlcnMsXHJcbiAgICAgIG1heDogcG9vbE9wdGlvbnMubWF4V29ya2VycyxcclxuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmFjcXVpcmVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVUaW1lb3V0LFxyXG4gICAgICBkZXN0cm95VGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuZGVzdHJveVRpbWVvdXQsXHJcbiAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5pZGxlVGltZW91dCxcclxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlUmV0cnlJbnRlcnZhbCxcclxuICAgICAgcmVhcEludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5yZWFwZXJJbnRlcnZhbCxcclxuICAgICAgcHJvcGFnYXRlQ3JlYXRlRXJyb3I6IGZhbHNlXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBTZXQgZXZlbnRzXHJcbiAgICBwb29sLm9uKCdyZWxlYXNlJywgYXN5bmMgKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIENsZWFyIHBhZ2VcclxuICAgICAgY29uc3QgY2xlYXJTdGF0dXMgPSBhd2FpdCBjbGVhclBhZ2UocmVzb3VyY2UsIGZhbHNlKTtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBSZWxlYXNpbmcgYSB3b3JrZXIuIENsZWFyIHBhZ2Ugc3RhdHVzOiAke2NsZWFyU3RhdHVzfS5gXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChfZXZlbnRJZCwgcmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBEZXN0cm95ZWQgYSB3b3JrZXIgc3VjY2Vzc2Z1bGx5LmBcclxuICAgICAgKTtcclxuICAgICAgcmVzb3VyY2UucGFnZSA9IG51bGw7XHJcbiAgICB9KTtcclxuXHJcbiAgICBjb25zdCBpbml0aWFsUmVzb3VyY2VzID0gW107XHJcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBvb2xPcHRpb25zLm1pbldvcmtlcnM7IGkrKykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuICAgICAgICBpbml0aWFsUmVzb3VyY2VzLnB1c2gocmVzb3VyY2UpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIGFuIGluaXRpYWwgcmVzb3VyY2UuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXMgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgaW5pdGlhbFJlc291cmNlcy5mb3JFYWNoKChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICBwb29sLnJlbGVhc2UocmVzb3VyY2UpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIFRoZSBwb29sIGlzIHJlYWR5JHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aCA/IGAgd2l0aCAke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RofSBpbml0aWFsIHJlc291cmNlcyB3YWl0aW5nLmAgOiAnLid9YFxyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3Bvb2xdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogVGVybWluYXRlcyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24ga2lsbFBvb2xcclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgYWxsIHdvcmtlcnMgYXJlXHJcbiAqIHRlcm1pbmF0ZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIHN1Y2Nlc3NmdWxseSBjbG9zZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XHJcbiAgbG9nKDMsICdbcG9vbF0gS2lsbGluZyBwb29sIHdpdGggYWxsIHdvcmtlcnMgYW5kIGNsb3NpbmcgYnJvd3Nlci4nKTtcclxuXHJcbiAgLy8gSWYgc3RpbGwgYWxpdmUsIGRlc3Ryb3kgdGhlIHBvb2wgb2YgcGFnZXMgYmVmb3JlIGNsb3NpbmcgYSBicm93c2VyXHJcbiAgaWYgKHBvb2wpIHtcclxuICAgIC8vIEZyZWUgdXAgbm90IHJlbGVhc2VkIHdvcmtlcnNcclxuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHBvb2wudXNlZCkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VyLnJlc291cmNlKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IHRoZSBwb29sIGlmIGl0IGlzIHN0aWxsIGF2YWlsYWJsZVxyXG4gICAgaWYgKCFwb29sLmRlc3Ryb3llZCkge1xyXG4gICAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gRGVzdHJveWVkIHRoZSBwb29sIG9mIHJlc291cmNlcy4nKTtcclxuICAgIH1cclxuICAgIHBvb2wgPSBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICBhd2FpdCBjbG9zZUJyb3dzZXIoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByb2Nlc3NlcyB0aGUgZXhwb3J0IHdvcmsgdXNpbmcgYSB3b3JrZXIgZnJvbSB0aGUgcG9vbC4gQWNxdWlyZXMgYSB3b3JrZXJcclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcclxuICogdGhlIHdvcmtlciBoYW5kbGUgYmFjayB0byB0aGUgcG9vbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0V29ya1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZXhwb3J0IHJlc3VsdFxyXG4gKiBhbmQgb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3RXb3JrKG9wdGlvbnMpIHtcclxuICBsZXQgd29ya2VySGFuZGxlO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgICAvLyBBbiBleHBvcnQgYXR0ZW1wdCBjb3VudGVkXHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzQXR0ZW1wdGVkO1xyXG5cclxuICAgIC8vIERpc3BsYXkgdGhlIHBvb2wgaW5mb3JtYXRpb24gaWYgbmVlZGVkXHJcbiAgICBpZiAob3B0aW9ucy5wb29sLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGluIGNhc2Ugb2YgbGFja2luZyB0aGUgcG9vbCBpbnN0YW5jZVxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhlIGFjcXVpcmUgY291bnRlclxyXG4gICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFRyeSB0byBhY3F1aXJlIHRoZSB3b3JrZXIgYWxvbmcgd2l0aCB0aGUgaWQsIHdvcmtzIGNvdW50IGFuZCBwYWdlXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgICAgLy8gQWNxdWlyZSBhIHBvb2wgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxyXG4gICAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgICAgYEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUgdG9vayAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFJlc29sdmVkIHdvcmtlciBwYWdlIGlzIGludmFsaWQ6IHRoZSBwb29sIHNldHVwIGlzIHdvbmt5LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgY29uc3Qgd29ya1N0YXJ0ID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHt3b3JrZXJIYW5kbGUuaWR9XSAtIFN0YXJ0aW5nIHdvcmsgb24gdGhpcyBwb29sIGVudHJ5LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU3RhcnQgbWVhc3VyaW5nIGV4cG9ydCB0aW1lXHJcbiAgICBjb25zdCBleHBvcnRDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KFxyXG4gICAgICB3b3JrZXJIYW5kbGUucGFnZSxcclxuICAgICAgb3B0aW9ucy5leHBvcnQsXHJcbiAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKHJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIElmIHRoZXJlJ3MgYSByYXN0ZXJpemF0aW9uIHRpbWVvdXQsIHdlIHdhbnQgbmVlZCB0byBmbHVzaCB0aGUgcGFnZS5cclxuICAgICAgLy8gVGhpcyBpcyBiZWNhdXNlIHRoZSBwYWdlIG1heSBiZSBpbiBhIHN0YXRlIHdoZXJlIGl0J3Mgd2FpdGluZyBmb3JcclxuICAgICAgLy8gdGhlIHNjcmVlbnNob3QgdG8gZmluaXNoIGV2ZW4gdGhvdWdoIHRoZSB0aW1lb3V0IGhhcyBvY2N1cmVkLlxyXG4gICAgICAvLyBXaGljaCBvZiBjb3Vyc2UgY2F1c2VzIGEgbG90IG9mIGlzc3VlcyB3aXRoIHRoZSBldmVudCBzeXN0ZW0sXHJcbiAgICAgIC8vIGFuZCBwYWdlIGNvbnNpc3RlbmN5LlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBPbmx5IHBhZ2Uuc2NyZWVuc2hvdCB3aWxsIHRocm93IHRoaXMsIHRpbWVvdXRzIGZvciBQREYncyBhcmVcclxuICAgICAgLy8gaGFuZGxlZCBieSB0aGUgcGFnZS5wZGYgZnVuY3Rpb24gaXRzZWxmLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyAuLi55ZXMsIHRoaXMgaXMgdWdseS5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlID0gbnVsbDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJlc3VsdC5uYW1lID09PSAnVGltZW91dEVycm9yJyB8fFxyXG4gICAgICAgIHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0J1xyXG4gICAgICApIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfVJhc3Rlcml6YXRpb24gdGltZW91dDogeW91ciBjaGFydCBtYXkgYmUgdG9vIGNvbXBsZXggb3IgbGFyZ2UsIGFuZCBmYWlsZWQgdG8gcmVuZGVyIHdpdGhpbiB0aGUgYWxsb3R0ZWQgdGltZS5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGV4cG9ydDogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIHRoZSBQdXBwZXRlZXIgZXhwb3J0IHRpbWVcclxuICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDUsXHJcbiAgICAgICAgYFtiZW5jaG1hcmtdICR7b3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnfWAsXHJcbiAgICAgICAgYEV4cG9ydGluZyBhIGNoYXJ0IHN1Y2Vzc2Z1bGx5IHRvb2sgJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIHJlc291cmNlIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG5cclxuICAgIC8vIFVzZWQgZm9yIHN0YXRpc3RpY3MgaW4gYXZlcmFnZVRpbWUgYW5kIHByb2Nlc3NlZFdvcmtDb3VudCwgd2hpY2hcclxuICAgIC8vIGluIHR1cm4gaXMgdXNlZCBieSB0aGUgL2hlYWx0aCByb3V0ZS5cclxuICAgIGNvbnN0IHdvcmtFbmQgPSBnZXROZXdEYXRlVGltZSgpO1xyXG4gICAgY29uc3QgZXhwb3J0VGltZSA9IHdvcmtFbmQgLSB3b3JrU3RhcnQ7XHJcblxyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UgPVxyXG4gICAgICBwb29sU3RhdHMudGltZVNwZW50IC8gKytwb29sU3RhdHMuZXhwb3J0c1BlcmZvcm1lZDtcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBXb3JrIGNvbXBsZXRlZCBpbiAke2V4cG9ydFRpbWV9bXMuYCk7XHJcblxyXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICByZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICsrcG9vbFN0YXRzLmV4cG9ydHNEcm9wcGVkO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhyb3cgZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fG51bGwpfSBUaGUgY3VycmVudCBwb29sIGluc3RhbmNlIGlmIGluaXRpYWxpemVkLCBvciBudWxsXHJcbiAqIGlmIHRoZSBwb29sIGhhcyBub3QgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2woKSB7XHJcbiAgcmV0dXJuIHBvb2w7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBzdGF0aXN0aWMgb2YgYSBwb29sIGluc3RhY2UgYWJvdXQgZXhwb3J0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xTdGF0c1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY3VycmVudCBwb29sIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbFN0YXRzKCkge1xyXG4gIHJldHVybiBwb29sU3RhdHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgcG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdCwgaW5jbHVkaW5nIG1pbmltdW0gYW5kIG1heGltdW1cclxuICogd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OKCkge1xyXG4gIHJldHVybiB7XHJcbiAgICBtaW46IHBvb2wubWluLFxyXG4gICAgbWF4OiBwb29sLm1heCxcclxuICAgIHVzZWQ6IHBvb2wubnVtVXNlZCgpLFxyXG4gICAgYXZhaWxhYmxlOiBwb29sLm51bUZyZWUoKSxcclxuICAgIGFsbENyZWF0ZWQ6IHBvb2wubnVtVXNlZCgpICsgcG9vbC5udW1GcmVlKCksXHJcbiAgICBwZW5kaW5nQWNxdWlyZXM6IHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCksXHJcbiAgICBwZW5kaW5nQ3JlYXRlczogcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zOiBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzOiBwb29sLnBlbmRpbmdEZXN0cm95cy5sZW5ndGgsXHJcbiAgICBhYnNvbHV0ZUFsbDpcclxuICAgICAgcG9vbC5udW1Vc2VkKCkgK1xyXG4gICAgICBwb29sLm51bUZyZWUoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdDcmVhdGVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpICtcclxuICAgICAgcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoXHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHBvb2wsIGluY2x1ZGluZyB0aGUgbWluaW11bVxyXG4gKiBhbmQgbWF4aW11bSB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmVcclxuICogcmVxdWVzdHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sSW5mb1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvKCkge1xyXG4gIGNvbnN0IHtcclxuICAgIG1pbixcclxuICAgIG1heCxcclxuICAgIHVzZWQsXHJcbiAgICBhdmFpbGFibGUsXHJcbiAgICBhbGxDcmVhdGVkLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXMsXHJcbiAgICBwZW5kaW5nVmFsaWRhdGlvbnMsXHJcbiAgICBwZW5kaW5nRGVzdHJveXMsXHJcbiAgICBhYnNvbHV0ZUFsbFxyXG4gIH0gPSBnZXRQb29sSW5mb0pTT04oKTtcclxuXHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1pbmltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWlufS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttYXh9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgdXNlZCByZXNvdXJjZXM6ICR7dXNlZH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBmcmVlIHJlc291cmNlczogJHthdmFpbGFibGV9LmApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIGNyZWF0ZWQgKHVzZWQgYW5kIGZyZWUpIHJlc291cmNlczogJHthbGxDcmVhdGVkfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGFjcXVpcmVkOiAke3BlbmRpbmdBY3F1aXJlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBjcmVhdGVkOiAke3BlbmRpbmdDcmVhdGVzfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIHZhbGlkYXRlZDogJHtwZW5kaW5nVmFsaWRhdGlvbnN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgZGVzdHJveWVkOiAke3BlbmRpbmdEZXN0cm95c30uYFxyXG4gICk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgcmVzb3VyY2VzOiAke2Fic29sdXRlQWxsfS5gKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZhY3RvcnkgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGFuIG9iamVjdCB3aXRoIGBjcmVhdGVgLCBgdmFsaWRhdGVgLFxyXG4gKiBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9ucyBmb3IgdGhlIHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmFjdG9yeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9mYWN0b3J5KHBvb2xPcHRpb25zKSB7XHJcbiAgcmV0dXJuIHtcclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyB3b3JrZXIgcGFnZSBmb3IgdGhlIGV4cG9ydCBwb29sLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGNyZWF0ZVxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdFxyXG4gICAgICogY29udGFpbmluZyB0aGUgd29ya2VyIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgaW5pdGlhbFxyXG4gICAgICogd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gZXJyb3IgZHVyaW5nXHJcbiAgICAgKiB0aGUgY3JlYXRpb24gb2YgdGhlIG5ldyBwYWdlLlxyXG4gICAgICovXHJcbiAgICBjcmVhdGU6IGFzeW5jICgpID0+IHtcclxuICAgICAgLy8gSW5pdCB0aGUgcmVzb3VyY2Ugd2l0aCB1bmlxdWUgaWQgYW5kIHdvcmsgY291bnRcclxuICAgICAgY29uc3QgcG9vbFJlc291cmNlID0ge1xyXG4gICAgICAgIGlkOiB1dWlkKCksXHJcbiAgICAgICAgLy8gVHJ5IHRvIGRpc3RyaWJ1dGUgdGhlIGluaXRpYWwgd29yayBjb3VudFxyXG4gICAgICAgIHdvcmtDb3VudDogTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogKHBvb2xPcHRpb25zLndvcmtMaW1pdCAvIDIpKVxyXG4gICAgICB9O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBTdGFydCBtZWFzdXJpbmcgYSBwYWdlIGNyZWF0aW9uIHRpbWVcclxuICAgICAgICBjb25zdCBzdGFydERhdGUgPSBnZXROZXdEYXRlVGltZSgpO1xyXG5cclxuICAgICAgICAvLyBDcmVhdGUgYSBuZXcgcGFnZVxyXG4gICAgICAgIGF3YWl0IG5ld1BhZ2UocG9vbFJlc291cmNlKTtcclxuXHJcbiAgICAgICAgLy8gTWVhc3VyZSB0aGUgdGltZSBvZiBmdWxsIGNyZWF0aW9uIGFuZCBjb25maWd1cmF0aW9uIG9mIGEgcGFnZVxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBhIHdvcmtlciwgdG9vayAke1xyXG4gICAgICAgICAgICBnZXROZXdEYXRlVGltZSgpIC0gc3RhcnREYXRlXHJcbiAgICAgICAgICB9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHVybiByZWFkeSBwb29sIHJlc291cmNlXHJcbiAgICAgICAgcmV0dXJuIHBvb2xSZXNvdXJjZTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBjcmVhdGluZyBhIG5ldyBwYWdlLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcclxuICAgICAqIHRoZSB3b3JrIGxpbWl0LlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIHZhbGlkYXRlXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHBvb2xSZXNvdXJjZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAgICogdGhlIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBpZiB0aGUgd29ya2VyXHJcbiAgICAgKiBpcyB2YWxpZCBhbmQgd2l0aGluIHRoZSB3b3JrIGxpbWl0OyBvdGhlcndpc2UsIHRvIGZhbHNlLlxyXG4gICAgICovXHJcbiAgICB2YWxpZGF0ZTogYXN5bmMgKHBvb2xSZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJbiBjZXJ0YWluIGNhc2VzIGFjcXVpcmluZyB0aHJvd3MgYSBUYXJnZXRDbG9zZUVycm9yLCB3aGljaCBtYXlcclxuICAgICAgLy8gYmUgY2F1c2VkIGJ5IHR3byB0aGluZ3M6XHJcbiAgICAgIC8vIC0gVGhlIHBhZ2UgaXMgY2xvc2VkIGFuZCBhdHRlbXB0ZWQgdG8gYmUgcmV1c2VkLlxyXG4gICAgICAvLyAtIExvc3QgY29udGFjdCB3aXRoIHRoZSBicm93c2VyLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBXaGF0IHdlJ3JlIHNlZWluZyBpbiBsb2dzIGlzIHRoYXQgc3VjY2Vzc2l2ZSBleHBvcnRzIHR5cGljYWxseVxyXG4gICAgICAvLyBzdWNjZWVkcywgYW5kIHRoZSBzZXJ2ZXIgcmVjb3ZlcnMsIGluZGljYXRpbmcgdGhhdCBpdCdzIGxpa2VseVxyXG4gICAgICAvLyB0aGUgZmlyc3QgY2FzZS4gVGhpcyBpcyBhbiBhdHRlbXB0IGF0IGFsbGlldmF0aW5nIHRoZSBpc3N1ZSBieVxyXG4gICAgICAvLyBzaW1wbHkgbm90IHZhbGlkYXRpbmcgdGhlIHdvcmtlciBpZiB0aGUgcGFnZSBpcyBudWxsIG9yIGNsb3NlZC5cclxuICAgICAgLy9cclxuICAgICAgLy8gVGhlIGFjdHVhbCByZXN1bHQgZnJvbSB3aGVuIHRoaXMgaGFwcGVuZWQsIHdhcyB0aGF0IGEgd29ya2VyIHdvdWxkXHJcbiAgICAgIC8vIGJlIGNvbXBsZXRlbHkgbG9ja2VkLCBzdG9wcGluZyBpdCBmcm9tIGJlaW5nIGFjcXVpcmVkIHVudGlsXHJcbiAgICAgIC8vIGl0cyB3b3JrIGNvdW50IHJlYWNoZWQgdGhlIGxpbWl0LlxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBwYWdlYCBpcyB2YWxpZFxyXG4gICAgICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChubyB2YWxpZCBwYWdlIGlzIGZvdW5kKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgY2xvc2VkXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlIGlzIGNsb3NlZCBvciBpbnZhbGlkKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgbWFpbkZyYW1lYCBpcyBkZXRhY2hlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UubWFpbkZyYW1lKCkuZGV0YWNoZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKHBhZ2UncyBmcmFtZSBpcyBkZXRhY2hlZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHdvcmtMaW1pdGAgaXMgZXhjZWVkZWRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHBvb2xPcHRpb25zLndvcmtMaW1pdCAmJlxyXG4gICAgICAgICsrcG9vbFJlc291cmNlLndvcmtDb3VudCA+IHBvb2xPcHRpb25zLndvcmtMaW1pdFxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKGV4Y2VlZGVkIHRoZSAke3Bvb2xPcHRpb25zLndvcmtMaW1pdH0gd29ya3MgcGVyIHJlc291cmNlIGxpbWl0KS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSBgcG9vbFJlc291cmNlYCBpcyB2YWxpZGF0ZWRcclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gZGVzdHJveVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKi9cclxuICAgIGRlc3Ryb3k6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRGVzdHJveWluZyBhIHdvcmtlci5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgLy8gUmVtb3ZlIGFsbCBhdHRhY2hlZCBldmVudCBsaXN0ZW5lcnMgZnJvbSB0aGUgcmVzb3VyY2VcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygncGFnZWVycm9yJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2NvbnNvbGUnKTtcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygnZnJhbWVkZXRhY2hlZCcpO1xyXG5cclxuICAgICAgICAgIC8vIFdlIG5lZWQgdG8gd2FpdCBhcm91bmQgZm9yIHRoaXNcclxuICAgICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gUGFnZSBjb3VsZCBub3QgYmUgY2xvc2VkIHVwb24gZGVzdHJveWluZy5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGluaXRQb29sLFxyXG4gIGtpbGxQb29sLFxyXG4gIHBvc3RXb3JrLFxyXG4gIGdldFBvb2wsXHJcbiAgZ2V0UG9vbFN0YXRzLFxyXG4gIGdldFBvb2xJbmZvLFxyXG4gIGdldFBvb2xJbmZvSlNPTlxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVXNlZCB0byBzYW5pdGl6ZSB0aGUgc3RyaW5ncyBjb21pbmcgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZVxyXG4gKiB0byBwcmV2ZW50IFhTUyBhdHRhY2tzICh3aXRoIHRoZSBET01QdXJpZnkgbGlicmFyeSkuXHJcbiAqL1xyXG5cclxuaW1wb3J0IERPTVB1cmlmeSBmcm9tICdkb21wdXJpZnknO1xyXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcclxuXHJcbi8qKlxyXG4gKiBTYW5pdGl6ZXMgYSBnaXZlbiBIVE1MIHN0cmluZyBieSByZW1vdmluZyA8c2NyaXB0PiB0YWdzLiBUaGlzIGZ1bmN0aW9uIHVzZXNcclxuICogYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gZmluZCBhbmQgcmVtb3ZlIGFsbCBvY2N1cnJlbmNlcyBvZiA8c2NyaXB0Pjwvc2NyaXB0PlxyXG4gKiB0YWdzIGFuZCBhbnkgY29udGVudCB3aXRoaW4gdGhlbS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNhbml0aXplXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCAtIFRoZSBIVE1MIHN0cmluZyB0byBiZSBzYW5pdGl6ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTCBzdHJpbmcuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXQpIHtcclxuICAvLyBHZXQgdGhlIHZpcnR1YWwgRE9NXHJcbiAgY29uc3Qgd2luZG93ID0gbmV3IEpTRE9NKCcnKS53aW5kb3c7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHB1cmlmeWluZyBpbnN0YW5jZVxyXG4gIGNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cpO1xyXG5cclxuICAvLyBSZXR1cm4gc2FuaXRpemVkIGlucHV0XHJcbiAgcmV0dXJuIHB1cmlmeS5zYW5pdGl6ZShpbnB1dCwgeyBBRERfVEFHUzogWydmb3JlaWduT2JqZWN0J10gfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzYW5pdGl6ZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIHRvIHByZXBhcmUgZm9yIHRoZSBleHBvcnRpbmcgY2hhcnRzXHJcbiAqIGludG8gdmFyaW91cyBpbWFnZSBvdXRwdXQgZm9ybWF0cyBzdWNoIGFzIEpQRUcsIFBORywgUERGLCBhbmQgU1ZHcy5cclxuICogSXQgc3VwcG9ydHMgc2luZ2xlIGFuZCBiYXRjaCBleHBvcnQgb3BlcmF0aW9ucyBhbmQgYWxsb3dzIGN1c3RvbWl6YXRpb25cclxuICogdGhyb3VnaCBvcHRpb25zIHBhc3NlZCBmcm9tIGNvbmZpZ3VyYXRpb25zIG9yIEFQSXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnLCB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBpc09iamVjdCxcclxuICByb3VuZE51bWJlcixcclxuICB3cmFwQXJvdW5kXHJcbn0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGdsb2JhbCBmbGFnIGZvciB0aGUgY29kZSBleGVjdXRpb24gcGVybWlzc2lvblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucyBhbmQgc2F2ZXNcclxuICogdGhlIHJlc3VsdGluZyBpbWFnZSB0byB0aGUgcHJvdmlkZWQgb3V0cHV0IGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIFRoZSBvYmplY3QgbXVzdCBjb250YWluIGF0IGxlYXN0IG9uZVxyXG4gKiBvZiB0aGUgZm9sbG93aW5nIGBleHBvcnRgIHByb3BlcnRpZXM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgXHJcbiAqIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpbmdsZUV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0KSB7XHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQoXHJcbiAgICAgIHsgZXhwb3J0OiBvcHRpb25zLmV4cG9ydCwgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWMgfSxcclxuICAgICAgYXN5bmMgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSBvbmUgb2YgdGhlIGZvbGxvd2luZyBvcHRpb25zOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIGluZm9ybWF0aW9uXHJcbiAqIHByb3ZpZGVkIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZ1xyXG4gKiBmb3JtYXQ6IFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzXHJcbiAqIGFyZSBzYXZlZCB0byB0aGUgc3BlY2lmaWVkIG91dHB1dCBmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIGZyb21cclxuICogdGhlIGBleHBvcnRgIHNlY3Rpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3NlcyBhcmUgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJhdGNoRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQgJiYgb3B0aW9ucy5leHBvcnQuYmF0Y2gpIHtcclxuICAgIC8vIEFuIGFycmF5IGZvciBjb2xsZWN0aW5nIGJhdGNoIGV4cG9ydHNcclxuICAgIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIGBiYXRjaGAgYXJndW1lbnRzXHJcbiAgICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykgfHwgW10pIHtcclxuICAgICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgaW1hZ2VPcHRpb25zYCBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXRcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgcGFydGlhbFxyXG4gKiBvcHRpb25zIGFyZSBwcm92aWRlZCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICpcclxuICogVGhlIGBlbmRDYWxsYmFja2AgZnVuY3Rpb24gaXMgaW52b2tlZCB1cG9uIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBleHBvcnQsXHJcbiAqIGVpdGhlciBzdWNjZXNzZnVsbHkgb3Igd2l0aCBhbiBlcnJvci4gVGhlIGBlcnJvcmAgb2JqZWN0IGlzIHByb3ZpZGVkXHJcbiAqIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIGNvbnRhaW5pbmdcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5XHJcbiAqIGFuZCB0aGUgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW1hZ2VPcHRpb25zIC0gVGhlIGBpbWFnZU9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkXHJcbiAqIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgdGhlIHByb3ZpZGVkXHJcbiAqIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHRoZSBleHBvcnQgcHJvY2VzcyBvciB1cG9uIGVuY291bnRlcmluZyBhbiBlcnJvci4gVGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGlzIHRoZSBgZXJyb3JgIG9iamVjdCwgYW5kIHRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIGBkYXRhYCBvYmplY3QsXHJcbiAqIHdoaWNoIGluY2x1ZGVzIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YFxyXG4gKiBwcm9wZXJ0eSBhbmQgdGhlIGZ1bGwgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGltYWdlT3B0aW9ucywgZW5kQ2FsbGJhY2spIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgb3B0aW9ucyBhcmUgaW4gYW4gb2JqZWN0XHJcbiAgICBpZiAoIWlzT2JqZWN0KGltYWdlT3B0aW9ucykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgcHJvdmlkZWQgYGltYWdlT3B0aW9uc2AuIE5lZWRzIHRvIGJlIGFuIG9iamVjdC4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE1lcmdlIGFkZGl0aW9uYWwgb3B0aW9ucyB0byB0aGUgY29weSBvZiB0aGUgaW5zdGFuY2Ugb3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoXHJcbiAgICAgIHtcclxuICAgICAgICBleHBvcnQ6IGltYWdlT3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgY3VzdG9tTG9naWM6IGltYWdlT3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICByZXR1cm4gcG9zdFdvcmsob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBmb3IgY2hhcnQgZXhwb3J0cyBiYXNlZFxyXG4gKiBvbiB0aGUgcHJvdmlkZWQgZXhwb3J0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICogMS4gVGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBgc2NhbGVgIGZyb20gdGhlIGBleHBvcnRPcHRpb25zYC5cclxuICogMi4gT3B0aW9ucyBmcm9tIHRoZSBjaGFydCBjb25maWd1cmF0aW9uIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBnbG9iYWwgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgdGhlbWUgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCBzZWN0aW9ucykuXHJcbiAqIDUuIEZhbGxiYWNrIGRlZmF1bHQgdmFsdWVzIChgaGVpZ2h0ID0gNDAwYCwgYHdpZHRoID0gNjAwYCwgYHNjYWxlID0gMWApLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZpbmRDaGFydFNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb2RlIGV4ZWN1dGlvblxyXG4gKiBpcyBub3QgYWxsb3dlZCBidXQgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYXJlIHN0aWxsIHByb3ZpZGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgLy8gSW4gY2FzZSBvZiBhbGxvd2luZyBjb2RlIGV4ZWN1dGlvblxyXG4gIGlmIChhbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIC8vIFByb2Nlc3MgdGhlIGByZXNvdXJjZXNgIG9wdGlvblxyXG4gICAgaWYgKHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID09PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBDdXN0b20gc3RyaW5naWZpZWQgcmVzb3VyY2VzXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gTG9hZCB0aGUgZGVmYXVsdCBvbmVcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoJ3Jlc291cmNlcy5qc29uJyksICd1dGY4JyksXHJcbiAgICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IGByZXNvdXJjZXMuanNvbmAgZmlsZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGN1c3RvbSBjb2RlIGFuZCB3cmFwIGFyb3VuZCBpdCBpbiBhIHNlbGYgaW52b2tpbmcgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1tjaGFydF0gVGhlIGBjdXN0b21Db2RlYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gVHJ5IHRvIGxvYWQgY2FsbGJhY2sgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2ssXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY2FsbGJhY2tgIGNhbm5vdCBiZSBsb2FkZWQuJyk7XHJcblxyXG4gICAgICAvLyBJbiBjYXNlIG9mIGFuIGVycm9yLCBzZXQgdGhlIG9wdGlvbiB3aXRoIG51bGxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGN1c3RvbUNvZGVgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGN1c3RvbUNvZGVgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGNhbGxiYWNrYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgY2FsbGJhY2tgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHJlc291cmNlc2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gSWYgdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIGZsYWcgaXMgc2V0IHRvIGZhbHNlLCB3ZSBzaG91bGQgcmVmdXNlXHJcbiAgICAvLyB0aGUgdXNhZ2Ugb2YgdGhlIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgLCBhbmQgYGN1c3RvbUNvZGVgIG9wdGlvbnMuXHJcbiAgICAvLyBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbCByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LlxyXG4gICAgaWYgKFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFJlc2V0IGFsbCBjdXN0b20gY29kZSBvcHRpb25zXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcblxyXG4gICAgICAvLyBTZW5kIGEgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydCB0aGVzZSBzZXR0aW5nc1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnLCBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gLFxyXG4gICAgICAgIDQwM1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZnJvbSB0aGUgYHJlc291cmNlc2Agb3B0aW9uIGZvciBleHBvcnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFtyZXNvdXJjZXM9bnVsbF0gLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuXHJcbiAqIENhbiBiZSBlaXRoZXIgYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiwgYSBwYXRoIHRvIGEgSlNPTiBmaWxlLFxyXG4gKiBvciBudWxsLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgbnVsbGAuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2FkaW5nXHJcbiAqIHJlc291cmNlcyBmcm9tIGZpbGVzIGlzIGFsbG93ZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGhhbmRsZWQgcmVzb3VyY2VzIG9yIG51bGwgaWYgbm8gdmFsaWQgcmVzb3VyY2VzXHJcbiAqIGFyZSBmb3VuZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgcmVzb3VyY2VzID0gbnVsbCxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIExpc3Qgb2YgYWxsb3dlZCBzZWN0aW9ucyBpbiB0aGUgcmVzb3VyY2VzIEpTT05cclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKHJlc291cmNlcyksICd1dGY4JyksXHJcbiAgICAgICAgZmFsc2UsXHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxyXG4gICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhyZXNvdXJjZXMsIGZhbHNlLCBhbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIGZpbGVzIHNlY3Rpb25cclxuICAgIGlmIChoYW5kbGVkUmVzb3VyY2VzICYmICFhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaWx0ZXIgZnJvbSB1bm5lY2Vzc2FyeSBwcm9wZXJ0aWVzXHJcbiAgZm9yIChjb25zdCBwcm9wTmFtZSBpbiBoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXNbcHJvcE5hbWVdO1xyXG4gICAgfSBlbHNlIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiBhbGxvd2VkIHByb3BlcnRpZXMgaXMgcHJlc2VudFxyXG4gIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgbG9hZGluZyBhbmQgdmFsaWRhdGlvbiBvZiB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYFxyXG4gKiBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIElmIHRoZSBvcHRpb24gaXMgYSBzdHJpbmcgYW5kIHJlZmVyZW5jZXMgYSBKU09OIGZpbGVcclxuICogKHdoZW4gdGhlIGBhbGxvd0ZpbGVSZXNvdXJjZXNgIGlzIHRydWUpLCBpdCByZWFkcyBhbmQgcGFyc2VzIHRoZSBmaWxlLlxyXG4gKiBPdGhlcndpc2UsIGl0IGF0dGVtcHRzIHRvIHBhcnNlIHRoZSBzdHJpbmcgb3Igb2JqZWN0IGFzIEpTT04uIElmIGFueSBlcnJvcnNcclxuICogb2NjdXIgZHVyaW5nIHRoaXMgcHJvY2VzcywgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC4gSWYgdGhlcmUgaXMgYW4gZXJyb3JcclxuICogbG9hZGluZyBvciBwYXJzaW5nIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3IgYHRoZW1lT3B0aW9uc2AsIHRoZSBlcnJvciBpcyBsb2dnZWRcclxuICogYW5kIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBlbmFibGluZyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzcGVjaWZpZWQgRXhwcmVzcyBhcHAuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXRcclxuICogdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCByYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWQgYW5kIHRoZSBhcHAgZXhpc3RzXHJcbiAgICBpZiAoYXBwICYmIHJhdGVMaW1pdGluZ09wdGlvbnMuZW5hYmxlKSB7XHJcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPVxyXG4gICAgICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgICAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICAgICAgd2luZG93OiByYXRlTGltaXRpbmdPcHRpb25zLndpbmRvdyB8fCAxLFxyXG4gICAgICAgIG1heFJlcXVlc3RzOiByYXRlTGltaXRpbmdPcHRpb25zLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgICAgIGRlbGF5OiByYXRlTGltaXRpbmdPcHRpb25zLmRlbGF5IHx8IDAsXHJcbiAgICAgICAgdHJ1c3RQcm94eTogcmF0ZUxpbWl0aW5nT3B0aW9ucy50cnVzdFByb3h5IHx8IGZhbHNlLFxyXG4gICAgICAgIHNraXBLZXk6IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcEtleSB8fCBudWxsLFxyXG4gICAgICAgIHNraXBUb2tlbjogcmF0ZUxpbWl0aW5nT3B0aW9ucy5za2lwVG9rZW4gfHwgbnVsbFxyXG4gICAgICB9O1xyXG5cclxuICAgICAgLy8gU2V0IGlmIGJlaGluZCBhIHByb3h5XHJcbiAgICAgIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICAgICAgYXBwLmVuYWJsZSgndHJ1c3QgcHJveHknKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gICAgICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcclxuICAgICAgICAvLyBUaW1lIGZyYW1lIGZvciB3aGljaCByZXF1ZXN0cyBhcmUgY2hlY2tlZCBhbmQgcmVtZW1iZXJlZFxyXG4gICAgICAgIHdpbmRvd01zOiByYXRlT3B0aW9ucy53aW5kb3cgKiA2MCAqIDEwMDAsXHJcbiAgICAgICAgLy8gTGltaXQgZWFjaCBJUCB0byAxMDAgcmVxdWVzdHMgcGVyIGB3aW5kb3dNc2BcclxuICAgICAgICBsaW1pdDogcmF0ZU9wdGlvbnMubWF4UmVxdWVzdHMsXHJcbiAgICAgICAgLy8gRGlzYWJsZSBkZWxheWluZywgZnVsbCBzcGVlZCB1bnRpbCB0aGUgbWF4IGxpbWl0IGlzIHJlYWNoZWRcclxuICAgICAgICBkZWxheU1zOiByYXRlT3B0aW9ucy5kZWxheSxcclxuICAgICAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlLmZvcm1hdCh7XHJcbiAgICAgICAgICAgIGpzb246ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZSB9KTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgICAgIC8vIEFsbG93IGJ5cGFzc2luZyB0aGUgbGltaXRlciBpZiBhIHZhbGlkIGtleS90b2tlbiBoYXMgYmVlbiBzZW50XHJcbiAgICAgICAgICBpZiAoXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IG51bGwgJiZcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkuYWNjZXNzX3Rva2VuID09PSByYXRlT3B0aW9ucy5za2lwVG9rZW5cclxuICAgICAgICAgICkge1xyXG4gICAgICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcblxyXG4gICAgICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gICAgICBhcHAudXNlKGxpbWl0ZXIpO1xyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtyYXRlIGxpbWl0aW5nXSBFbmFibGVkIHJhdGUgbGltaXRpbmcgd2l0aCAke3JhdGVPcHRpb25zLm1heFJlcXVlc3RzfSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcmF0ZSBsaW1pdGluZ10gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciB2YWxpZGF0aW5nIGluY29taW5nIEhUVFAgcmVxdWVzdHNcclxuICogaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi4gVGhpcyBtb2R1bGUgZW5zdXJlcyB0aGF0IHJlcXVlc3RzIGNvbnRhaW5cclxuICogYXBwcm9wcmlhdGUgY29udGVudCB0eXBlcyBhbmQgdmFsaWQgcmVxdWVzdCBib2RpZXMsIGluY2x1ZGluZyBwcm9wZXIgSlNPTlxyXG4gKiBzdHJ1Y3R1cmVzIGFuZCBjaGFydCBkYXRhIGZvciBleHBvcnRzLiBJdCBjaGVja3MgZm9yIHBvdGVudGlhbCBpc3N1ZXMgc3VjaFxyXG4gKiBhcyBtaXNzaW5nIG9yIG1hbGZvcm1lZCBkYXRhLCBwcml2YXRlIHJhbmdlIFVSTHMgaW4gU1ZHIHBheWxvYWRzLCBhbmQgYWxsb3dzXHJcbiAqIGZvciBmbGV4aWJsZSBvcHRpb25zIHZhbGlkYXRpb24uIFRoZSBtaWRkbGV3YXJlIGxvZ3MgZGV0YWlsZWQgaW5mb3JtYXRpb25cclxuICogYW5kIGhhbmRsZXMgZXJyb3JzIHJlbGF0ZWQgdG8gaW5jb3JyZWN0IHBheWxvYWRzLCBjaGFydCBkYXRhLCBhbmQgcHJpdmF0ZSBVUkxcclxuICogdXNhZ2UuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uIH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpc09iamVjdEVtcHR5LCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgY29udGVudC10eXBlIGhlYWRlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBjb250ZW50LXR5cGVcclxuICogaXMgbm90IGNvcnJlY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSBjb250ZW50IHR5cGUgaGVhZGVyXHJcbiAgICBjb25zdCBjb250ZW50VHlwZSA9IHJlcXVlc3QuaGVhZGVyc1snY29udGVudC10eXBlJ10gfHwgJyc7XHJcblxyXG4gICAgLy8gQWxsb3cgb25seSBKU09OLCBVUkwtZW5jb2RlZCBhbmQgZm9ybSBkYXRhIHdpdGhvdXQgZmlsZXMgdHlwZXMgb2YgZGF0YVxyXG4gICAgaWYgKFxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL2pzb24nKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnbXVsdGlwYXJ0L2Zvcm0tZGF0YScpXHJcbiAgICApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbdmFsaWRhdGlvbl0gQ29udGVudC1UeXBlIG11c3QgYmUgYXBwbGljYXRpb24vanNvbiwgYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkLCBvciBtdWx0aXBhcnQvZm9ybS1kYXRhLicsXHJcbiAgICAgICAgNDE1XHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYHJlcXVlc3RCb2R5TWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIHJlcXVlc3QncyBib2R5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGJvZHkgaXMgbm90IGNvcnJlY3QuXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY2hhcnQgZGF0YSBmcm9tIHRoZSBib2R5XHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaW4gY2FzZSBvZiB0aGUgcHJpdmF0ZSByYW5nZVxyXG4gKiB1cmwgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGJvZHlcclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHV1aWQoKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgZW1wdHkuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGJvZHkgaXMgcmVxdWlyZWQuIFBsZWFzZSBlbnN1cmUgdGhhdCB5b3VyIENvbnRlbnQtVHlwZSBoZWFkZXIgaXMgY29ycmVjdC4gQWNjZXB0ZWQgdHlwZXMgYXJlICdhcHBsaWNhdGlvbi9qc29uJyBhbmQgJ211bHRpcGFydC9mb3JtLWRhdGEnLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uIGZvciB0aGUgc2VydmVyXHJcbiAgICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb24gPSBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKTtcclxuXHJcbiAgICAvLyBGaW5kIGEgY29ycmVjdCBjaGFydCBvcHRpb25zXHJcbiAgICBjb25zdCBpbnN0ciA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgLy8gVXNlIG9uZSBvZiB0aGUgYmVsb3dcclxuICAgICAgYm9keS5pbnN0ciB8fCBib2R5Lm9wdGlvbnMgfHwgYm9keS5pbmZpbGUgfHwgYm9keS5kYXRhLFxyXG4gICAgICAvLyBTdHJpbmdpZnkgb3B0aW9uc1xyXG4gICAgICB0cnVlLFxyXG4gICAgICAvLyBBbGxvdyBvciBkaXNhbGxvdyBmdW5jdGlvbnNcclxuICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRoZXJlIGlzIG5vIGNvcnJlY3QgY2hhcnQgZGF0YVxyXG4gICAgaWYgKGluc3RyID09PSBudWxsICYmICFib2R5LnN2Zykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIG1pc3NpbmcgY29ycmVjdCBjaGFydCBkYXRhIGZvciBleHBvcnQ6ICR7SlNPTi5zdHJpbmdpZnkoYm9keSl9LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGVzdCBvZiB4bGluazpocmVmIGVsZW1lbnRzIGZyb20gcGF5bG9hZCdzIFNWRyBmYWlsc1xyXG4gICAgaWYgKGJvZHkuc3ZnICYmIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoYm9keS5zdmcpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBTVkcgcG90ZW50aWFsbHkgY29udGFpbiBhdCBsZWFzdCBvbmUgZm9yYmlkZGVuIFVSTCBpbiAneGxpbms6aHJlZicgZWxlbWVudC4gUGxlYXNlIHJldmlldyB0aGUgU1ZHIGNvbnRlbnQgYW5kIGVuc3VyZSB0aGF0IGFsbCByZWZlcmVuY2VkIFVSTHMgY29tcGx5IHdpdGggc2VjdXJpdHkgcG9saWNpZXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3Qgb3B0aW9ucyBhbmQgc3RvcmUgcGFyc2VkIHN0cnVjdHVyZSBpbiB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC52YWxpZGF0ZWRPcHRpb25zID0ge1xyXG4gICAgICAvLyBTZXQgdGhlIGNyZWF0ZWQgSUQgYXMgYSBgcmVxdWVzdElkYCBwcm9wZXJ0eSBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICByZXF1ZXN0SWQsXHJcbiAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIHN2ZzogYm9keS5zdmcsXHJcbiAgICAgICAgb3V0ZmlsZTpcclxuICAgICAgICAgIGJvZHkub3V0ZmlsZSB8fFxyXG4gICAgICAgICAgYCR7cmVxdWVzdC5wYXJhbXMuZmlsZW5hbWUgfHwgJ2NoYXJ0J30uJHtib2R5LnR5cGUgfHwgJ3BuZyd9YCxcclxuICAgICAgICB0eXBlOiBib2R5LnR5cGUsXHJcbiAgICAgICAgY29uc3RyOiBib2R5LmNvbnN0cixcclxuICAgICAgICBiNjQ6IGJvZHkuYjY0LFxyXG4gICAgICAgIG5vRG93bmxvYWQ6IGJvZHkubm9Eb3dubG9hZCxcclxuICAgICAgICBoZWlnaHQ6IGJvZHkuaGVpZ2h0LFxyXG4gICAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxyXG4gICAgICAgIHNjYWxlOiBib2R5LnNjYWxlLFxyXG4gICAgICAgIGdsb2JhbE9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkuZ2xvYmFsT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApLFxyXG4gICAgICAgIHRoZW1lT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS50aGVtZU9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKVxyXG4gICAgICB9LFxyXG4gICAgICBjdXN0b21Mb2dpYzoge1xyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvbixcclxuICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IGZhbHNlLFxyXG4gICAgICAgIGN1c3RvbUNvZGU6IGJvZHkuY3VzdG9tQ29kZSxcclxuICAgICAgICBjYWxsYmFjazogYm9keS5jYWxsYmFjayxcclxuICAgICAgICByZXNvdXJjZXM6IGlzQWxsb3dlZENvbmZpZyhib2R5LnJlc291cmNlcywgdHJ1ZSwgYWxsb3dDb2RlRXhlY3V0aW9uKVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIC8vIENhbGwgdGhlIG5leHQgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApIHtcclxuICAvLyBBZGQgY29udGVudCB0eXBlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIGNvbnRlbnRUeXBlTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCByZXF1ZXN0IGJvZHkgcmVxdWVzdCB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCByZXF1ZXN0Qm9keU1pZGRsZXdhcmUpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIHRoZSBleHBvcnQgcm91dGVzIGFuZCBsb2dpYyBmb3IgaGFuZGxpbmcgY2hhcnQgZXhwb3J0XHJcbiAqIHJlcXVlc3RzIGluIGFuIEV4cHJlc3Mgc2VydmVyLiBUaGlzIG1vZHVsZSBwcm9jZXNzZXMgaW5jb21pbmcgcmVxdWVzdHNcclxuICogdG8gZXhwb3J0IGNoYXJ0cyBpbiB2YXJpb3VzIGZvcm1hdHMgKGUuZy4gSlBFRywgUE5HLCBQREYsIFNWRykuIEl0IGludGVncmF0ZXNcclxuICogd2l0aCBIaWdoY2hhcnRzJyBjb3JlIGZ1bmN0aW9uYWxpdGllcyBhbmQgc3VwcG9ydHMgYm90aCBpbW1lZGlhdGUgZG93bmxvYWRcclxuICogcmVzcG9uc2VzIGFuZCBCYXNlNjQtZW5jb2RlZCBjb250ZW50IHJldHVybnMuIFRoZSBjb2RlIGFsc28gZmVhdHVyZXNcclxuICogYmVuY2htYXJraW5nIGZvciBwZXJmb3JtYW5jZSBtb25pdG9yaW5nLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHN0YXJ0RXhwb3J0IH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRCYXNlNjQsIG1lYXN1cmVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBSZXZlcnNlZCBNSU1FIHR5cGVzXHJcbmNvbnN0IHJldmVyc2VkTWltZSA9IHtcclxuICBwbmc6ICdpbWFnZS9wbmcnLFxyXG4gIGpwZWc6ICdpbWFnZS9qcGVnJyxcclxuICBnaWY6ICdpbWFnZS9naWYnLFxyXG4gIHBkZjogJ2FwcGxpY2F0aW9uL3BkZicsXHJcbiAgc3ZnOiAnaW1hZ2Uvc3ZnK3htbCdcclxufTtcclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleHBvcnQgcmVxdWVzdHMgZnJvbSB0aGUgY2xpZW50LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnQocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gU3RhcnQgY291bnRpbmcgdGltZSBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gSW4gY2FzZSB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQsIGZvcmNlIHRvIGFib3J0IGZ1cnRoZXIgYWN0aW9uc1xyXG4gICAgbGV0IGNvbm5lY3Rpb25BYm9ydGVkID0gZmFsc2U7XHJcbiAgICByZXF1ZXN0LnNvY2tldC5vbignY2xvc2UnLCAoaGFkRXJyb3JzKSA9PiB7XHJcbiAgICAgIGlmIChoYWRFcnJvcnMpIHtcclxuICAgICAgICBjb25uZWN0aW9uQWJvcnRlZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgb3B0aW9ucyBwcmV2aW91c2x5IHZhbGlkYXRlZCBpbiB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucztcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3QgaWRcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHJlcXVlc3RPcHRpb25zLnJlcXVlc3RJZDtcclxuXHJcbiAgICAvLyBJbmZvIGFib3V0IGFuIGluY29taW5nIHJlcXVlc3Qgd2l0aCBjb3JyZWN0IGRhdGFcclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3QuYCk7XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChyZXF1ZXN0T3B0aW9ucywgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgIC8vIFJlbW92ZSB0aGUgY2xvc2UgZXZlbnQgZnJvbSB0aGUgc29ja2V0XHJcbiAgICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBjb25uZWN0aW9uIHdhcyBjbG9zZWQsIGRvIG5vdGhpbmdcclxuICAgICAgaWYgKGNvbm5lY3Rpb25BYm9ydGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gYmVmb3JlIHRoZSBjaGFydCBmaW5pc2hlZCBwcm9jZXNzaW5nLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZXJyb3IsIGxvZyBpdCBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCBsb2cgdGhlIG1lc3NhZ2UgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKCFkYXRhIHx8ICFkYXRhLnJlc3VsdCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8XHJcbiAgICAgICAgICAgIHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHJlc3VsdCBpcyAke2RhdGEucmVzdWx0fS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBVbmV4cGVjdGVkIHJldHVybiBvZiB0aGUgZXhwb3J0IHJlc3VsdCBmcm9tIHRoZSBjaGFydCBnZW5lcmF0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciByZXF1ZXN0IGRhdGEuYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFJldHVybiB0aGUgcmVzdWx0IGluIGFuIGFwcHJvcHJpYXRlIGZvcm1hdFxyXG4gICAgICBpZiAoZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3MgdG9vayAke3JlcXVlc3RDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYHR5cGVgLCBgYjY0YCwgYG5vRG93bmxvYWRgLCBhbmQgYG91dGZpbGVgIGZyb20gb3B0aW9uc1xyXG4gICAgICAgIGNvbnN0IHsgdHlwZSwgYjY0LCBub0Rvd25sb2FkLCBvdXRmaWxlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBJZiBvbmx5IEJhc2U2NCBpcyByZXF1aXJlZCwgcmV0dXJuIGl0XHJcbiAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTZXQgY29ycmVjdCBjb250ZW50IHR5cGVcclxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XHJcblxyXG4gICAgICAgIC8vIERlY2lkZSB3aGV0aGVyIHRvIGRvd25sb2FkIG9yIG5vdCBjaGFydCBmaWxlXHJcbiAgICAgICAgaWYgKCFub0Rvd25sb2FkKSB7XHJcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KG91dGZpbGUpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgU1ZHLCByZXR1cm4gcGxhaW4gY29udGVudCwgb3RoZXJ3aXNlIGEgYjY0IHN0cmluZyBmcm9tIGEgYnVmZmVyXHJcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXHJcbiAgICAgICAgICA/IHJlc3BvbnNlLnNlbmQoZGF0YS5yZXN1bHQpXHJcbiAgICAgICAgICA6IHJlc3BvbnNlLnNlbmQoQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYGV4cG9ydGAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZXhwb3J0Um91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gZXhwb3J0Um91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy8nIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyBhdCB0aGUgcm9vdFxyXG4gICAqIGVuZHBvaW50LlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvJywgcmVxdWVzdEV4cG9ydCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy86ZmlsZW5hbWUnIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyB3aXRoXHJcbiAgICogYSBzcGVjaWZpZWQgZmlsZW5hbWUgcGFyYW1ldGVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgcmVxdWVzdEV4cG9ydCk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmVyIGhlYWx0aCBtb25pdG9yaW5nLCBpbmNsdWRpbmdcclxuICogdXB0aW1lLCBzdWNjZXNzIHJhdGVzLCBhbmQgb3RoZXIgc2VydmVyIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBnZXRQb29sSW5mb0pTT04gfSBmcm9tICcuLi8uLi9wb29sLmpzJztcclxuaW1wb3J0IHsgYWRkVGltZXIgfSBmcm9tICcuLi8uLi90aW1lci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0TmV3RGF0ZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vLyBTZXQgdGhlIHN0YXJ0IGRhdGUgb2YgdGhlIHNlcnZlclxyXG5jb25zdCBzZXJ2ZXJTdGFydFRpbWUgPSBuZXcgRGF0ZSgpO1xyXG5cclxuLy8gR2V0IHRoZSBgcGFja2FnZS5qc29uYCBjb250ZW50XHJcbmNvbnN0IHBhY2thZ2VGaWxlID0gSlNPTi5wYXJzZShcclxuICByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICdwYWNrYWdlLmpzb24nKSwgJ3V0ZjgnKVxyXG4pO1xyXG5cclxuLy8gQW4gYXJyYXkgZm9yIHN1Y2Nlc3MgcmF0ZSByYXRpb3NcclxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XHJcblxyXG4vLyBSZWNvcmQgZXZlcnkgbWludXRlXHJcbmNvbnN0IHJlY29yZEludGVydmFsID0gNjAgKiAxMDAwO1xyXG5cclxuLy8gMzAgbWludXRlc1xyXG5jb25zdCB3aW5kb3dTaXplID0gMzA7XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgYHN1Y2Nlc3NSYXRlc2BcclxuICogYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBBIG1vdmluZyBhdmVyYWdlIGZvciBzdWNjZXNzIHJhdGlvIG9mIHRoZSBzZXJ2ZXIgZXhwb3J0cy5cclxuICovXHJcbmZ1bmN0aW9uIF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCkge1xyXG4gIHJldHVybiBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIHRoZSBpbnRlcnZhbCByZXNwb25zaWJsZSBmb3IgY2FsY3VsYXRpbmcgY3VycmVudCBzdWNjZXNzIHJhdGUgcmF0aW9cclxuICogYW5kIGNvbGxlY3RzIHJlY29yZHMgdG8gdGhlIGBzdWNjZXNzUmF0ZXNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3N0YXJ0U3VjY2Vzc1JhdGVcclxuICpcclxuICogQHJldHVybnMge05vZGVKUy5UaW1lb3V0fSBJZCBvZiBhbiBpbnRlcnZhbC5cclxuICovXHJcbmZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlKCkge1xyXG4gIHJldHVybiBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICBjb25zdCBzdGF0cyA9IGdldFBvb2xTdGF0cygpO1xyXG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID1cclxuICAgICAgc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCA9PT0gMFxyXG4gICAgICAgID8gMVxyXG4gICAgICAgIDogKHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQgLyBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkKSAqIDEwMDtcclxuXHJcbiAgICBzdWNjZXNzUmF0ZXMucHVzaChzdWNjZXNzUmF0aW8pO1xyXG4gICAgaWYgKHN1Y2Nlc3NSYXRlcy5sZW5ndGggPiB3aW5kb3dTaXplKSB7XHJcbiAgICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xyXG4gICAgfVxyXG4gIH0sIHJlY29yZEludGVydmFsKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBoZWFsdGhgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGhlYWx0aFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGhlYWx0aFJvdXRlcyhhcHApIHtcclxuICAvLyBTdGFydCBwcm9jZXNzaW5nIHN1Y2Nlc3MgcmF0ZSByYXRpbyBpbnRlcnZhbCBhbmQgc2F2ZSBpdHMgaWQgdG8gdGhlIGFycmF5XHJcbiAgLy8gZm9yIHRoZSBncmFjZWZ1bCBjbGVhcmluZyBvbiBzaHV0ZG93biB3aXRoIGluamVjdGVkIGBhZGRUaW1lcmAgZnVudGlvblxyXG4gIGFkZFRpbWVyKF9zdGFydFN1Y2Nlc3NSYXRlKCkpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy9oZWFsdGgnIC0gQSByb3V0ZSBmb3IgZ2V0dGluZyB0aGUgYmFzaWMgc3RhdHMgb2YgdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KCcvaGVhbHRoJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1toZWFsdGhdIFJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgICAgY29uc3QgbW92aW5nQXZlcmFnZSA9IF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgICAvLyBTZW5kIHRoZSBzZXJ2ZXIncyBzdGF0aXN0aWNzXHJcbiAgICAgIHJlc3BvbnNlLnNlbmQoe1xyXG4gICAgICAgIC8vIFN0YXR1cyBhbmQgdGltZXNcclxuICAgICAgICBzdGF0dXM6ICdPSycsXHJcbiAgICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcclxuICAgICAgICB1cHRpbWU6IGAke01hdGguZmxvb3IoKGdldE5ld0RhdGVUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MCl9IG1pbnV0ZXNgLFxyXG5cclxuICAgICAgICAvLyBWZXJzaW9uc1xyXG4gICAgICAgIHNlcnZlclZlcnNpb246IHBhY2thZ2VGaWxlLnZlcnNpb24sXHJcbiAgICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IGdldEhpZ2hjaGFydHNWZXJzaW9uKCksXHJcblxyXG4gICAgICAgIC8vIEV4cG9ydHNcclxuICAgICAgICBhdmVyYWdlRXhwb3J0VGltZTogc3RhdHMudGltZVNwZW50QXZlcmFnZSxcclxuICAgICAgICBhdHRlbXB0ZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkLFxyXG4gICAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQsXHJcbiAgICAgICAgZmFpbGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0Ryb3BwZWQsXHJcbiAgICAgICAgc3VjZXNzUmF0aW86IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDAsXHJcblxyXG4gICAgICAgIC8vIFBvb2xcclxuICAgICAgICBwb29sOiBnZXRQb29sSW5mb0pTT04oKSxcclxuXHJcbiAgICAgICAgLy8gTW92aW5nIGF2ZXJhZ2VcclxuICAgICAgICBwZXJpb2QsXHJcbiAgICAgICAgbW92aW5nQXZlcmFnZSxcclxuICAgICAgICBtZXNzYWdlOlxyXG4gICAgICAgICAgaXNOYU4obW92aW5nQXZlcmFnZSkgfHwgIXN1Y2Nlc3NSYXRlcy5sZW5ndGhcclxuICAgICAgICAgICAgPyAnVG9vIGVhcmx5IHRvIHJlcG9ydC4gTm8gZXhwb3J0cyBtYWRlIHlldC4gUGxlYXNlIGNoZWNrIGJhY2sgc29vbi4nXHJcbiAgICAgICAgICAgIDogYExhc3QgJHtwZXJpb2R9IG1pbnV0ZXMgaGFkIGEgc3VjY2VzcyByYXRlIG9mICR7bW92aW5nQXZlcmFnZS50b0ZpeGVkKDIpfSUuYCxcclxuXHJcbiAgICAgICAgLy8gU1ZHIGFuZCBKU09OIGV4cG9ydHNcclxuICAgICAgICBzdmdFeHBvcnRzOiBzdGF0cy5leHBvcnRzRnJvbVN2ZyxcclxuICAgICAgICBqc29uRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zLFxyXG4gICAgICAgIHN2Z0V4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyxcclxuICAgICAgICBqc29uRXhwb3J0c0F0dGVtcHRzOiBzdGF0cy5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0c1xyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciBzZXJ2aW5nIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXJcclxuICogd2hlbiBlbmFibGVkLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHVpYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1aVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHVpUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIEdFVCAnLycgLSBBIHJvdXRlIGZvciBhIFVJIHdoZW4gZW5hYmxlZCBvbiB0aGUgZXhwb3J0IHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KGdldE9wdGlvbnMoKS51aS5yb3V0ZSB8fCAnLycsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdWldIFJldHVybmluZyBVSSBmb3IgdGhlIGV4cG9ydC4nKTtcclxuXHJcbiAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwge1xyXG4gICAgICAgIGFjY2VwdFJhbmdlczogZmFsc2VcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3IgdXBkYXRpbmcgdGhlIEhpZ2hjaGFydHMgdmVyc2lvblxyXG4gKiBvbiB0aGUgc2VydmVyLCB3aXRoIGF1dGhlbnRpY2F0aW9uIGFuZCB2YWxpZGF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGdldEhpZ2hjaGFydHNWZXJzaW9uLCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHZlcnNpb25fY2hhbmdlYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nIC0gQSByb3V0ZSBmb3IgY2hhbmdpbmdcclxuICAgKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicsIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdmVyc2lvbl0gQ2hhbmdpbmcgSGlnaGNoYXJ0cyB2ZXJzaW9uLicpO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBkaXJlY3RseSBmcm9tIGVudnNcclxuICAgICAgY29uc3QgYWRtaW5Ub2tlbiA9IGVudnMuSElHSENIQVJUU19BRE1JTl9UT0tFTjtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBleGlzdGVuY2Ugb2YgdGhlIHRva2VuXHJcbiAgICAgIGlmICghYWRtaW5Ub2tlbiB8fCAhYWRtaW5Ub2tlbi5sZW5ndGgpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIFRoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBmcm9tIHRoZSBoYy1hdXRoIGhlYWRlclxyXG4gICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbdmVyc2lvbl0gSW52YWxpZCBvciBtaXNzaW5nIHRva2VuOiBTZXQgdGhlIHRva2VuIGluIHRoZSBoYy1hdXRoIGhlYWRlci4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xyXG4gICAgICBsZXQgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XHJcbiAgICAgIGlmIChuZXdWZXJzaW9uKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFVwZGF0ZSB2ZXJzaW9uXHJcbiAgICAgICAgICBhd2FpdCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICBgW3ZlcnNpb25dIFZlcnNpb24gY2hhbmdlOiAke2Vycm9yLm1lc3NhZ2V9YCxcclxuICAgICAgICAgICAgNDAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFN1Y2Nlc3NcclxuICAgICAgICByZXNwb25zZS5zdGF0dXMoMjAwKS5zZW5kKHtcclxuICAgICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcclxuICAgICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG4gICAgICAgICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIEhpZ2hjaGFydHMgdG8gdmVyc2lvbjogJHtuZXdWZXJzaW9ufS5gXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gTm8gdmVyc2lvbiBzcGVjaWZpZWRcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1t2ZXJzaW9uXSBObyBuZXcgdmVyc2lvbiBzdXBwbGllZC4nLCA0MDApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEEgbW9kdWxlIHRoYXQgc2V0cyB1cCBhbmQgbWFuYWdlcyBIVFRQIGFuZCBIVFRQUyBzZXJ2ZXJzXHJcbiAqIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBoYW5kbGVzIHNlcnZlciBpbml0aWFsaXphdGlvbixcclxuICogY29uZmlndXJhdGlvbiwgZXJyb3IgaGFuZGxpbmcsIG1pZGRsZXdhcmUgc2V0dXAsIHJvdXRlIGRlZmluaXRpb24sIGFuZCByYXRlXHJcbiAqIGxpbWl0aW5nLiBUaGUgbW9kdWxlIGV4cG9ydHMgZnVuY3Rpb25zIHRvIHN0YXJ0LCBzdG9wLCBhbmQgbWFuYWdlIHNlcnZlclxyXG4gKiBpbnN0YW5jZXMsIGFzIHdlbGwgYXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIGRlZmluaW5nIHJvdXRlcyBhbmQgYXR0YWNoaW5nXHJcbiAqIG1pZGRsZXdhcmVzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xyXG5cclxuaW1wb3J0IHsgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgZXJyb3JNaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvZXJyb3IuanMnO1xyXG5pbXBvcnQgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3JhdGVMaW1pdGluZy5qcyc7XHJcbmltcG9ydCB2YWxpZGF0aW9uTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3ZhbGlkYXRpb24uanMnO1xyXG5cclxuaW1wb3J0IGV4cG9ydFJvdXRlcyBmcm9tICcuL3JvdXRlcy9leHBvcnQuanMnO1xyXG5pbXBvcnQgaGVhbHRoUm91dGVzIGZyb20gJy4vcm91dGVzL2hlYWx0aC5qcyc7XHJcbmltcG9ydCB1aVJvdXRlcyBmcm9tICcuL3JvdXRlcy91aS5qcyc7XHJcbmltcG9ydCB2ZXJzaW9uQ2hhbmdlUm91dGVzIGZyb20gJy4vcm91dGVzL3ZlcnNpb25DaGFuZ2UuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBBcnJheSBvZiBhbiBhY3RpdmUgc2VydmVyc1xyXG5jb25zdCBhY3RpdmVTZXJ2ZXJzID0gbmV3IE1hcCgpO1xyXG5cclxuLy8gQ3JlYXRlIGV4cHJlc3MgYXBwXHJcbmNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gSFRUUCBhbmQvb3IgSFRUUFMgc2VydmVyIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLlxyXG4gKiBUaGUgYHNlcnZlck9wdGlvbnNgIG9iamVjdCBjb250YWlucyBzZXJ2ZXItcmVsYXRlZCBwcm9wZXJ0aWVzIChyZWZlclxyXG4gKiB0byB0aGUgYHNlcnZlcmAgc2VjdGlvbiBpbiB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIGZvciBkZXRhaWxzKS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydFNlcnZlclxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBzZXJ2ZXJgXHJcbiAqIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2YgdGhlIGBzZXJ2ZXJgXHJcbiAqIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlcyB3aWxsIGRlZmF1bHRcclxuICogdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgZWl0aGVyXHJcbiAqIG5vdCBlbmFibGVkIG9yIG5vIHZhbGlkIEV4cHJlc3MgYXBwIGlzIGZvdW5kLCBzaWduYWxpbmcgdGhlIGVuZCBvZiB0aGVcclxuICogZnVuY3Rpb24ncyBleGVjdXRpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgc2VydmVyIGNhbm5vdFxyXG4gKiBiZSBjb25maWd1cmVkIGFuZCBzdGFydGVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0U2VydmVyKHNlcnZlck9wdGlvbnMpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBzZXJ2ZXI6IHNlcnZlck9wdGlvbnNcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFVzZSB2YWxpZGF0ZWQgb3B0aW9uc1xyXG4gICAgc2VydmVyT3B0aW9ucyA9IG9wdGlvbnMuc2VydmVyO1xyXG5cclxuICAgIC8vIFN0b3AgaWYgbm90IGVuYWJsZWRcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5lbmFibGUgfHwgIWFwcCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tzZXJ2ZXJdIFNlcnZlciBjYW5ub3QgYmUgc3RhcnRlZCAobm90IGVuYWJsZWQgb3Igbm8gY29ycmVjdCBFeHByZXNzIGFwcCBmb3VuZCkuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUb28gYmlnIGxpbWl0cyBsZWFkIHRvIHRpbWVvdXRzIGluIHRoZSBleHBvcnQgcHJvY2VzcyB3aGVuXHJcbiAgICAvLyB0aGUgcmFzdGVyaXphdGlvbiB0aW1lb3V0IGlzIHNldCB0b28gbG93XHJcbiAgICBjb25zdCB1cGxvYWRMaW1pdEJ5dGVzID0gc2VydmVyT3B0aW9ucy51cGxvYWRMaW1pdCAqIDEwMjQgKiAxMDI0O1xyXG5cclxuICAgIC8vIE1lbW9yeSBzdG9yYWdlIGZvciBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XHJcblxyXG4gICAgLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3QgdXBsb2FkID0gbXVsdGVyKHtcclxuICAgICAgc3RvcmFnZSxcclxuICAgICAgbGltaXRzOiB7XHJcbiAgICAgICAgZmllbGRTaXplOiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIERpc2FibGUgdGhlIFgtUG93ZXJlZC1CeSBoZWFkZXJcclxuICAgIGFwcC5kaXNhYmxlKCd4LXBvd2VyZWQtYnknKTtcclxuXHJcbiAgICAvLyBFbmFibGUgQ09SUyBzdXBwb3J0XHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBjb3JzKHtcclxuICAgICAgICBtZXRob2RzOiBbJ1BPU1QnLCAnR0VUJywgJ09QVElPTlMnXVxyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXR0aW5nIGEgbG90IG9mIGBSYW5nZU5vdFNhdGlzZmlhYmxlRXJyb3JgIGV4Y2VwdGlvbnMgKGV2ZW4gdGhvdWdoIHRoaXNcclxuICAgIC8vIGlzIGEgZGVwcmVjYXRlZCBvcHRpb25zLCBsZXQncyB0cnkgdG8gc2V0IGl0IHRvIGZhbHNlKVxyXG4gICAgYXBwLnVzZSgocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgICAgcmVzcG9uc2Uuc2V0KCdBY2NlcHQtUmFuZ2VzJywgJ25vbmUnKTtcclxuICAgICAgbmV4dCgpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBKU09OIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MuanNvbih7XHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBVUkwtZW5jb2RlZCBmb3JtIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MudXJsZW5jb2RlZCh7XHJcbiAgICAgICAgZXh0ZW5kZWQ6IHRydWUsXHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gVXNlIG9ubHkgbm9uLWZpbGUgbXVsdGlwYXJ0IGZvcm0gZmllbGRzXHJcbiAgICBhcHAudXNlKHVwbG9hZC5ub25lKCkpO1xyXG5cclxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcclxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQIHNlcnZlclxyXG4gICAgaWYgKCFzZXJ2ZXJPcHRpb25zLnNzbC5mb3JjZSkge1xyXG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcCk7XHJcblxyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKGh0dHBTZXJ2ZXIpO1xyXG5cclxuICAgICAgLy8gTGlzdGVuXHJcbiAgICAgIGh0dHBTZXJ2ZXIubGlzdGVuKHNlcnZlck9wdGlvbnMucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFAgc2VydmVyXHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5zZXQoc2VydmVyT3B0aW9ucy5wb3J0LCBodHRwU2VydmVyKTtcclxuXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyT3B0aW9ucy5ob3N0fToke3NlcnZlck9wdGlvbnMucG9ydH0uYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQUyBzZXJ2ZXJcclxuICAgIGlmIChzZXJ2ZXJPcHRpb25zLnNzbC5lbmFibGUpIHtcclxuICAgICAgLy8gU2V0IHVwIGFuIFNTTCBzZXJ2ZXIgYWxzb1xyXG4gICAgICBsZXQga2V5LCBjZXJ0O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBrZXlcclxuICAgICAgICBrZXkgPSByZWFkRmlsZVN5bmMoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIua2V5JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBjZXJ0aWZpY2F0ZVxyXG4gICAgICAgIGNlcnQgPSByZWFkRmlsZVN5bmMoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIuY3J0JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gVW5hYmxlIHRvIGxvYWQga2V5L2NlcnRpZmljYXRlIGZyb20gdGhlICcke3NlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRofScgcGF0aC4gQ291bGQgbm90IHJ1biBzZWN1cmVkIGxheWVyIHNlcnZlci5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKGtleSAmJiBjZXJ0KSB7XHJcbiAgICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFBTKVxyXG4gICAgICAgIGNvbnN0IGh0dHBzU2VydmVyID0gaHR0cHMuY3JlYXRlU2VydmVyKHsga2V5LCBjZXJ0IH0sIGFwcCk7XHJcblxyXG4gICAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgIC8vIExpc3RlblxyXG4gICAgICAgIGh0dHBzU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBzZXJ2ZXJPcHRpb25zLmhvc3QsICgpID0+IHtcclxuICAgICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQUyBzZXJ2ZXJcclxuICAgICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMuc3NsLnBvcnQsIGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0fS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBzZXJ2ZXJPcHRpb25zLnJhdGVMaW1pdGluZyk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSB2YWxpZGF0aW9uIGhhbmRsZXJcclxuICAgIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHJvdXRlc1xyXG4gICAgZXhwb3J0Um91dGVzKGFwcCk7XHJcbiAgICBoZWFsdGhSb3V0ZXMoYXBwKTtcclxuICAgIHVpUm91dGVzKGFwcCk7XHJcbiAgICB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXHJcbiAgICBlcnJvck1pZGRsZXdhcmUoYXBwKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbG9zZVNlcnZlcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbG9zZVNlcnZlcnMoKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgYXJlIHNlcnZlcnMgd29ya2luZ1xyXG4gIGlmIChhY3RpdmVTZXJ2ZXJzLnNpemUgPiAwKSB7XHJcbiAgICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NpbmcgYWxsIHNlcnZlcnMuYCk7XHJcblxyXG4gICAgLy8gQ2xvc2UgZWFjaCBvbmUgb2Ygc2VydmVyc1xyXG4gICAgZm9yIChjb25zdCBbcG9ydCwgc2VydmVyXSBvZiBhY3RpdmVTZXJ2ZXJzKSB7XHJcbiAgICAgIHNlcnZlci5jbG9zZSgoKSA9PiB7XHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5kZWxldGUocG9ydCk7XHJcbiAgICAgICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zZWQgc2VydmVyIG9uIHBvcnQ6ICR7cG9ydH0uYCk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRTZXJ2ZXJzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtBcnJheS48T2JqZWN0Pn0gU2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VydmVycygpIHtcclxuICByZXR1cm4gYWN0aXZlU2VydmVycztcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEV4cHJlc3NcclxuICpcclxuICogQHJldHVybnMge0V4cHJlc3N9IFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEV4cHJlc3MoKSB7XHJcbiAgcmV0dXJuIGV4cHJlc3M7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QXBwXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXBwKCkge1xyXG4gIHJldHVybiBhcHA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXRcclxuICogb2YgdGhlIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlc1xyXG4gKiB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlUmF0ZUxpbWl0aW5nKHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgc2VydmVyOiB7XHJcbiAgICAgIHJhdGVMaW1pdGluZzogcmF0ZUxpbWl0aW5nT3B0aW9uc1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9uc1xyXG4gIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBvcHRpb25zLnNlcnZlci5yYXRlTGltaXRpbmdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFwcGx5IG1pZGRsZXdhcmUocykgdG8gYSBzcGVjaWZpYyBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXNlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggR0VUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIFBPU1QgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5wb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEF0dGFjaCBlcnJvciBoYW5kbGVycyB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnNcclxuICpcclxuICogQHBhcmFtIHsoaHR0cC5TZXJ2ZXJ8aHR0cHMuU2VydmVyKX0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoc2VydmVyKSB7XHJcbiAgc2VydmVyLm9uKCdjbGllbnRFcnJvcicsIChlcnJvciwgc29ja2V0KSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDEsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9LCBkZXN0cm95aW5nIHNvY2tldC5gXHJcbiAgICApO1xyXG4gICAgc29ja2V0LmRlc3Ryb3koKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignY29ubmVjdGlvbicsIChzb2NrZXQpID0+IHtcclxuICAgIHNvY2tldC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGNsb3NlU2VydmVycyxcclxuICBnZXRTZXJ2ZXJzLFxyXG4gIGdldEV4cHJlc3MsXHJcbiAgZ2V0QXBwLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhhbmRsZXMgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciwgZW5zdXJpbmdcclxuICogcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzIHN1Y2ggYXMgYnJvd3NlciwgcGFnZXMsIHNlcnZlcnMsIGFuZCB0aW1lcnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBjbGVhckFsbFRpbWVycyB9IGZyb20gJy4vdGltZXIuanMnO1xyXG5cclxuaW1wb3J0IHsgY2xvc2VTZXJ2ZXJzIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBjbGVhbnVwIG9wZXJhdGlvbnMgdG8gZW5zdXJlIGEgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIHByb2Nlc3MuXHJcbiAqIFRoaXMgaW5jbHVkZXMgY2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgdGltZW91dHMvaW50ZXJ2YWxzLCBjbG9zaW5nIGFjdGl2ZVxyXG4gKiBzZXJ2ZXJzLCB0ZXJtaW5hdGluZyByZXNvdXJjZXMgKHBhZ2VzKSBvZiB0aGUgcG9vbCwgcG9vbCBpdHNlbGYsIGFuZCBjbG9zaW5nXHJcbiAqIHRoZSBicm93c2VyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbZXhpdENvZGU9MF0gLSBUaGUgZXhpdCBjb2RlIHRvIHVzZSB3aXRoIGBwcm9jZXNzLmV4aXQoKWAuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGAwYC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXAoZXhpdENvZGUgPSAwKSB7XHJcbiAgLy8gQXdhaXQgZnJlZWluZyBhbGwgcmVzb3VyY2VzXHJcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcclxuICAgIC8vIENsZWFyIGFsbCBvbmdvaW5nIGludGVydmFsc1xyXG4gICAgY2xlYXJBbGxUaW1lcnMoKSxcclxuXHJcbiAgICAvLyBHZXQgYXZhaWxhYmxlIHNlcnZlciBpbnN0YW5jZXMgKEhUVFAvSFRUUFMpIGFuZCBjbG9zZSB0aGVtXHJcbiAgICBjbG9zZVNlcnZlcnMoKSxcclxuXHJcbiAgICAvLyBDbG9zZSBhbiBhY3RpdmUgcG9vbCBhbG9uZyB3aXRoIGl0cyB3b3JrZXJzIGFuZCB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gICAga2lsbFBvb2woKVxyXG4gIF0pO1xyXG5cclxuICAvLyBFeGl0IHByb2Nlc3Mgd2l0aCBhIGNvcnJlY3QgY29kZVxyXG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaHV0ZG93bkNsZWFuVXBcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvcmUgbW9kdWxlIGZvciBpbml0aWFsaXppbmcgYW5kIG1hbmFnaW5nIHRoZSBIaWdoY2hhcnRzIEV4cG9ydFxyXG4gKiBTZXJ2ZXIuIFByb3ZpZGVzIGZ1bmN0aW9uYWxpdGllcyBmb3IgY29uZmlndXJpbmcgZXhwb3J0cywgc2V0dGluZyB1cCBzZXJ2ZXJcclxuICogb3BlcmF0aW9ucywgbG9nZ2luZywgc2NyaXB0cyBjYWNoaW5nLCByZXNvdXJjZSBwb29saW5nLCBhbmQgZ3JhY2VmdWwgcHJvY2Vzc1xyXG4gKiBjbGVhbnVwLlxyXG4gKi9cclxuXHJcbmltcG9ydCAnY29sb3JzJztcclxuXHJcbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMsIG1hcFRvTmV3T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHtcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIGluaXRMb2dnaW5nLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG4gIHNldExvZ0xldmVsXHJcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpbml0UG9vbCwga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzaHV0ZG93bkNsZWFuVXAgfSBmcm9tICcuL3Jlc291cmNlUmVsZWFzZS5qcyc7XHJcblxyXG5pbXBvcnQgc2VydmVyIGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwcm9jZXNzLiBUYXNrcyBzdWNoIGFzIGNvbmZpZ3VyaW5nIGxvZ2dpbmcsIGNoZWNraW5nXHJcbiAqIHRoZSBjYWNoZSBhbmQgc291cmNlcywgYW5kIGluaXRpYWxpemluZyB0aGUgcmVzb3VyY2UgcG9vbCBvY2N1ciBkdXJpbmcgdGhpc1xyXG4gKiBzdGFnZS5cclxuICpcclxuICogVGhpcyBmdW5jdGlvbiBtdXN0IGJlIGNhbGxlZCBiZWZvcmUgYXR0ZW1wdGluZyB0byBleHBvcnQgY2hhcnRzIG9yIHNldFxyXG4gKiB1cCBhIHNlcnZlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpbml0T3B0aW9ucyAtIFRoZSBgaW5pdE9wdGlvbnNgIG9iamVjdCwgd2hpY2ggbWF5XHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmdcclxuICogdmFsdWVzIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0RXhwb3J0KGluaXRPcHRpb25zKSB7XHJcbiAgLy8gSW5pdCBhbmQgdXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKGluaXRPcHRpb25zKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBwZXIgZXhwb3J0IG1vZHVsZSBzY29wZVxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gIC8vIEluaXQgdGhlIGxvZ2dpbmdcclxuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAob3B0aW9ucy5vdGhlci5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXHJcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMsIG9wdGlvbnMuc2VydmVyLnByb3h5KTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgcG9vbFxyXG4gIGF3YWl0IGluaXRQb29sKG9wdGlvbnMucG9vbCwgb3B0aW9ucy5wdXBwZXRlZXIuYXJncyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXHJcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nXHJcbiAqIGFuZCAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVyc1xyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCkge1xyXG4gIGxvZygzLCAnW3Byb2Nlc3NdIEF0dGFjaGluZyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdleGl0J1xyXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gUHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0uYCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSU5UJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdURVJNJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICd1bmNhdWdodEV4Y2VwdGlvbidcclxuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGVycm9yLmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDEpO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgLy8gU2VydmVyXHJcbiAgLi4uc2VydmVyLFxyXG5cclxuICAvLyBPcHRpb25zXHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuXHJcbiAgLy8gRXhwb3J0aW5nXHJcbiAgaW5pdEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcblxyXG4gIC8vIFJlbGVhc2VcclxuICBraWxsUG9vbCxcclxuICBzaHV0ZG93bkNsZWFuVXAsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsOiBmdW5jdGlvbiAobGV2ZWwpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGxldmVsXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICBzZXRMb2dMZXZlbChvcHRpb25zLmxvZ2dpbmcubGV2ZWwpO1xyXG4gIH0sXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmc6IGZ1bmN0aW9uICh0b0NvbnNvbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIHRvQ29uc29sZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlQ29uc29sZUxvZ2dpbmcob3B0aW9ucy5sb2dnaW5nLnRvQ29uc29sZSk7XHJcbiAgfSxcclxuICBlbmFibGVGaWxlTG9nZ2luZzogZnVuY3Rpb24gKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBsb2dnaW5nOiB7XHJcbiAgICAgICAgZGVzdCxcclxuICAgICAgICBmaWxlLFxyXG4gICAgICAgIHRvRmlsZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy5kZXN0LFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZmlsZSxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLnRvRmlsZVxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcbiJdLCJuYW1lcyI6WyJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInRhZ05hbWUiLCJ0b1VwcGVyQ2FzZSIsInNyYyIsImJhc2VVUkkiLCJkZWVwQ29weSIsIm9iakFyciIsIm9iakFyckNvcHkiLCJBcnJheSIsImlzQXJyYXkiLCJrZXkiLCJPYmplY3QiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJmaXhDb25zdHIiLCJjb25zdHIiLCJmaXhlZENvbnN0ciIsInRvTG93ZXJDYXNlIiwicmVwbGFjZSIsImluY2x1ZGVzIiwiZml4T3V0ZmlsZSIsInR5cGUiLCJvdXRmaWxlIiwiZ2V0QWJzb2x1dGVQYXRoIiwic3BsaXQiLCJzaGlmdCIsImZpeFR5cGUiLCJtaW1lVHlwZXMiLCJmb3JtYXRzIiwidmFsdWVzIiwib3V0VHlwZSIsInBvcCIsImZpbmQiLCJ0IiwicGF0aCIsImlzQWJzb2x1dGUiLCJub3JtYWxpemUiLCJyZXNvbHZlIiwiZ2V0QmFzZTY0IiwiaW5wdXQiLCJCdWZmZXIiLCJmcm9tIiwidG9TdHJpbmciLCJnZXROZXdEYXRlIiwiRGF0ZSIsInRyaW0iLCJnZXROZXdEYXRlVGltZSIsImdldFRpbWUiLCJpc09iamVjdCIsIml0ZW0iLCJpc09iamVjdEVtcHR5Iiwia2V5cyIsImxlbmd0aCIsImlzUHJpdmF0ZVJhbmdlVXJsRm91bmQiLCJzb21lIiwicGF0dGVybiIsInRlc3QiLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwicHJvY2VzcyIsImhydGltZSIsImJpZ2ludCIsIk51bWJlciIsInJvdW5kTnVtYmVyIiwidmFsdWUiLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwiTWF0aCIsInBvdyIsInJvdW5kIiwid3JhcEFyb3VuZCIsImN1c3RvbUNvZGUiLCJhbGxvd0ZpbGVSZXNvdXJjZXMiLCJpc0NhbGxiYWNrIiwiZW5kc1dpdGgiLCJyZWFkRmlsZVN5bmMiLCJzdGFydHNXaXRoIiwiY29sb3JzIiwibG9nZ2luZyIsInRvQ29uc29sZSIsInRvRmlsZSIsInBhdGhDcmVhdGVkIiwicGF0aFRvTG9nIiwibGV2ZWxzRGVzYyIsInRpdGxlIiwiY29sb3IiLCJsb2ciLCJhcmdzIiwibmV3TGV2ZWwiLCJ0ZXh0cyIsImxldmVsIiwicHJlZml4IiwiX2xvZ1RvRmlsZSIsImNvbnNvbGUiLCJhcHBseSIsInVuZGVmaW5lZCIsImNvbmNhdCIsImxvZ1dpdGhTdGFjayIsImVycm9yIiwiY3VzdG9tTWVzc2FnZSIsIm1haW5NZXNzYWdlIiwibWVzc2FnZSIsInN0YWNrTWVzc2FnZSIsInN0YWNrIiwicHVzaCIsImluaXRMb2dnaW5nIiwibG9nZ2luZ09wdGlvbnMiLCJkZXN0IiwiZmlsZSIsInNldExvZ0xldmVsIiwiZW5hYmxlQ29uc29sZUxvZ2dpbmciLCJlbmFibGVGaWxlTG9nZ2luZyIsImlzSW50ZWdlciIsImV4aXN0c1N5bmMiLCJta2RpclN5bmMiLCJqb2luIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiX2luaXRPcHRpb25zIiwiZ2V0T3B0aW9ucyIsImdldENvcHkiLCJ1cGRhdGVPcHRpb25zIiwibmV3T3B0aW9ucyIsIl9tZXJnZU9wdGlvbnMiLCJtYXBUb05ld09wdGlvbnMiLCJvbGRPcHRpb25zIiwiZW50cmllcyIsInByb3BlcnRpZXNDaGFpbiIsInJlZHVjZSIsIm9iaiIsInByb3AiLCJpbmRleCIsImlzQWxsb3dlZENvbmZpZyIsImFsbG93RnVuY3Rpb25zIiwib2JqZWN0Q29uZmlnIiwiZXZhbCIsIkpTT04iLCJzdHJpbmdpZmllZE9wdGlvbnMiLCJfb3B0aW9uc1N0cmluZ2lmeSIsInBhcnNlZE9wdGlvbnMiLCJfIiwibmFtZSIsIm9yaWdpbmFsT3B0aW9ucyIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJFcnJvciIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZWplY3QiLCJfZ2V0UHJvdG9jb2xNb2R1bGUiLCJnZXQiLCJyZXNwb25zZSIsInJlc3BvbnNlRGF0YSIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiaHR0cHMiLCJodHRwIiwiRXhwb3J0RXJyb3IiLCJjb25zdHJ1Y3RvciIsInN0YXR1c0NvZGUiLCJzdXBlciIsInRoaXMiLCJzZXRTdGF0dXMiLCJzZXRFcnJvciIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsImkiLCJzZXR1cEhpZ2hjaGFydHMiLCJIaWdoY2hhcnRzIiwiYW5pbU9iamVjdCIsImR1cmF0aW9uIiwiY3JlYXRlQ2hhcnQiLCJleHBvcnRPcHRpb25zIiwiY3VzdG9tTG9naWNPcHRpb25zIiwic2V0T3B0aW9ucyIsIm1lcmdlIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJpc1JlbmRlckNvbXBsZXRlIiwiQ2hhcnQiLCJwcm9jZWVkIiwidXNlck9wdGlvbnMiLCJjYiIsImV4cG9ydGluZyIsImVuYWJsZWQiLCJwbG90T3B0aW9ucyIsInNlcmllcyIsImxhYmVsIiwidG9vbHRpcCIsImFuaW1hdGlvbiIsIm9uSGlnaGNoYXJ0c1JlbmRlciIsImFkZEV2ZW50IiwiU2VyaWVzIiwiY2hhcnQiLCJhZGRpdGlvbmFsT3B0aW9ucyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImRlZmF1bHRPcHRpb25zIiwidGVtcGxhdGUiLCJicm93c2VyIiwiY3JlYXRlQnJvd3NlciIsInB1cHBldGVlckFyZ3MiLCJlbmFibGVkRGVidWciLCJkZWJ1Z09wdGlvbnMiLCJsYXVuY2hPcHRpb25zIiwidXNlckRhdGFEaXIiLCJoYW5kbGVTSUdJTlQiLCJoYW5kbGVTSUdURVJNIiwiaGFuZGxlU0lHSFVQIiwid2FpdEZvckluaXRpYWxQYWdlIiwiZGVmYXVsdFZpZXdwb3J0IiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwic2V0VGltZW91dCIsImNsb3NlQnJvd3NlciIsImNvbm5lY3RlZCIsImNsb3NlIiwibmV3UGFnZSIsInBvb2xSZXNvdXJjZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJfc2V0UGFnZUNvbnRlbnQiLCJfc2V0UGFnZUV2ZW50cyIsImlzQ2xvc2VkIiwiY2xlYXJQYWdlIiwiaGFyZFJlc2V0IiwiZ290byIsIndhaXRVbnRpbCIsImV2YWx1YXRlIiwiYm9keSIsImlubmVySFRNTCIsImlkIiwid29ya0NvdW50IiwiYWRkUGFnZVJlc291cmNlcyIsImluamVjdGVkUmVzb3VyY2VzIiwiaW5qZWN0ZWRKcyIsImpzIiwiY29udGVudCIsImZpbGVzIiwiaXNMb2NhbCIsImpzUmVzb3VyY2UiLCJhZGRTY3JpcHRUYWciLCJpbmplY3RlZENzcyIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJjc3NSZXNvdXJjZSIsImFkZFN0eWxlVGFnIiwiY2xlYXJQYWdlUmVzb3VyY2VzIiwicmVzb3VyY2UiLCJkaXNwb3NlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJlbGVtZW50IiwicmVtb3ZlIiwic2V0Q29udGVudCIsImNzc1RlbXBsYXRlIiwic3ZnVGVtcGxhdGUiLCJwdXBwZXRlZXJFeHBvcnQiLCJpc1NWRyIsInNpemUiLCJzdmdFbGVtZW50IiwicXVlcnlTZWxlY3RvciIsImNoYXJ0SGVpZ2h0IiwiYmFzZVZhbCIsImNoYXJ0V2lkdGgiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ4IiwieSIsIl9nZXRDbGlwUmVnaW9uIiwidmlld3BvcnRIZWlnaHQiLCJhYnMiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsInJlc3VsdCIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJfY3JlYXRlU1ZHIiwiX2NyZWF0ZUltYWdlIiwiX2NyZWF0ZVBERiIsIiRldmFsIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidHJ1bmMiLCJvdXRlckhUTUwiLCJjbGlwIiwicmFjZSIsInNjcmVlbnNob3QiLCJlbmNvZGluZyIsImZ1bGxQYWdlIiwib3B0aW1pemVGb3JTcGVlZCIsImNhcHR1cmVCZXlvbmRWaWV3cG9ydCIsInF1YWxpdHkiLCJvbWl0QmFja2dyb3VuZCIsIl9yZXNvbHZlIiwiZW11bGF0ZU1lZGlhVHlwZSIsInBkZiIsInBvb2xTdGF0cyIsImV4cG9ydHNBdHRlbXB0ZWQiLCJleHBvcnRzUGVyZm9ybWVkIiwiZXhwb3J0c0Ryb3BwZWQiLCJleHBvcnRzRnJvbVN2ZyIsImV4cG9ydHNGcm9tT3B0aW9ucyIsImV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMiLCJleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cyIsInRpbWVTcGVudCIsInRpbWVTcGVudEF2ZXJhZ2UiLCJpbml0UG9vbCIsInBvb2xPcHRpb25zIiwiUG9vbCIsIl9mYWN0b3J5IiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsImNsZWFyU3RhdHVzIiwiX2V2ZW50SWQiLCJpbml0aWFsUmVzb3VyY2VzIiwiYWNxdWlyZSIsInByb21pc2UiLCJyZWxlYXNlIiwia2lsbFBvb2wiLCJ3b3JrZXIiLCJ1c2VkIiwiZGVzdHJveWVkIiwicG9zdFdvcmsiLCJ3b3JrZXJIYW5kbGUiLCJnZXRQb29sSW5mbyIsImFjcXVpcmVDb3VudGVyIiwicmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsImV4cG9ydFRpbWUiLCJnZXRQb29sU3RhdHMiLCJnZXRQb29sSW5mb0pTT04iLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwibnVtRnJlZSIsImFsbENyZWF0ZWQiLCJwZW5kaW5nQWNxdWlyZXMiLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwZW5kaW5nQ3JlYXRlcyIsIm51bVBlbmRpbmdDcmVhdGVzIiwicGVuZGluZ1ZhbGlkYXRpb25zIiwibnVtUGVuZGluZ1ZhbGlkYXRpb25zIiwicGVuZGluZ0Rlc3Ryb3lzIiwiYWJzb2x1dGVBbGwiLCJjcmVhdGUiLCJ1dWlkIiwicmFuZG9tIiwic3RhcnREYXRlIiwidmFsaWRhdGUiLCJtYWluRnJhbWUiLCJkZXRhY2hlZCIsInJlbW92ZUFsbExpc3RlbmVycyIsInNhbml0aXplIiwiSlNET00iLCJET01QdXJpZnkiLCJBRERfVEFHUyIsInNpbmdsZUV4cG9ydCIsInN0YXJ0RXhwb3J0IiwiZGF0YSIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiYmF0Y2hSZXN1bHRzIiwiYWxsU2V0dGxlZCIsInJlYXNvbiIsImltYWdlT3B0aW9ucyIsImVuZENhbGxiYWNrIiwiZmlsZUNvbnRlbnQiLCJfZXhwb3J0RnJvbVN2ZyIsIl9leHBvcnRGcm9tT3B0aW9ucyIsImdldEFsbG93Q29kZUV4ZWN1dGlvbiIsInNldEFsbG93Q29kZUV4ZWN1dGlvbiIsImlucHV0VG9FeHBvcnQiLCJfcHJlcGFyZUV4cG9ydCIsIl9oYW5kbGVDdXN0b21Mb2dpYyIsIl9oYW5kbGVHbG9iYWxBbmRUaGVtZSIsIl9maW5kQ2hhcnRTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidGltZXJJZHMiLCJhZGRUaW1lciIsImNsZWFyQWxsVGltZXJzIiwiY2xlYXJJbnRlcnZhbCIsImNsZWFyVGltZW91dCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcXVlc3QiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RhdHVzIiwianNvbiIsImVycm9yTWlkZGxld2FyZSIsImFwcCIsInVzZSIsInJhdGVMaW1pdGluZ01pZGRsZXdhcmUiLCJyYXRlTGltaXRpbmdPcHRpb25zIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwicmF0ZUxpbWl0Iiwid2luZG93TXMiLCJsaW1pdCIsImRlbGF5TXMiLCJoYW5kbGVyIiwiZm9ybWF0Iiwic2VuZCIsImRlZmF1bHQiLCJza2lwIiwicXVlcnkiLCJhY2Nlc3NfdG9rZW4iLCJjb250ZW50VHlwZU1pZGRsZXdhcmUiLCJjb250ZW50VHlwZSIsImhlYWRlcnMiLCJyZXF1ZXN0Qm9keU1pZGRsZXdhcmUiLCJjb25uZWN0aW9uIiwicmVtb3RlQWRkcmVzcyIsInZhbGlkYXRlZE9wdGlvbnMiLCJwYXJhbXMiLCJmaWxlbmFtZSIsInZhbGlkYXRpb25NaWRkbGV3YXJlIiwicG9zdCIsInJldmVyc2VkTWltZSIsInBuZyIsImpwZWciLCJnaWYiLCJyZXF1ZXN0RXhwb3J0IiwicmVxdWVzdENvdW50ZXIiLCJjb25uZWN0aW9uQWJvcnRlZCIsInNvY2tldCIsImhhZEVycm9ycyIsImhlYWRlciIsImF0dGFjaG1lbnQiLCJleHBvcnRSb3V0ZXMiLCJzZXJ2ZXJTdGFydFRpbWUiLCJwYWNrYWdlRmlsZSIsInN1Y2Nlc3NSYXRlcyIsInJlY29yZEludGVydmFsIiwid2luZG93U2l6ZSIsIl9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlIiwiYSIsImIiLCJfc3RhcnRTdWNjZXNzUmF0ZSIsInNldEludGVydmFsIiwic3RhdHMiLCJzdWNjZXNzUmF0aW8iLCJoZWFsdGhSb3V0ZXMiLCJwZXJpb2QiLCJtb3ZpbmdBdmVyYWdlIiwiYm9vdFRpbWUiLCJ1cHRpbWUiLCJmbG9vciIsInNlcnZlclZlcnNpb24iLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VFeHBvcnRUaW1lIiwiYXR0ZW1wdGVkRXhwb3J0cyIsInBlcmZvcm1lZEV4cG9ydHMiLCJmYWlsZWRFeHBvcnRzIiwic3VjZXNzUmF0aW8iLCJ0b0ZpeGVkIiwic3ZnRXhwb3J0cyIsImpzb25FeHBvcnRzIiwic3ZnRXhwb3J0c0F0dGVtcHRzIiwianNvbkV4cG9ydHNBdHRlbXB0cyIsInVpUm91dGVzIiwic2VuZEZpbGUiLCJhY2NlcHRSYW5nZXMiLCJ2ZXJzaW9uQ2hhbmdlUm91dGVzIiwiYWRtaW5Ub2tlbiIsInRva2VuIiwiYWN0aXZlU2VydmVycyIsIk1hcCIsImV4cHJlc3MiLCJzdGFydFNlcnZlciIsInNlcnZlck9wdGlvbnMiLCJ1cGxvYWRMaW1pdEJ5dGVzIiwic3RvcmFnZSIsIm11bHRlciIsIm1lbW9yeVN0b3JhZ2UiLCJ1cGxvYWQiLCJsaW1pdHMiLCJmaWVsZFNpemUiLCJkaXNhYmxlIiwiY29ycyIsIm1ldGhvZHMiLCJzZXQiLCJ1cmxlbmNvZGVkIiwiZXh0ZW5kZWQiLCJub25lIiwic3RhdGljIiwiaHR0cFNlcnZlciIsImNyZWF0ZVNlcnZlciIsIl9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwibGlzdGVuIiwiY2VydCIsImh0dHBzU2VydmVyIiwiY2xvc2VTZXJ2ZXJzIiwiZGVsZXRlIiwiZ2V0U2VydmVycyIsImdldEV4cHJlc3MiLCJnZXRBcHAiLCJlbmFibGVSYXRlTGltaXRpbmciLCJtaWRkbGV3YXJlcyIsInNodXRkb3duQ2xlYW5VcCIsImV4aXRDb2RlIiwiZXhpdCIsImluaXRFeHBvcnQiLCJpbml0T3B0aW9ucyIsIl9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyIsImNvZGUiXSwi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 diff --git a/dist/index.esm.js b/dist/index.esm.js index b7fc6154..b730bda3 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -1,2 +1,2 @@ -import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?e:join(__dirname,e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; +import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,normalize,resolve,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?normalize(e):resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; //# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index 2e4e3aee..1c954d2f 100644 --- a/dist/index.esm.js.map +++ b/dist/index.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? path : join(__dirname, path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getCopy=true] - Specifies whether to return a copied\r\n * object of the global options (`true`) or a reference to the global options\r\n * object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} A copy of the global options object, or a reference\r\n * to the global options object.\r\n */\r\nexport function getOptions(getCopy = true) {\r\n return getCopy ? deepCopy(globalOptions) : globalOptions;\r\n}\r\n\r\n/**\r\n * Updates a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(getOptions(getCopy), newOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return reference to the global options\r\n return getOptions(false);\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Get the allow flags for the custom logic check\r\n const { allowCodeExecution, allowFileResources } = getOptions().customLogic;\r\n\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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') ? false : true;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n path: getAbsolutePath(cssImportPath)\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `imageOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} imageOptions - The `imageOptions` object, which should\r\n * include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(imageOptions, endCallback) {\r\n try {\r\n // Check if provided options are in an object\r\n if (!isObject(imageOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: imageOptions.export,\r\n customLogic: imageOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 = readFileSync(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = readFileSync(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","join","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","getCopy","updateOptions","newOptions","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","resolve","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","imageOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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CKA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMkB,cAAgB,CAC3BC,UAAW,CACT9B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFsD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbpG,KAAM,OACNqG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP7D,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbpG,KAAM,SAGVwG,OAAQ,CACN9D,MAAO,8BACPsD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbpG,KAAM,SAGVyG,WAAY,CACV/D,OAAO,EACPsD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbpG,KAAM,WAGV0G,UAAW,CACThE,MAAO,SACPsD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbpG,KAAM,SAGV2G,YAAa,CACXjE,MAAO,CAAC,aAAc,kBAAmB,iBACzCsD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbpG,KAAM,cACN4G,aAAc,0DAGlBC,cAAe,CACbnE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join, normalize, resolve } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? normalize(path) : resolve(path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getCopy=true] - Specifies whether to return a copied\r\n * object of the global options (`true`) or a reference to the global options\r\n * object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} A copy of the global options object, or a reference\r\n * to the global options object.\r\n */\r\nexport function getOptions(getCopy = true) {\r\n return getCopy ? deepCopy(globalOptions) : globalOptions;\r\n}\r\n\r\n/**\r\n * Updates a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(getOptions(getCopy), newOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return reference to the global options\r\n return getOptions(false);\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Get the allow flags for the custom logic check\r\n const { allowCodeExecution, allowFileResources } = getOptions().customLogic;\r\n\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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') ? false : true;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n path: getAbsolutePath(cssImportPath)\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `imageOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} imageOptions - The `imageOptions` object, which should\r\n * include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(imageOptions, endCallback) {\r\n try {\r\n // Check if provided options are in an object\r\n if (!isObject(imageOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: imageOptions.export,\r\n customLogic: imageOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 = readFileSync(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = readFileSync(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","normalize","resolve","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","join","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","getCopy","updateOptions","newOptions","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","imageOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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KA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMmB,cAAgB,CAC3BC,UAAW,CACT/B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFuD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbtG,KAAM,OACNuG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP9D,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbtG,KAAM,SAGV0G,OAAQ,CACN/D,MAAO,8BACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbtG,KAAM,SAGV2G,WAAY,CACVhE,OAAO,EACPuD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbtG,KAAM,WAGV4G,UAAW,CACTjE,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbtG,KAAM,SAGV6G,YAAa,CACXlE,MAAO,CAAC,aAAc,kBAAmB,iBACzCuD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbtG,KAAM,cACN8G,aAAc,0DAGlBC,cAAe,CACbpE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,k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"} \ No newline at end of file diff --git a/lib/utils.js b/lib/utils.js index 0624e79f..ca07f9e9 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -19,7 +19,7 @@ See LICENSE file in root for details. */ import { readFileSync } from 'fs'; -import { isAbsolute, join } from 'path'; +import { isAbsolute, join, normalize, resolve } from 'path'; import { fileURLToPath } from 'url'; const MAX_BACKOFF_ATTEMPTS = 6; @@ -225,7 +225,7 @@ export function fixType(type, outfile = null) { * @returns {string} The absolute path. */ export function getAbsolutePath(path) { - return isAbsolute(path) ? path : join(__dirname, path); + return isAbsolute(path) ? normalize(path) : resolve(path); } /** diff --git a/package-lock.json b/package-lock.json index 41988281..7940663c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3327,9 +3327,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.84", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.84.tgz", - "integrity": "sha512-I+DQ8xgafao9Ha6y0qjHHvpZ9OfyA1qKlkHkjywxzniORU2awxyz7f/iVJcULmrF2yrM3nHQf+iDjJtbbexd/g==", + "version": "1.5.85", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.85.tgz", + "integrity": "sha512-UgTI7ZHxtSjOUwV0vZLpqT604U1Z8L3bq8mAtAKtuRPlMZ/6dLFMYgYnLdXSi/urbVTP2ykDb9EDDUrdIzw4Qg==", "dev": true, "license": "ISC" }, @@ -8915,21 +8915,21 @@ "license": "MIT" }, "node_modules/tldts": { - "version": "6.1.73", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.73.tgz", - "integrity": "sha512-/h4bVmuEMm57c2uCiAf1Q9mlQk7cA22m+1Bu0K92vUUtTVT9D4mOFWD9r4WQuTULcG9eeZtNKhLl0Il1LdKGog==", + "version": "6.1.74", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.74.tgz", + "integrity": "sha512-O5vTZ1UmmEmrLl/59U9igitnSMlprALLaLgbv//dEvjobPT9vyURhHXKMCDLEhn3qxZFIkb9PwAfNYV0Ol7RPQ==", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.73" + "tldts-core": "^6.1.74" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.73", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.73.tgz", - "integrity": "sha512-k1g5eX87vxu3g//6XMn62y4qjayu4cYby/PF7Ksnh4F4uUK1Z1ze/mJ4a+y5OjdJ+cXRp+YTInZhH+FGdUWy1w==", + "version": "6.1.74", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.74.tgz", + "integrity": "sha512-gTwtY6L2GfuxiL4CWpLknv9JDYYqBvKCk/BT5uAaAvCA0s6pzX7lr2IrkQZSUlnSjRHIjTl8ZwKCVXJ7XNRWYw==", "license": "MIT" }, "node_modules/tmpl": { diff --git a/tests/http/scenarios/globalAndThemeFromFiles.json b/tests/http/scenarios/doNotAllowGlobalAndThemeFromFiles.json similarity index 100% rename from tests/http/scenarios/globalAndThemeFromFiles.json rename to tests/http/scenarios/doNotAllowGlobalAndThemeFromFiles.json From 49e7f8aec62f7f6229c6ed541ae2be4050248d0f Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Thu, 23 Jan 2025 02:37:30 +0100 Subject: [PATCH 084/102] Corrections in the README file. --- README.md | 8 ++++++-- dist/index.cjs | 2 +- dist/index.esm.js.map | 2 +- lib/utils.js | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 79532e48..9d399698 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Significant changes have been made to the API for using the server as a Node.js An important note is that the Export Server now requires `Node.js v18.12.0` or a higher version. -Additionally, with the v3 release, we transitioned from HTTP to HTTPS for export.highcharts.com, so all requests sent to our public server now must use the HTTPS protocol. +Additionally, with the v3 release, we transitioned from HTTP to HTTPS for `export.highcharts.com`, so all requests sent to our public server now must use the HTTPS protocol. ## Changelog @@ -111,6 +111,10 @@ This flexibility is particularly useful for deciding whether global options shou The `singleExport()`, `batchExport()`, and `startExport()` functions must be provided with at least partial options that include one of the following options from the `export` section: `infile`, `instr`, `svg`, or `batch`. Any missing values in the provided options object will automatically default to those specified in the global options object. Unlike other API functions, options provided to the export functions will not be merged into the global options object, as these options represent a specific export process. To make the export options global, you can use the `updateOptions()` function before initiating the export. +### Options Setting + +Essentially, all options can be configured through `.env`, the CLI, and prompts, with one exception: the `HIGHCHARTS_ADMIN_TOKEN`, which is only available as an environment variable. + ## Default JSON Config The JSON below represents the default configuration stored in the `./lib/schemas/config.js` file. If no `.env` file is found (more details on the file and environment variables below), these options will be used. The configuration is not recommended to be modified directly, as it can typically be managed through other sources. @@ -908,7 +912,7 @@ The Export Server attaches event listeners to `process.exit`, `uncaughtException Listeners are also attached to handle `uncaught exceptions`. If an exception occurs, the entire pool and browser instance are terminated, and the application is shut down. -If you do not want this behavior, start the server with `--listenToProcessExits 0` or `--listenToProcessExits false`. +If you do not want this behavior, start the server with `--listenToProcessExits false`. Be aware though, that if you disable this and you do not take great care to manually kill the pool of resources along with a browser instance, your server will bleed memory when the app is terminated. diff --git a/dist/index.cjs b/dist/index.cjs index 62632696..5ea96214 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?path.normalize(e):path.resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIGpvaW4sIG5vcm1hbGl6ZSwgcmVzb2x2ZSB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBkaXJlY3RvcnkgcGF0aFxyXG5leHBvcnQgY29uc3QgX19kaXJuYW1lID0gZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuLi8uJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFuZCBzdGFuZGFyZGl6ZXMgdGV4dCBieSByZXBsYWNpbmcgbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZVxyXG4gKiBjaGFyYWN0ZXJzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIHRyaW1taW5nIGFueSBsZWFkaW5nIG9yIHRyYWlsaW5nXHJcbiAqIHdoaXRlc3BhY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhclRleHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyB0aGUgJy9cXHNcXHMrL2cnIFJlZ0V4cC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtyZXBsYWNlcj0nICddIC0gVGhlIHN0cmluZyB1c2VkIHRvIHJlcGxhY2UgbXVsdGlwbGVcclxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgJyAnIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyVGV4dCh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpIHtcclxuICByZXR1cm4gdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBkZWVwQ29weVxyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8QXJyYXkpfSBvYmpBcnIgLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fEFycmF5KX0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDb3B5KG9iakFycikge1xyXG4gIC8vIElmIHRoZSBgb2JqQXJyYCBpcyBudWxsIG9yIG5vdCBvZiB0aGUgYG9iamVjdGAgdHlwZSwgcmV0dXJuIGl0XHJcbiAgaWYgKG9iakFyciA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqQXJyICE9PSAnb2JqZWN0Jykge1xyXG4gICAgcmV0dXJuIG9iakFycjtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZWl0aGVyIGEgbmV3IGFycmF5IG9yIGEgbmV3IG9iamVjdFxyXG4gIGNvbnN0IG9iakFyckNvcHkgPSBBcnJheS5pc0FycmF5KG9iakFycikgPyBbXSA6IHt9O1xyXG5cclxuICAvLyBSZWN1cnNpdmVseSBjb3B5IGVhY2ggcHJvcGVydHlcclxuICBmb3IgKGNvbnN0IGtleSBpbiBvYmpBcnIpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqQXJyLCBrZXkpKSB7XHJcbiAgICAgIG9iakFyckNvcHlba2V5XSA9IGRlZXBDb3B5KG9iakFycltrZXldKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY29waWVkIG9iamVjdFxyXG4gIHJldHVybiBvYmpBcnJDb3B5O1xyXG59XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZXhwQmFja29mZlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBmdW5jdGlvbiB0byBiZSByZXRyaWVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYDBgLlxyXG4gKiBAcGFyYW0gey4uLnVua25vd259IGFyZ3MgLSBBcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSByZXN1bHRcclxuICogb2YgdGhlIGZ1bmN0aW9uIGlmIHN1Y2Nlc3NmdWwuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCBpZiB0aGUgbWF4aW11bSBudW1iZXIgb2YgYXR0ZW1wdHNcclxuICogaXMgcmVhY2hlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleHBCYWNrb2ZmKGZuLCBhdHRlbXB0ID0gMCwgLi4uYXJncykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBUcnkgdG8gY2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIHJldHVybiBhd2FpdCBmbiguLi5hcmdzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gQ2FsY3VsYXRlIGRlbGF5IGluIG1zXHJcbiAgICBjb25zdCBkZWxheUluTXMgPSAyICoqIGF0dGVtcHQgKiAxMDAwO1xyXG5cclxuICAgIC8vIElmIHRoZSBhdHRlbXB0IGV4Y2VlZHMgdGhlIG1heGltdW0gYXR0ZW1wdHMgb2YgcmVwZWF0LCB0aHJvdyBhbiBlcnJvclxyXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXYWl0IGdpdmVuIGFtb3VudCBvZiB0aW1lXHJcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIGRlbGF5SW5NcykpO1xyXG5cclxuICAgIC8vLyBUTyBETzogQ29ycmVjdFxyXG4gICAgLy8gLy8gSW5mb3JtYXRpb24gYWJvdXQgdGhlIHJlc291cmNlIHRpbWVvdXRcclxuICAgIC8vIGxvZyhcclxuICAgIC8vICAgMyxcclxuICAgIC8vICAgYFt1dGlsc10gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIG9mIElEOiAke2FyZ3NbMF19LmBcclxuICAgIC8vICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRqdXN0cyB0aGUgY29uc3RydWN0b3IgbmFtZSBieSB0cmFuc2Zvcm1pbmcgYW5kIG5vcm1hbGl6aW5nIGl0IGJhc2VkXHJcbiAqIG9uIGNvbW1vbiBjaGFydCB0eXBlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeENvbnN0clxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY29uc3RyIC0gVGhlIG9yaWdpbmFsIGNvbnN0cnVjdG9yIG5hbWUgdG8gYmUgZml4ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IgbmFtZSwgb3IgJ2NoYXJ0JyBpZiB0aGUgaW5wdXRcclxuICogaXMgbm90IHJlY29nbml6ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4Q29uc3RyKGNvbnN0cikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBGaXggdGhlIGNvbnN0cnVjdG9yIGJ5IGxvd2VyaW5nIGNhc2luZ1xyXG4gICAgY29uc3QgZml4ZWRDb25zdHIgPSBgJHtjb25zdHIudG9Mb3dlckNhc2UoKS5yZXBsYWNlKCdjaGFydCcsICcnKX1DaGFydGA7XHJcblxyXG4gICAgLy8gSGFuZGxlIHRoZSBjYXNlIHdoZXJlIHRoZSByZXN1bHQgaXMganVzdCAnQ2hhcnQnXHJcbiAgICBpZiAoZml4ZWRDb25zdHIgPT09ICdDaGFydCcpIHtcclxuICAgICAgZml4ZWRDb25zdHIudG9Mb3dlckNhc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZXR1cm4gdGhlIGNvcnJlY3RlZCBjb25zdHJ1Y3Rvciwgb3RoZXJ3aXNlIGRlZmF1bHQgdG8gJ2NoYXJ0J1xyXG4gICAgcmV0dXJuIFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10uaW5jbHVkZXMoXHJcbiAgICAgIGZpeGVkQ29uc3RyXHJcbiAgICApXHJcbiAgICAgID8gZml4ZWRDb25zdHJcclxuICAgICAgOiAnY2hhcnQnO1xyXG4gIH0gY2F0Y2gge1xyXG4gICAgLy8gRGVmYXVsdCB0byAnY2hhcnQnIGluIGNhc2Ugb2YgYW55IGVycm9yXHJcbiAgICByZXR1cm4gJ2NoYXJ0JztcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGaXhlcyB0aGUgb3V0ZmlsZSBiYXNlZCBvbiBwcm92aWRlZCB0eXBlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZml4T3V0ZmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSBvcmlnaW5hbCBleHBvcnQgdHlwZS5cclxuICogQHBhcmFtIHtzdHJpbmd9IG91dGZpbGUgLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgb3V0ZmlsZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhPdXRmaWxlKHR5cGUsIG91dGZpbGUpIHtcclxuICAvLyBHZXQgdGhlIGZpbGUgbmFtZSBmcm9tIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgY29uc3QgZmlsZU5hbWUgPSBnZXRBYnNvbHV0ZVBhdGgob3V0ZmlsZSB8fCAnY2hhcnQnKVxyXG4gICAgLnNwbGl0KCcuJylcclxuICAgIC5zaGlmdCgpO1xyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IG91dGZpbGVcclxuICByZXR1cm4gYCR7ZmlsZU5hbWV9LiR7dHlwZX1gO1xyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIGV4cG9ydCB0eXBlIGJhc2VkIG9uIE1JTUUgdHlwZXMgYW5kIGZpbGUgZXh0ZW5zaW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeFR5cGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbb3V0ZmlsZT1udWxsXSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYG51bGxgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeFR5cGUodHlwZSwgb3V0ZmlsZSA9IG51bGwpIHtcclxuICAvLyBNSU1FIHR5cGVzXHJcbiAgY29uc3QgbWltZVR5cGVzID0ge1xyXG4gICAgJ2ltYWdlL3BuZyc6ICdwbmcnLFxyXG4gICAgJ2ltYWdlL2pwZWcnOiAnanBlZycsXHJcbiAgICAnYXBwbGljYXRpb24vcGRmJzogJ3BkZicsXHJcbiAgICAnaW1hZ2Uvc3ZnK3htbCc6ICdzdmcnXHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IGZvcm1hdHNcclxuICBjb25zdCBmb3JtYXRzID0gT2JqZWN0LnZhbHVlcyhtaW1lVHlwZXMpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICAvLyBTdXBwb3J0IHRoZSBKUEcgdHlwZVxyXG4gICAgaWYgKG91dFR5cGUgPT09ICdqcGcnKSB7XHJcbiAgICAgIHR5cGUgPSAnanBlZyc7XHJcbiAgICB9IGVsc2UgaWYgKGZvcm1hdHMuaW5jbHVkZXMob3V0VHlwZSkgJiYgdHlwZSAhPT0gb3V0VHlwZSkge1xyXG4gICAgICB0eXBlID0gb3V0VHlwZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGNvcnJlY3QgdHlwZVxyXG4gIHJldHVybiBtaW1lVHlwZXNbdHlwZV0gfHwgZm9ybWF0cy5maW5kKCh0KSA9PiB0ID09PSB0eXBlKSB8fCAncG5nJztcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gcGF0aCBpcyByZWxhdGl2ZSBvciBhYnNvbHV0ZSBhbmQgcmV0dXJucyB0aGUgY29ycmVjdGVkLFxyXG4gKiBhYnNvbHV0ZSBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBYnNvbHV0ZVBhdGhcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byBiZSBjaGVja2VkIG9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgYWJzb2x1dGUgcGF0aC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBYnNvbHV0ZVBhdGgocGF0aCkge1xyXG4gIHJldHVybiBpc0Fic29sdXRlKHBhdGgpID8gbm9ybWFsaXplKHBhdGgpIDogcmVzb2x2ZShwYXRoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGlucHV0IGRhdGEgdG8gYSBCYXNlNjQgc3RyaW5nIGJhc2VkIG9uIHRoZSBleHBvcnQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJhc2U2NFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgaW5wdXQgdG8gYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZvcm1hdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBCYXNlNjQgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRCYXNlNjQoaW5wdXQsIHR5cGUpIHtcclxuICAvLyBGb3IgcGRmIGFuZCBzdmcgdHlwZXMgdGhlIGlucHV0IG11c3QgYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZyb20gYSBidWZmZXJcclxuICBpZiAodHlwZSA9PT0gJ3BkZicgfHwgdHlwZSA9PSAnc3ZnJykge1xyXG4gICAgcmV0dXJuIEJ1ZmZlci5mcm9tKGlucHV0LCAndXRmOCcpLnRvU3RyaW5nKCdiYXNlNjQnKTtcclxuICB9XHJcblxyXG4gIC8vIEZvciBwbmcgYW5kIGpwZWcgaW5wdXQgaXMgYWxyZWFkeSBhIEJhc2U2NCBzdHJpbmdcclxuICByZXR1cm4gaW5wdXQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHN0cmluZ2lmaWVkIGRhdGUgd2l0aG91dCB0aGUgR01UIHRleHQgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3RGF0ZSgpIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIHJldHVybiBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBzdG9yZWQgdGltZSB2YWx1ZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlVGltZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGVUaW1lKCkge1xyXG4gIHJldHVybiBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gaXRlbSBpcyBhbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgaXRlbSBpcyBhbiBvYmplY3QsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdChpdGVtKSB7XHJcbiAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChpdGVtKSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIG9iamVjdCBpcyBlbXB0eS5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW0gLSBUaGUgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdEVtcHR5KGl0ZW0pIHtcclxuICByZXR1cm4gKFxyXG4gICAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgICAhQXJyYXkuaXNBcnJheShpdGVtKSAmJlxyXG4gICAgaXRlbSAhPT0gbnVsbCAmJlxyXG4gICAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kLCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChpdGVtKSB7XHJcbiAgY29uc3QgcmVnZXhQYXR0ZXJucyA9IFtcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT9sb2NhbGhvc3RcXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEwXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMjdcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE3MlxcLigxWzYtOV18MlswLTldfDNbMC0xXSlcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xOTJcXC4xNjhcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiL1xyXG4gIF07XHJcblxyXG4gIHJldHVybiByZWdleFBhdHRlcm5zLnNvbWUoKHBhdHRlcm4pID0+IHBhdHRlcm4udGVzdChpdGVtKSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVdGlsaXR5IHRvIG1lYXN1cmUgZWxhcHNlZCB0aW1lIHVzaW5nIHRoZSBOb2RlLmpzIGBwcm9jZXNzLmhydGltZSgpYCBtZXRob2QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtZWFzdXJlVGltZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IEEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBlbGFwc2VkIHRpbWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1lYXN1cmVUaW1lKCkge1xyXG4gIGNvbnN0IHN0YXJ0ID0gcHJvY2Vzcy5ocnRpbWUuYmlnaW50KCk7XHJcbiAgcmV0dXJuICgpID0+IE51bWJlcihwcm9jZXNzLmhydGltZS5iaWdpbnQoKSAtIHN0YXJ0KSAvIDEwMDAwMDA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSb3VuZHMgYSBudW1iZXIgdG8gdGhlIHNwZWNpZmllZCBwcmVjaXNpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByb3VuZE51bWJlclxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBwcmVjaXNpb24gLSBUaGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIHRvIHJvdW5kIHRvLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgcm91bmRlZCBudW1iZXIuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcm91bmROdW1iZXIodmFsdWUsIHByZWNpc2lvbiA9IDEpIHtcclxuICBjb25zdCBtdWx0aXBsaWVyID0gTWF0aC5wb3coMTAsIHByZWNpc2lvbiB8fCAwKTtcclxuICByZXR1cm4gTWF0aC5yb3VuZCgrdmFsdWUgKiBtdWx0aXBsaWVyKSAvIG11bHRpcGxpZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBhIHZhbHVlIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHRvQm9vbGVhblxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSBib29sZWFuIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dCB2YWx1ZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB0b0Jvb2xlYW4oaXRlbSkge1xyXG4gIHJldHVybiBbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTicsICcwJywgJyddLmluY2x1ZGVzKGl0ZW0pXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6ICEhaXRlbTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFdyYXBzIGN1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgaXQgc2FmZWx5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gd3JhcEFyb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tQ29kZSAtIFRoZSBjdXN0b20gY29kZSB0byBiZSB3cmFwcGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEZsYWcgdG8gYWxsb3cgbG9hZGluZyBjb2RlIGZyb20gYSBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtpc0NhbGxiYWNrPWZhbHNlXSAtIEZsYWcgdGhhdCBpbmRpY2F0ZXMgdGhlIHJldHVybmVkIGNvZGVcclxuICogbXVzdCBiZSBpbiBhIGNhbGxiYWNrIGZvcm1hdC5cclxuICpcclxuICogQHJldHVybnMgeyhzdHJpbmd8bnVsbCl9IFRoZSB3cmFwcGVkIGN1c3RvbSBjb2RlIG9yIG51bGwgaWYgd3JhcHBpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gd3JhcEFyb3VuZChjdXN0b21Db2RlLCBhbGxvd0ZpbGVSZXNvdXJjZXMsIGlzQ2FsbGJhY2sgPSBmYWxzZSkge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICAvLyBMb2FkIGEgZmlsZSBpZiB0aGUgZmlsZSByZXNvdXJjZXMgYXJlIGFsbG93ZWRcclxuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICAgID8gd3JhcEFyb3VuZChcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChjdXN0b21Db2RlKSwgJ3V0ZjgnKSxcclxuICAgICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgICBpc0NhbGxiYWNrXHJcbiAgICAgICAgICApXHJcbiAgICAgICAgOiBudWxsO1xyXG4gICAgfSBlbHNlIGlmIChcclxuICAgICAgIWlzQ2FsbGJhY2sgJiZcclxuICAgICAgKGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24oKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbiAoKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpID0+JykpXHJcbiAgICApIHtcclxuICAgICAgLy8gVHJlYXQgYSBmdW5jdGlvbiBhcyBhIHNlbGYtaW52b2tpbmcgZXhwcmVzc2lvblxyXG4gICAgICByZXR1cm4gYCgke2N1c3RvbUNvZGV9KSgpYDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBPciByZXR1cm4gYXMgYSBzdHJpbmdpZmllZCBjb2RlXHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGRlZXBDb3B5LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4Q29uc3RyLFxyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIGdldE5ld0RhdGUsXHJcbiAgZ2V0TmV3RGF0ZVRpbWUsXHJcbiAgaXNPYmplY3QsXHJcbiAgaXNPYmplY3RFbXB0eSxcclxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxyXG4gIG1lYXN1cmVUaW1lLFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHRvQm9vbGVhbixcclxuICB3cmFwQXJvdW5kXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSBmb3IgbWFuYWdpbmcgbG9nZ2luZyBmdW5jdGlvbmFsaXR5IHdpdGggY3VzdG9taXphYmxlXHJcbiAqIGxvZyBsZXZlbHMsIGNvbnNvbGUgYW5kIGZpbGUgbG9nZ2luZyBvcHRpb25zLCBhbmQgZXJyb3IgaGFuZGxpbmcgc3VwcG9ydC5cclxuICogVGhlIG1vZHVsZSBhbHNvIGVuc3VyZXMgdGhhdCBmaWxlLWJhc2VkIGxvZ3MgYXJlIHN0b3JlZCBpbiBhIHN0cnVjdHVyZWRcclxuICogZGlyZWN0b3J5LCBjcmVhdGluZyB0aGUgbmVjZXNzYXJ5IHBhdGhzIGF1dG9tYXRpY2FsbHkgaWYgdGhleSBkbyBub3QgZXhpc3QuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYXBwZW5kRmlsZSwgZXhpc3RzU3luYywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGgsIGdldE5ld0RhdGUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmNvbnN0IGxvZ2dpbmcgPSB7XHJcbiAgLy8gRmxhZ3MgZm9yIGxvZ2dpbmcgc3RhdHVzXHJcbiAgdG9Db25zb2xlOiB0cnVlLFxyXG4gIHRvRmlsZTogZmFsc2UsXHJcbiAgcGF0aENyZWF0ZWQ6IGZhbHNlLFxyXG4gIC8vIEZ1bGwgcGF0aCB0byB0aGUgbG9nIGZpbGVcclxuICBwYXRoVG9Mb2c6ICcnLFxyXG4gIC8vIExvZyBsZXZlbHNcclxuICBsZXZlbHNEZXNjOiBbXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnZXJyb3InLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzBdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3dhcm5pbmcnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzFdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ25vdGljZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMl1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAndmVyYm9zZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbM11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnYmVuY2htYXJrJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1s0XVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGEgbWVzc2FnZSB3aXRoIGEgc3BlY2lmaWVkIGxvZyBsZXZlbC4gQWNjZXB0cyBhIHZhcmlhYmxlIG51bWJlclxyXG4gKiBvZiBhcmd1bWVudHMuIFRoZSBhcmd1bWVudHMgYWZ0ZXIgdGhlIGBsZXZlbGAgYXJlIHBhc3NlZCB0byBgY29uc29sZS5sb2dgXHJcbiAqIGFuZC9vciB1c2VkIHRvIGNvbnN0cnVjdCBhbmQgYXBwZW5kIG1lc3NhZ2VzIHRvIGEgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dcclxuICpcclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQW4gYXJyYXkgb2YgYXJndW1lbnRzIHdoZXJlIHRoZSBmaXJzdCBpcyB0aGUgbG9nXHJcbiAqIGxldmVsIGFuZCB0aGUgcmVtYWluaW5nIGFyZSBzdHJpbmdzIHVzZWQgdG8gYnVpbGQgdGhlIGxvZyBtZXNzYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nKC4uLmFyZ3MpIHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbHNEZXNjLCBsZXZlbCB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlIG9yIGlzIGl0IGEgYmVuY2htYXJrIGxvZ1xyXG4gIGlmIChcclxuICAgIG5ld0xldmVsICE9PSA1ICYmXHJcbiAgICAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKVxyXG4gICkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdCh0ZXh0cylcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIGFsb25nIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ1dpdGhTdGFja1xyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbG9nIGxldmVsLlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QgY29udGFpbmluZyB0aGUgc3RhY2sgdHJhY2UuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21NZXNzYWdlIC0gQW4gb3B0aW9uYWwgY3VzdG9tIG1lc3NhZ2UgdG8gYmUgaW5jbHVkZWRcclxuICogaW4gdGhlIGxvZyBhbG9uZ3NpZGUgdGhlIGVycm9yLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nV2l0aFN0YWNrKG5ld0xldmVsLCBlcnJvciwgY3VzdG9tTWVzc2FnZSkge1xyXG4gIC8vIEdldCB0aGUgbWFpbiBtZXNzYWdlXHJcbiAgY29uc3QgbWFpbk1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlIHx8IChlcnJvciAmJiBlcnJvci5tZXNzYWdlKSB8fCAnJztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQWRkIHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXHJcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID0gZXJyb3IgJiYgZXJyb3Iuc3RhY2s7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2UsIGlmIGV4aXN0c1xyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlXTtcclxuICBpZiAoc3RhY2tNZXNzYWdlKSB7XHJcbiAgICB0ZXh0cy5wdXNoKCdcXG4nLCBzdGFja01lc3NhZ2UpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcclxuICAgICAgICB0ZXh0cy5zaGlmdCgpW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAuLi50ZXh0c1xyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpbml0TG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgbG9nZ2luZ2Agb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpbml0TG9nZ2luZyhsb2dnaW5nT3B0aW9ucykge1xyXG4gIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGBsb2dnaW5nT3B0aW9uc2Agb2JqZWN0XHJcbiAgY29uc3QgeyBsZXZlbCwgZGVzdCwgZmlsZSwgdG9Db25zb2xlLCB0b0ZpbGUgfSA9IGxvZ2dpbmdPcHRpb25zO1xyXG5cclxuICAvLyBSZXNldCBmbGFncyB0byB0aGUgZGVmYXVsdCB2YWx1ZXNcclxuICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgbG9nZ2luZy5wYXRoVG9Mb2cgPSAnJztcclxuXHJcbiAgLy8gU2V0IHRoZSBsb2dnaW5nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobGV2ZWwpO1xyXG5cclxuICAvLyBTZXQgdGhlIGNvbnNvbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nKHRvQ29uc29sZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgZmlsZSBsb2dnaW5nXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGxvZyBsZXZlbCB0byB0aGUgc3BlY2lmaWVkIHZhbHVlLiBMb2cgbGV2ZWxzIGFyZSAoYDBgID0gbm8gbG9nZ2luZyxcclxuICogYDFgID0gZXJyb3IsIGAyYCA9IHdhcm5pbmcsIGAzYCA9IG5vdGljZSwgYDRgID0gdmVyYm9zZSwgb3IgYDVgID0gYmVuY2htYXJrKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldExvZ0xldmVsXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsb2cgbGV2ZWwgdG8gYmUgc2V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldExvZ0xldmVsKGxldmVsKSB7XHJcbiAgaWYgKFxyXG4gICAgTnVtYmVyLmlzSW50ZWdlcihsZXZlbCkgJiZcclxuICAgIGxldmVsID49IDAgJiZcclxuICAgIGxldmVsIDw9IGxvZ2dpbmcubGV2ZWxzRGVzYy5sZW5ndGhcclxuICApIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgbGV2ZWxgIG9wdGlvblxyXG4gICAgbG9nZ2luZy5sZXZlbCA9IGxldmVsO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgY29uc29sZSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB0b0NvbnNvbGUgLSBUaGUgZmxhZyBmb3Igc2V0dGluZyB0aGUgbG9nZ2luZyB0byB0aGUgY29uc29sZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpIHtcclxuICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYHRvQ29uc29sZWAgb3B0aW9uXHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSAhIXRvQ29uc29sZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUgbmFtZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBkZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggd2hlcmUgdGhlIGxvZyBmaWxlIHNob3VsZFxyXG4gKiBiZSBzYXZlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGZpbGUgLSBUaGUgbmFtZSBvZiB0aGUgbG9nIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9GaWxlIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2dnaW5nIHNob3VsZFxyXG4gKiBiZSBkaXJlY3RlZCB0byBhIGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0ZpbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9GaWxlID0gISF0b0ZpbGU7XHJcblxyXG4gIC8vIFNldCB0aGUgYGRlc3RgIGFuZCBgZmlsZWAgb3B0aW9ucyBvbmx5IGlmIHRoZSBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZFxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ID0gZGVzdCB8fCAnJztcclxuICAgIGxvZ2dpbmcuZmlsZSA9IGZpbGUgfHwgJyc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyB0aGUgcHJvdmlkZWQgdGV4dHMgdG8gYSBmaWxlLCBpZiBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZC4gSXQgY3JlYXRlc1xyXG4gKiB0aGUgbmVjZXNzYXJ5IGRpcmVjdG9yeSBzdHJ1Y3R1cmUgaWYgbm90IGFscmVhZHkgY3JlYXRlZCBhbmQgYXBwZW5kc1xyXG4gKiB0aGUgY29udGVudCwgaW5jbHVkaW5nIGFuIG9wdGlvbmFsIHByZWZpeCwgdG8gdGhlIHNwZWNpZmllZCBsb2cgZmlsZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2dUb0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBBbiBvcHRpb25hbCBwcmVmaXggdG8gYmUgYWRkZWQgdG8gZWFjaCBsb2cgZW50cnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpIHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpICYmXHJcbiAgICAgIG1rZGlyU3luYyhnZXRBYnNvbHV0ZVBhdGgobG9nZ2luZy5kZXN0KSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBmdWxsIHBhdGhcclxuICAgIGxvZ2dpbmcucGF0aFRvTG9nID0gZ2V0QWJzb2x1dGVQYXRoKGpvaW4obG9nZ2luZy5kZXN0LCBsb2dnaW5nLmZpbGUpKTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yICYmIGxvZ2dpbmcudG9GaWxlICYmIGxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29uZmlndXJhdGlvbiBtYW5hZ2VtZW50IG1vZHVsZSBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci5cclxuICogUHJvdmlkZXMgZGVmYXVsdCBjb25maWd1cmF0aW9ucyB0aGF0IHN1cHBvcnQgZW52aXJvbm1lbnQgdmFyaWFibGVzLCBDTElcclxuICogYXJndW1lbnRzLCBhbmQgaW50ZXJhY3RpdmUgcHJvbXB0cyBmb3IgY3VzdG9taXphdGlvbiBvZiBvcHRpb25zIGFuZCBmZWF0dXJlcy5cclxuICogQWRkaXRpb25hbGx5LCBpdCBtYXBzIGxlZ2FjeSBvcHRpb25zIHRvIG1vZGVybiBzdHJ1Y3R1cmVzLCBnZW5lcmF0ZXMgbmVzdGVkXHJcbiAqIGFyZ3VtZW50IG1hcHBpbmdzLCBhbmQgZGlzcGxheXMgQ0xJIHVzYWdlIGluZm9ybWF0aW9uLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBhbGwgYXZhaWxhYmxlIG9wdGlvbnMsIG9yZ2FuaXplZFxyXG4gKiBieSBzZWN0aW9ucy5cclxuICpcclxuICogVGhpcyBvYmplY3QgaW5jbHVkZXM6XHJcbiAqIC0gRGVmYXVsdCB2YWx1ZXMgZm9yIGVhY2ggb3B0aW9uXHJcbiAqIC0gRGF0YSB0eXBlcyBmb3IgdmFsaWRhdGlvblxyXG4gKiAtIE5hbWVzIG9mIGNvcnJlc3BvbmRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIC0gRGVzY3JpcHRpb25zIG9mIGVhY2ggcHJvcGVydHlcclxuICogLSBJbmZvcm1hdGlvbiB1c2VkIGZvciBwcm9tcHRzIGluIGludGVyYWN0aXZlIGNvbmZpZ3VyYXRpb25cclxuICogLSBbT3B0aW9uYWxdIENvcnJlc3BvbmRpbmcgQ0xJIGFyZ3VtZW50IG5hbWVzIGZvciBDTEkgdXNhZ2VcclxuICogLSBbT3B0aW9uYWxdIExlZ2FjeSBuYW1lcyBmcm9tIHRoZSBwcmV2aW91cyBQaGFudG9tSlMtYmFzZWQgc2VydmVyXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IHtcclxuICAgIGFyZ3M6IHtcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnLS1hbGxvdy1ydW5uaW5nLWluc2VjdXJlLWNvbnRlbnQnLFxyXG4gICAgICAgICctLWFzaC1uby1udWRnZXMnLFxyXG4gICAgICAgICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICAgICAgICctLWJsb2NrLW5ldy13ZWItY29udGVudHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYWNjZWxlcmF0ZWQtMmQtY2FudmFzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtbmV0d29ya2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLXRpbWVyLXRocm90dGxpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZGluZy1vY2NsdWRlZC13aW5kb3dzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJyZWFrcGFkJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNoZWNrZXItaW1hZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jbGllbnQtc2lkZS1waGlzaGluZy1kZXRlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LWV4dGVuc2lvbnMtd2l0aC1iYWNrZ3JvdW5kLXBhZ2VzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICAgICAgICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAgICAgICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZmVhdHVyZXM9Q2FsY3VsYXRlTmF0aXZlV2luT2NjbHVzaW9uLEludGVyZXN0RmVlZENvbnRlbnRTdWdnZXN0aW9ucyxXZWJPVFAnLFxyXG4gICAgICAgICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWlwYy1mbG9vZGluZy1wcm90ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWxvZ2dpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtbm90aWZpY2F0aW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1vZmZlci1zdG9yZS11bm1hc2tlZC13YWxsZXQtY2FyZHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtcG9wdXAtYmxvY2tpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJpbnQtcHJldmlldycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcm9tcHQtb24tcmVwb3N0JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXJlbmRlcmVyLWJhY2tncm91bmRpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2VhcmNoLWVuZ2luZS1jaG9pY2Utc2NyZWVuJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlc3Npb24tY3Jhc2hlZC1idWJibGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2l0ZS1pc29sYXRpb24tdHJpYWxzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICAgICAgICctLWRpc2FibGUtc3luYycsXHJcbiAgICAgICAgJy0tZW5hYmxlLXVuc2FmZS13ZWJncHUnLFxyXG4gICAgICAgICctLWhpZGUtY3Jhc2gtcmVzdG9yZS1idWJibGUnLFxyXG4gICAgICAgICctLWhpZGUtc2Nyb2xsYmFycycsXHJcbiAgICAgICAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgICAgICAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgICAgICAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAgICAgICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICAgICAgICctLW5vLXBpbmdzJyxcclxuICAgICAgICAnLS1uby1zYW5kYm94JyxcclxuICAgICAgICAnLS1uby1zdGFydHVwLXdpbmRvdycsXHJcbiAgICAgICAgJy0tbm8tenlnb3RlJyxcclxuICAgICAgICAnLS1wYXNzd29yZC1zdG9yZT1iYXNpYycsXHJcbiAgICAgICAgJy0tcHJvY2Vzcy1wZXItdGFiJyxcclxuICAgICAgICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuICAgICAgXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ1BVUFBFVEVFUl9BUkdTJyxcclxuICAgICAgY2xpTmFtZTogJ3B1cHBldGVlckFyZ3MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FycmF5IG9mIFB1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfVkVSU0lPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2RuVXJsOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NETl9VUkwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0NETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGbGFnIHRvIHJlZmV0Y2ggc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdG9yeSBwYXRoIGZvciBjYWNoZWQgSGlnaGNoYXJ0cyBzY3JpcHRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DT1JFX1NDUklQVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtb2R1bGVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ3N0b2NrJyxcclxuICAgICAgICAnbWFwJyxcclxuICAgICAgICAnZ2FudHQnLFxyXG4gICAgICAgICdleHBvcnRpbmcnLFxyXG4gICAgICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAgICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgICAgIC8vICdhbm5vdGF0aW9ucy1hZHZhbmNlZCcsXHJcbiAgICAgICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAgICAgJ2Jvb3N0JyxcclxuICAgICAgICAnZGF0YScsXHJcbiAgICAgICAgJ2RhdGEtdG9vbHMnLFxyXG4gICAgICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICAgICAnc3RhdGljLXNjYWxlJyxcclxuICAgICAgICAnYnJva2VuLWF4aXMnLFxyXG4gICAgICAgICdoZWF0bWFwJyxcclxuICAgICAgICAndGlsZW1hcCcsXHJcbiAgICAgICAgJ3RpbGVkd2VibWFwJyxcclxuICAgICAgICAndGltZWxpbmUnLFxyXG4gICAgICAgICd0cmVlbWFwJyxcclxuICAgICAgICAndHJlZWdyYXBoJyxcclxuICAgICAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgICAgICdkcmlsbGRvd24nLFxyXG4gICAgICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICAgICAnYnVsbGV0JyxcclxuICAgICAgICAnZnVubmVsJyxcclxuICAgICAgICAnZnVubmVsM2QnLFxyXG4gICAgICAgICdnZW9oZWF0bWFwJyxcclxuICAgICAgICAncHlyYW1pZDNkJyxcclxuICAgICAgICAnbmV0d29ya2dyYXBoJyxcclxuICAgICAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAgICAgJ3BhcmV0bycsXHJcbiAgICAgICAgJ3BhdHRlcm4tZmlsbCcsXHJcbiAgICAgICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAgICAgJ3ByaWNlLWluZGljYXRvcicsXHJcbiAgICAgICAgJ3NhbmtleScsXHJcbiAgICAgICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICAgICAnZGVwZW5kZW5jeS13aGVlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1sYWJlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1vbi1wb2ludCcsXHJcbiAgICAgICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICAgICAnc29uaWZpY2F0aW9uJyxcclxuICAgICAgICAvLyAnc3RvY2stdG9vbHMnLFxyXG4gICAgICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAgICAgJ3N1bmJ1cnN0JyxcclxuICAgICAgICAndmFyaWFibGUtcGllJyxcclxuICAgICAgICAndmFyaXdpZGUnLFxyXG4gICAgICAgICd2ZWN0b3InLFxyXG4gICAgICAgICd2ZW5uJyxcclxuICAgICAgICAnd2luZGJhcmInLFxyXG4gICAgICAgICd3b3JkY2xvdWQnLFxyXG4gICAgICAgICd4cmFuZ2UnLFxyXG4gICAgICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgICAgICdkcmFnLXBhbmVzJyxcclxuICAgICAgICAnZGVidWdnZXInLFxyXG4gICAgICAgICdkdW1iYmVsbCcsXHJcbiAgICAgICAgJ2xvbGxpcG9wJyxcclxuICAgICAgICAnY3lsaW5kZXInLFxyXG4gICAgICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgICAgICdkb3RwbG90JyxcclxuICAgICAgICAnbWFya2VyLWNsdXN0ZXJzJyxcclxuICAgICAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgICAgICdoZWlraW5hc2hpJyxcclxuICAgICAgICAnZmxvd21hcCcsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAnbmF2aWdhdG9yJyxcclxuICAgICAgICAndGV4dHBhdGgnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIG1vZHVsZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbmRpY2F0b3JTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2luZGljYXRvcnMtYWxsJ10sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGluZGljYXRvciBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjMwLjEvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuNDUvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBZGRpdGlvbmFsIGN1c3RvbSBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgICAgc2VwYXJhdG9yOiAnOydcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5GSUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0IGZpbGVuYW1lIHdpdGggdHlwZSwgZm9ybWF0dGVkIGNvcnJlY3RseSBhcyBKU09OIG9yIFNWRycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9JTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdmVycmlkZXMgdGhlIGBpbmZpbGVgIHdpdGggSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgU1ZHIGlucHV0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgb3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWxpYXMgZm9yIHRoZSBgaW5zdHJgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHN2Zzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TVkcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1NWRyBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IHRvIHJlbmRlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJhdGNoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0JBVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0JhdGNoIGpvYiBzdHJpbmcgd2l0aCBpbnB1dC9vdXRwdXQgcGFpcnM6IFwiaW49b3V0O2luPW91dDsuLi5cIicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG91dGZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1VURklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdXRwdXQgZmlsZW5hbWUgd2l0aCB0eXBlLiBDYW4gYmUganBlZywgcG5nLCBwZGYsIG9yIHN2ZyBhbmQgaWdub3JlcyBgdHlwZWAgb3B0aW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdHlwZToge1xyXG4gICAgICB2YWx1ZTogJ3BuZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1RZUEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0ZpbGUgZXhwb3J0IGZvcm1hdC4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3NlbGVjdCcsXHJcbiAgICAgICAgaGludDogJ0RlZmF1bHQ6IHBuZycsXHJcbiAgICAgICAgY2hvaWNlczogWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvbnN0cjoge1xyXG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfQ09OU1RSJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NoYXJ0IGNvbnN0cnVjdG9yLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBjaGFydCcsXHJcbiAgICAgICAgY2hvaWNlczogWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYjY0OiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0I2NCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byB0aGUgY2hhcnQgc2hvdWxkIGJlIHJlY2VpdmVkIGluIEJhc2U2NCBmb3JtYXQgaW5zdGVhZCBvZiBiaW5hcnknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vRG93bmxvYWQ6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfTk9fRE9XTkxPQUQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gaW5jbHVkZSBvciBleGNsdWRlIGF0dGFjaG1lbnQgaGVhZGVycyBpbiB0aGUgcmVzcG9uc2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgd2lkdGg6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfV0lEVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1dpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGVzIGNoYXJ0IHNldHRpbmdzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzY2FsZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TQ0FMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdTY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncy4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdEhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX0hFSUdIVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRXaWR0aDoge1xyXG4gICAgICB2YWx1ZTogNjAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWZhdWx0IHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0U2NhbGU6IHtcclxuICAgICAgdmFsdWU6IDEsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGVmYXVsdCBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldC4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBtaW46IDAuMSxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGdsb2JhbE9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0dMT0JBTF9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCBnbG9iYWwgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdGhlbWVPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9USEVNRV9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCB0aGVtZSBvcHRpb25zIGZvciBIaWdoY2hhcnRzLnNldE9wdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXN0ZXJpemF0aW9uVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMTUwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaWxsaXNlY29uZHMgdG8gd2FpdCBmb3Igd2VicGFnZSByZW5kZXJpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgY3VzdG9tTG9naWM6IHtcclxuICAgIGFsbG93Q29kZUV4ZWN1dGlvbjoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbGxvd3Mgb3IgZGlzYWxsb3dzIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBkdXJpbmcgZXhwb3J0aW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBpbmplY3Rpb24gb2YgZmlsZXN5c3RlbSByZXNvdXJjZXMgKGRpc2FibGVkIGluIHNlcnZlciBtb2RlKScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tQ29kZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DVVNUT01fQ09ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDdXN0b20gY29kZSB0byBleGVjdXRlIGJlZm9yZSBjaGFydCBpbml0aWFsaXphdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24sIGNvZGUgd3JhcHBlZCBpbiBhIGZ1bmN0aW9uLCBvciBhIC5qcyBmaWxlbmFtZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhbGxiYWNrOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0NBTExCQUNLJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0phdmFTY3JpcHQgY29kZSB0byBydW4gZHVyaW5nIGNvbnN0cnVjdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24gb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX1JFU09VUkNFUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBZGRpdGlvbmFsIHJlc291cmNlcyBhcyBKU09OLCBzdHJpbmdpZmllZCBKU09OLCBvciBmaWxlbmFtZSwgY29udGFpbmluZyBmaWxlcywganMsIGFuZCBjc3Mgc2VjdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsb2FkQ29uZmlnOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHJyxcclxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIHdpdGggYSBwcmUtZGVmaW5lZCBjb25maWd1cmF0aW9uIHRvIHVzZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZUNvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1Byb21wdC1iYXNlZCBvcHRpb24gc2V0dGluZywgc2F2ZWQgdG8gYSBwcm92aWRlZCBjb25maWcgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU3RhcnRzIHRoZSBzZXJ2ZXIgd2hlbiB0cnVlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIHZhbHVlOiAnMC4wLjAuMCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hvc3RuYW1lIG9mIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA3ODAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG51bWJlciBmb3IgdGhlIHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdXBsb2FkTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDMsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX1VQTE9BRF9MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSByZXF1ZXN0IGJvZHkgc2l6ZSBpbiBNQicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGlzcGxheXMgb3Igbm90IGFjdGlvbiBkdXJhdGlvbnMgaW4gbWlsbGlzZWNvbmRzIGR1cmluZyBzZXJ2ZXIgcmVxdWVzdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHByb3h5OiB7XHJcbiAgICAgIGhvc3Q6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfSE9TVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5SG9zdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdIb3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcG9ydDoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgb2YgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRpbWVvdXQ6IHtcclxuICAgICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX1RJTUVPVVQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVRpbWVvdXQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJhdGVMaW1pdGluZzoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdlbmFibGVSYXRlTGltaXRpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzZXJ2ZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBtYXhSZXF1ZXN0czoge1xyXG4gICAgICAgIHZhbHVlOiAxMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAncmF0ZUxpbWl0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgcGVyIG1pbnV0ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIHZhbHVlOiAxLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1cnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGltZSB3aW5kb3cgaW4gbWludXRlcyBmb3IgcmF0ZSBsaW1pdGluZycsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGRlbGF5OiB7XHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdEZWxheSBkdXJhdGlvbiBiZXR3ZWVuIHN1Y2Nlc3NpdmUgcmVxdWVzdHMgYmVmb3JlIHJlYWNoaW5nIHRoZSBsaW1pdCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRydXN0UHJveHk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTZXQgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBza2lwS2V5OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnS2V5IHRvIGJ5cGFzcyB0aGUgcmF0ZSBsaW1pdGVyLCB1c2VkIHdpdGggYHNraXBUb2tlbmAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTicsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUb2tlbiB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwS2V5YCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgU1NMIHByb3RvY29sJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgZm9yY2U6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0ZPUkNFJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsRm9yY2UnLFxyXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdzc2xPbmx5JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0ZvcmNlcyB0aGUgc2VydmVyIHRvIHVzZSBIVFRQUyBvbmx5IHdoZW4gdHJ1ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogNDQzLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX1BPUlQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgZm9yIHRoZSBTU0wgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgY2VydFBhdGg6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0NFUlRfUEFUSCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbENlcnRQYXRoJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsUGF0aCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHRoZSBTU0wgY2VydGlmaWNhdGUva2V5IGZpbGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX01JTl9XT1JLRVJTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaW5pbXVtIGFuZCBpbml0aWFsIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG1heFdvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDgsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NQVhfV09SS0VSUycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICd3b3JrZXJzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdvcmtMaW1pdDoge1xyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9XT1JLX0xJTUlUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdOdW1iZXIgb2YgdGFza3MgYSB3b3JrZXIgY2FuIGhhbmRsZSBiZWZvcmUgcmVzdGFydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWNxdWlyZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBhY3F1aXJpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3JlYXRlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGlkbGVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiAzMDAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0lETEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGRlc3Ryb3lpbmcgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWw6IHtcclxuICAgICAgdmFsdWU6IDIwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYmVmb3JlIHJldHJ5aW5nIHJlc291cmNlIGNyZWF0aW9uIG9uIGZhaWx1cmUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0ludGVydmFsIGluIG1pbGxpc2Vjb25kcyB0byBjaGVjayBhbmQgZGVzdHJveSBpZGxlIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAncG9vbEJlbmNobWFya2luZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU2hvd3Mgc3RhdGlzdGljcyBmb3IgdGhlIHBvb2wgb2YgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfTEVWRUwnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0xvZ2dpbmcgdmVyYm9zaXR5IGxldmVsJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIHJvdW5kOiAwLFxyXG4gICAgICAgIG1pbjogMCxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgdmFsdWU6ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdMb2cgZmlsZSBuYW1lLiBSZXF1aXJlcyBgbG9nVG9GaWxlYCBhbmQgYGxvZ0Rlc3RgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Q6IHtcclxuICAgICAgdmFsdWU6ICdsb2cnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfREVTVCcsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgdG8gYmUgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9Db25zb2xlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0NPTlNPTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9Db25zb2xlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGNvbnNvbGUgbG9nZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9GaWxlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGxvZ2dpbmcgdG8gYSBmaWxlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcm91dGU6IHtcclxuICAgICAgdmFsdWU6ICcvJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9ST1VURScsXHJcbiAgICAgIGNsaU5hbWU6ICd1aVJvdXRlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgZW5kcG9pbnQgcm91dGUgZm9yIHRoZSBVSScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgb3RoZXI6IHtcclxuICAgIG5vZGVFbnY6IHtcclxuICAgICAgdmFsdWU6ICdwcm9kdWN0aW9uJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT0RFX0VOVicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIE5vZGUuanMgZW52aXJvbm1lbnQgdHlwZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvUHJvY2Vzc0V4aXRzOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gYXR0YWNoIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbm9Mb2dvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9fTE9HTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGlzcGxheSBvciBza2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIHN0YXJ0dXAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhhcmRSZXNldFBhZ2U6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9IQVJEX1JFU0VUX1BBR0UnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHJlc2V0IHRoZSBwYWdlIGNvbnRlbnQgZW50aXJlbHknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJyb3dzZXJTaGVsbE1vZGU6IHtcclxuICAgICAgdmFsdWU6IHRydWUsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX0JST1dTRVJfU0hFTExfTU9ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBzaGVsbCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGRlYnVnOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlRGVidWcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIHVuZGVybHlpbmcgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaGVhZGxlc3M6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19IRUFETEVTUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byBzZXQgdGhlIGJyb3dzZXIgdG8gcnVuIGluIGhlYWRsZXNzIG1vZGUgZHVyaW5nIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGV2dG9vbHM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19ERVZUT09MUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBEZXZUb29scyBpbiBoZWFkZnVsIG1vZGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0xJU1RFTl9UT19DT05TT0xFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VuYWJsZXMgb3IgZGlzYWJsZXMgbGlzdGVuaW5nIHRvIGNvbnNvbGUgbWVzc2FnZXMgZnJvbSB0aGUgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZHVtcGlvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRFVNUElPJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1JlZGlyZWN0cyBvciBub3QgYnJvd3NlciBzdGRvdXQgYW5kIHN0ZGVyciB0byBwcm9jZXNzLnN0ZG91dCBhbmQgcHJvY2Vzcy5zdGRlcnInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNsb3dNbzoge1xyXG4gICAgICB2YWx1ZTogMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19TTE9XX01PJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWxheXMgUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBtaWxsaXNlY29uZHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlYnVnZ2luZ1BvcnQ6IHtcclxuICAgICAgdmFsdWU6IDkyMjIsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVCVUdHSU5HX1BPUlQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgdXNlZCBmb3IgZGVidWdnaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vLyBQcm9wZXJ0aWVzIG5lc3RpbmcgbGV2ZWwgb2YgYWxsIG9wdGlvbnNcclxuZXhwb3J0IGNvbnN0IG5lc3RlZFByb3BzID0gX2NyZWF0ZU5lc3RlZFByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLy8gUHJvcGVydGllcyBuYW1lcyB0aGF0IHNob3VsZCBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkXHJcbmV4cG9ydCBjb25zdCBhYnNvbHV0ZVByb3BzID0gX2NyZWF0ZUFic29sdXRlUHJvcHMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgZ2VuZXJhdGVzIGEgbWFwcGluZyBvZiBuZXN0ZWQgYXJndW1lbnQgY2hhaW5zIGZyb20gYSBuZXN0ZWRcclxuICogY29uZmlnIG9iamVjdC4gVGhpcyBmdW5jdGlvbiB0cmF2ZXJzZXMgYSBuZXN0ZWQgb2JqZWN0IGFuZCBjcmVhdGVzIGEgbWFwcGluZ1xyXG4gKiB3aGVyZSBlYWNoIGtleSBpcyBhbiBhcmd1bWVudCBuYW1lIChlaXRoZXIgZnJvbSBgY2xpTmFtZWAsIGBsZWdhY3lOYW1lYCxcclxuICogb3IgdGhlIG9yaWdpbmFsIGtleSkgYW5kIGVhY2ggdmFsdWUgaXMgYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBjaGFpblxyXG4gKiBvZiBuZXN0ZWQgcHJvcGVydGllcyBsZWFkaW5nIHRvIHRoYXQgYXJndW1lbnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtuZXN0ZWRQcm9wcz17fV0gLSBUaGUgYWNjdW11bGF0b3Igb2JqZWN0IGZvciBzdG9yaW5nXHJcbiAqIHRoZSByZXN1bHRpbmcgYXJndW1lbnRzIGNoYWlucy4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtwcm9wQ2hhaW49JyddIC0gVGhlIGN1cnJlbnQgY2hhaW4gb2YgbmVzdGVkIHByb3BlcnRpZXMsXHJcbiAqIHVzZWQgaW50ZXJuYWxseSBkdXJpbmcgcmVjdXJzaW9uLiAgVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgc3RyaW5nLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgbWFwcGluZyBhcmd1bWVudCBuYW1lcyB0byB0aGVpciBjb3JyZXNwb25kaW5nXHJcbiAqIG5lc3RlZCBwcm9wZXJ0eSBjaGFpbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHMoY29uZmlnLCBuZXN0ZWRQcm9wcyA9IHt9LCBwcm9wQ2hhaW4gPSAnJykge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHMgb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBfY3JlYXRlTmVzdGVkUHJvcHMoZW50cnksIG5lc3RlZFByb3BzLCBgJHtwcm9wQ2hhaW59LiR7a2V5fWApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIHRoZSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzXHJcbiAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmNsaU5hbWUgfHwga2V5XSA9IGAke3Byb3BDaGFpbn0uJHtrZXl9YC5zdWJzdHJpbmcoMSk7XHJcblxyXG4gICAgICAvLyBTdXBwb3J0IGZvciB0aGUgbGVnYWN5LCBQaGFudG9tSlMgcHJvcGVydGllcyBuYW1lc1xyXG4gICAgICBpZiAoZW50cnkubGVnYWN5TmFtZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgbmVzdGVkUHJvcHNbZW50cnkubGVnYWN5TmFtZV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb2JqZWN0IHdpdGggbmVzdGVkIGFyZ3VtZW50IGNoYWluc1xyXG4gIHJldHVybiBuZXN0ZWRQcm9wcztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdhdGhlcnMgdGhlIG5hbWVzIG9mIHByb3BlcnRpZXMgZnJvbSBhIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRoYXRcclxuICogY2FuIGJlIHRyZWF0ZWQgYXMgYWJzb2x1dGUgcHJvcGVydGllcy4gVGhlc2UgcHJvcGVydGllcyBoYXZlIHZhbHVlcyB0aGF0XHJcbiAqIGFyZSBvYmplY3RzIGFuZCBkbyBub3QgY29udGFpbiBmdXJ0aGVyIG5lc3RlZCBkZXB0aCB3aGVuIG1lcmdpbmcgYW4gb2JqZWN0XHJcbiAqIGNvbnRhaW5pbmcgdGhlc2Ugb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVBYnNvbHV0ZVByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IFthYnNvbHV0ZVByb3BzPVtdXSAtIEFuIGFycmF5IHRvIGNvbGxlY3QgdGhlIG5hbWVzXHJcbiAqIG9mIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPHN0cmluZz59IEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG5hbWVzIG9mIGFic29sdXRlXHJcbiAqIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wcyhjb25maWcsIGFic29sdXRlUHJvcHMgPSBbXSkge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS50eXBlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHNcclxuICAgICAgX2NyZWF0ZUFic29sdXRlUHJvcHMoZW50cnksIGFic29sdXRlUHJvcHMpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSWYgdGhlIG9wdGlvbiBjYW4gYmUgYW4gb2JqZWN0LCBzYXZlIGl0cyB0eXBlIGluIHRoZSBhcnJheVxyXG4gICAgICBpZiAoZW50cnkudHlwZXMuaW5jbHVkZXMoJ09iamVjdCcpKSB7XHJcbiAgICAgICAgYWJzb2x1dGVQcm9wcy5wdXNoKGtleSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBhcnJheSB3aXRoIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzXHJcbiAgcmV0dXJuIGFic29sdXRlUHJvcHM7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBkZWZhdWx0Q29uZmlnLFxyXG4gIG5lc3RlZFByb3BzLFxyXG4gIGFic29sdXRlUHJvcHNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgZmlsZSBpcyByZXNwb25zaWJsZSBmb3IgcGFyc2luZyB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIHdpdGggdGhlICd6b2QnIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkXHJcbiAqIHRvIGJlIHVzZWQgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIGBlbnZzYC4gV2Ugc2hvdWxkIG5vdCB1c2UgdGhlIGBwcm9jZXNzLmVudmBcclxuICogZGlyZWN0bHkgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuXHJcbiAqIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lclxyXG4gKiBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICovXHJcblxyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5cclxuaW1wb3J0IHsgZGVmYXVsdENvbmZpZyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuLy8gTG9hZCAuZW52IGludG8gZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbi8vIE9iamVjdCB3aXRoIGN1c3RvbSB2YWxpZGF0b3JzIGFuZCB0cmFuc2Zvcm1lcnMsIHRvIGF2b2lkIHJlcGV0aXRpb25cclxuLy8gaW4gdGhlIENvbmZpZyBvYmplY3RcclxuY29uc3QgdiA9IHtcclxuICAvLyBTcGxpdHMgc3RyaW5nIHZhbHVlIGludG8gZWxlbWVudHMgaW4gYW4gYXJyYXksIHRyaW1zIGV2ZXJ5IGVsZW1lbnQsIGNoZWNrc1xyXG4gIC8vIGlmIGFuIGFycmF5IGlzIGNvcnJlY3QsIGlmIGl0IGlzIGVtcHR5LCBhbmQgaWYgaXQgaXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgYXJyYXk6IChmaWx0ZXJBcnJheSkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlXHJcbiAgICAgICAgICAuc3BsaXQoJywnKVxyXG4gICAgICAgICAgLm1hcCgodmFsdWUpID0+IHZhbHVlLnRyaW0oKSlcclxuICAgICAgICAgIC5maWx0ZXIoKHZhbHVlKSA9PiBmaWx0ZXJBcnJheS5pbmNsdWRlcyh2YWx1ZSkpXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZS5sZW5ndGggPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3Mgb25seSB0cnVlLCBmYWxzZSBhbmQgY29ycmVjdGx5IHBhcnNlIHRoZSB2YWx1ZSB0byBib29sZWFuXHJcbiAgLy8gb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSB1bmRlZmluZWRcclxuICBib29sZWFuOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbJ3RydWUnLCAnZmFsc2UnLCAnJ10pXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgPT09ICd0cnVlJyA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3MgcGFzc2VkIHZhbHVlcyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsXHJcbiAgLy8gYmUgdW5kZWZpbmVkXHJcbiAgZW51bTogKHZhbHVlcykgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWy4uLnZhbHVlcywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIFRyaW1zIHRoZSBzdHJpbmcgdmFsdWUgYW5kIGNoZWNrcyBpZiBpdCBpcyBlbXB0eSBvciBjb250YWlucyBzdHJpbmdpZmllZFxyXG4gIC8vIHZhbHVlcyBzdWNoIGFzIGZhbHNlLCB1bmRlZmluZWQsIG51bGwsIE5hTiwgaWYgaXQgZG9lcywgcmV0dXJucyB1bmRlZmluZWRcclxuICBzdHJpbmc6ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgIVsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJ10uaW5jbHVkZXModmFsdWUpIHx8XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSBzdHJpbmcgY29udGFpbnMgZm9yYmlkZGVuIHZhbHVlcywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBvc2l0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIHBvc2l0aXZlTnVtOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBwb3NpdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG5vbi1uZWdhdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlXHJcbiAgLy8gd2lsbCBiZSB1bmRlZmluZWRcclxuICBub25OZWdhdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCksXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSB2YWx1ZSBtdXN0IGJlIG51bWVyaWMgYW5kIG5vbi1uZWdhdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKVxyXG59O1xyXG5cclxuZXhwb3J0IGNvbnN0IENvbmZpZyA9IHoub2JqZWN0KHtcclxuICAvLyBwdXBwZXRlZXJcclxuICBQVVBQRVRFRVJfQVJHUzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEhJR0hDSEFSVFNfVkVSU0lPTiBtdXN0IGJlICdsYXRlc3QnLCBhIG1ham9yIHZlcnNpb24sIG9yIGluIHRoZSBmb3JtIFhYLllZLlpaLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfQ0ROX1VSTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHA6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcbiAgSElHSENIQVJUU19GT1JDRV9GRVRDSDogdi5ib29sZWFuKCksXHJcbiAgSElHSENIQVJUU19DQUNIRV9QQVRIOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQURNSU5fVE9LRU46IHYuc3RyaW5nKCksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNvcmVTY3JpcHRzLnZhbHVlKSxcclxuICBISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLm1vZHVsZVNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuaW5kaWNhdG9yU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcbiAgSElHSENIQVJUU19DVVNUT01fU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jdXN0b21TY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuXHJcbiAgLy8gZXhwb3J0XHJcbiAgRVhQT1JUX0lORklMRTogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfSU5TVFI6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1NWRzogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfQkFUQ0g6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09VVEZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RZUEU6IHYuZW51bShbJ2pwZWcnLCAncG5nJywgJ3BkZicsICdzdmcnXSksXHJcbiAgRVhQT1JUX0NPTlNUUjogdi5lbnVtKFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10pLFxyXG4gIEVYUE9SVF9CNjQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9OT19ET1dOTE9BRDogdi5ib29sZWFuKCksXHJcbiAgRVhQT1JUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1dJRFRIOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfR0xPQkFMX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RIRU1FX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG5cclxuICAvLyBjdXN0b21cclxuICBDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT046IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUzogdi5ib29sZWFuKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NVU1RPTV9DT0RFOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19DQUxMQkFDSzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfUkVTT1VSQ0VTOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gc2VydmVyXHJcbiAgU0VSVkVSX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX0hPU1Q6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfVVBMT0FEX0xJTUlUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX0JFTkNITUFSS0lORzogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIHNlcnZlciBwcm94eVxyXG4gIFNFUlZFUl9QUk9YWV9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QUk9YWV9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1BST1hZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gc2VydmVyIHJhdGUgbGltaXRpbmdcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFk6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU46IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlciBzc2xcclxuICBTRVJWRVJfU1NMX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9GT1JDRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1NTTF9DRVJUX1BBVEg6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHBvb2xcclxuICBQT09MX01JTl9XT1JLRVJTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9NQVhfV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfV09SS19MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfREVTVFJPWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfUkVBUEVSX0lOVEVSVkFMOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBsb2dnZXJcclxuICBMT0dHSU5HX0xFVkVMOiB6XHJcbiAgICAuc3RyaW5nKClcclxuICAgIC50cmltKClcclxuICAgIC5yZWZpbmUoXHJcbiAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZSA9PT0gJycgfHxcclxuICAgICAgICAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPD0gNSksXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgTE9HR0lOR19MRVZFTC4gV2Ugb25seSBhY2NlcHQgdmFsdWVzIGZyb20gMCB0byA1IGFzIGxvZ2dpbmcgbGV2ZWxzLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpLFxyXG4gIExPR0dJTkdfRklMRTogdi5zdHJpbmcoKSxcclxuICBMT0dHSU5HX0RFU1Q6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19UT19DT05TT0xFOiB2LmJvb2xlYW4oKSxcclxuICBMT0dHSU5HX1RPX0ZJTEU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyB1aVxyXG4gIFVJX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgVUlfUk9VVEU6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIG90aGVyXHJcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcclxuICBPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfTk9fTE9HTzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcclxuICBPVEhFUl9CUk9XU0VSX1NIRUxMX01PREU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBkZWJ1Z2dlclxyXG4gIERFQlVHX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfSEVBRExFU1M6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RFVlRPT0xTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19MSVNURU5fVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfRFVNUElPOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19TTE9XX01POiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgREVCVUdfREVCVUdHSU5HX1BPUlQ6IHYucG9zaXRpdmVOdW0oKVxyXG59KTtcclxuXHJcbmV4cG9ydCBjb25zdCBlbnZzID0gQ29uZmlnLnBhcnRpYWwoKS5wYXJzZShwcm9jZXNzLmVudik7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBNYW5hZ2VzIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgYnkgbG9hZGluZ1xyXG4gKiBhbmQgbWVyZ2luZyBvcHRpb25zIGZyb20gbXVsdGlwbGUgc291cmNlcywgc3VjaCBhcyBkZWZhdWx0IHNldHRpbmdzLFxyXG4gKiBlbnZpcm9ubWVudCB2YXJpYWJsZXMsIHVzZXItcHJvdmlkZWQgb3B0aW9ucywgYW5kIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqIEVuc3VyZXMgdGhlIGdsb2JhbCBvcHRpb25zIGFyZSB1cC10by1kYXRlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHkgdmFsdWVzLlxyXG4gKiBQcm92aWRlcyBmdW5jdGlvbnMgZm9yIGFjY2Vzc2luZyBhbmQgdXBkYXRpbmcgY29uZmlndXJhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBkZWVwQ29weSwgZ2V0QWJzb2x1dGVQYXRoLCBpc09iamVjdCB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IHsgYWJzb2x1dGVQcm9wcywgbmVzdGVkUHJvcHMsIGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0T3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PXRydWVdIC0gU3BlY2lmaWVzIHdoZXRoZXIgdG8gcmV0dXJuIGEgY29waWVkXHJcbiAqIG9iamVjdCBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgKGB0cnVlYCkgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCBvciBhIHJlZmVyZW5jZVxyXG4gKiB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoZ2V0Q29weSA9IHRydWUpIHtcclxuICByZXR1cm4gZ2V0Q29weSA/IGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpIDogZ2xvYmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXBkYXRlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBuZXcgb3B0aW9ucyB0byBiZVxyXG4gKiBtZXJnZWQgaW50byB0aGUgZ2xvYmFsIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldENvcHk9ZmFsc2VdIC0gRGV0ZXJtaW5lcyB3aGV0aGVyIHRvIG1lcmdlIHRoZSBuZXdcclxuICogb3B0aW9ucyBpbnRvIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IChgdHJ1ZWApIG9yIGRpcmVjdGx5IGludG9cclxuICogdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0LCBlaXRoZXIgdGhlIG1vZGlmaWVkIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9yIGEgbW9kaWZpZWQgY29weSwgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGBnZXRDb3B5YC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVPcHRpb25zKG5ld09wdGlvbnMsIGdldENvcHkgPSBmYWxzZSkge1xyXG4gIC8vIE1lcmdlIG5ldyBvcHRpb25zIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyBvciBpdHMgY29weSBhbmQgcmV0dXJuIHRoZSByZXN1bHRcclxuICByZXR1cm4gX21lcmdlT3B0aW9ucyhnZXRPcHRpb25zKGdldENvcHkpLCBuZXdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggdmFsdWVzIHByb3ZpZGVkIHRocm91Z2ggdGhlIENMSSwga2VlcGluZ1xyXG4gKiB0aGUgcHJpbmNpcGxlIG9mIG9wdGlvbnMgbG9hZCBwcmlvcml0eS4gVGhpcyBmdW5jdGlvbiBhY2NlcHRzIGEgYGNsaUFyZ3NgXHJcbiAqIGFycmF5IGNvbnRhaW5pbmcgYXJndW1lbnRzIGZyb20gdGhlIENMSSwgd2hpY2ggd2lsbCBiZSB2YWxpZGF0ZWQgYW5kIGFwcGxpZWRcclxuICogaWYgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIFRoZSBwcmlvcml0eSBvcmRlciBmb3Igc2V0dGluZyB2YWx1ZXMgaXM6XHJcbiAqXHJcbiAqIDEuIFZhbHVlcyBmcm9tIGEgY3VzdG9tIEpTT04gZmlsZSAobG9hZGVkIGJ5IHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24pLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSB0aGUgY29tbWFuZCBsaW5lIGludGVyZmFjZSAoQ0xJKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldENsaU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQgbGluZSBhcmd1bWVudHMgdXNlZFxyXG4gKiBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHJlZmxlY3RpbmcgdGhlIG1lcmdlZFxyXG4gKiBjb25maWd1cmF0aW9uIGZyb20gc291cmNlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEkuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0Q2xpT3B0aW9ucyhjbGlBcmdzKSB7XHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzICYmIEFycmF5LmlzQXJyYXkoY2xpQXJncykgJiYgY2xpQXJncy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGxvYWRlZCB2aWEgdGhlIGAtLWxvYWRDb25maWdgXHJcbiAgICBjb25zdCBjb25maWdPcHRpb25zID0gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNvbmZpZ09wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNvbmZpZ09wdGlvbnMpO1xyXG5cclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIENMSVxyXG4gICAgY29uc3QgY2xpT3B0aW9ucyA9IF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncyk7XHJcblxyXG4gICAgLy8gVXBkYXRlIGdsb2JhbCBvcHRpb25zIHdpdGggdGhlIHZhbHVlcyBmcm9tIHRoZSBgY2xpT3B0aW9uc2BcclxuICAgIHVwZGF0ZU9wdGlvbnMoY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnZXRPcHRpb25zKGZhbHNlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG9sZE9wdGlvbnMpKSB7XHJcbiAgICAvLyBJdGVyYXRlIG92ZXIgZWFjaCBrZXktdmFsdWUgcGFpciBpbiB0aGUgb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgdGhlcmUgaXMgYSBuZXN0ZWQgbWFwcGluZywgc3BsaXQgaXQgaW50byBhIHByb3BlcnRpZXMgY2hhaW5cclxuICAgICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNba2V5XVxyXG4gICAgICAgID8gbmVzdGVkUHJvcHNba2V5XS5zcGxpdCgnLicpXHJcbiAgICAgICAgOiBbXTtcclxuXHJcbiAgICAgIC8vIElmIGl0IGlzIHRoZSBsYXN0IHByb3BlcnR5IGluIHRoZSBjaGFpbiwgYXNzaWduIHRoZSB2YWx1ZSwgb3RoZXJ3aXNlLFxyXG4gICAgICAvLyBjcmVhdGUgb3IgcmV1c2UgdGhlIG5lc3RlZCBvYmplY3RcclxuICAgICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cclxuICAgICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgICAgbmV3T3B0aW9uc1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgICdbY29uZmlnXSBObyBjb3JyZWN0IG9iamVjdCB3aXRoIG9wdGlvbnMgd2FzIHByb3ZpZGVkLiBSZXR1cm5pbmcgYW4gZW1wdHkgYXJyYXkuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgbmV3LCBzdHJ1Y3R1cmVkIG9wdGlvbnMgb2JqZWN0XHJcbiAgcmV0dXJuIG5ld09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBWYWxpZGF0ZXMsIHBhcnNlcywgYW5kIGNoZWNrcyBpZiB0aGUgcHJvdmlkZWQgY29uZmlnIGlzIGFsbG93ZWQgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0FsbG93ZWRDb25maWdcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBjb25maWcgLSBUaGUgY29uZmlnIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkIGFzIGEgc2V0XHJcbiAqIG9mIG9wdGlvbnMuIE11c3QgYmUgZWl0aGVyIGFuIG9iamVjdCBvciBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBbdG9TdHJpbmc9ZmFsc2VdIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCB2ZXJzaW9uXHJcbiAqIG9mIHRoZSBwYXJzZWQgY29uZmlnLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFthbGxvd0Z1bmN0aW9ucz1mYWxzZV0gLSBXaGV0aGVyIHRvIGFsbG93IGZ1bmN0aW9uc1xyXG4gKiBpbiB0aGUgcGFyc2VkIGNvbmZpZy4gSWYgdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWQuIE90aGVyd2lzZSwgd2hlblxyXG4gKiBhIGZ1bmN0aW9uIGlzIGZvdW5kLCBudWxsIGlzIHJldHVybmVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFZlcnNpb24obm9Mb2dvID0gZmFsc2UpIHtcclxuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGAuZW52YCBvciBmcm9tIGBwYWNrYWdlLmpzb25gXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuICApLnZlcnNpb247XHJcblxyXG4gIC8vIFByaW50IHRleHQgb25seVxyXG4gIGlmIChub0xvZ28pIHtcclxuICAgIGNvbnNvbGUubG9nKGBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259YCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFByaW50IHRoZSBsb2dvXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAnbXNnJywgJ3N0YXJ0dXAubXNnJyksICd1dGY4JykudG9TdHJpbmcoKVxyXG4gICAgICAgIC5ib2xkLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IGJhc2VkIG9uIHRoZSBwcm92aWRlZFxyXG4gKiBjb25maWd1cmF0aW9uLCBzZXR0aW5nIHZhbHVlcyBmcm9tIG5lc3RlZCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5LlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIChkZWZhdWx0cykuXHJcbiAqIDIuIFZhbHVlcyBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcyAoc3BlY2lmaWVkIGluIHRoZSBgLmVudmAgZmlsZSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaW5pdE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogdGhlIGdsb2JhbCBvcHRpb25zLiBJdCBzaG91bGQgaW5jbHVkZSBuZXN0ZWQgcHJvcGVydGllcyB3aXRoIGEgYHZhbHVlYFxyXG4gKiBhbmQgYW4gYGVudkxpbmtgIGZvciBsaW5raW5nIHRvIGVudmlyb25tZW50IHZhcmlhYmxlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGluaXRpYWxpemVkIGdsb2JhbCBvcHRpb25zIG9iamVjdCwgcG9wdWxhdGVkIHdpdGhcclxuICogdmFsdWVzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHlcclxuICogb3JkZXIuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaW5pdE9wdGlvbnMoY29uZmlnKSB7XHJcbiAgLy8gSW5pdCB0aGUgb2JqZWN0IGZvciBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBTdGFydCBpbml0aWFsaXppbmcgdGhlIGBvcHRpb25zYCBvYmplY3QgcmVjdXJzaXZlbHlcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBpdGVtXSBvZiBPYmplY3QuZW50cmllcyhjb25maWcpKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY29ycmVjdCB2YWx1ZSBiYXNlZCBvbiB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHkgb3JkZXJcclxuICAgICAgaWYgKGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gdW5kZWZpbmVkICYmIGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gbnVsbCkge1xyXG4gICAgICAgIC8vIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdmFsdWVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gZW52c1tpdGVtLmVudkxpbmtdO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRoZSB2YWx1ZSBmcm9tIHRoZSBjb25maWcgZmlsZVxyXG4gICAgICAgIG9wdGlvbnNbbmFtZV0gPSBpdGVtLnZhbHVlO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDcmVhdGUgYSBzZWN0aW9uIGluIHRoZSBvcHRpb25zXHJcbiAgICAgIG9wdGlvbnNbbmFtZV0gPSBfaW5pdE9wdGlvbnMoaXRlbSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNyZWF0ZWQgYG9wdGlvbnNgIG9iamVjdFxyXG4gIHJldHVybiBvcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogTWVyZ2VzIHR3byBzZXRzIG9mIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgY29uc2lkZXJpbmcgYWJzb2x1dGUgcHJvcGVydGllcy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9tZXJnZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9yaWdpbmFsT3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTWVyZ2VkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9ucywgbmV3T3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgb3JpZ2luYWxPcHRpb25zYCBhbmQgYG5ld09wdGlvbnNgIGFyZSBjb3JyZWN0IG9iamVjdHNcclxuICBpZiAoaXNPYmplY3Qob3JpZ2luYWxPcHRpb25zKSAmJiBpc09iamVjdChuZXdPcHRpb25zKSkge1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobmV3T3B0aW9ucykpIHtcclxuICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gPVxyXG4gICAgICAgIGlzT2JqZWN0KHZhbHVlKSAmJlxyXG4gICAgICAgICFhYnNvbHV0ZVByb3BzLmluY2x1ZGVzKGtleSkgJiZcclxuICAgICAgICBvcmlnaW5hbE9wdGlvbnNba2V5XSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICA/IF9tZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zW2tleV0sIHZhbHVlKVxyXG4gICAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgICAgOiBvcmlnaW5hbE9wdGlvbnNba2V5XSB8fCBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBvcmlnaW5hbCAobW9kaWZpZWQgb3Igbm90KSBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIHRoZSBwcm92aWRlZCBvcHRpb25zIG9iamVjdCB0byBhIEpTT04tZm9ybWF0dGVkIHN0cmluZ1xyXG4gKiB3aXRoIHRoZSBvcHRpb24gdG8gcHJlc2VydmUgZnVuY3Rpb25zLiBJbiBvcmRlciBmb3IgYSBmdW5jdGlvblxyXG4gKiB0byBiZSBwcmVzZXJ2ZWQsIGl0IG5lZWRzIHRvIGZvbGxvdyB0aGUgZm9ybWF0IGBmdW5jdGlvbiAoLi4uKSB7Li4ufWAuXHJcbiAqIFN1Y2ggYSBmdW5jdGlvbiBjYW4gYWxzbyBiZSBzdHJpbmdpZmllZC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9vcHRpb25zU3RyaW5naWZ5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IHRvIGJlIGNvbnZlcnRlZCB0byBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0Z1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHByZXNlcnZlZFxyXG4gKiBpbiB0aGUgb3V0cHV0LiBPdGhlcndpc2UgYW4gZXJyb3IgaXMgdGhyb3duLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHN0cmluZ2lmeUZ1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHNhdmVkXHJcbiAqIGFzIHN0cmluZ3MuIFRoZSBgYWxsb3dGdW5jdGlvbnNgIG11c3QgYmUgc2V0IHRvIHRydWUgYXMgd2VsbCBmb3IgdGhpcyB0byB0YWtlXHJcbiAqIGFuIGVmZmVjdC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCB3aGVuIGZ1bmN0aW9ucyBhcmUgbm90IGFsbG93ZWQgYnV0IGFyZVxyXG4gKiBmb3VuZCBpbiBwcm92aWRlZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeShvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucywgc3RyaW5naWZ5RnVuY3Rpb25zKSB7XHJcbiAgY29uc3QgcmVwbGFjZXJDYWxsYmFjayA9IChfLCB2YWx1ZSkgPT4ge1xyXG4gICAgLy8gVHJpbSBzdHJpbmcgdmFsdWVzXHJcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xyXG4gICAgICB2YWx1ZSA9IHZhbHVlLnRyaW0oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBJZiB2YWx1ZSBpcyBhIGZ1bmN0aW9uIG9yIHN0cmluZ2lmaWVkIGZ1bmN0aW9uXHJcbiAgICBpZiAoXHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJyB8fFxyXG4gICAgICAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJykgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIElmIGFsbG93RnVuY3Rpb25zIGlzIHNldCB0byB0cnVlLCBwcmVzZXJ2ZSBmdW5jdGlvbnNcclxuICAgICAgaWYgKGFsbG93RnVuY3Rpb25zKSB7XHJcbiAgICAgICAgLy8gQmFzZWQgb24gdGhlIGBzdHJpbmdpZnlGdW5jdGlvbnNgIG9wdGlvbnMsIHNldCBmdW5jdGlvbiB2YWx1ZXNcclxuICAgICAgICByZXR1cm4gc3RyaW5naWZ5RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IC8vIEFzIHN0cmluZ2lmaWVkIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgXCJFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5cImBcclxuICAgICAgICAgIDogLy8gQXMgZnVuY3Rpb25zXHJcbiAgICAgICAgICAgIGBFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5gO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRocm93IGFuIGVycm9yIG90aGVyd2lzZVxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcigpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSW4gYWxsIG90aGVyIGNhc2VzLCBzaW1wbHkgcmV0dXJuIHRoZSB2YWx1ZVxyXG4gICAgcmV0dXJuIHZhbHVlO1xyXG4gIH07XHJcblxyXG4gIC8vIFN0cmluZ2lmeSBvcHRpb25zIGFuZCBpZiByZXF1aXJlZCwgcmVwbGFjZSBzcGVjaWFsIGZ1bmN0aW9ucyBtYXJrc1xyXG4gIHJldHVybiBKU09OLnN0cmluZ2lmeShvcHRpb25zLCByZXBsYWNlckNhbGxiYWNrKS5yZXBsYWNlQWxsKFxyXG4gICAgc3RyaW5naWZ5RnVuY3Rpb25zID8gL1xcXFxcIkVYUF9GVU58RVhQX0ZVTlxcXFxcIi9nIDogL1wiRVhQX0ZVTnxFWFBfRlVOXCIvZyxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgcHJvdmlkZWQgdmlhXHJcbiAqIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gaW4gdGhlIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgdG8gc2VhcmNoXHJcbiAqIGZvciB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncykge1xyXG4gIC8vIEdldCB0aGUgYWxsb3cgZmxhZ3MgZm9yIHRoZSBjdXN0b20gbG9naWMgY2hlY2tcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiwgYWxsb3dGaWxlUmVzb3VyY2VzIH0gPSBnZXRPcHRpb25zKCkuY3VzdG9tTG9naWM7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiB2YWx1ZVxyXG4gIGNvbnN0IGNvbmZpZ0ZpbGVOYW1lID0gY29uZmlnSW5kZXggPiAtMSAmJiBjbGlBcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lICYmIGFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICByZXR1cm4gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7Y29uZmlnRmlsZU5hbWV9IGZpbGUuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gYWRkaXRpb25hbCBvcHRpb25zIHRvIHJldHVyblxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhcnNlcyBjb21tYW5kLWxpbmUgYXJndW1lbnRzIGFuZCBwYWlycyBlYWNoIGFyZ3VtZW50IHdpdGggaXRzIGNvcnJlc3BvbmRpbmdcclxuICogb3B0aW9uIGluIHRoZSBjb25maWd1cmF0aW9uLiBUaGUgdmFsdWVzIGFyZSBzdHJ1Y3R1cmVkIGludG8gYSBuZXN0ZWQgb3B0aW9uc1xyXG4gKiBvYmplY3QsIGJhc2VkIG9uIHByZWRlZmluZWQgbWFwcGluZ3MuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gbmVzdGVkUHJvcHMgLSBBbiBhcnJheSBvZiBuZXN0aW5nIGxldmVsIGZvciBhbGxcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQtbGluZSBhcmd1bWVudHNcclxuICogY29udGFpbmluZyBvcHRpb25zIGFuZCB0aGVpciBhc3NvY2lhdGVkIHZhbHVlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gdXBkYXRlZCBvcHRpb25zIG9iamVjdCB3aGVyZSBlYWNoIG9wdGlvbiBmcm9tXHJcbiAqIHRoZSBjb21tYW5kLWxpbmUgaXMgcGFpcmVkIHdpdGggaXRzIHZhbHVlLCBzdHJ1Y3R1cmVkIGludG8gbmVzdGVkIG9iamVjdHNcclxuICogYXMgZGVmaW5lZC5cclxuICovXHJcbmZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncykge1xyXG4gIC8vIEFuIGVtcHR5IG9iamVjdCB0byBjb2xsZWN0IGFuZCBzdHJ1Y3R1cml6ZSBkYXRhIGZyb20gdGhlIGFyZ3NcclxuICBjb25zdCBjbGlPcHRpb25zID0ge307XHJcblxyXG4gIC8vIEN5Y2xlIHRocm91Z2ggYWxsIENMSSBhcmdzIGFuZCBmaWx0ZXIgdGhlbVxyXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2xpQXJncy5sZW5ndGg7IGkrKykge1xyXG4gICAgY29uc3Qgb3B0aW9uID0gY2xpQXJnc1tpXS5yZXBsYWNlKC8tL2csICcnKTtcclxuXHJcbiAgICAvLyBGaW5kIHRoZSByaWdodCBwbGFjZSBmb3IgcHJvcGVydHkncyB2YWx1ZVxyXG4gICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZFByb3BzW29wdGlvbl0uc3BsaXQoJy4nKVxyXG4gICAgICA6IFtdO1xyXG5cclxuICAgIC8vIENyZWF0ZSBvcHRpb25zIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIENMSSBmb3IgbGF0ZXIgcGFyc2luZyBhbmQgbWVyZ2luZ1xyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZSgob2JqLCBwcm9wLCBpbmRleCkgPT4ge1xyXG4gICAgICBpZiAocHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4KSB7XHJcbiAgICAgICAgY29uc3QgdmFsdWUgPSBjbGlBcmdzWysraV07XHJcbiAgICAgICAgaWYgKCF2YWx1ZSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NvbmZpZ10gTWlzc2luZyB2YWx1ZSBmb3IgdGhlIENMSSAnLS0ke29wdGlvbn0nIGFyZ3VtZW50LiBVc2luZyB0aGUgZGVmYXVsdCB2YWx1ZS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgICBvYmpbcHJvcF0gPSB2YWx1ZSB8fCBudWxsO1xyXG4gICAgICB9IGVsc2UgaWYgKG9ialtwcm9wXSA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgb2JqW3Byb3BdID0ge307XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcclxuICAgIH0sIGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHBhcnNlZCBDTEkgb3B0aW9uc1xyXG4gIHJldHVybiBjbGlPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgdHJhdmVyc2VzIHRoZSBvcHRpb25zIG9iamVjdCB0byBwcmludCB0aGUgdXNhZ2UgaW5mb3JtYXRpb25cclxuICogZm9yIGVhY2ggb3B0aW9uIGNhdGVnb3J5IGFuZCBpbmRpdmlkdWFsIG9wdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBDTEkgb3B0aW9ucy4gSXQgbWF5XHJcbiAqIGluY2x1ZGUgbmVzdGVkIGNhdGVnb3JpZXMgYW5kIGluZGl2aWR1YWwgb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9ucykge1xyXG4gIGZvciAoY29uc3QgW25hbWUsIG9wdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcclxuICAgIC8vIElmIHRoZSBjdXJyZW50IGVudHJ5IGlzIGEgY2F0ZWdvcnkgYW5kIG5vdCBhIGxlYWYgb3B0aW9uLCByZWN1cnNlIGludG8gaXRcclxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9wdGlvbiwgJ3ZhbHVlJykpIHtcclxuICAgICAgX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb24pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gUHJlcGFyZSBkZXNjcmlwdGlvblxyXG4gICAgICBjb25zdCBkZXNjTmFtZSA9IGAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9YDtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdmFsdWVcclxuICAgICAgbGV0IG9wdGlvblZhbHVlID0gb3B0aW9uLnZhbHVlO1xyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGFycmF5IG9mIHN0cmluZ3NcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nW10nKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID1cclxuICAgICAgICAgICdbJyArIG9wdGlvblZhbHVlLm1hcCgoaXRlbSkgPT4gYCcke2l0ZW19J2ApLmpvaW4oJywgJykgKyAnXSc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhIHN0cmluZ1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmcnKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID0gYCcke29wdGlvblZhbHVlfSdgO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgIGRlc2NOYW1lLmdyZWVuLFxyXG4gICAgICAgIGAkeygnPCcgKyBvcHRpb24udHlwZXMuam9pbignfCcpICsgJz4nKS55ZWxsb3d9YCxcclxuICAgICAgICBgJHtTdHJpbmcob3B0aW9uVmFsdWUpLmJvbGR9YC5ibHVlLFxyXG4gICAgICAgIGAtICR7b3B0aW9uLmRlc2NyaXB0aW9ufS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIHNldENsaU9wdGlvbnMsXHJcbiAgbWFwVG9OZXdPcHRpb25zLFxyXG4gIGlzQWxsb3dlZENvbmZpZyxcclxuICBwcmludExpY2Vuc2UsXHJcbiAgcHJpbnRVc2FnZSxcclxuICBwcmludFZlcnNpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhUVFAgdXRpbGl0eSBtb2R1bGUgZm9yIGZldGNoaW5nIGFuZCBwb3N0aW5nIGRhdGEuIFN1cHBvcnRzIGJvdGhcclxuICogSFRUUCBhbmQgSFRUUFMgcHJvdG9jb2xzLCBwcm92aWRpbmcgbWV0aG9kcyB0byBtYWtlIEdFVCBhbmQgUE9TVCByZXF1ZXN0c1xyXG4gKiB3aXRoIGN1c3RvbWl6YWJsZSBvcHRpb25zLiBJbmNsdWRlcyBwcm90b2NvbCBkZXRlcm1pbmF0aW9uIGJhc2VkIG9uIFVSTFxyXG4gKiBhbmQgYXVnbWVudHMgcmVzcG9uc2Ugb2JqZWN0cyB3aXRoIGEgJ3RleHQnIHByb3BlcnR5IGZvciBlYXNpZXIgZGF0YSBhY2Nlc3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBkYXRhIGZyb20gdGhlIHNwZWNpZmllZCBVUkwgdXNpbmcgZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZmV0Y2hcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZmV0Y2ggZGF0YSBmcm9tLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZldGNoKHVybCwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAuZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIGlmICghcmVzcG9uc2VEYXRhKSB7XHJcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgUE9TVCByZXF1ZXN0IHRvIHRoZSBzcGVjaWZpZWQgVVJMIHdpdGggdGhlIHByb3ZpZGVkIEpTT04gYm9keSB1c2luZ1xyXG4gKiBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIHNlbmQgdGhlIFBPU1QgcmVxdWVzdCB0by5cclxuICogQHBhcmFtIHtPYmplY3R9IFtib2R5PXt9XSAtIFRoZSBKU09OIGJvZHkgdG8gaW5jbHVkZSBpbiB0aGUgUE9TVCByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcG9zdCh1cmwsIGJvZHkgPSB7fSwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoYm9keSk7XHJcblxyXG4gICAgLy8gU2V0IGRlZmF1bHQgaGVhZGVycyBhbmQgbWVyZ2Ugd2l0aCByZXF1ZXN0T3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oXHJcbiAgICAgIHtcclxuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcclxuICAgICAgICBoZWFkZXJzOiB7XHJcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxyXG4gICAgICAgICAgJ0NvbnRlbnQtTGVuZ3RoJzogZGF0YS5sZW5ndGhcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zXHJcbiAgICApO1xyXG5cclxuICAgIGNvbnN0IHJlcXVlc3QgPSBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAucmVxdWVzdCh1cmwsIG9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgIC8vIFdyaXRlIHRoZSByZXF1ZXN0IGJvZHkgYW5kIGVuZCB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC53cml0ZShkYXRhKTtcclxuICAgIHJlcXVlc3QuZW5kKCk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgVVJMLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGRldGVybWluZSB0aGUgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSAoaHR0cCBvciBodHRwcykuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKSB7XHJcbiAgcmV0dXJuIHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZmV0Y2gsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBlcnJvciBjbGFzcyBmb3IgaGFuZGxpbmcgZXhwb3J0LXJlbGF0ZWQgZXJyb3JzLiBFeHRlbmRzIHRoZSBuYXRpdmVcclxuICogYEVycm9yYCBjbGFzcyB0byBpbmNsdWRlIGFkZGl0aW9uYWwgcHJvcGVydGllcyBsaWtlIHN0YXR1cyBjb2RlIGFuZCBzdGFja1xyXG4gKiB0cmFjZSBkZXRhaWxzLlxyXG4gKi9cclxuY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gVGhlIGVycm9yIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkLlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gT3B0aW9uYWwgSFRUUCBzdGF0dXMgY29kZSBhc3NvY2lhdGVkXHJcbiAgICogd2l0aCB0aGUgZXJyb3IgKGUuZy4sIDQwMCwgNTAwKS5cclxuICAgKi9cclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXNDb2RlKSB7XHJcbiAgICBzdXBlcigpO1xyXG5cclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcblxyXG4gICAgaWYgKHN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgb3IgdXBkYXRlcyB0aGUgSFRUUCBzdGF0dXMgY29kZSBmb3IgdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBUaGUgSFRUUCBzdGF0dXMgY29kZSB0byBhc3NpZ24gdG8gdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgYWRkaXRpb25hbCBlcnJvciBkZXRhaWxzIGJhc2VkIG9uIGFuIGV4aXN0aW5nIGVycm9yIG9iamVjdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gQW4gZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgZGV0YWlscyB0byBwb3B1bGF0ZVxyXG4gICAqIHRoZSBgRXhwb3J0RXJyb3JgIGluc3RhbmNlLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG5cclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoZSBjYWNoZSBtYW5hZ2VyIGlzIHJlc3BvbnNpYmxlIGZvciBoYW5kbGluZyBhbmQgbWFuYWdpbmdcclxuICogdGhlIEhpZ2hjaGFydHMgbGlicmFyeSBhbG9uZyB3aXRoIGl0cyBkZXBlbmRlbmNpZXMuIEl0IGVuc3VyZXMgdGhhdCB0aGVzZVxyXG4gKiByZXNvdXJjZXMgYXJlIHN0b3JlZCBhbmQgcmV0cmlldmVkIGVmZmljaWVudGx5IHRvIG9wdGltaXplIHBlcmZvcm1hbmNlXHJcbiAqIGFuZCByZWR1Y2UgcmVkdW5kYW50IG5ldHdvcmsgcmVxdWVzdHMuIFRoZSBjYWNoZSBpcyBzdG9yZWQgaW4gdGhlIGAuY2FjaGVgXHJcbiAqIGRpcmVjdG9yeSBieSBkZWZhdWx0LCB3aGljaCBzZXJ2ZXMgYXMgYSBkZWRpY2F0ZWQgZm9sZGVyIGZvciBrZWVwaW5nIGNhY2hlZFxyXG4gKiBmaWxlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnNcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNhY2hlIHBhdGhcclxuICAgIGNvbnN0IGNhY2hlUGF0aCA9IGdldENhY2hlUGF0aCgpO1xyXG5cclxuICAgIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgICBjb25zdCBtYW5pZmVzdFBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKTtcclxuICAgIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ3NvdXJjZXMuanMnKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcclxuXHJcbiAgICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIHRoZSBgbWFuaWZlc3QuanNvbmAgZG9lcyBub3QgZXhpc3RcclxuICAgIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICAgIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGhpZ2hjaGFydHNPcHRpb25zLmZvcmNlRmV0Y2gpIHtcclxuICAgICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAgIC8vIFJlYWQgdGhlIG1hbmlmZXN0IEpTT05cclxuICAgICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpLCAndXRmOCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG1vZHVsZXMgaXMgYW4gYXJyYXksIGlmIHNvLCB3ZSByZXdyaXRlIGl0IHRvIGEgbWFwIHRvIG1ha2VcclxuICAgICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIGFjdHVhbCBudW1iZXIgb2Ygc2NyaXB0cyB0byBiZSBmZXRjaGVkXHJcbiAgICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9XHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnM7XHJcbiAgICAgIGNvbnN0IG51bWJlck9mTW9kdWxlcyA9XHJcbiAgICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcclxuICAgICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24pIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAgIE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzXHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgX3VwZGF0ZUNhY2hlKFxyXG4gICAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgICBzb3VyY2VQYXRoXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xyXG5cclxuICAgICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEZpbmFsbHksIHNhdmUgdGhlIG5ldyBtYW5pZmVzdCwgd2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjdXJyZW50IGNvbmZpZ1xyXG4gICAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgICBhd2FpdCBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY29uZmlndXJlIGNhY2hlIGFuZCBjcmVhdGUgb3IgdXBkYXRlIHRoZSBjb25maWcgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIFVwZGF0ZSB0byB0aGUgbmV3IHZlcnNpb25cclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICBoaWdoY2hhcnRzOiB7XHJcbiAgICAgIHZlcnNpb246IG5ld1ZlcnNpb25cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgsICd1dGY4Jyk7IC8vICM1NjJcclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBgZmV0Y2hlZE1vZHVsZXNgIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxyXG4gKiB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxyXG4gKiBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuIFRoZSBkZWZhdWx0XHJcbiAqIHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbVxyXG4gKiB3aXRoIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxyXG4gIHNjcmlwdCxcclxuICByZXF1ZXN0T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlcyxcclxuICBzaG91bGRUaHJvd0Vycm9yID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgLmpzIGZyb20gdGhlIGN1c3RvbSBzdHJpbmdzXHJcbiAgaWYgKHNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgIHNjcmlwdCA9IHNjcmlwdC5zdWJzdHJpbmcoMCwgc2NyaXB0Lmxlbmd0aCAtIDMpO1xyXG4gIH1cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gIH1cclxuXHJcbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYCxcclxuICAgICAgNDA0XHJcbiAgICApLnNldEVycm9yKHJlc3BvbnNlKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uLmBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3RcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbZmV0Y2hlZE1vZHVsZXM9e31dIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzT3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMgPSB7fSkge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzXHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihnZXRDYWNoZVBhdGgoKSwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgYHNjcmlwdHNgIGFuZCBgY3VzdG9tU2NyaXB0c2AgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNvcmVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBjb3JlIHNjcmlwdHMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG1vZHVsZVNjcmlwdHMgLSBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGN1c3RvbVNjcmlwdHMgLSBDdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsXHJcbiAqIFVSTHMpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmZXRjaGVkIHNjcmlwdHNcclxuICogY29udGVudCBqb2luZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogc2V0dGluZyBhbiBIVFRQIEFnZW50IGZvciBwcm94eS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHMoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pIHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gc2VydmVyUHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gc2VydmVyUHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiBleGlzdHMsIGFkZCBwcm94eSBhZ2VudCB0byByZXF1ZXN0IG9wdGlvbnNcclxuICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHByb3h5QWdlbnRcclxuICAgID8ge1xyXG4gICAgICAgIGFnZW50OiBwcm94eUFnZW50LFxyXG4gICAgICAgIHRpbWVvdXQ6IHNlcnZlclByb3h5T3B0aW9ucy50aW1lb3V0XHJcbiAgICAgIH1cclxuICAgIDoge307XHJcblxyXG4gIGNvbnN0IGFsbEZldGNoUHJvbWlzZXMgPSBbXHJcbiAgICAuLi5jb3JlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcylcclxuICAgICksXHJcbiAgICAuLi5jdXN0b21TY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucylcclxuICAgIClcclxuICBdO1xyXG5cclxuICBjb25zdCBmZXRjaGVkU2NyaXB0cyA9IGF3YWl0IFByb21pc2UuYWxsKGFsbEZldGNoUHJvbWlzZXMpO1xyXG4gIHJldHVybiBmZXRjaGVkU2NyaXB0cy5qb2luKCc7XFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgcmVwcmVzZW50aW5nXHJcbiAqIHRoZSBmZXRjaGVkIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF91cGRhdGVDYWNoZShoaWdoY2hhcnRzT3B0aW9ucywgc2VydmVyUHJveHlPcHRpb25zLCBzb3VyY2VQYXRoKSB7XHJcbiAgLy8gR2V0IEhpZ2hjaGFydHMgdmVyc2lvbiBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGhjVmVyc2lvbiA9XHJcbiAgICBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uID09PSAnbGF0ZXN0J1xyXG4gICAgICA/IG51bGxcclxuICAgICAgOiBgJHtoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9ufWA7XHJcblxyXG4gIC8vIEdldCB0aGUgQ0ROIHVybCBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGNkblVybCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVybCB8fCBjYWNoZS5jZG5Vcmw7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5zb3VyY2VzID0gYXdhaXQgX2ZldGNoU2NyaXB0cyhcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoYykgPT5cclxuICAgICAgICAgIGhjVmVyc2lvbiA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259LyR7Y31gIDogYCR7Y2RuVXJsfS8ke2N9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vbWFwcy8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tYXBzL21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgOiBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICksXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGkpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb25cclxuICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L3N0b2NrLyR7aGNWZXJzaW9ufS9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9zdG9jay9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldEhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIGV4dHJhY3RWZXJzaW9uLFxyXG4gIGV4dHJhY3RNb2R1bGVOYW1lLFxyXG4gIGdldENhY2hlLFxyXG4gIGdldENhY2hlUGF0aFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWV0aG9kcyBmb3IgaW5pdGlhbGl6aW5nIEhpZ2hjaGFydHMgd2l0aCBjdXN0b21pemVkXHJcbiAqIGFuaW1hdGlvbiBzZXR0aW5ncyBhbmQgdHJpZ2dlcmluZyB0aGUgY3JlYXRpb24gb2YgSGlnaGNoYXJ0cyBjaGFydHMgd2l0aFxyXG4gKiBleHBvcnQtc3BlY2lmaWMgY29uZmlndXJhdGlvbnMgaW4gdGhlIHBhZ2UgY29udGV4dC4gU3VwcG9ydHMgZHluYW1pYyBvcHRpb25cclxuICogbWVyZ2luZywgY3VzdG9tIGxvZ2ljIGluamVjdGlvbiwgYW5kIGNvbnRyb2wgb3ZlciByZW5kZXJpbmcgYmVoYXZpb3JzLiBVc2VkXHJcbiAqIGJ5IHRoZSBQdXBwZXRlZXIgcGFnZS5cclxuICovXHJcblxyXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xyXG5cclxuLyoqXHJcbiAqIFNldHRpbmcgdGhlIGBIaWdoY2hhcnRzLmFuaW1PYmplY3RgIGZ1bmN0aW9uLiBDYWxsZWQgd2hlbiBpbml0aW5nIHRoZSBwYWdlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xyXG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcclxuICAgIHJldHVybiB7IGR1cmF0aW9uOiAwIH07XHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBIaWdoY2hhcnRzIGNoYXJ0IG9uIGEgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVDaGFydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQ2hhcnQoZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgc2V0T3B0aW9ucywgbWVyZ2UsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHRoZSBgdXNlck9wdGlvbnNgIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB0aGUgYHVzZXJPcHRpb25zYCB3aGVuIGBmb3JFeHBvcnRgIGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBTb21lIG1hbmRhdG9yeSBhZGRpdGlvbmFsIGBjaGFydGAgYW5kIGBleHBvcnRpbmdgIG9wdGlvbnNcclxuICBjb25zdCBhZGRpdGlvbmFsT3B0aW9ucyA9IHtcclxuICAgIGNoYXJ0OiB7XHJcbiAgICAgIC8vIEJ5IGRlZmF1bHQgYW5pbWF0aW9uIGlzIGRpc2FibGVkXHJcbiAgICAgIGFuaW1hdGlvbjogZmFsc2UsXHJcbiAgICAgIC8vIEdldCB0aGUgcmlnaHQgc2l6ZSB2YWx1ZXNcclxuICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcclxuICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmluc3RyfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYHRoZW1lT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgdGhlbWVPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9uc31gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb25cclxuICBjb25zdCBnbG9iYWxPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gTWVyZ2UgdGhlIGZvbGxvd2luZyBvcHRpb25zIG9iamVjdHMgdG8gY3JlYXRlIGZpbmFsIG9wdGlvbnNcclxuICBjb25zdCBmaW5hbE9wdGlvbnMgPSBtZXJnZShcclxuICAgIGZhbHNlLFxyXG4gICAgdGhlbWVPcHRpb25zLFxyXG4gICAgdXNlck9wdGlvbnMsXHJcbiAgICAvLyBQbGFjZWQgaXQgaGVyZSBpbnN0ZWFkIGluIHRoZSBpbml0IGJlY2F1c2Ugb2YgdGhlIHNpemUgaXNzdWVzXHJcbiAgICBhZGRpdGlvbmFsT3B0aW9uc1xyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjYWxsYmFja2Agb3B0aW9uXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke2N1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja31gKSgpXHJcbiAgICA6IG51bGw7XHJcblxyXG4gIC8vIFRyaWdnZXIgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbignb3B0aW9ucycsIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbZXhwb3J0T3B0aW9ucy5jb25zdHJdKCdjb250YWluZXInLCBmaW5hbE9wdGlvbnMsIGZpbmFsQ2FsbGJhY2spO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBgc2V0T3B0aW9uc2Agd2FzIHVzZWQgaW4gdGhlIGBjdXN0b21Db2RlYClcclxuICBmb3IgKGNvbnN0IHByb3AgaW4gZGVmYXVsdE9wdGlvbnMpIHtcclxuICAgIGlmICh0eXBlb2YgZGVmYXVsdE9wdGlvbnNbcHJvcF0gIT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgZGVsZXRlIGRlZmF1bHRPcHRpb25zW3Byb3BdO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBkZWZhdWx0IG9wdGlvbnMgYmFja1xyXG4gIHNldE9wdGlvbnMoSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqKTtcclxuXHJcbiAgLy8gRW1wdHkgdGhlIGN1c3RvbSBnbG9iYWwgb3B0aW9ucyBvYmplY3RcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSB7fTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNldHVwSGlnaGNoYXJ0cyxcclxuICBjcmVhdGVDaGFydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZSwgY3JlYXRpbmcgYW5kIGNsZWFyaW5nIHBhZ2VzLCBpbmplY3RpbmcgY3VzdG9tIHJlc291cmNlcyxcclxuICogYW5kIHNldHRpbmcgdXAgSGlnaGNoYXJ0cyBmb3Igc2VydmVyLXNpZGUgcmVuZGVyaW5nLiBUaGUgbW9kdWxlIGVuc3VyZXNcclxuICogdGhhdCByZXNvdXJjZXMgYXJlIGNvcnJlY3RseSBtYW5hZ2VkIGFuZCBjYW4gaGFuZGxlIGZhaWx1cmVzIGR1cmluZ1xyXG4gKiBvcGVyYXRpb25zIGxpa2UgbGF1bmNoaW5nIHRoZSBicm93c2VyIG9yIGNyZWF0aW5nIG5ldyBwYWdlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciBwYWdlc1xyXG5jb25zdCB0ZW1wbGF0ZSA9IHJlYWRGaWxlU3luYyhcclxuICBqb2luKF9fZGlybmFtZSwgJ3RlbXBsYXRlcycsICd0ZW1wbGF0ZS5odG1sJyksXHJcbiAgJ3V0ZjgnXHJcbik7XHJcblxyXG4vLyBUbyBzYXZlIHRoZSBicm93c2VyXHJcbmxldCBicm93c2VyID0gbnVsbDtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QnJvd3NlclxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBubyB2YWxpZCBicm93c2VyXHJcbiAqIGhhcyBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QnJvd3NlcigpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nLCA1MDApO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXJcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgY3JlYXRlZCBQdXBwZXRlZXJcclxuICogYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG1heCByZXRyaWVzIHRvIG9wZW5cclxuICogYSBicm93c2VyIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyXHJcbiAqIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2AgYW5kIGBvdGhlcmAgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWdPcHRpb25zIH0gPSBkZWJ1ZztcclxuXHJcbiAgLy8gTGF1bmNoIG9wdGlvbnMgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgY29uc3QgbGF1bmNoT3B0aW9ucyA9IHtcclxuICAgIGhlYWRsZXNzOiBvdGhlci5icm93c2VyU2hlbGxNb2RlID8gJ3NoZWxsJyA6IHRydWUsXHJcbiAgICB1c2VyRGF0YURpcjogJ3RtcCcsXHJcbiAgICBhcmdzOiBwdXBwZXRlZXJBcmdzIHx8IFtdLFxyXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHSFVQOiBmYWxzZSxcclxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXHJcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXHJcbiAgICAuLi4oZW5hYmxlZERlYnVnICYmIGRlYnVnT3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICAvLyBBIGNvdW50ZXIgZm9yIHRoZSBicm93c2VyJ3MgbGF1bmNoIHJldHJpZXNcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFticm93c2VyXSBBdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeSAkeysrdHJ5Q291bnR9KS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gTGF1bmNoIHRoZSBicm93c2VyXHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgJ1ticm93c2VyXSBGYWlsZWQgdG8gbGF1bmNoIGEgYnJvd3NlciBpbnN0YW5jZS4nXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0cnkgdG8gbGF1bmNoIGJyb3dzZXIgdW50aWwgcmVhY2hpbmcgbWF4IGF0dGVtcHRzXHJcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcclxuICAgICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIFJldHJ5IHRvIG9wZW4gYSBicm93c2VyICgke3RyeUNvdW50fSBvdXQgb2YgMjUpLmApO1xyXG5cclxuICAgICAgICAgIC8vIFdhaXQgZm9yIGEgNCBzZWNvbmRzIGJlZm9yZSB0cnlpbmcgYWdhaW5cclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG5cclxuICAgICAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGxhdW5jaE9wdGlvbnMuaGVhZGxlc3MgPT09ICdzaGVsbCcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIHNoZWxsIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERlYnVnIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChlbmFibGVkRGVidWcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIGRlYnVnIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2Jyb3dzZXJdIE1heGltdW0gcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlciBpbnN0YW5jZSByZWFjaGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nLCA1MDApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xvc2VCcm93c2VyXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2VCcm93c2VyKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIgJiYgYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgYnJvd3NlciA9IG51bGw7XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgcGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHRoZSBgX3NldFBhZ2VDb250ZW50KClgLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBuZXdQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIGBpZGAsXHJcbiAqIGB3b3JrQ291bnRgLCBhbmQgYHBhZ2VgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjb25uZWN0ZWQgb3IgaWYgYSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG5ld1BhZ2UocG9vbFJlc291cmNlKSB7XHJcbiAgLy8gRXJyb3IgaW4gY2FzZSBvZiBubyBjb25uZWN0ZWQgYnJvd3NlclxyXG4gIGlmICghYnJvd3NlciB8fCAhYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW2Jyb3dzZXJdIEJyb3dzZXIgaXMgbm90IHlldCBjb25uZWN0ZWQuYCwgNTAwKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBwb29sUmVzb3VyY2UucGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBEaXNhYmxlIGNhY2hlXHJcbiAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXHJcbiAgX3NldFBhZ2VFdmVudHMocG9vbFJlc291cmNlLnBhZ2UpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgcGFnZSBpcyBjb3JyZWN0bHkgY3JlYXRlZFxyXG4gIGlmICghcG9vbFJlc291cmNlLnBhZ2UgfHwgcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgdGhlIGNvbnRlbnQgb2YgYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG1vZGUuIExvZ3NcclxuICogdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIG9mIGEgcGFnZSdzIGNvbnRlbnQgZmFpbHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIHBhZ2UgYW5kIGlkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtoYXJkUmVzZXQ9ZmFsc2VdIC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gYGFib3V0OmJsYW5rYCBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgd2hlbiBwYWdlXHJcbiAqIGlzIGNvcnJlY3RseSBjbGVhcmVkIGFuZCBmYWxzZSB3aGVuIGl0IGlzIG5vdC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2UocG9vbFJlc291cmNlLCBoYXJkUmVzZXQgPSBmYWxzZSkge1xyXG4gIHRyeSB7XHJcbiAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGBhYm91dDpibGFua2BcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHtcclxuICAgICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBfc2V0UGFnZUNvbnRlbnQocG9vbFJlc291cmNlLnBhZ2UpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gQ29udGVudCBvZiB0aGUgcGFnZSBjb3VsZCBub3QgYmUgY2xlYXJlZC5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICBwb29sUmVzb3VyY2Uud29ya0NvdW50ID0gZ2V0T3B0aW9ucygpLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICB9XHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyBjdXN0b20gSlMgYW5kIENTUyByZXNvdXJjZXMgdG8gYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYWRkUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9IGZpbGUuc3RhcnRzV2l0aCgnaHR0cCcpID8gZmFsc2UgOiB0cnVlO1xyXG5cclxuICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gc2NyaXB0IGZyb20gcmVzb3VyY2VzJyBmaWxlc1xyXG4gICAgICAgIGluamVjdGVkSnMucHVzaChcclxuICAgICAgICAgIGlzTG9jYWxcclxuICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICBjb250ZW50OiByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGZpbGUpLCAndXRmOCcpXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA6IHtcclxuICAgICAgICAgICAgICAgIHVybDogZmlsZVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgZm9yIChjb25zdCBqc1Jlc291cmNlIG9mIGluamVjdGVkSnMpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU2NyaXB0VGFnKGpzUmVzb3VyY2UpKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gVGhlIEpTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGluamVjdGVkSnMubGVuZ3RoID0gMDtcclxuXHJcbiAgICAvLyBMb2FkIENTU1xyXG4gICAgY29uc3QgaW5qZWN0ZWRDc3MgPSBbXTtcclxuICAgIGlmIChyZXNvdXJjZXMuY3NzKSB7XHJcbiAgICAgIGxldCBjc3NJbXBvcnRzID0gcmVzb3VyY2VzLmNzcy5tYXRjaCgvQGltcG9ydFxccyooW147XSopOy9nKTtcclxuICAgICAgaWYgKGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAvLyBIYW5kbGUgY3NzIHNlY3Rpb25cclxuICAgICAgICBmb3IgKGxldCBjc3NJbXBvcnRQYXRoIG9mIGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoKSB7XHJcbiAgICAgICAgICAgIGNzc0ltcG9ydFBhdGggPSBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ3VybCgnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgnQGltcG9ydCcsICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvJy9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvOy8sICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cXCkvZywgJycpXHJcbiAgICAgICAgICAgICAgLnRyaW0oKTtcclxuXHJcbiAgICAgICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBjc3MgZnJvbSByZXNvdXJjZXNcclxuICAgICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGguc3RhcnRzV2l0aCgnaHR0cCcpKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICBwYXRoOiBnZXRBYnNvbHV0ZVBhdGgoY3NzSW1wb3J0UGF0aClcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVGhlIHJlc3Qgb2YgdGhlIENTUyBzZWN0aW9uIHdpbGwgYmUgY29udGVudCBieSBub3dcclxuICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmNzcy5yZXBsYWNlKC9AaW1wb3J0XFxzKihbXjtdKik7L2csICcnKSB8fCAnICdcclxuICAgICAgfSk7XHJcblxyXG4gICAgICBmb3IgKGNvbnN0IGNzc1Jlc291cmNlIG9mIGluamVjdGVkQ3NzKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyhjc3NSZXNvdXJjZSkpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICBgW2Jyb3dzZXJdIFRoZSBDU1MgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBpbmplY3RlZENzcy5sZW5ndGggPSAwO1xyXG4gICAgfVxyXG4gIH1cclxuICByZXR1cm4gaW5qZWN0ZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgb3V0IGFsbCBzdGF0ZSBzZXQgb24gdGhlIHBhZ2Ugd2l0aCBgYWRkU2NyaXB0VGFnYCBhbmQgYGFkZFN0eWxlVGFnYC5cclxuICogUmVtb3ZlcyBpbmplY3RlZCByZXNvdXJjZXMgYW5kIHJlc2V0cyBDU1MgYW5kIHNjcmlwdCB0YWdzIG9uIHRoZSBwYWdlLlxyXG4gKiBBZGRpdGlvbmFsbHksIGl0IGRlc3Ryb3lzIHByZXZpb3VzbHkgZXhpc3RpbmcgY2hhcnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgZnJvbSB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxPYmplY3Q+fSBpbmplY3RlZFJlc291cmNlcyAtIEFycmF5IG9mIGluamVjdGVkIHJlc291cmNlc1xyXG4gKiB0byBiZSBjbGVhcmVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcykge1xyXG4gIHRyeSB7XHJcbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlc291cmNlLmRpc3Bvc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IG9sZCBjaGFydHMgYWZ0ZXIgZXhwb3J0IGlzIGRvbmUgYW5kIHJlc2V0IGFsbCBDU1MgYW5kIHNjcmlwdCB0YWdzXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgLy8gV2UgYXJlIG5vdCBndWFyYW50ZWVkIHRoYXQgSGlnaGNoYXJ0cyBpcyBsb2FkZWQsIHdoZW4gZG9pbmcgU1ZHIGV4cG9ydHNcclxuICAgICAgaWYgKHR5cGVvZiBIaWdoY2hhcnRzICE9PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgIGNvbnN0IG9sZENoYXJ0cyA9IEhpZ2hjaGFydHMuY2hhcnRzO1xyXG5cclxuICAgICAgICAvLyBDaGVjayBpbiBhbnkgYWxyZWFkeSBleGlzdGluZyBjaGFydHNcclxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShvbGRDaGFydHMpICYmIG9sZENoYXJ0cy5sZW5ndGgpIHtcclxuICAgICAgICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0c1xyXG4gICAgICAgICAgZm9yIChjb25zdCBvbGRDaGFydCBvZiBvbGRDaGFydHMpIHtcclxuICAgICAgICAgICAgb2xkQ2hhcnQgJiYgb2xkQ2hhcnQuZGVzdHJveSgpO1xyXG4gICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgICAgSGlnaGNoYXJ0cy5jaGFydHMuc2hpZnQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gQ291bGQgbm90IGNsZWFyIHBhZ2UncyByZXNvdXJjZXMuYCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgY29udGVudCBmb3IgYSBQdXBwZXRlZXIgUGFnZSB1c2luZyBhIHByZWRlZmluZWQgdGVtcGxhdGVcclxuICogYW5kIGFkZGl0aW9uYWwgc2NyaXB0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBjb250ZW50XHJcbiAqIGlzIGJlaW5nIHNldC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zZXRQYWdlQ29udGVudChwYWdlKSB7XHJcbiAgLy8gU2V0IHRoZSBpbml0aWFsIHBhZ2UgY29udGVudFxyXG4gIGF3YWl0IHBhZ2Uuc2V0Q29udGVudCh0ZW1wbGF0ZSwgeyB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJyB9KTtcclxuXHJcbiAgLy8gQWRkIGFsbCByZWdpc3RlcmVkIEhpZ2NoYXJ0cyBzY3JpcHRzLCBxdWl0ZSBkZW1hbmRpbmdcclxuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdzb3VyY2VzLmpzJykgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBgYW5pbU9iamVjdGAgZm9yIEhpZ2hjaGFydHNcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKHNldHVwSGlnaGNoYXJ0cyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgZXZlbnRzIChsaWtlIGBwYWdlZXJyb3JgIGFuZCBgY29uc29sZWApIGZvciBhIFB1cHBldGVlciBQYWdlIGluIG9yZGVyXHJcbiAqIHRvIGNhdGNoIGFuZCBkaXNwbGF5IGVycm9ycyBhbmQgY29uc29sZSBsb2dzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VFdmVudHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBsaXN0ZW5lcnNcclxuICogYXJlIGJlaW5nIHNldC5cclxuICovXHJcbmZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzKHBhZ2UpIHtcclxuICAvLyBHZXQgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBkZWJ1ZyB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBTZXQgdGhlIGBwYWdlZXJyb3JgIGxpc3RlbmVyXHJcbiAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKCkgPT4ge1xyXG4gICAgLy8gSXQgd291bGQgc2VlbSBsaWtlIHRoaXMgbWF5IGZpcmUgYXQgdGhlIHNhbWUgdGltZSBvciBzaG9ydGx5IGJlZm9yZVxyXG4gICAgLy8gYSBwYWdlIGlzIGNsb3NlZC5cclxuICAgIGlmIChwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGBjb25zb2xlYCBsaXN0ZW5lciwgaWYgbmVlZGVkXHJcbiAgaWYgKGRlYnVnLmVuYWJsZSAmJiBkZWJ1Zy5saXN0ZW5Ub0NvbnNvbGUpIHtcclxuICAgIHBhZ2Uub24oJ2NvbnNvbGUnLCAobWVzc2FnZSkgPT4ge1xyXG4gICAgICBjb25zb2xlLmxvZyhgW2RlYnVnXSAke21lc3NhZ2UudGV4dCgpfWApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0QnJvd3NlcixcclxuICBjcmVhdGVCcm93c2VyLFxyXG4gIGNsb3NlQnJvd3NlcixcclxuICBuZXdQYWdlLFxyXG4gIGNsZWFyUGFnZSxcclxuICBhZGRQYWdlUmVzb3VyY2VzLFxyXG4gIGNsZWFyUGFnZVJlc291cmNlc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgQ1NTIHRvIGJlIHVzZWQgb24gdGhlIGV4cG9ydGVkIHBhZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBDU1MgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0ICgpID0+IGBcclxuXHJcbmh0bWwsIGJvZHkge1xyXG4gIG1hcmdpbjogMDtcclxuICBwYWRkaW5nOiAwO1xyXG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XHJcbn1cclxuXHJcbiN0YWJsZS1kaXYsICNzbGlkZXJzLCAjZGF0YXRhYmxlLCAjY29udHJvbHMsIC5sZC1yb3cge1xyXG4gIGRpc3BsYXk6IG5vbmU7XHJcbiAgaGVpZ2h0OiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyIHtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG4gIG1hcmdpbjogMDtcclxuICBvdmVyZmxvdzogYXV0bztcclxuICBmb250LXNpemU6IDA7XHJcbn1cclxuXHJcbiNjaGFydC1jb250YWluZXIgPiBmaWd1cmUsIGRpdiB7XHJcbiAgbWFyZ2luLXRvcDogMCAhaW1wb3J0YW50O1xyXG4gIG1hcmdpbi1ib3R0b206IDAgIWltcG9ydGFudDtcclxufVxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY3NzVGVtcGxhdGUgZnJvbSAnLi9jc3MuanMnO1xyXG5cclxuLyoqXHJcbiAqIFRoZSBTVkcgdGVtcGxhdGUgdG8gdXNlIHdoZW4gbG9hZGluZyBTVkcgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHN2ZyAtIFRoZSBTVkcgaW5wdXQgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIFNWRyB0ZW1wbGF0ZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChzdmcpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNoYXJ0cyBFeHBvcnQ8L3RpdGxlPlxyXG4gIDwvaGVhZD5cclxuICA8c3R5bGU+XHJcbiAgICAke2Nzc1RlbXBsYXRlKCl9XHJcbiAgPC9zdHlsZT5cclxuICA8Ym9keT5cclxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cclxuICAgICAgJHtzdmd9XHJcbiAgICA8L2Rpdj5cclxuICA8L2JvZHk+XHJcbjwvaHRtbD5cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBoYW5kbGVzIGNoYXJ0IGV4cG9ydCBmdW5jdGlvbmFsaXR5IHVzaW5nIFB1cHBldGVlci5cclxuICogSXQgc3VwcG9ydHMgZXhwb3J0aW5nIGNoYXJ0cyBhcyBTVkcsIFBORywgSlBFRywgYW5kIFBERiBmb3JtYXRzLiBUaGUgbW9kdWxlXHJcbiAqIG1hbmFnZXMgcGFnZSByZXNvdXJjZXMsIHNldHMgdXAgdGhlIGV4cG9ydCBlbnZpcm9ubWVudCwgYW5kIHByb2Nlc3NlcyBjaGFydFxyXG4gKiBjb25maWd1cmF0aW9ucyBvciBTVkcgaW5wdXRzIGZvciByZW5kZXJpbmcuIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZVxyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYWRkUGFnZVJlc291cmNlcywgY2xlYXJQYWdlUmVzb3VyY2VzIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgY3JlYXRlQ2hhcnQgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgc3ZnVGVtcGxhdGUgZnJvbSAnLi4vdGVtcGxhdGVzL3N2Z0V4cG9ydC9zdmdFeHBvcnQuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2UgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTwoc3RyaW5nfEJ1ZmZlcnxFeHBvcnRFcnJvcik+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlc1xyXG4gKiB0byB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgZXhwb3J0IHRvIGFuIHVuc3VwcG9ydGVkXHJcbiAqIG91dHB1dCBmb3JtYXQgb2NjdXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydChwYWdlLCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXkgKGFkZGl0aW9uYWwgSlMgYW5kIENTUylcclxuICBjb25zdCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICB0cnkge1xyXG4gICAgbGV0IGlzU1ZHID0gZmFsc2U7XHJcblxyXG4gICAgLy8gRGVjaWRlIG9uIHRoZSBleHBvcnQgbWV0aG9kXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy5zdmcpIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgYHR5cGVgIGlzIGFsc28gU1ZHLCByZXR1cm4gdGhlIGlucHV0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGV4cG9ydE9wdGlvbnMuc3ZnO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBNYXJrIGFzIFNWRyBleHBvcnQgZm9yIHRoZSBsYXRlciBzaXplIGNvcnJlY3Rpb25zXHJcbiAgICAgIGlzU1ZHID0gdHJ1ZTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRcclxuICAgICAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKGV4cG9ydE9wdGlvbnMuc3ZnKSwge1xyXG4gICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgIH0pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBKU09OIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoY3JlYXRlQ2hhcnQsIGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXHJcbiAgICAvLyBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAuLi4oYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBjdXN0b21Mb2dpY09wdGlvbnMpKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZSBhbmQgc2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XHJcbiAgICBjb25zdCBzaXplID0gaXNTVkdcclxuICAgICAgPyBhd2FpdCBwYWdlLmV2YWx1YXRlKChzY2FsZSkgPT4ge1xyXG4gICAgICAgICAgY29uc3Qgc3ZnRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXHJcbiAgICAgICAgICAgICcjY2hhcnQtY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJ1xyXG4gICAgICAgICAgKTtcclxuXHJcbiAgICAgICAgICAvLyBHZXQgdGhlIHZhbHVlcyBjb3JyZWN0bHkgc2NhbGVkXHJcbiAgICAgICAgICBjb25zdCBjaGFydEhlaWdodCA9IHN2Z0VsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0V2lkdGggPSBzdmdFbGVtZW50LndpZHRoLmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBJbiBjYXNlIG9mIFNWRyB0aGUgem9vbSBtdXN0IGJlIHNldCBkaXJlY3RseSBmb3IgYm9keSBhcyBzY2FsZVxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBTZXQgdGhlIG1hcmdpbiB0byAwcHhcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcclxuXHJcbiAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICBjaGFydEhlaWdodCxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aFxyXG4gICAgICAgICAgfTtcclxuICAgICAgICB9LCBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpKVxyXG4gICAgICA6IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBjb25zdCB7IGNoYXJ0SGVpZ2h0LCBjaGFydFdpZHRoIH0gPSB3aW5kb3cuSGlnaGNoYXJ0cy5jaGFydHNbMF07XHJcblxyXG4gICAgICAgICAgLy8gTm8gbmVlZCBmb3Igc3VjaCBzY2FsZSBtYW5pcHVsYXRpb24gaW4gY2FzZSBvZiBvdGhlciB0eXBlc1xyXG4gICAgICAgICAgLy8gb2YgZXhwb3J0cy4gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjbGlwIHJlZ2lvbiBmb3IgdGhlIHBhZ2VcclxuICAgIGNvbnN0IHsgeCwgeSB9ID0gYXdhaXQgX2dldENsaXBSZWdpb24ocGFnZSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGBoZWlnaHRgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRIZWlnaHQgfHwgZXhwb3J0T3B0aW9ucy5oZWlnaHQpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgd2lkdGhgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRXaWR0aCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcclxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgIH0pO1xyXG5cclxuICAgIGxldCByZXN1bHQ7XHJcbiAgICAvLyBSYXN0ZXJpemF0aW9uIHByb2Nlc3NcclxuICAgIHN3aXRjaCAoZXhwb3J0T3B0aW9ucy50eXBlKSB7XHJcbiAgICAgIGNhc2UgJ3N2Zyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVNWRyhwYWdlKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncG5nJzpcclxuICAgICAgY2FzZSAnanBlZyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZUltYWdlKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMudHlwZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIHgsXHJcbiAgICAgICAgICAgIHlcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncGRmJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlUERGKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBkZWZhdWx0OlxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBVbnN1cHBvcnRlZCBvdXRwdXQgZm9ybWF0OiAke2V4cG9ydE9wdGlvbnMudHlwZX0uYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2xlYXIgcHJldmlvdXNseSBpbmplY3RlZCBKUyBhbmQgQ1NTIHJlc291cmNlc1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMgb2YgdGhlIHNwZWNpZmllZCBwYWdlIGVsZW1lbnRcclxuICogd2l0aCB0aGUgJ2NoYXJ0LWNvbnRhaW5lcicgaWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2dldENsaXBSZWdpb25cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGB4YCwgYHlgLCBgd2lkdGhgLCBhbmQgYGhlaWdodGAgcHJvcGVydGllcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XHJcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB4LFxyXG4gICAgICB5LFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXHJcbiAgICB9O1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBTVkcgYnkgZXZhbHVhdGluZyB0aGUgYG91dGVySFRNTGAgb2YgdGhlIGZpcnN0ICdzdmcnIGVsZW1lbnRcclxuICogaW5zaWRlIGFuIGVsZW1lbnQgd2l0aCB0aGUgaWQgJ2NvbnRhaW5lcicuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVNWR1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFNWRyBzdHJpbmcuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlU1ZHKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbChcclxuICAgICcjY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJyxcclxuICAgIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIGltYWdlIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgYHNjcmVlbnNob3RgIGZ1bmN0aW9uYWxpdHkgd2l0aFxyXG4gKiBzcGVjaWZpZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlSW1hZ2VcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gSW1hZ2UgdHlwZS5cclxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgaW1hZ2UgYnVmZmVyXHJcbiAqIG9yIHJlamVjdGluZyB3aXRoIGFuIGBFeHBvcnRFcnJvcmAgZm9yIHRpbWVvdXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlSW1hZ2UocGFnZSwgdHlwZSwgY2xpcCwgcmFzdGVyaXphdGlvblRpbWVvdXQpIHtcclxuICByZXR1cm4gUHJvbWlzZS5yYWNlKFtcclxuICAgIHBhZ2Uuc2NyZWVuc2hvdCh7XHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGNsaXAsXHJcbiAgICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgICAgZnVsbFBhZ2U6IGZhbHNlLFxyXG4gICAgICBvcHRpbWl6ZUZvclNwZWVkOiB0cnVlLFxyXG4gICAgICBjYXB0dXJlQmV5b25kVmlld3BvcnQ6IHRydWUsXHJcbiAgICAgIC4uLih0eXBlICE9PSAncG5nJyA/IHsgcXVhbGl0eTogODAgfSA6IHt9KSxcclxuICAgICAgLy8gQWx3YXlzIHJlbmRlciBvbiBhIHRyYW5zcGFyZW50IHBhZ2UgaWYgdGhlIGV4cGVjdGVkIHR5cGUgZm9ybWF0IGlzIFBOR1xyXG4gICAgICBvbWl0QmFja2dyb3VuZDogdHlwZSA9PSAncG5nJyAvLyAjNDQ3LCAjNDYzXHJcbiAgICB9KSxcclxuICAgIG5ldyBQcm9taXNlKChfcmVzb2x2ZSwgcmVqZWN0KSA9PlxyXG4gICAgICBzZXRUaW1lb3V0KFxyXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcsIDQwOCkpLFxyXG4gICAgICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICAgICAgKVxyXG4gICAgKVxyXG4gIF0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIFBERiB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBwZGZgIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlUERGXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0IC0gUERGIGhlaWdodC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoIC0gUERGIHdpZHRoLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFBERiBidWZmZXIuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlUERGKHBhZ2UsIGhlaWdodCwgd2lkdGgsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgYXdhaXQgcGFnZS5lbXVsYXRlTWVkaWFUeXBlKCdzY3JlZW4nKTtcclxuICByZXR1cm4gcGFnZS5wZGYoe1xyXG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXHJcbiAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICB3aWR0aCxcclxuICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgIHRpbWVvdXQ6IHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHB1cHBldGVlckV4cG9ydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgYSB3b3JrZXIgcG9vbCBpbXBsZW1lbnRhdGlvbiBmb3IgbWFuYWdpbmdcclxuICogdGhlIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHBhZ2VzLCBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIHVzZSB3aXRoXHJcbiAqIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuIEl0IG9wdGltaXplcyByZXNvdXJjZXMgdXNhZ2UgYW5kIHBlcmZvcm1hbmNlXHJcbiAqIGJ5IG1haW50YWluaW5nIGEgcG9vbCBvZiB3b3JrZXJzIHRoYXQgY2FuIGhhbmRsZSBjb25jdXJyZW50IGV4cG9ydCB0YXNrc1xyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgUG9vbCB9IGZyb20gJ3Rhcm4nO1xyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBjcmVhdGVCcm93c2VyLCBjbG9zZUJyb3dzZXIsIG5ld1BhZ2UsIGNsZWFyUGFnZSB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IHB1cHBldGVlckV4cG9ydCB9IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldE5ld0RhdGVUaW1lLCBtZWFzdXJlVGltZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBwb29sIGluc3RhbmNlXHJcbmxldCBwb29sID0gbnVsbDtcclxuXHJcbi8vIFBvb2wgc3RhdGlzdGljc1xyXG5jb25zdCBwb29sU3RhdHMgPSB7XHJcbiAgZXhwb3J0c0F0dGVtcHRlZDogMCxcclxuICBleHBvcnRzUGVyZm9ybWVkOiAwLFxyXG4gIGV4cG9ydHNEcm9wcGVkOiAwLFxyXG4gIGV4cG9ydHNGcm9tU3ZnOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uczogMCxcclxuICBleHBvcnRzRnJvbVN2Z0F0dGVtcHRzOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzOiAwLFxyXG4gIHRpbWVTcGVudDogMCxcclxuICB0aW1lU3BlbnRBdmVyYWdlOiAwXHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwb29sIHdpdGggdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24sIGNyZWF0aW5nXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhbmQgc2V0dGluZyB1cCB3b3JrZXIgcmVzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGluaXRQb29sXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlclxyXG4gKiBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBlbmRpbmcgdGhlIGZ1bmN0aW9uXHJcbiAqIGV4ZWN1dGlvbiB3aGVuIGFuIGFscmVhZHkgaW5pdGlhbGl6ZWQgcG9vbCBvZiByZXNvdXJjZXMgaXMgZm91bmQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY3JlYXRlIHRoZSBwb29sXHJcbiAqIG9mIHdvcmtlcnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdFBvb2wocG9vbE9wdGlvbnMsIHB1cHBldGVlckFyZ3MpIHtcclxuICAvLyBDcmVhdGUgYSBicm93c2VyIGluc3RhbmNlIHdpdGggdGhlIHB1cHBldGVlciBhcmd1bWVudHNcclxuICBhd2FpdCBjcmVhdGVCcm93c2VyKHB1cHBldGVlckFyZ3MpO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIEluaXRpYWxpemluZyBwb29sIHdpdGggd29ya2VyczogbWluICR7cG9vbE9wdGlvbnMubWluV29ya2Vyc30sIG1heCAke3Bvb2xPcHRpb25zLm1heFdvcmtlcnN9LmBcclxuICAgICk7XHJcblxyXG4gICAgaWYgKHBvb2wpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXAgYW4gZXllIG9uIGEgY29ycmVjdCBtaW4gYW5kIG1heCB3b3JrZXJzIG51bWJlclxyXG4gICAgaWYgKHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzKSB7XHJcbiAgICAgIHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPSBwb29sT3B0aW9ucy5tYXhXb3JrZXJzO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENyZWF0ZSBhIHBvb2wgYWxvbmcgd2l0aCBhIG1pbmltYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgcG9vbCA9IG5ldyBQb29sKHtcclxuICAgICAgLy8gR2V0IHRoZSBgY3JlYXRlYCwgYHZhbGlkYXRlYCwgYW5kIGBkZXN0cm95YCBmdW5jdGlvbnNcclxuICAgICAgLi4uX2ZhY3RvcnkocG9vbE9wdGlvbnMpLFxyXG4gICAgICBtaW46IHBvb2xPcHRpb25zLm1pbldvcmtlcnMsXHJcbiAgICAgIG1heDogcG9vbE9wdGlvbnMubWF4V29ya2VycyxcclxuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmFjcXVpcmVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVUaW1lb3V0LFxyXG4gICAgICBkZXN0cm95VGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuZGVzdHJveVRpbWVvdXQsXHJcbiAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5pZGxlVGltZW91dCxcclxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlUmV0cnlJbnRlcnZhbCxcclxuICAgICAgcmVhcEludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5yZWFwZXJJbnRlcnZhbCxcclxuICAgICAgcHJvcGFnYXRlQ3JlYXRlRXJyb3I6IGZhbHNlXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBTZXQgZXZlbnRzXHJcbiAgICBwb29sLm9uKCdyZWxlYXNlJywgYXN5bmMgKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIENsZWFyIHBhZ2VcclxuICAgICAgY29uc3QgY2xlYXJTdGF0dXMgPSBhd2FpdCBjbGVhclBhZ2UocmVzb3VyY2UsIGZhbHNlKTtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBSZWxlYXNpbmcgYSB3b3JrZXIuIENsZWFyIHBhZ2Ugc3RhdHVzOiAke2NsZWFyU3RhdHVzfS5gXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChfZXZlbnRJZCwgcmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBEZXN0cm95ZWQgYSB3b3JrZXIgc3VjY2Vzc2Z1bGx5LmBcclxuICAgICAgKTtcclxuICAgICAgcmVzb3VyY2UucGFnZSA9IG51bGw7XHJcbiAgICB9KTtcclxuXHJcbiAgICBjb25zdCBpbml0aWFsUmVzb3VyY2VzID0gW107XHJcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBvb2xPcHRpb25zLm1pbldvcmtlcnM7IGkrKykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuICAgICAgICBpbml0aWFsUmVzb3VyY2VzLnB1c2gocmVzb3VyY2UpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIGFuIGluaXRpYWwgcmVzb3VyY2UuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXMgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgaW5pdGlhbFJlc291cmNlcy5mb3JFYWNoKChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICBwb29sLnJlbGVhc2UocmVzb3VyY2UpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIFRoZSBwb29sIGlzIHJlYWR5JHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aCA/IGAgd2l0aCAke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RofSBpbml0aWFsIHJlc291cmNlcyB3YWl0aW5nLmAgOiAnLid9YFxyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3Bvb2xdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogVGVybWluYXRlcyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24ga2lsbFBvb2xcclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgYWxsIHdvcmtlcnMgYXJlXHJcbiAqIHRlcm1pbmF0ZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIHN1Y2Nlc3NmdWxseSBjbG9zZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XHJcbiAgbG9nKDMsICdbcG9vbF0gS2lsbGluZyBwb29sIHdpdGggYWxsIHdvcmtlcnMgYW5kIGNsb3NpbmcgYnJvd3Nlci4nKTtcclxuXHJcbiAgLy8gSWYgc3RpbGwgYWxpdmUsIGRlc3Ryb3kgdGhlIHBvb2wgb2YgcGFnZXMgYmVmb3JlIGNsb3NpbmcgYSBicm93c2VyXHJcbiAgaWYgKHBvb2wpIHtcclxuICAgIC8vIEZyZWUgdXAgbm90IHJlbGVhc2VkIHdvcmtlcnNcclxuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHBvb2wudXNlZCkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VyLnJlc291cmNlKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IHRoZSBwb29sIGlmIGl0IGlzIHN0aWxsIGF2YWlsYWJsZVxyXG4gICAgaWYgKCFwb29sLmRlc3Ryb3llZCkge1xyXG4gICAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gRGVzdHJveWVkIHRoZSBwb29sIG9mIHJlc291cmNlcy4nKTtcclxuICAgIH1cclxuICAgIHBvb2wgPSBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICBhd2FpdCBjbG9zZUJyb3dzZXIoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByb2Nlc3NlcyB0aGUgZXhwb3J0IHdvcmsgdXNpbmcgYSB3b3JrZXIgZnJvbSB0aGUgcG9vbC4gQWNxdWlyZXMgYSB3b3JrZXJcclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcclxuICogdGhlIHdvcmtlciBoYW5kbGUgYmFjayB0byB0aGUgcG9vbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0V29ya1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZXhwb3J0IHJlc3VsdFxyXG4gKiBhbmQgb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3RXb3JrKG9wdGlvbnMpIHtcclxuICBsZXQgd29ya2VySGFuZGxlO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgICAvLyBBbiBleHBvcnQgYXR0ZW1wdCBjb3VudGVkXHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzQXR0ZW1wdGVkO1xyXG5cclxuICAgIC8vIERpc3BsYXkgdGhlIHBvb2wgaW5mb3JtYXRpb24gaWYgbmVlZGVkXHJcbiAgICBpZiAob3B0aW9ucy5wb29sLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGluIGNhc2Ugb2YgbGFja2luZyB0aGUgcG9vbCBpbnN0YW5jZVxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhlIGFjcXVpcmUgY291bnRlclxyXG4gICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFRyeSB0byBhY3F1aXJlIHRoZSB3b3JrZXIgYWxvbmcgd2l0aCB0aGUgaWQsIHdvcmtzIGNvdW50IGFuZCBwYWdlXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgICAgLy8gQWNxdWlyZSBhIHBvb2wgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxyXG4gICAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgICAgYEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUgdG9vayAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFJlc29sdmVkIHdvcmtlciBwYWdlIGlzIGludmFsaWQ6IHRoZSBwb29sIHNldHVwIGlzIHdvbmt5LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgY29uc3Qgd29ya1N0YXJ0ID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHt3b3JrZXJIYW5kbGUuaWR9XSAtIFN0YXJ0aW5nIHdvcmsgb24gdGhpcyBwb29sIGVudHJ5LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU3RhcnQgbWVhc3VyaW5nIGV4cG9ydCB0aW1lXHJcbiAgICBjb25zdCBleHBvcnRDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KFxyXG4gICAgICB3b3JrZXJIYW5kbGUucGFnZSxcclxuICAgICAgb3B0aW9ucy5leHBvcnQsXHJcbiAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKHJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIElmIHRoZXJlJ3MgYSByYXN0ZXJpemF0aW9uIHRpbWVvdXQsIHdlIHdhbnQgbmVlZCB0byBmbHVzaCB0aGUgcGFnZS5cclxuICAgICAgLy8gVGhpcyBpcyBiZWNhdXNlIHRoZSBwYWdlIG1heSBiZSBpbiBhIHN0YXRlIHdoZXJlIGl0J3Mgd2FpdGluZyBmb3JcclxuICAgICAgLy8gdGhlIHNjcmVlbnNob3QgdG8gZmluaXNoIGV2ZW4gdGhvdWdoIHRoZSB0aW1lb3V0IGhhcyBvY2N1cmVkLlxyXG4gICAgICAvLyBXaGljaCBvZiBjb3Vyc2UgY2F1c2VzIGEgbG90IG9mIGlzc3VlcyB3aXRoIHRoZSBldmVudCBzeXN0ZW0sXHJcbiAgICAgIC8vIGFuZCBwYWdlIGNvbnNpc3RlbmN5LlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBPbmx5IHBhZ2Uuc2NyZWVuc2hvdCB3aWxsIHRocm93IHRoaXMsIHRpbWVvdXRzIGZvciBQREYncyBhcmVcclxuICAgICAgLy8gaGFuZGxlZCBieSB0aGUgcGFnZS5wZGYgZnVuY3Rpb24gaXRzZWxmLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyAuLi55ZXMsIHRoaXMgaXMgdWdseS5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlID0gbnVsbDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJlc3VsdC5uYW1lID09PSAnVGltZW91dEVycm9yJyB8fFxyXG4gICAgICAgIHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0J1xyXG4gICAgICApIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfVJhc3Rlcml6YXRpb24gdGltZW91dDogeW91ciBjaGFydCBtYXkgYmUgdG9vIGNvbXBsZXggb3IgbGFyZ2UsIGFuZCBmYWlsZWQgdG8gcmVuZGVyIHdpdGhpbiB0aGUgYWxsb3R0ZWQgdGltZS5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGV4cG9ydDogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIHRoZSBQdXBwZXRlZXIgZXhwb3J0IHRpbWVcclxuICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDUsXHJcbiAgICAgICAgYFtiZW5jaG1hcmtdICR7b3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnfWAsXHJcbiAgICAgICAgYEV4cG9ydGluZyBhIGNoYXJ0IHN1Y2Vzc2Z1bGx5IHRvb2sgJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIHJlc291cmNlIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG5cclxuICAgIC8vIFVzZWQgZm9yIHN0YXRpc3RpY3MgaW4gYXZlcmFnZVRpbWUgYW5kIHByb2Nlc3NlZFdvcmtDb3VudCwgd2hpY2hcclxuICAgIC8vIGluIHR1cm4gaXMgdXNlZCBieSB0aGUgL2hlYWx0aCByb3V0ZS5cclxuICAgIGNvbnN0IHdvcmtFbmQgPSBnZXROZXdEYXRlVGltZSgpO1xyXG4gICAgY29uc3QgZXhwb3J0VGltZSA9IHdvcmtFbmQgLSB3b3JrU3RhcnQ7XHJcblxyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UgPVxyXG4gICAgICBwb29sU3RhdHMudGltZVNwZW50IC8gKytwb29sU3RhdHMuZXhwb3J0c1BlcmZvcm1lZDtcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBXb3JrIGNvbXBsZXRlZCBpbiAke2V4cG9ydFRpbWV9bXMuYCk7XHJcblxyXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICByZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICsrcG9vbFN0YXRzLmV4cG9ydHNEcm9wcGVkO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhyb3cgZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fG51bGwpfSBUaGUgY3VycmVudCBwb29sIGluc3RhbmNlIGlmIGluaXRpYWxpemVkLCBvciBudWxsXHJcbiAqIGlmIHRoZSBwb29sIGhhcyBub3QgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2woKSB7XHJcbiAgcmV0dXJuIHBvb2w7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBzdGF0aXN0aWMgb2YgYSBwb29sIGluc3RhY2UgYWJvdXQgZXhwb3J0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xTdGF0c1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY3VycmVudCBwb29sIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbFN0YXRzKCkge1xyXG4gIHJldHVybiBwb29sU3RhdHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgcG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdCwgaW5jbHVkaW5nIG1pbmltdW0gYW5kIG1heGltdW1cclxuICogd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OKCkge1xyXG4gIHJldHVybiB7XHJcbiAgICBtaW46IHBvb2wubWluLFxyXG4gICAgbWF4OiBwb29sLm1heCxcclxuICAgIHVzZWQ6IHBvb2wubnVtVXNlZCgpLFxyXG4gICAgYXZhaWxhYmxlOiBwb29sLm51bUZyZWUoKSxcclxuICAgIGFsbENyZWF0ZWQ6IHBvb2wubnVtVXNlZCgpICsgcG9vbC5udW1GcmVlKCksXHJcbiAgICBwZW5kaW5nQWNxdWlyZXM6IHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCksXHJcbiAgICBwZW5kaW5nQ3JlYXRlczogcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zOiBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzOiBwb29sLnBlbmRpbmdEZXN0cm95cy5sZW5ndGgsXHJcbiAgICBhYnNvbHV0ZUFsbDpcclxuICAgICAgcG9vbC5udW1Vc2VkKCkgK1xyXG4gICAgICBwb29sLm51bUZyZWUoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdDcmVhdGVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpICtcclxuICAgICAgcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoXHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHBvb2wsIGluY2x1ZGluZyB0aGUgbWluaW11bVxyXG4gKiBhbmQgbWF4aW11bSB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmVcclxuICogcmVxdWVzdHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sSW5mb1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvKCkge1xyXG4gIGNvbnN0IHtcclxuICAgIG1pbixcclxuICAgIG1heCxcclxuICAgIHVzZWQsXHJcbiAgICBhdmFpbGFibGUsXHJcbiAgICBhbGxDcmVhdGVkLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXMsXHJcbiAgICBwZW5kaW5nVmFsaWRhdGlvbnMsXHJcbiAgICBwZW5kaW5nRGVzdHJveXMsXHJcbiAgICBhYnNvbHV0ZUFsbFxyXG4gIH0gPSBnZXRQb29sSW5mb0pTT04oKTtcclxuXHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1pbmltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWlufS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttYXh9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgdXNlZCByZXNvdXJjZXM6ICR7dXNlZH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBmcmVlIHJlc291cmNlczogJHthdmFpbGFibGV9LmApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIGNyZWF0ZWQgKHVzZWQgYW5kIGZyZWUpIHJlc291cmNlczogJHthbGxDcmVhdGVkfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGFjcXVpcmVkOiAke3BlbmRpbmdBY3F1aXJlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBjcmVhdGVkOiAke3BlbmRpbmdDcmVhdGVzfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIHZhbGlkYXRlZDogJHtwZW5kaW5nVmFsaWRhdGlvbnN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgZGVzdHJveWVkOiAke3BlbmRpbmdEZXN0cm95c30uYFxyXG4gICk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgcmVzb3VyY2VzOiAke2Fic29sdXRlQWxsfS5gKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZhY3RvcnkgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGFuIG9iamVjdCB3aXRoIGBjcmVhdGVgLCBgdmFsaWRhdGVgLFxyXG4gKiBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9ucyBmb3IgdGhlIHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmFjdG9yeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9mYWN0b3J5KHBvb2xPcHRpb25zKSB7XHJcbiAgcmV0dXJuIHtcclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyB3b3JrZXIgcGFnZSBmb3IgdGhlIGV4cG9ydCBwb29sLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGNyZWF0ZVxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdFxyXG4gICAgICogY29udGFpbmluZyB0aGUgd29ya2VyIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgaW5pdGlhbFxyXG4gICAgICogd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gZXJyb3IgZHVyaW5nXHJcbiAgICAgKiB0aGUgY3JlYXRpb24gb2YgdGhlIG5ldyBwYWdlLlxyXG4gICAgICovXHJcbiAgICBjcmVhdGU6IGFzeW5jICgpID0+IHtcclxuICAgICAgLy8gSW5pdCB0aGUgcmVzb3VyY2Ugd2l0aCB1bmlxdWUgaWQgYW5kIHdvcmsgY291bnRcclxuICAgICAgY29uc3QgcG9vbFJlc291cmNlID0ge1xyXG4gICAgICAgIGlkOiB1dWlkKCksXHJcbiAgICAgICAgLy8gVHJ5IHRvIGRpc3RyaWJ1dGUgdGhlIGluaXRpYWwgd29yayBjb3VudFxyXG4gICAgICAgIHdvcmtDb3VudDogTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogKHBvb2xPcHRpb25zLndvcmtMaW1pdCAvIDIpKVxyXG4gICAgICB9O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBTdGFydCBtZWFzdXJpbmcgYSBwYWdlIGNyZWF0aW9uIHRpbWVcclxuICAgICAgICBjb25zdCBzdGFydERhdGUgPSBnZXROZXdEYXRlVGltZSgpO1xyXG5cclxuICAgICAgICAvLyBDcmVhdGUgYSBuZXcgcGFnZVxyXG4gICAgICAgIGF3YWl0IG5ld1BhZ2UocG9vbFJlc291cmNlKTtcclxuXHJcbiAgICAgICAgLy8gTWVhc3VyZSB0aGUgdGltZSBvZiBmdWxsIGNyZWF0aW9uIGFuZCBjb25maWd1cmF0aW9uIG9mIGEgcGFnZVxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBhIHdvcmtlciwgdG9vayAke1xyXG4gICAgICAgICAgICBnZXROZXdEYXRlVGltZSgpIC0gc3RhcnREYXRlXHJcbiAgICAgICAgICB9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHVybiByZWFkeSBwb29sIHJlc291cmNlXHJcbiAgICAgICAgcmV0dXJuIHBvb2xSZXNvdXJjZTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBjcmVhdGluZyBhIG5ldyBwYWdlLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcclxuICAgICAqIHRoZSB3b3JrIGxpbWl0LlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIHZhbGlkYXRlXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHBvb2xSZXNvdXJjZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAgICogdGhlIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBpZiB0aGUgd29ya2VyXHJcbiAgICAgKiBpcyB2YWxpZCBhbmQgd2l0aGluIHRoZSB3b3JrIGxpbWl0OyBvdGhlcndpc2UsIHRvIGZhbHNlLlxyXG4gICAgICovXHJcbiAgICB2YWxpZGF0ZTogYXN5bmMgKHBvb2xSZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJbiBjZXJ0YWluIGNhc2VzIGFjcXVpcmluZyB0aHJvd3MgYSBUYXJnZXRDbG9zZUVycm9yLCB3aGljaCBtYXlcclxuICAgICAgLy8gYmUgY2F1c2VkIGJ5IHR3byB0aGluZ3M6XHJcbiAgICAgIC8vIC0gVGhlIHBhZ2UgaXMgY2xvc2VkIGFuZCBhdHRlbXB0ZWQgdG8gYmUgcmV1c2VkLlxyXG4gICAgICAvLyAtIExvc3QgY29udGFjdCB3aXRoIHRoZSBicm93c2VyLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBXaGF0IHdlJ3JlIHNlZWluZyBpbiBsb2dzIGlzIHRoYXQgc3VjY2Vzc2l2ZSBleHBvcnRzIHR5cGljYWxseVxyXG4gICAgICAvLyBzdWNjZWVkcywgYW5kIHRoZSBzZXJ2ZXIgcmVjb3ZlcnMsIGluZGljYXRpbmcgdGhhdCBpdCdzIGxpa2VseVxyXG4gICAgICAvLyB0aGUgZmlyc3QgY2FzZS4gVGhpcyBpcyBhbiBhdHRlbXB0IGF0IGFsbGlldmF0aW5nIHRoZSBpc3N1ZSBieVxyXG4gICAgICAvLyBzaW1wbHkgbm90IHZhbGlkYXRpbmcgdGhlIHdvcmtlciBpZiB0aGUgcGFnZSBpcyBudWxsIG9yIGNsb3NlZC5cclxuICAgICAgLy9cclxuICAgICAgLy8gVGhlIGFjdHVhbCByZXN1bHQgZnJvbSB3aGVuIHRoaXMgaGFwcGVuZWQsIHdhcyB0aGF0IGEgd29ya2VyIHdvdWxkXHJcbiAgICAgIC8vIGJlIGNvbXBsZXRlbHkgbG9ja2VkLCBzdG9wcGluZyBpdCBmcm9tIGJlaW5nIGFjcXVpcmVkIHVudGlsXHJcbiAgICAgIC8vIGl0cyB3b3JrIGNvdW50IHJlYWNoZWQgdGhlIGxpbWl0LlxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBwYWdlYCBpcyB2YWxpZFxyXG4gICAgICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChubyB2YWxpZCBwYWdlIGlzIGZvdW5kKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgY2xvc2VkXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlIGlzIGNsb3NlZCBvciBpbnZhbGlkKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgbWFpbkZyYW1lYCBpcyBkZXRhY2hlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UubWFpbkZyYW1lKCkuZGV0YWNoZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKHBhZ2UncyBmcmFtZSBpcyBkZXRhY2hlZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHdvcmtMaW1pdGAgaXMgZXhjZWVkZWRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHBvb2xPcHRpb25zLndvcmtMaW1pdCAmJlxyXG4gICAgICAgICsrcG9vbFJlc291cmNlLndvcmtDb3VudCA+IHBvb2xPcHRpb25zLndvcmtMaW1pdFxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKGV4Y2VlZGVkIHRoZSAke3Bvb2xPcHRpb25zLndvcmtMaW1pdH0gd29ya3MgcGVyIHJlc291cmNlIGxpbWl0KS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSBgcG9vbFJlc291cmNlYCBpcyB2YWxpZGF0ZWRcclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gZGVzdHJveVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKi9cclxuICAgIGRlc3Ryb3k6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRGVzdHJveWluZyBhIHdvcmtlci5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgLy8gUmVtb3ZlIGFsbCBhdHRhY2hlZCBldmVudCBsaXN0ZW5lcnMgZnJvbSB0aGUgcmVzb3VyY2VcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygncGFnZWVycm9yJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2NvbnNvbGUnKTtcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygnZnJhbWVkZXRhY2hlZCcpO1xyXG5cclxuICAgICAgICAgIC8vIFdlIG5lZWQgdG8gd2FpdCBhcm91bmQgZm9yIHRoaXNcclxuICAgICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gUGFnZSBjb3VsZCBub3QgYmUgY2xvc2VkIHVwb24gZGVzdHJveWluZy5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGluaXRQb29sLFxyXG4gIGtpbGxQb29sLFxyXG4gIHBvc3RXb3JrLFxyXG4gIGdldFBvb2wsXHJcbiAgZ2V0UG9vbFN0YXRzLFxyXG4gIGdldFBvb2xJbmZvLFxyXG4gIGdldFBvb2xJbmZvSlNPTlxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVXNlZCB0byBzYW5pdGl6ZSB0aGUgc3RyaW5ncyBjb21pbmcgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZVxyXG4gKiB0byBwcmV2ZW50IFhTUyBhdHRhY2tzICh3aXRoIHRoZSBET01QdXJpZnkgbGlicmFyeSkuXHJcbiAqL1xyXG5cclxuaW1wb3J0IERPTVB1cmlmeSBmcm9tICdkb21wdXJpZnknO1xyXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcclxuXHJcbi8qKlxyXG4gKiBTYW5pdGl6ZXMgYSBnaXZlbiBIVE1MIHN0cmluZyBieSByZW1vdmluZyA8c2NyaXB0PiB0YWdzLiBUaGlzIGZ1bmN0aW9uIHVzZXNcclxuICogYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gZmluZCBhbmQgcmVtb3ZlIGFsbCBvY2N1cnJlbmNlcyBvZiA8c2NyaXB0Pjwvc2NyaXB0PlxyXG4gKiB0YWdzIGFuZCBhbnkgY29udGVudCB3aXRoaW4gdGhlbS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNhbml0aXplXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCAtIFRoZSBIVE1MIHN0cmluZyB0byBiZSBzYW5pdGl6ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTCBzdHJpbmcuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXQpIHtcclxuICAvLyBHZXQgdGhlIHZpcnR1YWwgRE9NXHJcbiAgY29uc3Qgd2luZG93ID0gbmV3IEpTRE9NKCcnKS53aW5kb3c7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHB1cmlmeWluZyBpbnN0YW5jZVxyXG4gIGNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cpO1xyXG5cclxuICAvLyBSZXR1cm4gc2FuaXRpemVkIGlucHV0XHJcbiAgcmV0dXJuIHB1cmlmeS5zYW5pdGl6ZShpbnB1dCwgeyBBRERfVEFHUzogWydmb3JlaWduT2JqZWN0J10gfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzYW5pdGl6ZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIHRvIHByZXBhcmUgZm9yIHRoZSBleHBvcnRpbmcgY2hhcnRzXHJcbiAqIGludG8gdmFyaW91cyBpbWFnZSBvdXRwdXQgZm9ybWF0cyBzdWNoIGFzIEpQRUcsIFBORywgUERGLCBhbmQgU1ZHcy5cclxuICogSXQgc3VwcG9ydHMgc2luZ2xlIGFuZCBiYXRjaCBleHBvcnQgb3BlcmF0aW9ucyBhbmQgYWxsb3dzIGN1c3RvbWl6YXRpb25cclxuICogdGhyb3VnaCBvcHRpb25zIHBhc3NlZCBmcm9tIGNvbmZpZ3VyYXRpb25zIG9yIEFQSXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnLCB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBpc09iamVjdCxcclxuICByb3VuZE51bWJlcixcclxuICB3cmFwQXJvdW5kXHJcbn0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGdsb2JhbCBmbGFnIGZvciB0aGUgY29kZSBleGVjdXRpb24gcGVybWlzc2lvblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucyBhbmQgc2F2ZXNcclxuICogdGhlIHJlc3VsdGluZyBpbWFnZSB0byB0aGUgcHJvdmlkZWQgb3V0cHV0IGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIFRoZSBvYmplY3QgbXVzdCBjb250YWluIGF0IGxlYXN0IG9uZVxyXG4gKiBvZiB0aGUgZm9sbG93aW5nIGBleHBvcnRgIHByb3BlcnRpZXM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgXHJcbiAqIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpbmdsZUV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0KSB7XHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQoXHJcbiAgICAgIHsgZXhwb3J0OiBvcHRpb25zLmV4cG9ydCwgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWMgfSxcclxuICAgICAgYXN5bmMgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSBvbmUgb2YgdGhlIGZvbGxvd2luZyBvcHRpb25zOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIGluZm9ybWF0aW9uXHJcbiAqIHByb3ZpZGVkIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZ1xyXG4gKiBmb3JtYXQ6IFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzXHJcbiAqIGFyZSBzYXZlZCB0byB0aGUgc3BlY2lmaWVkIG91dHB1dCBmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIGZyb21cclxuICogdGhlIGBleHBvcnRgIHNlY3Rpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3NlcyBhcmUgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJhdGNoRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQgJiYgb3B0aW9ucy5leHBvcnQuYmF0Y2gpIHtcclxuICAgIC8vIEFuIGFycmF5IGZvciBjb2xsZWN0aW5nIGJhdGNoIGV4cG9ydHNcclxuICAgIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIGBiYXRjaGAgYXJndW1lbnRzXHJcbiAgICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykgfHwgW10pIHtcclxuICAgICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgaW1hZ2VPcHRpb25zYCBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXRcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgcGFydGlhbFxyXG4gKiBvcHRpb25zIGFyZSBwcm92aWRlZCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICpcclxuICogVGhlIGBlbmRDYWxsYmFja2AgZnVuY3Rpb24gaXMgaW52b2tlZCB1cG9uIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBleHBvcnQsXHJcbiAqIGVpdGhlciBzdWNjZXNzZnVsbHkgb3Igd2l0aCBhbiBlcnJvci4gVGhlIGBlcnJvcmAgb2JqZWN0IGlzIHByb3ZpZGVkXHJcbiAqIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIGNvbnRhaW5pbmdcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5XHJcbiAqIGFuZCB0aGUgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW1hZ2VPcHRpb25zIC0gVGhlIGBpbWFnZU9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkXHJcbiAqIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgdGhlIHByb3ZpZGVkXHJcbiAqIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHRoZSBleHBvcnQgcHJvY2VzcyBvciB1cG9uIGVuY291bnRlcmluZyBhbiBlcnJvci4gVGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGlzIHRoZSBgZXJyb3JgIG9iamVjdCwgYW5kIHRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIGBkYXRhYCBvYmplY3QsXHJcbiAqIHdoaWNoIGluY2x1ZGVzIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YFxyXG4gKiBwcm9wZXJ0eSBhbmQgdGhlIGZ1bGwgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGltYWdlT3B0aW9ucywgZW5kQ2FsbGJhY2spIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgb3B0aW9ucyBhcmUgaW4gYW4gb2JqZWN0XHJcbiAgICBpZiAoIWlzT2JqZWN0KGltYWdlT3B0aW9ucykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgcHJvdmlkZWQgYGltYWdlT3B0aW9uc2AuIE5lZWRzIHRvIGJlIGFuIG9iamVjdC4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE1lcmdlIGFkZGl0aW9uYWwgb3B0aW9ucyB0byB0aGUgY29weSBvZiB0aGUgaW5zdGFuY2Ugb3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoXHJcbiAgICAgIHtcclxuICAgICAgICBleHBvcnQ6IGltYWdlT3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgY3VzdG9tTG9naWM6IGltYWdlT3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICByZXR1cm4gcG9zdFdvcmsob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBmb3IgY2hhcnQgZXhwb3J0cyBiYXNlZFxyXG4gKiBvbiB0aGUgcHJvdmlkZWQgZXhwb3J0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICogMS4gVGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBgc2NhbGVgIGZyb20gdGhlIGBleHBvcnRPcHRpb25zYC5cclxuICogMi4gT3B0aW9ucyBmcm9tIHRoZSBjaGFydCBjb25maWd1cmF0aW9uIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBnbG9iYWwgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgdGhlbWUgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCBzZWN0aW9ucykuXHJcbiAqIDUuIEZhbGxiYWNrIGRlZmF1bHQgdmFsdWVzIChgaGVpZ2h0ID0gNDAwYCwgYHdpZHRoID0gNjAwYCwgYHNjYWxlID0gMWApLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZpbmRDaGFydFNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb2RlIGV4ZWN1dGlvblxyXG4gKiBpcyBub3QgYWxsb3dlZCBidXQgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYXJlIHN0aWxsIHByb3ZpZGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgLy8gSW4gY2FzZSBvZiBhbGxvd2luZyBjb2RlIGV4ZWN1dGlvblxyXG4gIGlmIChhbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIC8vIFByb2Nlc3MgdGhlIGByZXNvdXJjZXNgIG9wdGlvblxyXG4gICAgaWYgKHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID09PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBDdXN0b20gc3RyaW5naWZpZWQgcmVzb3VyY2VzXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gTG9hZCB0aGUgZGVmYXVsdCBvbmVcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoJ3Jlc291cmNlcy5qc29uJyksICd1dGY4JyksXHJcbiAgICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IGByZXNvdXJjZXMuanNvbmAgZmlsZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGN1c3RvbSBjb2RlIGFuZCB3cmFwIGFyb3VuZCBpdCBpbiBhIHNlbGYgaW52b2tpbmcgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1tjaGFydF0gVGhlIGBjdXN0b21Db2RlYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gVHJ5IHRvIGxvYWQgY2FsbGJhY2sgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2ssXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY2FsbGJhY2tgIGNhbm5vdCBiZSBsb2FkZWQuJyk7XHJcblxyXG4gICAgICAvLyBJbiBjYXNlIG9mIGFuIGVycm9yLCBzZXQgdGhlIG9wdGlvbiB3aXRoIG51bGxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGN1c3RvbUNvZGVgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGN1c3RvbUNvZGVgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGNhbGxiYWNrYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgY2FsbGJhY2tgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHJlc291cmNlc2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gSWYgdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIGZsYWcgaXMgc2V0IHRvIGZhbHNlLCB3ZSBzaG91bGQgcmVmdXNlXHJcbiAgICAvLyB0aGUgdXNhZ2Ugb2YgdGhlIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgLCBhbmQgYGN1c3RvbUNvZGVgIG9wdGlvbnMuXHJcbiAgICAvLyBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbCByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LlxyXG4gICAgaWYgKFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFJlc2V0IGFsbCBjdXN0b20gY29kZSBvcHRpb25zXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcblxyXG4gICAgICAvLyBTZW5kIGEgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydCB0aGVzZSBzZXR0aW5nc1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnLCBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gLFxyXG4gICAgICAgIDQwM1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZnJvbSB0aGUgYHJlc291cmNlc2Agb3B0aW9uIGZvciBleHBvcnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFtyZXNvdXJjZXM9bnVsbF0gLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuXHJcbiAqIENhbiBiZSBlaXRoZXIgYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiwgYSBwYXRoIHRvIGEgSlNPTiBmaWxlLFxyXG4gKiBvciBudWxsLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgbnVsbGAuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2FkaW5nXHJcbiAqIHJlc291cmNlcyBmcm9tIGZpbGVzIGlzIGFsbG93ZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGhhbmRsZWQgcmVzb3VyY2VzIG9yIG51bGwgaWYgbm8gdmFsaWQgcmVzb3VyY2VzXHJcbiAqIGFyZSBmb3VuZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgcmVzb3VyY2VzID0gbnVsbCxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIExpc3Qgb2YgYWxsb3dlZCBzZWN0aW9ucyBpbiB0aGUgcmVzb3VyY2VzIEpTT05cclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKHJlc291cmNlcyksICd1dGY4JyksXHJcbiAgICAgICAgZmFsc2UsXHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxyXG4gICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhyZXNvdXJjZXMsIGZhbHNlLCBhbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIGZpbGVzIHNlY3Rpb25cclxuICAgIGlmIChoYW5kbGVkUmVzb3VyY2VzICYmICFhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaWx0ZXIgZnJvbSB1bm5lY2Vzc2FyeSBwcm9wZXJ0aWVzXHJcbiAgZm9yIChjb25zdCBwcm9wTmFtZSBpbiBoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXNbcHJvcE5hbWVdO1xyXG4gICAgfSBlbHNlIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiBhbGxvd2VkIHByb3BlcnRpZXMgaXMgcHJlc2VudFxyXG4gIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgbG9hZGluZyBhbmQgdmFsaWRhdGlvbiBvZiB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYFxyXG4gKiBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIElmIHRoZSBvcHRpb24gaXMgYSBzdHJpbmcgYW5kIHJlZmVyZW5jZXMgYSBKU09OIGZpbGVcclxuICogKHdoZW4gdGhlIGBhbGxvd0ZpbGVSZXNvdXJjZXNgIGlzIHRydWUpLCBpdCByZWFkcyBhbmQgcGFyc2VzIHRoZSBmaWxlLlxyXG4gKiBPdGhlcndpc2UsIGl0IGF0dGVtcHRzIHRvIHBhcnNlIHRoZSBzdHJpbmcgb3Igb2JqZWN0IGFzIEpTT04uIElmIGFueSBlcnJvcnNcclxuICogb2NjdXIgZHVyaW5nIHRoaXMgcHJvY2VzcywgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC4gSWYgdGhlcmUgaXMgYW4gZXJyb3JcclxuICogbG9hZGluZyBvciBwYXJzaW5nIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3IgYHRoZW1lT3B0aW9uc2AsIHRoZSBlcnJvciBpcyBsb2dnZWRcclxuICogYW5kIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBlbmFibGluZyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzcGVjaWZpZWQgRXhwcmVzcyBhcHAuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXRcclxuICogdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCByYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWQgYW5kIHRoZSBhcHAgZXhpc3RzXHJcbiAgICBpZiAoYXBwICYmIHJhdGVMaW1pdGluZ09wdGlvbnMuZW5hYmxlKSB7XHJcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPVxyXG4gICAgICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgICAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICAgICAgd2luZG93OiByYXRlTGltaXRpbmdPcHRpb25zLndpbmRvdyB8fCAxLFxyXG4gICAgICAgIG1heFJlcXVlc3RzOiByYXRlTGltaXRpbmdPcHRpb25zLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgICAgIGRlbGF5OiByYXRlTGltaXRpbmdPcHRpb25zLmRlbGF5IHx8IDAsXHJcbiAgICAgICAgdHJ1c3RQcm94eTogcmF0ZUxpbWl0aW5nT3B0aW9ucy50cnVzdFByb3h5IHx8IGZhbHNlLFxyXG4gICAgICAgIHNraXBLZXk6IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcEtleSB8fCBudWxsLFxyXG4gICAgICAgIHNraXBUb2tlbjogcmF0ZUxpbWl0aW5nT3B0aW9ucy5za2lwVG9rZW4gfHwgbnVsbFxyXG4gICAgICB9O1xyXG5cclxuICAgICAgLy8gU2V0IGlmIGJlaGluZCBhIHByb3h5XHJcbiAgICAgIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICAgICAgYXBwLmVuYWJsZSgndHJ1c3QgcHJveHknKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gICAgICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcclxuICAgICAgICAvLyBUaW1lIGZyYW1lIGZvciB3aGljaCByZXF1ZXN0cyBhcmUgY2hlY2tlZCBhbmQgcmVtZW1iZXJlZFxyXG4gICAgICAgIHdpbmRvd01zOiByYXRlT3B0aW9ucy53aW5kb3cgKiA2MCAqIDEwMDAsXHJcbiAgICAgICAgLy8gTGltaXQgZWFjaCBJUCB0byAxMDAgcmVxdWVzdHMgcGVyIGB3aW5kb3dNc2BcclxuICAgICAgICBsaW1pdDogcmF0ZU9wdGlvbnMubWF4UmVxdWVzdHMsXHJcbiAgICAgICAgLy8gRGlzYWJsZSBkZWxheWluZywgZnVsbCBzcGVlZCB1bnRpbCB0aGUgbWF4IGxpbWl0IGlzIHJlYWNoZWRcclxuICAgICAgICBkZWxheU1zOiByYXRlT3B0aW9ucy5kZWxheSxcclxuICAgICAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlLmZvcm1hdCh7XHJcbiAgICAgICAgICAgIGpzb246ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZSB9KTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgICAgIC8vIEFsbG93IGJ5cGFzc2luZyB0aGUgbGltaXRlciBpZiBhIHZhbGlkIGtleS90b2tlbiBoYXMgYmVlbiBzZW50XHJcbiAgICAgICAgICBpZiAoXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IG51bGwgJiZcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkuYWNjZXNzX3Rva2VuID09PSByYXRlT3B0aW9ucy5za2lwVG9rZW5cclxuICAgICAgICAgICkge1xyXG4gICAgICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcblxyXG4gICAgICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gICAgICBhcHAudXNlKGxpbWl0ZXIpO1xyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtyYXRlIGxpbWl0aW5nXSBFbmFibGVkIHJhdGUgbGltaXRpbmcgd2l0aCAke3JhdGVPcHRpb25zLm1heFJlcXVlc3RzfSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcmF0ZSBsaW1pdGluZ10gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciB2YWxpZGF0aW5nIGluY29taW5nIEhUVFAgcmVxdWVzdHNcclxuICogaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi4gVGhpcyBtb2R1bGUgZW5zdXJlcyB0aGF0IHJlcXVlc3RzIGNvbnRhaW5cclxuICogYXBwcm9wcmlhdGUgY29udGVudCB0eXBlcyBhbmQgdmFsaWQgcmVxdWVzdCBib2RpZXMsIGluY2x1ZGluZyBwcm9wZXIgSlNPTlxyXG4gKiBzdHJ1Y3R1cmVzIGFuZCBjaGFydCBkYXRhIGZvciBleHBvcnRzLiBJdCBjaGVja3MgZm9yIHBvdGVudGlhbCBpc3N1ZXMgc3VjaFxyXG4gKiBhcyBtaXNzaW5nIG9yIG1hbGZvcm1lZCBkYXRhLCBwcml2YXRlIHJhbmdlIFVSTHMgaW4gU1ZHIHBheWxvYWRzLCBhbmQgYWxsb3dzXHJcbiAqIGZvciBmbGV4aWJsZSBvcHRpb25zIHZhbGlkYXRpb24uIFRoZSBtaWRkbGV3YXJlIGxvZ3MgZGV0YWlsZWQgaW5mb3JtYXRpb25cclxuICogYW5kIGhhbmRsZXMgZXJyb3JzIHJlbGF0ZWQgdG8gaW5jb3JyZWN0IHBheWxvYWRzLCBjaGFydCBkYXRhLCBhbmQgcHJpdmF0ZSBVUkxcclxuICogdXNhZ2UuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uIH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpc09iamVjdEVtcHR5LCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgY29udGVudC10eXBlIGhlYWRlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBjb250ZW50LXR5cGVcclxuICogaXMgbm90IGNvcnJlY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSBjb250ZW50IHR5cGUgaGVhZGVyXHJcbiAgICBjb25zdCBjb250ZW50VHlwZSA9IHJlcXVlc3QuaGVhZGVyc1snY29udGVudC10eXBlJ10gfHwgJyc7XHJcblxyXG4gICAgLy8gQWxsb3cgb25seSBKU09OLCBVUkwtZW5jb2RlZCBhbmQgZm9ybSBkYXRhIHdpdGhvdXQgZmlsZXMgdHlwZXMgb2YgZGF0YVxyXG4gICAgaWYgKFxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL2pzb24nKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnbXVsdGlwYXJ0L2Zvcm0tZGF0YScpXHJcbiAgICApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbdmFsaWRhdGlvbl0gQ29udGVudC1UeXBlIG11c3QgYmUgYXBwbGljYXRpb24vanNvbiwgYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkLCBvciBtdWx0aXBhcnQvZm9ybS1kYXRhLicsXHJcbiAgICAgICAgNDE1XHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYHJlcXVlc3RCb2R5TWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIHJlcXVlc3QncyBib2R5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGJvZHkgaXMgbm90IGNvcnJlY3QuXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY2hhcnQgZGF0YSBmcm9tIHRoZSBib2R5XHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaW4gY2FzZSBvZiB0aGUgcHJpdmF0ZSByYW5nZVxyXG4gKiB1cmwgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGJvZHlcclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHV1aWQoKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgZW1wdHkuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGJvZHkgaXMgcmVxdWlyZWQuIFBsZWFzZSBlbnN1cmUgdGhhdCB5b3VyIENvbnRlbnQtVHlwZSBoZWFkZXIgaXMgY29ycmVjdC4gQWNjZXB0ZWQgdHlwZXMgYXJlICdhcHBsaWNhdGlvbi9qc29uJyBhbmQgJ211bHRpcGFydC9mb3JtLWRhdGEnLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uIGZvciB0aGUgc2VydmVyXHJcbiAgICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb24gPSBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKTtcclxuXHJcbiAgICAvLyBGaW5kIGEgY29ycmVjdCBjaGFydCBvcHRpb25zXHJcbiAgICBjb25zdCBpbnN0ciA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgLy8gVXNlIG9uZSBvZiB0aGUgYmVsb3dcclxuICAgICAgYm9keS5pbnN0ciB8fCBib2R5Lm9wdGlvbnMgfHwgYm9keS5pbmZpbGUgfHwgYm9keS5kYXRhLFxyXG4gICAgICAvLyBTdHJpbmdpZnkgb3B0aW9uc1xyXG4gICAgICB0cnVlLFxyXG4gICAgICAvLyBBbGxvdyBvciBkaXNhbGxvdyBmdW5jdGlvbnNcclxuICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRoZXJlIGlzIG5vIGNvcnJlY3QgY2hhcnQgZGF0YVxyXG4gICAgaWYgKGluc3RyID09PSBudWxsICYmICFib2R5LnN2Zykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIG1pc3NpbmcgY29ycmVjdCBjaGFydCBkYXRhIGZvciBleHBvcnQ6ICR7SlNPTi5zdHJpbmdpZnkoYm9keSl9LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGVzdCBvZiB4bGluazpocmVmIGVsZW1lbnRzIGZyb20gcGF5bG9hZCdzIFNWRyBmYWlsc1xyXG4gICAgaWYgKGJvZHkuc3ZnICYmIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoYm9keS5zdmcpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBTVkcgcG90ZW50aWFsbHkgY29udGFpbiBhdCBsZWFzdCBvbmUgZm9yYmlkZGVuIFVSTCBpbiAneGxpbms6aHJlZicgZWxlbWVudC4gUGxlYXNlIHJldmlldyB0aGUgU1ZHIGNvbnRlbnQgYW5kIGVuc3VyZSB0aGF0IGFsbCByZWZlcmVuY2VkIFVSTHMgY29tcGx5IHdpdGggc2VjdXJpdHkgcG9saWNpZXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3Qgb3B0aW9ucyBhbmQgc3RvcmUgcGFyc2VkIHN0cnVjdHVyZSBpbiB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC52YWxpZGF0ZWRPcHRpb25zID0ge1xyXG4gICAgICAvLyBTZXQgdGhlIGNyZWF0ZWQgSUQgYXMgYSBgcmVxdWVzdElkYCBwcm9wZXJ0eSBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICByZXF1ZXN0SWQsXHJcbiAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIHN2ZzogYm9keS5zdmcsXHJcbiAgICAgICAgb3V0ZmlsZTpcclxuICAgICAgICAgIGJvZHkub3V0ZmlsZSB8fFxyXG4gICAgICAgICAgYCR7cmVxdWVzdC5wYXJhbXMuZmlsZW5hbWUgfHwgJ2NoYXJ0J30uJHtib2R5LnR5cGUgfHwgJ3BuZyd9YCxcclxuICAgICAgICB0eXBlOiBib2R5LnR5cGUsXHJcbiAgICAgICAgY29uc3RyOiBib2R5LmNvbnN0cixcclxuICAgICAgICBiNjQ6IGJvZHkuYjY0LFxyXG4gICAgICAgIG5vRG93bmxvYWQ6IGJvZHkubm9Eb3dubG9hZCxcclxuICAgICAgICBoZWlnaHQ6IGJvZHkuaGVpZ2h0LFxyXG4gICAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxyXG4gICAgICAgIHNjYWxlOiBib2R5LnNjYWxlLFxyXG4gICAgICAgIGdsb2JhbE9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkuZ2xvYmFsT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApLFxyXG4gICAgICAgIHRoZW1lT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS50aGVtZU9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKVxyXG4gICAgICB9LFxyXG4gICAgICBjdXN0b21Mb2dpYzoge1xyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvbixcclxuICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IGZhbHNlLFxyXG4gICAgICAgIGN1c3RvbUNvZGU6IGJvZHkuY3VzdG9tQ29kZSxcclxuICAgICAgICBjYWxsYmFjazogYm9keS5jYWxsYmFjayxcclxuICAgICAgICByZXNvdXJjZXM6IGlzQWxsb3dlZENvbmZpZyhib2R5LnJlc291cmNlcywgdHJ1ZSwgYWxsb3dDb2RlRXhlY3V0aW9uKVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIC8vIENhbGwgdGhlIG5leHQgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApIHtcclxuICAvLyBBZGQgY29udGVudCB0eXBlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIGNvbnRlbnRUeXBlTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCByZXF1ZXN0IGJvZHkgcmVxdWVzdCB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCByZXF1ZXN0Qm9keU1pZGRsZXdhcmUpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIHRoZSBleHBvcnQgcm91dGVzIGFuZCBsb2dpYyBmb3IgaGFuZGxpbmcgY2hhcnQgZXhwb3J0XHJcbiAqIHJlcXVlc3RzIGluIGFuIEV4cHJlc3Mgc2VydmVyLiBUaGlzIG1vZHVsZSBwcm9jZXNzZXMgaW5jb21pbmcgcmVxdWVzdHNcclxuICogdG8gZXhwb3J0IGNoYXJ0cyBpbiB2YXJpb3VzIGZvcm1hdHMgKGUuZy4gSlBFRywgUE5HLCBQREYsIFNWRykuIEl0IGludGVncmF0ZXNcclxuICogd2l0aCBIaWdoY2hhcnRzJyBjb3JlIGZ1bmN0aW9uYWxpdGllcyBhbmQgc3VwcG9ydHMgYm90aCBpbW1lZGlhdGUgZG93bmxvYWRcclxuICogcmVzcG9uc2VzIGFuZCBCYXNlNjQtZW5jb2RlZCBjb250ZW50IHJldHVybnMuIFRoZSBjb2RlIGFsc28gZmVhdHVyZXNcclxuICogYmVuY2htYXJraW5nIGZvciBwZXJmb3JtYW5jZSBtb25pdG9yaW5nLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHN0YXJ0RXhwb3J0IH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRCYXNlNjQsIG1lYXN1cmVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBSZXZlcnNlZCBNSU1FIHR5cGVzXHJcbmNvbnN0IHJldmVyc2VkTWltZSA9IHtcclxuICBwbmc6ICdpbWFnZS9wbmcnLFxyXG4gIGpwZWc6ICdpbWFnZS9qcGVnJyxcclxuICBnaWY6ICdpbWFnZS9naWYnLFxyXG4gIHBkZjogJ2FwcGxpY2F0aW9uL3BkZicsXHJcbiAgc3ZnOiAnaW1hZ2Uvc3ZnK3htbCdcclxufTtcclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleHBvcnQgcmVxdWVzdHMgZnJvbSB0aGUgY2xpZW50LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnQocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gU3RhcnQgY291bnRpbmcgdGltZSBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gSW4gY2FzZSB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQsIGZvcmNlIHRvIGFib3J0IGZ1cnRoZXIgYWN0aW9uc1xyXG4gICAgbGV0IGNvbm5lY3Rpb25BYm9ydGVkID0gZmFsc2U7XHJcbiAgICByZXF1ZXN0LnNvY2tldC5vbignY2xvc2UnLCAoaGFkRXJyb3JzKSA9PiB7XHJcbiAgICAgIGlmIChoYWRFcnJvcnMpIHtcclxuICAgICAgICBjb25uZWN0aW9uQWJvcnRlZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgb3B0aW9ucyBwcmV2aW91c2x5IHZhbGlkYXRlZCBpbiB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucztcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3QgaWRcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHJlcXVlc3RPcHRpb25zLnJlcXVlc3RJZDtcclxuXHJcbiAgICAvLyBJbmZvIGFib3V0IGFuIGluY29taW5nIHJlcXVlc3Qgd2l0aCBjb3JyZWN0IGRhdGFcclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3QuYCk7XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChyZXF1ZXN0T3B0aW9ucywgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgIC8vIFJlbW92ZSB0aGUgY2xvc2UgZXZlbnQgZnJvbSB0aGUgc29ja2V0XHJcbiAgICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBjb25uZWN0aW9uIHdhcyBjbG9zZWQsIGRvIG5vdGhpbmdcclxuICAgICAgaWYgKGNvbm5lY3Rpb25BYm9ydGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gYmVmb3JlIHRoZSBjaGFydCBmaW5pc2hlZCBwcm9jZXNzaW5nLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZXJyb3IsIGxvZyBpdCBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCBsb2cgdGhlIG1lc3NhZ2UgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKCFkYXRhIHx8ICFkYXRhLnJlc3VsdCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8XHJcbiAgICAgICAgICAgIHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHJlc3VsdCBpcyAke2RhdGEucmVzdWx0fS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBVbmV4cGVjdGVkIHJldHVybiBvZiB0aGUgZXhwb3J0IHJlc3VsdCBmcm9tIHRoZSBjaGFydCBnZW5lcmF0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciByZXF1ZXN0IGRhdGEuYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFJldHVybiB0aGUgcmVzdWx0IGluIGFuIGFwcHJvcHJpYXRlIGZvcm1hdFxyXG4gICAgICBpZiAoZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3MgdG9vayAke3JlcXVlc3RDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYHR5cGVgLCBgYjY0YCwgYG5vRG93bmxvYWRgLCBhbmQgYG91dGZpbGVgIGZyb20gb3B0aW9uc1xyXG4gICAgICAgIGNvbnN0IHsgdHlwZSwgYjY0LCBub0Rvd25sb2FkLCBvdXRmaWxlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBJZiBvbmx5IEJhc2U2NCBpcyByZXF1aXJlZCwgcmV0dXJuIGl0XHJcbiAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTZXQgY29ycmVjdCBjb250ZW50IHR5cGVcclxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XHJcblxyXG4gICAgICAgIC8vIERlY2lkZSB3aGV0aGVyIHRvIGRvd25sb2FkIG9yIG5vdCBjaGFydCBmaWxlXHJcbiAgICAgICAgaWYgKCFub0Rvd25sb2FkKSB7XHJcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KG91dGZpbGUpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgU1ZHLCByZXR1cm4gcGxhaW4gY29udGVudCwgb3RoZXJ3aXNlIGEgYjY0IHN0cmluZyBmcm9tIGEgYnVmZmVyXHJcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXHJcbiAgICAgICAgICA/IHJlc3BvbnNlLnNlbmQoZGF0YS5yZXN1bHQpXHJcbiAgICAgICAgICA6IHJlc3BvbnNlLnNlbmQoQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYGV4cG9ydGAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZXhwb3J0Um91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gZXhwb3J0Um91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy8nIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyBhdCB0aGUgcm9vdFxyXG4gICAqIGVuZHBvaW50LlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvJywgcmVxdWVzdEV4cG9ydCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy86ZmlsZW5hbWUnIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyB3aXRoXHJcbiAgICogYSBzcGVjaWZpZWQgZmlsZW5hbWUgcGFyYW1ldGVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgcmVxdWVzdEV4cG9ydCk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmVyIGhlYWx0aCBtb25pdG9yaW5nLCBpbmNsdWRpbmdcclxuICogdXB0aW1lLCBzdWNjZXNzIHJhdGVzLCBhbmQgb3RoZXIgc2VydmVyIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBnZXRQb29sSW5mb0pTT04gfSBmcm9tICcuLi8uLi9wb29sLmpzJztcclxuaW1wb3J0IHsgYWRkVGltZXIgfSBmcm9tICcuLi8uLi90aW1lci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0TmV3RGF0ZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vLyBTZXQgdGhlIHN0YXJ0IGRhdGUgb2YgdGhlIHNlcnZlclxyXG5jb25zdCBzZXJ2ZXJTdGFydFRpbWUgPSBuZXcgRGF0ZSgpO1xyXG5cclxuLy8gR2V0IHRoZSBgcGFja2FnZS5qc29uYCBjb250ZW50XHJcbmNvbnN0IHBhY2thZ2VGaWxlID0gSlNPTi5wYXJzZShcclxuICByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICdwYWNrYWdlLmpzb24nKSwgJ3V0ZjgnKVxyXG4pO1xyXG5cclxuLy8gQW4gYXJyYXkgZm9yIHN1Y2Nlc3MgcmF0ZSByYXRpb3NcclxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XHJcblxyXG4vLyBSZWNvcmQgZXZlcnkgbWludXRlXHJcbmNvbnN0IHJlY29yZEludGVydmFsID0gNjAgKiAxMDAwO1xyXG5cclxuLy8gMzAgbWludXRlc1xyXG5jb25zdCB3aW5kb3dTaXplID0gMzA7XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgYHN1Y2Nlc3NSYXRlc2BcclxuICogYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBBIG1vdmluZyBhdmVyYWdlIGZvciBzdWNjZXNzIHJhdGlvIG9mIHRoZSBzZXJ2ZXIgZXhwb3J0cy5cclxuICovXHJcbmZ1bmN0aW9uIF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCkge1xyXG4gIHJldHVybiBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIHRoZSBpbnRlcnZhbCByZXNwb25zaWJsZSBmb3IgY2FsY3VsYXRpbmcgY3VycmVudCBzdWNjZXNzIHJhdGUgcmF0aW9cclxuICogYW5kIGNvbGxlY3RzIHJlY29yZHMgdG8gdGhlIGBzdWNjZXNzUmF0ZXNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3N0YXJ0U3VjY2Vzc1JhdGVcclxuICpcclxuICogQHJldHVybnMge05vZGVKUy5UaW1lb3V0fSBJZCBvZiBhbiBpbnRlcnZhbC5cclxuICovXHJcbmZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlKCkge1xyXG4gIHJldHVybiBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICBjb25zdCBzdGF0cyA9IGdldFBvb2xTdGF0cygpO1xyXG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID1cclxuICAgICAgc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCA9PT0gMFxyXG4gICAgICAgID8gMVxyXG4gICAgICAgIDogKHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQgLyBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkKSAqIDEwMDtcclxuXHJcbiAgICBzdWNjZXNzUmF0ZXMucHVzaChzdWNjZXNzUmF0aW8pO1xyXG4gICAgaWYgKHN1Y2Nlc3NSYXRlcy5sZW5ndGggPiB3aW5kb3dTaXplKSB7XHJcbiAgICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xyXG4gICAgfVxyXG4gIH0sIHJlY29yZEludGVydmFsKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBoZWFsdGhgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGhlYWx0aFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGhlYWx0aFJvdXRlcyhhcHApIHtcclxuICAvLyBTdGFydCBwcm9jZXNzaW5nIHN1Y2Nlc3MgcmF0ZSByYXRpbyBpbnRlcnZhbCBhbmQgc2F2ZSBpdHMgaWQgdG8gdGhlIGFycmF5XHJcbiAgLy8gZm9yIHRoZSBncmFjZWZ1bCBjbGVhcmluZyBvbiBzaHV0ZG93biB3aXRoIGluamVjdGVkIGBhZGRUaW1lcmAgZnVudGlvblxyXG4gIGFkZFRpbWVyKF9zdGFydFN1Y2Nlc3NSYXRlKCkpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy9oZWFsdGgnIC0gQSByb3V0ZSBmb3IgZ2V0dGluZyB0aGUgYmFzaWMgc3RhdHMgb2YgdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KCcvaGVhbHRoJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1toZWFsdGhdIFJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgICAgY29uc3QgbW92aW5nQXZlcmFnZSA9IF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgICAvLyBTZW5kIHRoZSBzZXJ2ZXIncyBzdGF0aXN0aWNzXHJcbiAgICAgIHJlc3BvbnNlLnNlbmQoe1xyXG4gICAgICAgIC8vIFN0YXR1cyBhbmQgdGltZXNcclxuICAgICAgICBzdGF0dXM6ICdPSycsXHJcbiAgICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcclxuICAgICAgICB1cHRpbWU6IGAke01hdGguZmxvb3IoKGdldE5ld0RhdGVUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MCl9IG1pbnV0ZXNgLFxyXG5cclxuICAgICAgICAvLyBWZXJzaW9uc1xyXG4gICAgICAgIHNlcnZlclZlcnNpb246IHBhY2thZ2VGaWxlLnZlcnNpb24sXHJcbiAgICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IGdldEhpZ2hjaGFydHNWZXJzaW9uKCksXHJcblxyXG4gICAgICAgIC8vIEV4cG9ydHNcclxuICAgICAgICBhdmVyYWdlRXhwb3J0VGltZTogc3RhdHMudGltZVNwZW50QXZlcmFnZSxcclxuICAgICAgICBhdHRlbXB0ZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkLFxyXG4gICAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQsXHJcbiAgICAgICAgZmFpbGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0Ryb3BwZWQsXHJcbiAgICAgICAgc3VjZXNzUmF0aW86IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDAsXHJcblxyXG4gICAgICAgIC8vIFBvb2xcclxuICAgICAgICBwb29sOiBnZXRQb29sSW5mb0pTT04oKSxcclxuXHJcbiAgICAgICAgLy8gTW92aW5nIGF2ZXJhZ2VcclxuICAgICAgICBwZXJpb2QsXHJcbiAgICAgICAgbW92aW5nQXZlcmFnZSxcclxuICAgICAgICBtZXNzYWdlOlxyXG4gICAgICAgICAgaXNOYU4obW92aW5nQXZlcmFnZSkgfHwgIXN1Y2Nlc3NSYXRlcy5sZW5ndGhcclxuICAgICAgICAgICAgPyAnVG9vIGVhcmx5IHRvIHJlcG9ydC4gTm8gZXhwb3J0cyBtYWRlIHlldC4gUGxlYXNlIGNoZWNrIGJhY2sgc29vbi4nXHJcbiAgICAgICAgICAgIDogYExhc3QgJHtwZXJpb2R9IG1pbnV0ZXMgaGFkIGEgc3VjY2VzcyByYXRlIG9mICR7bW92aW5nQXZlcmFnZS50b0ZpeGVkKDIpfSUuYCxcclxuXHJcbiAgICAgICAgLy8gU1ZHIGFuZCBKU09OIGV4cG9ydHNcclxuICAgICAgICBzdmdFeHBvcnRzOiBzdGF0cy5leHBvcnRzRnJvbVN2ZyxcclxuICAgICAgICBqc29uRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zLFxyXG4gICAgICAgIHN2Z0V4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyxcclxuICAgICAgICBqc29uRXhwb3J0c0F0dGVtcHRzOiBzdGF0cy5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0c1xyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciBzZXJ2aW5nIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXJcclxuICogd2hlbiBlbmFibGVkLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHVpYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1aVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHVpUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIEdFVCAnLycgLSBBIHJvdXRlIGZvciBhIFVJIHdoZW4gZW5hYmxlZCBvbiB0aGUgZXhwb3J0IHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KGdldE9wdGlvbnMoKS51aS5yb3V0ZSB8fCAnLycsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdWldIFJldHVybmluZyBVSSBmb3IgdGhlIGV4cG9ydC4nKTtcclxuXHJcbiAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwge1xyXG4gICAgICAgIGFjY2VwdFJhbmdlczogZmFsc2VcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3IgdXBkYXRpbmcgdGhlIEhpZ2hjaGFydHMgdmVyc2lvblxyXG4gKiBvbiB0aGUgc2VydmVyLCB3aXRoIGF1dGhlbnRpY2F0aW9uIGFuZCB2YWxpZGF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGdldEhpZ2hjaGFydHNWZXJzaW9uLCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHZlcnNpb25fY2hhbmdlYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nIC0gQSByb3V0ZSBmb3IgY2hhbmdpbmdcclxuICAgKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicsIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdmVyc2lvbl0gQ2hhbmdpbmcgSGlnaGNoYXJ0cyB2ZXJzaW9uLicpO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBkaXJlY3RseSBmcm9tIGVudnNcclxuICAgICAgY29uc3QgYWRtaW5Ub2tlbiA9IGVudnMuSElHSENIQVJUU19BRE1JTl9UT0tFTjtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBleGlzdGVuY2Ugb2YgdGhlIHRva2VuXHJcbiAgICAgIGlmICghYWRtaW5Ub2tlbiB8fCAhYWRtaW5Ub2tlbi5sZW5ndGgpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIFRoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBmcm9tIHRoZSBoYy1hdXRoIGhlYWRlclxyXG4gICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbdmVyc2lvbl0gSW52YWxpZCBvciBtaXNzaW5nIHRva2VuOiBTZXQgdGhlIHRva2VuIGluIHRoZSBoYy1hdXRoIGhlYWRlci4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xyXG4gICAgICBsZXQgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XHJcbiAgICAgIGlmIChuZXdWZXJzaW9uKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFVwZGF0ZSB2ZXJzaW9uXHJcbiAgICAgICAgICBhd2FpdCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICBgW3ZlcnNpb25dIFZlcnNpb24gY2hhbmdlOiAke2Vycm9yLm1lc3NhZ2V9YCxcclxuICAgICAgICAgICAgNDAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFN1Y2Nlc3NcclxuICAgICAgICByZXNwb25zZS5zdGF0dXMoMjAwKS5zZW5kKHtcclxuICAgICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcclxuICAgICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG4gICAgICAgICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIEhpZ2hjaGFydHMgdG8gdmVyc2lvbjogJHtuZXdWZXJzaW9ufS5gXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gTm8gdmVyc2lvbiBzcGVjaWZpZWRcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1t2ZXJzaW9uXSBObyBuZXcgdmVyc2lvbiBzdXBwbGllZC4nLCA0MDApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEEgbW9kdWxlIHRoYXQgc2V0cyB1cCBhbmQgbWFuYWdlcyBIVFRQIGFuZCBIVFRQUyBzZXJ2ZXJzXHJcbiAqIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBoYW5kbGVzIHNlcnZlciBpbml0aWFsaXphdGlvbixcclxuICogY29uZmlndXJhdGlvbiwgZXJyb3IgaGFuZGxpbmcsIG1pZGRsZXdhcmUgc2V0dXAsIHJvdXRlIGRlZmluaXRpb24sIGFuZCByYXRlXHJcbiAqIGxpbWl0aW5nLiBUaGUgbW9kdWxlIGV4cG9ydHMgZnVuY3Rpb25zIHRvIHN0YXJ0LCBzdG9wLCBhbmQgbWFuYWdlIHNlcnZlclxyXG4gKiBpbnN0YW5jZXMsIGFzIHdlbGwgYXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIGRlZmluaW5nIHJvdXRlcyBhbmQgYXR0YWNoaW5nXHJcbiAqIG1pZGRsZXdhcmVzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xyXG5cclxuaW1wb3J0IHsgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgZXJyb3JNaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvZXJyb3IuanMnO1xyXG5pbXBvcnQgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3JhdGVMaW1pdGluZy5qcyc7XHJcbmltcG9ydCB2YWxpZGF0aW9uTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3ZhbGlkYXRpb24uanMnO1xyXG5cclxuaW1wb3J0IGV4cG9ydFJvdXRlcyBmcm9tICcuL3JvdXRlcy9leHBvcnQuanMnO1xyXG5pbXBvcnQgaGVhbHRoUm91dGVzIGZyb20gJy4vcm91dGVzL2hlYWx0aC5qcyc7XHJcbmltcG9ydCB1aVJvdXRlcyBmcm9tICcuL3JvdXRlcy91aS5qcyc7XHJcbmltcG9ydCB2ZXJzaW9uQ2hhbmdlUm91dGVzIGZyb20gJy4vcm91dGVzL3ZlcnNpb25DaGFuZ2UuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBBcnJheSBvZiBhbiBhY3RpdmUgc2VydmVyc1xyXG5jb25zdCBhY3RpdmVTZXJ2ZXJzID0gbmV3IE1hcCgpO1xyXG5cclxuLy8gQ3JlYXRlIGV4cHJlc3MgYXBwXHJcbmNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gSFRUUCBhbmQvb3IgSFRUUFMgc2VydmVyIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLlxyXG4gKiBUaGUgYHNlcnZlck9wdGlvbnNgIG9iamVjdCBjb250YWlucyBzZXJ2ZXItcmVsYXRlZCBwcm9wZXJ0aWVzIChyZWZlclxyXG4gKiB0byB0aGUgYHNlcnZlcmAgc2VjdGlvbiBpbiB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIGZvciBkZXRhaWxzKS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydFNlcnZlclxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBzZXJ2ZXJgXHJcbiAqIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2YgdGhlIGBzZXJ2ZXJgXHJcbiAqIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlcyB3aWxsIGRlZmF1bHRcclxuICogdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgZWl0aGVyXHJcbiAqIG5vdCBlbmFibGVkIG9yIG5vIHZhbGlkIEV4cHJlc3MgYXBwIGlzIGZvdW5kLCBzaWduYWxpbmcgdGhlIGVuZCBvZiB0aGVcclxuICogZnVuY3Rpb24ncyBleGVjdXRpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgc2VydmVyIGNhbm5vdFxyXG4gKiBiZSBjb25maWd1cmVkIGFuZCBzdGFydGVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0U2VydmVyKHNlcnZlck9wdGlvbnMpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBzZXJ2ZXI6IHNlcnZlck9wdGlvbnNcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFVzZSB2YWxpZGF0ZWQgb3B0aW9uc1xyXG4gICAgc2VydmVyT3B0aW9ucyA9IG9wdGlvbnMuc2VydmVyO1xyXG5cclxuICAgIC8vIFN0b3AgaWYgbm90IGVuYWJsZWRcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5lbmFibGUgfHwgIWFwcCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tzZXJ2ZXJdIFNlcnZlciBjYW5ub3QgYmUgc3RhcnRlZCAobm90IGVuYWJsZWQgb3Igbm8gY29ycmVjdCBFeHByZXNzIGFwcCBmb3VuZCkuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUb28gYmlnIGxpbWl0cyBsZWFkIHRvIHRpbWVvdXRzIGluIHRoZSBleHBvcnQgcHJvY2VzcyB3aGVuXHJcbiAgICAvLyB0aGUgcmFzdGVyaXphdGlvbiB0aW1lb3V0IGlzIHNldCB0b28gbG93XHJcbiAgICBjb25zdCB1cGxvYWRMaW1pdEJ5dGVzID0gc2VydmVyT3B0aW9ucy51cGxvYWRMaW1pdCAqIDEwMjQgKiAxMDI0O1xyXG5cclxuICAgIC8vIE1lbW9yeSBzdG9yYWdlIGZvciBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XHJcblxyXG4gICAgLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3QgdXBsb2FkID0gbXVsdGVyKHtcclxuICAgICAgc3RvcmFnZSxcclxuICAgICAgbGltaXRzOiB7XHJcbiAgICAgICAgZmllbGRTaXplOiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIERpc2FibGUgdGhlIFgtUG93ZXJlZC1CeSBoZWFkZXJcclxuICAgIGFwcC5kaXNhYmxlKCd4LXBvd2VyZWQtYnknKTtcclxuXHJcbiAgICAvLyBFbmFibGUgQ09SUyBzdXBwb3J0XHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBjb3JzKHtcclxuICAgICAgICBtZXRob2RzOiBbJ1BPU1QnLCAnR0VUJywgJ09QVElPTlMnXVxyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXR0aW5nIGEgbG90IG9mIGBSYW5nZU5vdFNhdGlzZmlhYmxlRXJyb3JgIGV4Y2VwdGlvbnMgKGV2ZW4gdGhvdWdoIHRoaXNcclxuICAgIC8vIGlzIGEgZGVwcmVjYXRlZCBvcHRpb25zLCBsZXQncyB0cnkgdG8gc2V0IGl0IHRvIGZhbHNlKVxyXG4gICAgYXBwLnVzZSgocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgICAgcmVzcG9uc2Uuc2V0KCdBY2NlcHQtUmFuZ2VzJywgJ25vbmUnKTtcclxuICAgICAgbmV4dCgpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBKU09OIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MuanNvbih7XHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBVUkwtZW5jb2RlZCBmb3JtIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MudXJsZW5jb2RlZCh7XHJcbiAgICAgICAgZXh0ZW5kZWQ6IHRydWUsXHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gVXNlIG9ubHkgbm9uLWZpbGUgbXVsdGlwYXJ0IGZvcm0gZmllbGRzXHJcbiAgICBhcHAudXNlKHVwbG9hZC5ub25lKCkpO1xyXG5cclxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcclxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQIHNlcnZlclxyXG4gICAgaWYgKCFzZXJ2ZXJPcHRpb25zLnNzbC5mb3JjZSkge1xyXG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcCk7XHJcblxyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKGh0dHBTZXJ2ZXIpO1xyXG5cclxuICAgICAgLy8gTGlzdGVuXHJcbiAgICAgIGh0dHBTZXJ2ZXIubGlzdGVuKHNlcnZlck9wdGlvbnMucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFAgc2VydmVyXHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5zZXQoc2VydmVyT3B0aW9ucy5wb3J0LCBodHRwU2VydmVyKTtcclxuXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyT3B0aW9ucy5ob3N0fToke3NlcnZlck9wdGlvbnMucG9ydH0uYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQUyBzZXJ2ZXJcclxuICAgIGlmIChzZXJ2ZXJPcHRpb25zLnNzbC5lbmFibGUpIHtcclxuICAgICAgLy8gU2V0IHVwIGFuIFNTTCBzZXJ2ZXIgYWxzb1xyXG4gICAgICBsZXQga2V5LCBjZXJ0O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBrZXlcclxuICAgICAgICBrZXkgPSByZWFkRmlsZVN5bmMoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIua2V5JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBjZXJ0aWZpY2F0ZVxyXG4gICAgICAgIGNlcnQgPSByZWFkRmlsZVN5bmMoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIuY3J0JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gVW5hYmxlIHRvIGxvYWQga2V5L2NlcnRpZmljYXRlIGZyb20gdGhlICcke3NlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRofScgcGF0aC4gQ291bGQgbm90IHJ1biBzZWN1cmVkIGxheWVyIHNlcnZlci5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKGtleSAmJiBjZXJ0KSB7XHJcbiAgICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFBTKVxyXG4gICAgICAgIGNvbnN0IGh0dHBzU2VydmVyID0gaHR0cHMuY3JlYXRlU2VydmVyKHsga2V5LCBjZXJ0IH0sIGFwcCk7XHJcblxyXG4gICAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgIC8vIExpc3RlblxyXG4gICAgICAgIGh0dHBzU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBzZXJ2ZXJPcHRpb25zLmhvc3QsICgpID0+IHtcclxuICAgICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQUyBzZXJ2ZXJcclxuICAgICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMuc3NsLnBvcnQsIGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0fS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBzZXJ2ZXJPcHRpb25zLnJhdGVMaW1pdGluZyk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSB2YWxpZGF0aW9uIGhhbmRsZXJcclxuICAgIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHJvdXRlc1xyXG4gICAgZXhwb3J0Um91dGVzKGFwcCk7XHJcbiAgICBoZWFsdGhSb3V0ZXMoYXBwKTtcclxuICAgIHVpUm91dGVzKGFwcCk7XHJcbiAgICB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXHJcbiAgICBlcnJvck1pZGRsZXdhcmUoYXBwKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbG9zZVNlcnZlcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbG9zZVNlcnZlcnMoKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgYXJlIHNlcnZlcnMgd29ya2luZ1xyXG4gIGlmIChhY3RpdmVTZXJ2ZXJzLnNpemUgPiAwKSB7XHJcbiAgICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NpbmcgYWxsIHNlcnZlcnMuYCk7XHJcblxyXG4gICAgLy8gQ2xvc2UgZWFjaCBvbmUgb2Ygc2VydmVyc1xyXG4gICAgZm9yIChjb25zdCBbcG9ydCwgc2VydmVyXSBvZiBhY3RpdmVTZXJ2ZXJzKSB7XHJcbiAgICAgIHNlcnZlci5jbG9zZSgoKSA9PiB7XHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5kZWxldGUocG9ydCk7XHJcbiAgICAgICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zZWQgc2VydmVyIG9uIHBvcnQ6ICR7cG9ydH0uYCk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRTZXJ2ZXJzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtBcnJheS48T2JqZWN0Pn0gU2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VydmVycygpIHtcclxuICByZXR1cm4gYWN0aXZlU2VydmVycztcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEV4cHJlc3NcclxuICpcclxuICogQHJldHVybnMge0V4cHJlc3N9IFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEV4cHJlc3MoKSB7XHJcbiAgcmV0dXJuIGV4cHJlc3M7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QXBwXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXBwKCkge1xyXG4gIHJldHVybiBhcHA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXRcclxuICogb2YgdGhlIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlc1xyXG4gKiB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlUmF0ZUxpbWl0aW5nKHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgc2VydmVyOiB7XHJcbiAgICAgIHJhdGVMaW1pdGluZzogcmF0ZUxpbWl0aW5nT3B0aW9uc1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9uc1xyXG4gIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBvcHRpb25zLnNlcnZlci5yYXRlTGltaXRpbmdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFwcGx5IG1pZGRsZXdhcmUocykgdG8gYSBzcGVjaWZpYyBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXNlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggR0VUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIFBPU1QgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5wb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEF0dGFjaCBlcnJvciBoYW5kbGVycyB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnNcclxuICpcclxuICogQHBhcmFtIHsoaHR0cC5TZXJ2ZXJ8aHR0cHMuU2VydmVyKX0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoc2VydmVyKSB7XHJcbiAgc2VydmVyLm9uKCdjbGllbnRFcnJvcicsIChlcnJvciwgc29ja2V0KSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDEsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9LCBkZXN0cm95aW5nIHNvY2tldC5gXHJcbiAgICApO1xyXG4gICAgc29ja2V0LmRlc3Ryb3koKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignY29ubmVjdGlvbicsIChzb2NrZXQpID0+IHtcclxuICAgIHNvY2tldC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGNsb3NlU2VydmVycyxcclxuICBnZXRTZXJ2ZXJzLFxyXG4gIGdldEV4cHJlc3MsXHJcbiAgZ2V0QXBwLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhhbmRsZXMgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciwgZW5zdXJpbmdcclxuICogcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzIHN1Y2ggYXMgYnJvd3NlciwgcGFnZXMsIHNlcnZlcnMsIGFuZCB0aW1lcnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBjbGVhckFsbFRpbWVycyB9IGZyb20gJy4vdGltZXIuanMnO1xyXG5cclxuaW1wb3J0IHsgY2xvc2VTZXJ2ZXJzIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBjbGVhbnVwIG9wZXJhdGlvbnMgdG8gZW5zdXJlIGEgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIHByb2Nlc3MuXHJcbiAqIFRoaXMgaW5jbHVkZXMgY2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgdGltZW91dHMvaW50ZXJ2YWxzLCBjbG9zaW5nIGFjdGl2ZVxyXG4gKiBzZXJ2ZXJzLCB0ZXJtaW5hdGluZyByZXNvdXJjZXMgKHBhZ2VzKSBvZiB0aGUgcG9vbCwgcG9vbCBpdHNlbGYsIGFuZCBjbG9zaW5nXHJcbiAqIHRoZSBicm93c2VyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbZXhpdENvZGU9MF0gLSBUaGUgZXhpdCBjb2RlIHRvIHVzZSB3aXRoIGBwcm9jZXNzLmV4aXQoKWAuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGAwYC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXAoZXhpdENvZGUgPSAwKSB7XHJcbiAgLy8gQXdhaXQgZnJlZWluZyBhbGwgcmVzb3VyY2VzXHJcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcclxuICAgIC8vIENsZWFyIGFsbCBvbmdvaW5nIGludGVydmFsc1xyXG4gICAgY2xlYXJBbGxUaW1lcnMoKSxcclxuXHJcbiAgICAvLyBHZXQgYXZhaWxhYmxlIHNlcnZlciBpbnN0YW5jZXMgKEhUVFAvSFRUUFMpIGFuZCBjbG9zZSB0aGVtXHJcbiAgICBjbG9zZVNlcnZlcnMoKSxcclxuXHJcbiAgICAvLyBDbG9zZSBhbiBhY3RpdmUgcG9vbCBhbG9uZyB3aXRoIGl0cyB3b3JrZXJzIGFuZCB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gICAga2lsbFBvb2woKVxyXG4gIF0pO1xyXG5cclxuICAvLyBFeGl0IHByb2Nlc3Mgd2l0aCBhIGNvcnJlY3QgY29kZVxyXG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaHV0ZG93bkNsZWFuVXBcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvcmUgbW9kdWxlIGZvciBpbml0aWFsaXppbmcgYW5kIG1hbmFnaW5nIHRoZSBIaWdoY2hhcnRzIEV4cG9ydFxyXG4gKiBTZXJ2ZXIuIFByb3ZpZGVzIGZ1bmN0aW9uYWxpdGllcyBmb3IgY29uZmlndXJpbmcgZXhwb3J0cywgc2V0dGluZyB1cCBzZXJ2ZXJcclxuICogb3BlcmF0aW9ucywgbG9nZ2luZywgc2NyaXB0cyBjYWNoaW5nLCByZXNvdXJjZSBwb29saW5nLCBhbmQgZ3JhY2VmdWwgcHJvY2Vzc1xyXG4gKiBjbGVhbnVwLlxyXG4gKi9cclxuXHJcbmltcG9ydCAnY29sb3JzJztcclxuXHJcbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMsIG1hcFRvTmV3T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHtcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIGluaXRMb2dnaW5nLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG4gIHNldExvZ0xldmVsXHJcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpbml0UG9vbCwga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzaHV0ZG93bkNsZWFuVXAgfSBmcm9tICcuL3Jlc291cmNlUmVsZWFzZS5qcyc7XHJcblxyXG5pbXBvcnQgc2VydmVyIGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwcm9jZXNzLiBUYXNrcyBzdWNoIGFzIGNvbmZpZ3VyaW5nIGxvZ2dpbmcsIGNoZWNraW5nXHJcbiAqIHRoZSBjYWNoZSBhbmQgc291cmNlcywgYW5kIGluaXRpYWxpemluZyB0aGUgcmVzb3VyY2UgcG9vbCBvY2N1ciBkdXJpbmcgdGhpc1xyXG4gKiBzdGFnZS5cclxuICpcclxuICogVGhpcyBmdW5jdGlvbiBtdXN0IGJlIGNhbGxlZCBiZWZvcmUgYXR0ZW1wdGluZyB0byBleHBvcnQgY2hhcnRzIG9yIHNldFxyXG4gKiB1cCBhIHNlcnZlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpbml0T3B0aW9ucyAtIFRoZSBgaW5pdE9wdGlvbnNgIG9iamVjdCwgd2hpY2ggbWF5XHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmdcclxuICogdmFsdWVzIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0RXhwb3J0KGluaXRPcHRpb25zKSB7XHJcbiAgLy8gSW5pdCBhbmQgdXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKGluaXRPcHRpb25zKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBwZXIgZXhwb3J0IG1vZHVsZSBzY29wZVxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gIC8vIEluaXQgdGhlIGxvZ2dpbmdcclxuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAob3B0aW9ucy5vdGhlci5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXHJcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMsIG9wdGlvbnMuc2VydmVyLnByb3h5KTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgcG9vbFxyXG4gIGF3YWl0IGluaXRQb29sKG9wdGlvbnMucG9vbCwgb3B0aW9ucy5wdXBwZXRlZXIuYXJncyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXHJcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nXHJcbiAqIGFuZCAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVyc1xyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCkge1xyXG4gIGxvZygzLCAnW3Byb2Nlc3NdIEF0dGFjaGluZyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdleGl0J1xyXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gUHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0uYCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSU5UJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdURVJNJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICd1bmNhdWdodEV4Y2VwdGlvbidcclxuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGVycm9yLmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDEpO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgLy8gU2VydmVyXHJcbiAgLi4uc2VydmVyLFxyXG5cclxuICAvLyBPcHRpb25zXHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuXHJcbiAgLy8gRXhwb3J0aW5nXHJcbiAgaW5pdEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcblxyXG4gIC8vIFJlbGVhc2VcclxuICBraWxsUG9vbCxcclxuICBzaHV0ZG93bkNsZWFuVXAsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsOiBmdW5jdGlvbiAobGV2ZWwpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGxldmVsXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICBzZXRMb2dMZXZlbChvcHRpb25zLmxvZ2dpbmcubGV2ZWwpO1xyXG4gIH0sXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmc6IGZ1bmN0aW9uICh0b0NvbnNvbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIHRvQ29uc29sZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlQ29uc29sZUxvZ2dpbmcob3B0aW9ucy5sb2dnaW5nLnRvQ29uc29sZSk7XHJcbiAgfSxcclxuICBlbmFibGVGaWxlTG9nZ2luZzogZnVuY3Rpb24gKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBsb2dnaW5nOiB7XHJcbiAgICAgICAgZGVzdCxcclxuICAgICAgICBmaWxlLFxyXG4gICAgICAgIHRvRmlsZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy5kZXN0LFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZmlsZSxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLnRvRmlsZVxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcbiJdLCJuYW1lcyI6WyJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInRhZ05hbWUiLCJ0b1VwcGVyQ2FzZSIsInNyYyIsImJhc2VVUkkiLCJkZWVwQ29weSIsIm9iakFyciIsIm9iakFyckNvcHkiLCJBcnJheSIsImlzQXJyYXkiLCJrZXkiLCJPYmplY3QiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJmaXhDb25zdHIiLCJjb25zdHIiLCJmaXhlZENvbnN0ciIsInRvTG93ZXJDYXNlIiwicmVwbGFjZSIsImluY2x1ZGVzIiwiZml4T3V0ZmlsZSIsInR5cGUiLCJvdXRmaWxlIiwiZ2V0QWJzb2x1dGVQYXRoIiwic3BsaXQiLCJzaGlmdCIsImZpeFR5cGUiLCJtaW1lVHlwZXMiLCJmb3JtYXRzIiwidmFsdWVzIiwib3V0VHlwZSIsInBvcCIsImZpbmQiLCJ0IiwicGF0aCIsImlzQWJzb2x1dGUiLCJub3JtYWxpemUiLCJyZXNvbHZlIiwiZ2V0QmFzZTY0IiwiaW5wdXQiLCJCdWZmZXIiLCJmcm9tIiwidG9TdHJpbmciLCJnZXROZXdEYXRlIiwiRGF0ZSIsInRyaW0iLCJnZXROZXdEYXRlVGltZSIsImdldFRpbWUiLCJpc09iamVjdCIsIml0ZW0iLCJpc09iamVjdEVtcHR5Iiwia2V5cyIsImxlbmd0aCIsImlzUHJpdmF0ZVJhbmdlVXJsRm91bmQiLCJzb21lIiwicGF0dGVybiIsInRlc3QiLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwicHJvY2VzcyIsImhydGltZSIsImJpZ2ludCIsIk51bWJlciIsInJvdW5kTnVtYmVyIiwidmFsdWUiLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwiTWF0aCIsInBvdyIsInJvdW5kIiwid3JhcEFyb3VuZCIsImN1c3RvbUNvZGUiLCJhbGxvd0ZpbGVSZXNvdXJjZXMiLCJpc0NhbGxiYWNrIiwiZW5kc1dpdGgiLCJyZWFkRmlsZVN5bmMiLCJzdGFydHNXaXRoIiwiY29sb3JzIiwibG9nZ2luZyIsInRvQ29uc29sZSIsInRvRmlsZSIsInBhdGhDcmVhdGVkIiwicGF0aFRvTG9nIiwibGV2ZWxzRGVzYyIsInRpdGxlIiwiY29sb3IiLCJsb2ciLCJhcmdzIiwibmV3TGV2ZWwiLCJ0ZXh0cyIsImxldmVsIiwicHJlZml4IiwiX2xvZ1RvRmlsZSIsImNvbnNvbGUiLCJhcHBseSIsInVuZGVmaW5lZCIsImNvbmNhdCIsImxvZ1dpdGhTdGFjayIsImVycm9yIiwiY3VzdG9tTWVzc2FnZSIsIm1haW5NZXNzYWdlIiwibWVzc2FnZSIsInN0YWNrTWVzc2FnZSIsInN0YWNrIiwicHVzaCIsImluaXRMb2dnaW5nIiwibG9nZ2luZ09wdGlvbnMiLCJkZXN0IiwiZmlsZSIsInNldExvZ0xldmVsIiwiZW5hYmxlQ29uc29sZUxvZ2dpbmciLCJlbmFibGVGaWxlTG9nZ2luZyIsImlzSW50ZWdlciIsImV4aXN0c1N5bmMiLCJta2RpclN5bmMiLCJqb2luIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiX2luaXRPcHRpb25zIiwiZ2V0T3B0aW9ucyIsImdldENvcHkiLCJ1cGRhdGVPcHRpb25zIiwibmV3T3B0aW9ucyIsIl9tZXJnZU9wdGlvbnMiLCJtYXBUb05ld09wdGlvbnMiLCJvbGRPcHRpb25zIiwiZW50cmllcyIsInByb3BlcnRpZXNDaGFpbiIsInJlZHVjZSIsIm9iaiIsInByb3AiLCJpbmRleCIsImlzQWxsb3dlZENvbmZpZyIsImFsbG93RnVuY3Rpb25zIiwib2JqZWN0Q29uZmlnIiwiZXZhbCIsIkpTT04iLCJzdHJpbmdpZmllZE9wdGlvbnMiLCJfb3B0aW9uc1N0cmluZ2lmeSIsInBhcnNlZE9wdGlvbnMiLCJfIiwibmFtZSIsIm9yaWdpbmFsT3B0aW9ucyIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJFcnJvciIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZWplY3QiLCJfZ2V0UHJvdG9jb2xNb2R1bGUiLCJnZXQiLCJyZXNwb25zZSIsInJlc3BvbnNlRGF0YSIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiaHR0cHMiLCJodHRwIiwiRXhwb3J0RXJyb3IiLCJjb25zdHJ1Y3RvciIsInN0YXR1c0NvZGUiLCJzdXBlciIsInRoaXMiLCJzZXRTdGF0dXMiLCJzZXRFcnJvciIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsImkiLCJzZXR1cEhpZ2hjaGFydHMiLCJIaWdoY2hhcnRzIiwiYW5pbU9iamVjdCIsImR1cmF0aW9uIiwiY3JlYXRlQ2hhcnQiLCJleHBvcnRPcHRpb25zIiwiY3VzdG9tTG9naWNPcHRpb25zIiwic2V0T3B0aW9ucyIsIm1lcmdlIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJpc1JlbmRlckNvbXBsZXRlIiwiQ2hhcnQiLCJwcm9jZWVkIiwidXNlck9wdGlvbnMiLCJjYiIsImV4cG9ydGluZyIsImVuYWJsZWQiLCJwbG90T3B0aW9ucyIsInNlcmllcyIsImxhYmVsIiwidG9vbHRpcCIsImFuaW1hdGlvbiIsIm9uSGlnaGNoYXJ0c1JlbmRlciIsImFkZEV2ZW50IiwiU2VyaWVzIiwiY2hhcnQiLCJhZGRpdGlvbmFsT3B0aW9ucyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImRlZmF1bHRPcHRpb25zIiwidGVtcGxhdGUiLCJicm93c2VyIiwiY3JlYXRlQnJvd3NlciIsInB1cHBldGVlckFyZ3MiLCJlbmFibGVkRGVidWciLCJkZWJ1Z09wdGlvbnMiLCJsYXVuY2hPcHRpb25zIiwidXNlckRhdGFEaXIiLCJoYW5kbGVTSUdJTlQiLCJoYW5kbGVTSUdURVJNIiwiaGFuZGxlU0lHSFVQIiwid2FpdEZvckluaXRpYWxQYWdlIiwiZGVmYXVsdFZpZXdwb3J0IiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwic2V0VGltZW91dCIsImNsb3NlQnJvd3NlciIsImNvbm5lY3RlZCIsImNsb3NlIiwibmV3UGFnZSIsInBvb2xSZXNvdXJjZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJfc2V0UGFnZUNvbnRlbnQiLCJfc2V0UGFnZUV2ZW50cyIsImlzQ2xvc2VkIiwiY2xlYXJQYWdlIiwiaGFyZFJlc2V0IiwiZ290byIsIndhaXRVbnRpbCIsImV2YWx1YXRlIiwiYm9keSIsImlubmVySFRNTCIsImlkIiwid29ya0NvdW50IiwiYWRkUGFnZVJlc291cmNlcyIsImluamVjdGVkUmVzb3VyY2VzIiwiaW5qZWN0ZWRKcyIsImpzIiwiY29udGVudCIsImZpbGVzIiwiaXNMb2NhbCIsImpzUmVzb3VyY2UiLCJhZGRTY3JpcHRUYWciLCJpbmplY3RlZENzcyIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJjc3NSZXNvdXJjZSIsImFkZFN0eWxlVGFnIiwiY2xlYXJQYWdlUmVzb3VyY2VzIiwicmVzb3VyY2UiLCJkaXNwb3NlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJlbGVtZW50IiwicmVtb3ZlIiwic2V0Q29udGVudCIsImNzc1RlbXBsYXRlIiwic3ZnVGVtcGxhdGUiLCJwdXBwZXRlZXJFeHBvcnQiLCJpc1NWRyIsInNpemUiLCJzdmdFbGVtZW50IiwicXVlcnlTZWxlY3RvciIsImNoYXJ0SGVpZ2h0IiwiYmFzZVZhbCIsImNoYXJ0V2lkdGgiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ4IiwieSIsIl9nZXRDbGlwUmVnaW9uIiwidmlld3BvcnRIZWlnaHQiLCJhYnMiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsInJlc3VsdCIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJfY3JlYXRlU1ZHIiwiX2NyZWF0ZUltYWdlIiwiX2NyZWF0ZVBERiIsIiRldmFsIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidHJ1bmMiLCJvdXRlckhUTUwiLCJjbGlwIiwicmFjZSIsInNjcmVlbnNob3QiLCJlbmNvZGluZyIsImZ1bGxQYWdlIiwib3B0aW1pemVGb3JTcGVlZCIsImNhcHR1cmVCZXlvbmRWaWV3cG9ydCIsInF1YWxpdHkiLCJvbWl0QmFja2dyb3VuZCIsIl9yZXNvbHZlIiwiZW11bGF0ZU1lZGlhVHlwZSIsInBkZiIsInBvb2xTdGF0cyIsImV4cG9ydHNBdHRlbXB0ZWQiLCJleHBvcnRzUGVyZm9ybWVkIiwiZXhwb3J0c0Ryb3BwZWQiLCJleHBvcnRzRnJvbVN2ZyIsImV4cG9ydHNGcm9tT3B0aW9ucyIsImV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMiLCJleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cyIsInRpbWVTcGVudCIsInRpbWVTcGVudEF2ZXJhZ2UiLCJpbml0UG9vbCIsInBvb2xPcHRpb25zIiwiUG9vbCIsIl9mYWN0b3J5IiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsImNsZWFyU3RhdHVzIiwiX2V2ZW50SWQiLCJpbml0aWFsUmVzb3VyY2VzIiwiYWNxdWlyZSIsInByb21pc2UiLCJyZWxlYXNlIiwia2lsbFBvb2wiLCJ3b3JrZXIiLCJ1c2VkIiwiZGVzdHJveWVkIiwicG9zdFdvcmsiLCJ3b3JrZXJIYW5kbGUiLCJnZXRQb29sSW5mbyIsImFjcXVpcmVDb3VudGVyIiwicmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsImV4cG9ydFRpbWUiLCJnZXRQb29sU3RhdHMiLCJnZXRQb29sSW5mb0pTT04iLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwibnVtRnJlZSIsImFsbENyZWF0ZWQiLCJwZW5kaW5nQWNxdWlyZXMiLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwZW5kaW5nQ3JlYXRlcyIsIm51bVBlbmRpbmdDcmVhdGVzIiwicGVuZGluZ1ZhbGlkYXRpb25zIiwibnVtUGVuZGluZ1ZhbGlkYXRpb25zIiwicGVuZGluZ0Rlc3Ryb3lzIiwiYWJzb2x1dGVBbGwiLCJjcmVhdGUiLCJ1dWlkIiwicmFuZG9tIiwic3RhcnREYXRlIiwidmFsaWRhdGUiLCJtYWluRnJhbWUiLCJkZXRhY2hlZCIsInJlbW92ZUFsbExpc3RlbmVycyIsInNhbml0aXplIiwiSlNET00iLCJET01QdXJpZnkiLCJBRERfVEFHUyIsInNpbmdsZUV4cG9ydCIsInN0YXJ0RXhwb3J0IiwiZGF0YSIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiYmF0Y2hSZXN1bHRzIiwiYWxsU2V0dGxlZCIsInJlYXNvbiIsImltYWdlT3B0aW9ucyIsImVuZENhbGxiYWNrIiwiZmlsZUNvbnRlbnQiLCJfZXhwb3J0RnJvbVN2ZyIsIl9leHBvcnRGcm9tT3B0aW9ucyIsImdldEFsbG93Q29kZUV4ZWN1dGlvbiIsInNldEFsbG93Q29kZUV4ZWN1dGlvbiIsImlucHV0VG9FeHBvcnQiLCJfcHJlcGFyZUV4cG9ydCIsIl9oYW5kbGVDdXN0b21Mb2dpYyIsIl9oYW5kbGVHbG9iYWxBbmRUaGVtZSIsIl9maW5kQ2hhcnRTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidGltZXJJZHMiLCJhZGRUaW1lciIsImNsZWFyQWxsVGltZXJzIiwiY2xlYXJJbnRlcnZhbCIsImNsZWFyVGltZW91dCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcXVlc3QiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RhdHVzIiwianNvbiIsImVycm9yTWlkZGxld2FyZSIsImFwcCIsInVzZSIsInJhdGVMaW1pdGluZ01pZGRsZXdhcmUiLCJyYXRlTGltaXRpbmdPcHRpb25zIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwicmF0ZUxpbWl0Iiwid2luZG93TXMiLCJsaW1pdCIsImRlbGF5TXMiLCJoYW5kbGVyIiwiZm9ybWF0Iiwic2VuZCIsImRlZmF1bHQiLCJza2lwIiwicXVlcnkiLCJhY2Nlc3NfdG9rZW4iLCJjb250ZW50VHlwZU1pZGRsZXdhcmUiLCJjb250ZW50VHlwZSIsImhlYWRlcnMiLCJyZXF1ZXN0Qm9keU1pZGRsZXdhcmUiLCJjb25uZWN0aW9uIiwicmVtb3RlQWRkcmVzcyIsInZhbGlkYXRlZE9wdGlvbnMiLCJwYXJhbXMiLCJmaWxlbmFtZSIsInZhbGlkYXRpb25NaWRkbGV3YXJlIiwicG9zdCIsInJldmVyc2VkTWltZSIsInBuZyIsImpwZWciLCJnaWYiLCJyZXF1ZXN0RXhwb3J0IiwicmVxdWVzdENvdW50ZXIiLCJjb25uZWN0aW9uQWJvcnRlZCIsInNvY2tldCIsImhhZEVycm9ycyIsImhlYWRlciIsImF0dGFjaG1lbnQiLCJleHBvcnRSb3V0ZXMiLCJzZXJ2ZXJTdGFydFRpbWUiLCJwYWNrYWdlRmlsZSIsInN1Y2Nlc3NSYXRlcyIsInJlY29yZEludGVydmFsIiwid2luZG93U2l6ZSIsIl9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlIiwiYSIsImIiLCJfc3RhcnRTdWNjZXNzUmF0ZSIsInNldEludGVydmFsIiwic3RhdHMiLCJzdWNjZXNzUmF0aW8iLCJoZWFsdGhSb3V0ZXMiLCJwZXJpb2QiLCJtb3ZpbmdBdmVyYWdlIiwiYm9vdFRpbWUiLCJ1cHRpbWUiLCJmbG9vciIsInNlcnZlclZlcnNpb24iLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VFeHBvcnRUaW1lIiwiYXR0ZW1wdGVkRXhwb3J0cyIsInBlcmZvcm1lZEV4cG9ydHMiLCJmYWlsZWRFeHBvcnRzIiwic3VjZXNzUmF0aW8iLCJ0b0ZpeGVkIiwic3ZnRXhwb3J0cyIsImpzb25FeHBvcnRzIiwic3ZnRXhwb3J0c0F0dGVtcHRzIiwianNvbkV4cG9ydHNBdHRlbXB0cyIsInVpUm91dGVzIiwic2VuZEZpbGUiLCJhY2NlcHRSYW5nZXMiLCJ2ZXJzaW9uQ2hhbmdlUm91dGVzIiwiYWRtaW5Ub2tlbiIsInRva2VuIiwiYWN0aXZlU2VydmVycyIsIk1hcCIsImV4cHJlc3MiLCJzdGFydFNlcnZlciIsInNlcnZlck9wdGlvbnMiLCJ1cGxvYWRMaW1pdEJ5dGVzIiwic3RvcmFnZSIsIm11bHRlciIsIm1lbW9yeVN0b3JhZ2UiLCJ1cGxvYWQiLCJsaW1pdHMiLCJmaWVsZFNpemUiLCJkaXNhYmxlIiwiY29ycyIsIm1ldGhvZHMiLCJzZXQiLCJ1cmxlbmNvZGVkIiwiZXh0ZW5kZWQiLCJub25lIiwic3RhdGljIiwiaHR0cFNlcnZlciIsImNyZWF0ZVNlcnZlciIsIl9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwibGlzdGVuIiwiY2VydCIsImh0dHBzU2VydmVyIiwiY2xvc2VTZXJ2ZXJzIiwiZGVsZXRlIiwiZ2V0U2VydmVycyIsImdldEV4cHJlc3MiLCJnZXRBcHAiLCJlbmFibGVSYXRlTGltaXRpbmciLCJtaWRkbGV3YXJlcyIsInNodXRkb3duQ2xlYW5VcCIsImV4aXRDb2RlIiwiZXhpdCIsImluaXRFeHBvcnQiLCJpbml0T3B0aW9ucyIsIl9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyIsImNvZGUiXSwibWFwcGluZ3MiOiJ3bEJBMkJPLE1BQU1BLFlBQVlDLElBQWFBLGNBQUMsSUFBSUMsSUFBSSxPQUFRLG9CQUFBQyxTQUFBQyxRQUFBLE9BQUFDLGNBQUFDLFlBQUFDLEtBQUFDLHdCQUFBLFdBQUFBLHVCQUFBQyxRQUFBQyxlQUFBRix1QkFBQUcsS0FBQSxJQUFBVCxJQUFBLFlBQUFDLFNBQUFTLFNBQUFMLE9BK0JoRCxTQUFTTSxTQUFTQyxHQUV2QixHQUFlLE9BQVhBLEdBQXFDLGlCQUFYQSxFQUM1QixPQUFPQSxFQUlULE1BQU1DLEVBQWFDLE1BQU1DLFFBQVFILEdBQVUsR0FBSyxHQUdoRCxJQUFLLE1BQU1JLEtBQU9KLEVBQ1pLLE9BQU9DLFVBQVVDLGVBQWVDLEtBQUtSLEVBQVFJLEtBQy9DSCxFQUFXRyxHQUFPTCxTQUFTQyxFQUFPSSxLQUt0QyxPQUFPSCxDQUNULENBMkRPLFNBQVNRLFVBQVVDLEdBQ3hCLElBRUUsTUFBTUMsRUFBYyxHQUFHRCxFQUFPRSxjQUFjQyxRQUFRLFFBQVMsV0FRN0QsTUFMb0IsVUFBaEJGLEdBQ0ZBLEVBQVlDLGNBSVAsQ0FBQyxRQUFTLGFBQWMsV0FBWSxjQUFjRSxTQUN2REgsR0FFRUEsRUFDQSxPQUNSLENBQUksTUFFQSxNQUFPLE9BQ1IsQ0FDSCxDQVlPLFNBQVNJLFdBQVdDLEVBQU1DLEdBTy9CLE1BQU8sR0FMVUMsZ0JBQWdCRCxHQUFXLFNBQ3pDRSxNQUFNLEtBQ05DLFdBR21CSixHQUN4QixDQWFPLFNBQVNLLFFBQVFMLEVBQU1DLEVBQVUsTUFFdEMsTUFBTUssRUFBWSxDQUNoQixZQUFhLE1BQ2IsYUFBYyxPQUNkLGtCQUFtQixNQUNuQixnQkFBaUIsT0FJYkMsRUFBVWxCLE9BQU9tQixPQUFPRixHQUc5QixHQUFJTCxFQUFTLENBQ1gsTUFBTVEsRUFBVVIsRUFBUUUsTUFBTSxLQUFLTyxNQUduQixRQUFaRCxFQUNGVCxFQUFPLE9BQ0VPLEVBQVFULFNBQVNXLElBQVlULElBQVNTLElBQy9DVCxFQUFPUyxFQUVWLENBR0QsT0FBT0gsRUFBVU4sSUFBU08sRUFBUUksTUFBTUMsR0FBTUEsSUFBTVosS0FBUyxLQUMvRCxDQVlPLFNBQVNFLGdCQUFnQlcsR0FDOUIsT0FBT0MsS0FBQUEsV0FBV0QsR0FBUUUsS0FBQUEsVUFBVUYsR0FBUUcsS0FBQUEsUUFBUUgsRUFDdEQsQ0FZTyxTQUFTSSxVQUFVQyxFQUFPbEIsR0FFL0IsTUFBYSxRQUFUQSxHQUEwQixPQUFSQSxFQUNibUIsT0FBT0MsS0FBS0YsRUFBTyxRQUFRRyxTQUFTLFVBSXRDSCxDQUNULENBT08sU0FBU0ksYUFFZCxPQUFPLElBQUlDLE1BQU9GLFdBQVdsQixNQUFNLEtBQUssR0FBR3FCLE1BQzdDLENBT08sU0FBU0MsaUJBQ2QsT0FBTyxJQUFJRixNQUFPRyxTQUNwQixDQVdPLFNBQVNDLFNBQVNDLEdBQ3ZCLE1BQWdELG9CQUF6Q3ZDLE9BQU9DLFVBQVUrQixTQUFTN0IsS0FBS29DLEVBQ3hDLENBV08sU0FBU0MsY0FBY0QsR0FDNUIsTUFDa0IsaUJBQVRBLElBQ04xQyxNQUFNQyxRQUFReUMsSUFDTixPQUFUQSxHQUM2QixJQUE3QnZDLE9BQU95QyxLQUFLRixHQUFNRyxNQUV0QixDQVdPLFNBQVNDLHVCQUF1QkosR0FTckMsTUFSc0IsQ0FDcEIsbURBQ0EsdUVBQ0Esd0VBQ0EsdUZBQ0EscUVBR21CSyxNQUFNQyxHQUFZQSxFQUFRQyxLQUFLUCxJQUN0RCxDQVNPLFNBQVNRLGNBQ2QsTUFBTUMsRUFBUUMsUUFBUUMsT0FBT0MsU0FDN0IsTUFBTyxJQUFNQyxPQUFPSCxRQUFRQyxPQUFPQyxTQUFXSCxHQUFTLEdBQ3pELENBWU8sU0FBU0ssWUFBWUMsRUFBT0MsRUFBWSxHQUM3QyxNQUFNQyxFQUFhQyxLQUFLQyxJQUFJLEdBQUlILEdBQWEsR0FDN0MsT0FBT0UsS0FBS0UsT0FBT0wsRUFBUUUsR0FBY0EsQ0FDM0MsQ0E2Qk8sU0FBU0ksV0FBV0MsRUFBWUMsRUFBb0JDLEdBQWEsR0FDdEUsR0FBSUYsR0FBb0MsaUJBQWZBLEVBR3ZCLE9BRkFBLEVBQWFBLEVBQVcxQixRQUVUNkIsU0FBUyxPQUVmRixFQUNIRixXQUNFSyxHQUFBQSxhQUFhcEQsZ0JBQWdCZ0QsR0FBYSxRQUMxQ0MsRUFDQUMsR0FFRixNQUVIQSxJQUNBRixFQUFXSyxXQUFXLGVBQ3JCTCxFQUFXSyxXQUFXLGdCQUN0QkwsRUFBV0ssV0FBVyxTQUN0QkwsRUFBV0ssV0FBVyxVQUdqQixJQUFJTCxPQUlOQSxFQUFXckQsUUFBUSxLQUFNLEdBRXBDLENDdlhBLE1BQU0yRCxPQUFTLENBQUMsTUFBTyxTQUFVLE9BQVEsT0FBUSxTQUczQ0MsUUFBVSxDQUVkQyxXQUFXLEVBQ1hDLFFBQVEsRUFDUkMsYUFBYSxFQUViQyxVQUFXLEdBRVhDLFdBQVksQ0FDVixDQUNFQyxNQUFPLFFBQ1BDLE1BQU9SLE9BQU8sSUFFaEIsQ0FDRU8sTUFBTyxVQUNQQyxNQUFPUixPQUFPLElBRWhCLENBQ0VPLE1BQU8sU0FDUEMsTUFBT1IsT0FBTyxJQUVoQixDQUNFTyxNQUFPLFVBQ1BDLE1BQU9SLE9BQU8sSUFFaEIsQ0FDRU8sTUFBTyxZQUNQQyxNQUFPUixPQUFPLE1Ba0JiLFNBQVNTLE9BQU9DLEdBQ3JCLE1BQU9DLEtBQWFDLEdBQVNGLEdBR3ZCSixXQUFFQSxFQUFVTyxNQUFFQSxHQUFVWixRQUc5QixHQUNlLElBQWJVLElBQ2MsSUFBYkEsR0FBa0JBLEVBQVdFLEdBQVNBLEVBQVFQLEVBQVcvQixRQUUxRCxPQUlGLE1BQU11QyxFQUFTLEdBQUdoRCxpQkFBaUJ3QyxFQUFXSyxFQUFXLEdBQUdKLFdBR3hETixRQUFRRSxRQUNWWSxXQUFXSCxFQUFPRSxHQUloQmIsUUFBUUMsV0FDVmMsUUFBUVAsSUFBSVEsV0FDVkMsRUFDQSxDQUFDSixFQUFPakQsV0FBV29DLFFBQVFLLFdBQVdLLEVBQVcsR0FBR0gsUUFBUVcsT0FBT1AsR0FHekUsQ0FnQk8sU0FBU1EsYUFBYVQsRUFBVVUsRUFBT0MsR0FFNUMsTUFBTUMsRUFBY0QsR0FBa0JELEdBQVNBLEVBQU1HLFNBQVksSUFHM0RYLE1BQUVBLEVBQUtQLFdBQUVBLEdBQWVMLFFBRzlCLEdBQWlCLElBQWJVLEdBQWtCQSxFQUFXRSxHQUFTQSxFQUFRUCxFQUFXL0IsT0FDM0QsT0FJRixNQUFNdUMsRUFBUyxHQUFHaEQsaUJBQWlCd0MsRUFBV0ssRUFBVyxHQUFHSixXQUd0RGtCLEVBQWVKLEdBQVNBLEVBQU1LLE1BRzlCZCxFQUFRLENBQUNXLEdBQ1hFLEdBQ0ZiLEVBQU1lLEtBQUssS0FBTUYsR0FJZnhCLFFBQVFFLFFBQ1ZZLFdBQVdILEVBQU9FLEdBSWhCYixRQUFRQyxXQUNWYyxRQUFRUCxJQUFJUSxXQUNWQyxFQUNBLENBQUNKLEVBQU9qRCxXQUFXb0MsUUFBUUssV0FBV0ssRUFBVyxHQUFHSCxRQUFRVyxPQUFPLENBQ2pFUCxFQUFNaEUsUUFBUW9ELE9BQU9XLEVBQVcsT0FDN0JDLElBSVgsQ0FVTyxTQUFTZ0IsWUFBWUMsR0FFMUIsTUFBTWhCLE1BQUVBLEVBQUtpQixLQUFFQSxFQUFJQyxLQUFFQSxFQUFJN0IsVUFBRUEsRUFBU0MsT0FBRUEsR0FBVzBCLEVBR2pENUIsUUFBUUcsYUFBYyxFQUN0QkgsUUFBUUksVUFBWSxHQUdwQjJCLFlBQVluQixHQUdab0IscUJBQXFCL0IsR0FHckJnQyxrQkFBa0JKLEVBQU1DLEVBQU01QixFQUNoQyxDQVVPLFNBQVM2QixZQUFZbkIsR0FFeEI1QixPQUFPa0QsVUFBVXRCLElBQ2pCQSxHQUFTLEdBQ1RBLEdBQVNaLFFBQVFLLFdBQVcvQixTQUc1QjBCLFFBQVFZLE1BQVFBLEVBRXBCLENBU08sU0FBU29CLHFCQUFxQi9CLEdBRW5DRCxRQUFRQyxZQUFjQSxDQUN4QixDQWFPLFNBQVNnQyxrQkFBa0JKLEVBQU1DLEVBQU01QixHQUU1Q0YsUUFBUUUsU0FBV0EsRUFHZkYsUUFBUUUsU0FDVkYsUUFBUTZCLEtBQU9BLEdBQVEsR0FDdkI3QixRQUFROEIsS0FBT0EsR0FBUSxHQUUzQixDQVlBLFNBQVNoQixXQUFXSCxFQUFPRSxHQUNwQmIsUUFBUUcsZUFFVmdDLGNBQVcxRixnQkFBZ0J1RCxRQUFRNkIsUUFDbENPLEdBQUFBLFVBQVUzRixnQkFBZ0J1RCxRQUFRNkIsT0FHcEM3QixRQUFRSSxVQUFZM0QsZ0JBQWdCNEYsS0FBSUEsS0FBQ3JDLFFBQVE2QixLQUFNN0IsUUFBUThCLE9BSS9EOUIsUUFBUUcsYUFBYyxHQUl4Qm1DLEdBQVVBLFdBQ1J0QyxRQUFRSSxVQUNSLENBQUNTLEdBQVFLLE9BQU9QLEdBQU8wQixLQUFLLEtBQU8sTUFDbENqQixJQUNLQSxHQUFTcEIsUUFBUUUsUUFBVUYsUUFBUUcsY0FDckNILFFBQVFFLFFBQVMsRUFDakJGLFFBQVFHLGFBQWMsRUFDdEJnQixhQUFhLEVBQUdDLEVBQU8seUNBQ3hCLEdBR1AsQ0NqUE8sTUFBTW1CLGNBQWdCLENBQzNCQyxVQUFXLENBQ1QvQixLQUFNLENBQ0p2QixNQUFPLENBQ0wsbUNBQ0Esa0JBQ0EsMENBQ0EsMkJBQ0Esa0NBQ0Esa0NBQ0Esd0NBQ0EsMkNBQ0EscUJBQ0EsNEJBQ0EsMkNBQ0EsdURBQ0EsNkJBQ0EseUJBQ0EsMEJBQ0EsK0JBQ0EsdUJBQ0EsdUZBQ0EseUJBQ0Esb0NBQ0Esb0JBQ0EsMEJBQ0EsOENBQ0EsMkJBQ0EsMEJBQ0EsNkJBQ0EsbUNBQ0Esd0NBQ0EsbUNBQ0EsMkJBQ0Esa0NBQ0EsdUJBQ0EsaUJBQ0EseUJBQ0EsOEJBQ0Esb0JBQ0EsMkJBQ0EsZUFDQSw2QkFDQSxpQkFDQSxhQUNBLGVBQ0Esc0JBQ0EsY0FDQSx5QkFDQSxvQkFDQSx1QkFFRnVELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLGlCQUNUQyxRQUFTLGdCQUNUQyxZQUFhLCtCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLE9BQ051RyxVQUFXLE9BSWpCQyxXQUFZLENBQ1ZDLFFBQVMsQ0FDUDlELE1BQU8sU0FDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHFCQUNURSxZQUFhLHFCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1YwRyxPQUFRLENBQ04vRCxNQUFPLDhCQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMscUJBQ1RFLFlBQWEsaUNBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sU0FHVjJHLFdBQVksQ0FDVmhFLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLHlCQUNURSxZQUFhLGtEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Y0RyxVQUFXLENBQ1RqRSxNQUFPLFNBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyx3QkFDVEUsWUFBYSwrQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWNkcsWUFBYSxDQUNYbEUsTUFBTyxDQUFDLGFBQWMsa0JBQW1CLGlCQUN6Q3VELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLDBCQUNURSxZQUFhLG1DQUNiQyxjQUFlLENBQ2J0RyxLQUFNLGNBQ044RyxhQUFjLDBEQUdsQkMsY0FBZSxDQUNicEUsTUFBTyxDQUNMLFFBQ0EsTUFDQSxRQUNBLFlBQ0EsdUJBQ0EsZ0JBRUEsZUFDQSxRQUNBLE9BQ0EsYUFDQSxtQkFDQSxlQUNBLGNBQ0EsVUFDQSxVQUNBLGNBQ0EsV0FDQSxVQUNBLFlBQ0EsY0FDQSxZQUNBLHNCQUNBLFNBQ0EsU0FDQSxXQUNBLGFBQ0EsWUFDQSxlQUNBLHlCQUNBLFNBQ0EsZUFDQSxZQUNBLGtCQUNBLFNBQ0EsY0FDQSxtQkFDQSxlQUNBLGtCQUNBLGNBQ0EsZUFFQSxjQUNBLFdBQ0EsZUFDQSxXQUNBLFNBQ0EsT0FDQSxXQUNBLFlBQ0EsU0FDQSxxQkFDQSxhQUNBLFdBQ0EsV0FDQSxXQUNBLFdBQ0EsZUFDQSxVQUNBLGtCQUNBLG9CQUNBLGFBQ0EsVUFDQSxjQUNBLFlBQ0EsWUFFRnVELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLDRCQUNURSxZQUFhLHFDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLGNBQ044RyxhQUFjLDBEQUdsQkUsaUJBQWtCLENBQ2hCckUsTUFBTyxDQUFDLGtCQUNSdUQsTUFBTyxDQUFDLFlBQ1JDLFFBQVMsK0JBQ1RFLFlBQWEsd0NBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sY0FDTjhHLGFBQWMsMERBR2xCRyxjQUFlLENBQ2J0RSxNQUFPLENBQ0wsd0VBQ0Esa0dBRUZ1RCxNQUFPLENBQUMsWUFDUkMsUUFBUyw0QkFDVEUsWUFBYSxxREFDYkMsY0FBZSxDQUNidEcsS0FBTSxPQUNOdUcsVUFBVyxPQUlqQlcsT0FBUSxDQUNOQyxPQUFRLENBQ054RSxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQkFDVEUsWUFDRSwrREFDRkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWb0gsTUFBTyxDQUNMekUsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsU0FBVSxRQUM1QkMsUUFBUyxlQUNURSxZQUNFLG1FQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZxSCxRQUFTLENBQ1AxRSxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxTQUFVLFFBQzVCQyxRQUFTLGlCQUNURSxZQUFhLCtCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZzSCxJQUFLLENBQ0gzRSxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxhQUNURSxZQUFhLG1EQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1Z1SCxNQUFPLENBQ0w1RSxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxlQUNURSxZQUNFLGdFQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZDLFFBQVMsQ0FDUDBDLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLGlCQUNURSxZQUNFLHFGQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZBLEtBQU0sQ0FDSjJDLE1BQU8sTUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGNBQ1RFLFlBQWEsb0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sU0FDTndILEtBQU0sZUFDTkMsUUFBUyxDQUFDLE1BQU8sT0FBUSxNQUFPLFNBR3BDL0gsT0FBUSxDQUNOaUQsTUFBTyxRQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsZ0JBQ1RFLFlBQ0UsdUVBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sU0FDTndILEtBQU0saUJBQ05DLFFBQVMsQ0FBQyxRQUFTLGFBQWMsV0FBWSxnQkFHakRDLElBQUssQ0FDSC9FLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGFBQ1RFLFlBQ0Usb0ZBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVjJILFdBQVksQ0FDVmhGLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLHFCQUNURSxZQUNFLDBFQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Y0SCxPQUFRLENBQ05qRixNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQkFDVEUsWUFBYSx5REFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWNkgsTUFBTyxDQUNMbEYsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsZUFDVEUsWUFBYSx3REFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWOEgsTUFBTyxDQUNMbkYsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsZUFDVEUsWUFDRSxnRkFDRkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWK0gsY0FBZSxDQUNicEYsTUFBTyxJQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsd0JBQ1RFLFlBQWEsa0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVmdJLGFBQWMsQ0FDWnJGLE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHVCQUNURSxZQUFhLGlEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZpSSxhQUFjLENBQ1p0RixNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyx1QkFDVEUsWUFDRSx5RUFDRkMsY0FBZSxDQUNidEcsS0FBTSxTQUNOa0ksSUFBSyxHQUNMQyxJQUFLLElBR1RDLGNBQWUsQ0FDYnpGLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFNBQVUsUUFDNUJDLFFBQVMsd0JBQ1RFLFlBQ0UsbUZBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sU0FHVnFJLGFBQWMsQ0FDWjFGLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFNBQVUsUUFDNUJDLFFBQVMsdUJBQ1RFLFlBQ0Usa0ZBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sU0FHVnNJLHFCQUFzQixDQUNwQjNGLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLCtCQUNURSxZQUFhLDZDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFlBSVp1SSxZQUFhLENBQ1hDLG1CQUFvQixDQUNsQjdGLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG9DQUNURSxZQUNFLG1FQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZtRCxtQkFBb0IsQ0FDbEJSLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG9DQUNURSxZQUNFLGtGQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZrRCxXQUFZLENBQ1ZQLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLDJCQUNURSxZQUNFLHVIQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1Z5SSxTQUFVLENBQ1I5RixNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyx3QkFDVEUsWUFDRSxrRkFDRkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWMEksVUFBVyxDQUNUL0YsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsU0FBVSxRQUM1QkMsUUFBUyx5QkFDVEUsWUFDRSxzR0FDRkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWMkksV0FBWSxDQUNWaEcsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsMkJBQ1R5QyxXQUFZLFdBQ1p2QyxZQUFhLCtDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1Y2SSxhQUFjLENBQ1psRyxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyw2QkFDVEUsWUFDRSwrREFDRkMsY0FBZSxDQUNidEcsS0FBTSxVQUlaOEksT0FBUSxDQUNOQyxPQUFRLENBQ05wRyxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxnQkFDVEMsUUFBUyxlQUNUQyxZQUFhLDhCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZnSixLQUFNLENBQ0pyRyxNQUFPLFVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxjQUNURSxZQUFhLHlCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZpSixLQUFNLENBQ0p0RyxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxjQUNURSxZQUFhLDZCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZrSixZQUFhLENBQ1h2RyxNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxzQkFDVEUsWUFBYSxrQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWbUosYUFBYyxDQUNaeEcsT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsc0JBQ1RDLFFBQVMscUJBQ1RDLFlBQ0UsMEVBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVm9KLE1BQU8sQ0FDTEosS0FBTSxDQUNKckcsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsb0JBQ1RDLFFBQVMsWUFDVEMsWUFBYSwwQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWaUosS0FBTSxDQUNKdEcsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsb0JBQ1RDLFFBQVMsWUFDVEMsWUFBYSwwQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWcUosUUFBUyxDQUNQMUcsTUFBTyxJQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RDLFFBQVMsZUFDVEMsWUFDRSw4REFDRkMsY0FBZSxDQUNidEcsS0FBTSxZQUlac0osYUFBYyxDQUNaUCxPQUFRLENBQ05wRyxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyw4QkFDVEMsUUFBUyxxQkFDVEMsWUFBYSxrREFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWdUosWUFBYSxDQUNYNUcsTUFBTyxHQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsb0NBQ1R5QyxXQUFZLFlBQ1p2QyxZQUFhLGdEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Z3SixPQUFRLENBQ043RyxNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyw4QkFDVEUsWUFBYSwyQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWeUosTUFBTyxDQUNMOUcsTUFBTyxFQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsNkJBQ1RFLFlBQ0UsdUVBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVjBKLFdBQVksQ0FDVi9HLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG1DQUNURSxZQUFhLHNEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1YySixRQUFTLENBQ1BoSCxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQ0FDVEUsWUFBYSx3REFDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWNEosVUFBVyxDQUNUakgsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsa0NBQ1RFLFlBQWEsd0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sVUFJWjZKLElBQUssQ0FDSGQsT0FBUSxDQUNOcEcsT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsb0JBQ1RDLFFBQVMsWUFDVEMsWUFBYSxtQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWOEosTUFBTyxDQUNMbkgsT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsbUJBQ1RDLFFBQVMsV0FDVHdDLFdBQVksVUFDWnZDLFlBQWEsZ0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVmlKLEtBQU0sQ0FDSnRHLE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGtCQUNUQyxRQUFTLFVBQ1RDLFlBQWEsMEJBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVitKLFNBQVUsQ0FDUnBILE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLHVCQUNUQyxRQUFTLGNBQ1R3QyxXQUFZLFVBQ1p2QyxZQUFhLHVDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBS2RnSyxLQUFNLENBQ0pDLFdBQVksQ0FDVnRILE1BQU8sRUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLG1CQUNURSxZQUFhLHNEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZrSyxXQUFZLENBQ1Z2SCxNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxtQkFDVHlDLFdBQVksVUFDWnZDLFlBQWEsMENBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVm1LLFVBQVcsQ0FDVHhILE1BQU8sR0FDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGtCQUNURSxZQUFhLHdEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZvSyxlQUFnQixDQUNkekgsTUFBTyxJQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsbURBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVnFLLGNBQWUsQ0FDYjFILE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHNCQUNURSxZQUFhLGtEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZzSyxlQUFnQixDQUNkM0gsTUFBTyxJQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsb0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVnVLLFlBQWEsQ0FDWDVILE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLG9CQUNURSxZQUFhLHdEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Z3SyxvQkFBcUIsQ0FDbkI3SCxNQUFPLElBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyw2QkFDVEUsWUFDRSx3RUFDRkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWeUssZUFBZ0IsQ0FDZDlILE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHVCQUNURSxZQUNFLCtEQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZtSixhQUFjLENBQ1p4RyxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxvQkFDVEMsUUFBUyxtQkFDVEMsWUFBYSw2Q0FDYkMsY0FBZSxDQUNidEcsS0FBTSxZQUlaeUQsUUFBUyxDQUNQWSxNQUFPLENBQ0wxQixNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxnQkFDVEMsUUFBUyxXQUNUQyxZQUFhLDBCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBQ05nRCxNQUFPLEVBQ1BrRixJQUFLLEVBQ0xDLElBQUssSUFHVDVDLEtBQU0sQ0FDSjVDLE1BQU8sK0JBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxlQUNUQyxRQUFTLFVBQ1RDLFlBQ0UsOERBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sU0FHVnNGLEtBQU0sQ0FDSjNDLE1BQU8sTUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGVBQ1RDLFFBQVMsVUFDVEMsWUFBYSwwREFDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWMEQsVUFBVyxDQUNUZixPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxxQkFDVEMsUUFBUyxlQUNUQyxZQUFhLHNDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1YyRCxPQUFRLENBQ05oQixPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxrQkFDVEMsUUFBUyxZQUNUQyxZQUFhLHdDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFlBSVowSyxHQUFJLENBQ0YzQixPQUFRLENBQ05wRyxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxZQUNUQyxRQUFTLFdBQ1RDLFlBQWEsbURBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVjJLLE1BQU8sQ0FDTGhJLE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLFdBQ1RDLFFBQVMsVUFDVEMsWUFBYSxnQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxVQUlaNEssTUFBTyxDQUNMQyxRQUFTLENBQ1BsSSxNQUFPLGFBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxpQkFDVEUsWUFBYSwrQkFDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWOEsscUJBQXNCLENBQ3BCbkksT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsZ0NBQ1RFLFlBQWEsaURBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVitLLE9BQVEsQ0FDTnBJLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGdCQUNURSxZQUFhLCtDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZnTCxjQUFlLENBQ2JySSxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyx3QkFDVEUsWUFBYSxvREFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWaUwsaUJBQWtCLENBQ2hCdEksT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsMkJBQ1RFLFlBQWEseURBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sWUFJWmtMLE1BQU8sQ0FDTG5DLE9BQVEsQ0FDTnBHLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGVBQ1RDLFFBQVMsY0FDVEMsWUFBYSw0REFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWbUwsU0FBVSxDQUNSeEksT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsaUJBQ1RFLFlBQ0UsNkVBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVm9MLFNBQVUsQ0FDUnpJLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGlCQUNURSxZQUFhLCtDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZxTCxnQkFBaUIsQ0FDZjFJLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLDBCQUNURSxZQUNFLHFFQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZzTCxPQUFRLENBQ04zSSxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxlQUNURSxZQUNFLGtGQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Z1TCxPQUFRLENBQ041SSxNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxnQkFDVEUsWUFBYSw0REFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWd0wsY0FBZSxDQUNiN0ksTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsMEJBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sYUFPRHlMLFlBQWNDLG1CQUFtQjFGLGVBR2pDMkYsY0FBZ0JDLHFCQUFxQjVGLGVBb0JsRCxTQUFTMEYsbUJBQW1CRyxFQUFRSixFQUFjLENBQUEsRUFBSUssRUFBWSxJQXFCaEUsT0FwQkF6TSxPQUFPeUMsS0FBSytKLEdBQVFFLFNBQVMzTSxJQUUzQixNQUFNNE0sRUFBUUgsRUFBT3pNLFFBR00sSUFBaEI0TSxFQUFNckosTUFFZitJLG1CQUFtQk0sRUFBT1AsRUFBYSxHQUFHSyxLQUFhMU0sTUFHdkRxTSxFQUFZTyxFQUFNNUYsU0FBV2hILEdBQU8sR0FBRzBNLEtBQWExTSxJQUFNNk0sVUFBVSxRQUczQ3ZILElBQXJCc0gsRUFBTXBELGFBQ1I2QyxFQUFZTyxFQUFNcEQsWUFBYyxHQUFHa0QsS0FBYTFNLElBQU02TSxVQUFVLElBRW5FLElBSUlSLENBQ1QsQ0FpQkEsU0FBU0cscUJBQXFCQyxFQUFRRixFQUFnQixJQWtCcEQsT0FqQkF0TSxPQUFPeUMsS0FBSytKLEdBQVFFLFNBQVMzTSxJQUUzQixNQUFNNE0sRUFBUUgsRUFBT3pNLFFBR00sSUFBaEI0TSxFQUFNOUYsTUFFZjBGLHFCQUFxQkksRUFBT0wsR0FHeEJLLEVBQU05RixNQUFNcEcsU0FBUyxXQUN2QjZMLEVBQWN4RyxLQUFLL0YsRUFFdEIsSUFJSXVNLENBQ1QsQ0NyaENBTyxPQUFPTCxTQUlQLE1BQU1NLEVBQUksQ0FHUkMsTUFBUUMsR0FDTkMsSUFBQ0EsRUFDRUMsU0FDQUMsV0FBVzdKLEdBQ1ZBLEVBQ0d4QyxNQUFNLEtBQ05zTSxLQUFLOUosR0FBVUEsRUFBTW5CLFNBQ3JCa0wsUUFBUS9KLEdBQVUwSixFQUFZdk0sU0FBUzZDLE9BRTNDNkosV0FBVzdKLEdBQVdBLEVBQU1aLE9BQVNZLE9BQVErQixJQUlsRGlJLFFBQVMsSUFDUEwsSUFBQ0EsRUFDRU0sS0FBSyxDQUFDLE9BQVEsUUFBUyxLQUN2QkosV0FBVzdKLEdBQXFCLEtBQVZBLEVBQXlCLFNBQVZBLE9BQW1CK0IsSUFJN0RrSSxLQUFPcE0sR0FDTDhMLElBQUNBLEVBQ0VNLEtBQUssSUFBSXBNLEVBQVEsS0FDakJnTSxXQUFXN0osR0FBcUIsS0FBVkEsRUFBZUEsT0FBUStCLElBSWxENkgsT0FBUSxJQUNORCxJQUFDQSxFQUNFQyxTQUNBL0ssT0FDQXFMLFFBQ0VsSyxJQUNFLENBQUMsUUFBUyxZQUFhLE9BQVEsT0FBTzdDLFNBQVM2QyxJQUN0QyxLQUFWQSxJQUNEQSxJQUFXLENBQ1ZxQyxRQUFTLG1EQUFtRHJDLFNBRy9ENkosV0FBVzdKLEdBQXFCLEtBQVZBLEVBQWVBLE9BQVErQixJQUlsRG9JLFlBQWEsSUFDWFIsSUFBQ0EsRUFDRUMsU0FDQS9LLE9BQ0FxTCxRQUNFbEssR0FDVyxLQUFWQSxJQUFrQm9LLE1BQU1DLFdBQVdySyxLQUFXcUssV0FBV3JLLEdBQVMsSUFDbkVBLElBQVcsQ0FDVnFDLFFBQVMscURBQXFEckMsU0FHakU2SixXQUFXN0osR0FBcUIsS0FBVkEsRUFBZXFLLFdBQVdySyxRQUFTK0IsSUFJOUR1SSxlQUFnQixJQUNkWCxJQUFDQSxFQUNFQyxTQUNBL0ssT0FDQXFMLFFBQ0VsSyxHQUNXLEtBQVZBLElBQWtCb0ssTUFBTUMsV0FBV3JLLEtBQVdxSyxXQUFXckssSUFBVSxJQUNwRUEsSUFBVyxDQUNWcUMsUUFBUyx5REFBeURyQyxTQUdyRTZKLFdBQVc3SixHQUFxQixLQUFWQSxFQUFlcUssV0FBV3JLLFFBQVMrQixLQUduRHdJLE9BQVNaLElBQUNBLEVBQUNhLE9BQU8sQ0FFN0JDLGVBQWdCakIsRUFBRUksU0FHbEJjLG1CQUFvQmYsSUFBQ0EsRUFDbEJDLFNBQ0EvSyxPQUNBcUwsUUFDRWxLLEdBQVUsNkJBQTZCUixLQUFLUSxJQUFvQixLQUFWQSxJQUN0REEsSUFBVyxDQUNWcUMsUUFBUyw0RkFBNEZyQyxTQUd4RzZKLFdBQVc3SixHQUFxQixLQUFWQSxFQUFlQSxPQUFRK0IsSUFDaEQ0SSxtQkFBb0JoQixJQUFDQSxFQUNsQkMsU0FDQS9LLE9BQ0FxTCxRQUNFbEssR0FDQ0EsRUFBTVksV0FBVyxhQUNqQlosRUFBTVksV0FBVyxZQUNQLEtBQVZaLElBQ0RBLElBQVcsQ0FDVnFDLFFBQVMsNkZBQTZGckMsU0FHekc2SixXQUFXN0osR0FBcUIsS0FBVkEsRUFBZUEsT0FBUStCLElBQ2hENkksdUJBQXdCcEIsRUFBRVEsVUFDMUJhLHNCQUF1QnJCLEVBQUVJLFNBQ3pCa0IsdUJBQXdCdEIsRUFBRUksU0FDMUJtQix3QkFBeUJ2QixFQUFFQyxNQUFNcEcsY0FBY1EsV0FBV0ssWUFBWWxFLE9BQ3RFZ0wsMEJBQTJCeEIsRUFBRUMsTUFDM0JwRyxjQUFjUSxXQUFXTyxjQUFjcEUsT0FFekNpTCw2QkFBOEJ6QixFQUFFQyxNQUM5QnBHLGNBQWNRLFdBQVdRLGlCQUFpQnJFLE9BRTVDa0wsMEJBQTJCMUIsRUFBRUMsTUFDM0JwRyxjQUFjUSxXQUFXUyxjQUFjdEUsT0FJekNtTCxjQUFlM0IsRUFBRUksU0FDakJ3QixhQUFjNUIsRUFBRUksU0FDaEJ5QixlQUFnQjdCLEVBQUVJLFNBQ2xCMEIsV0FBWTlCLEVBQUVJLFNBQ2QyQixhQUFjL0IsRUFBRUksU0FDaEI0QixlQUFnQmhDLEVBQUVJLFNBQ2xCNkIsWUFBYWpDLEVBQUVTLEtBQUssQ0FBQyxPQUFRLE1BQU8sTUFBTyxRQUMzQ3lCLGNBQWVsQyxFQUFFUyxLQUFLLENBQUMsUUFBUyxhQUFjLFdBQVksZUFDMUQwQixXQUFZbkMsRUFBRVEsVUFDZDRCLG1CQUFvQnBDLEVBQUVRLFVBQ3RCNkIsY0FBZXJDLEVBQUVXLGNBQ2pCMkIsYUFBY3RDLEVBQUVXLGNBQ2hCNEIsYUFBY3ZDLEVBQUVXLGNBQ2hCNkIsc0JBQXVCeEMsRUFBRVcsY0FDekI4QixxQkFBc0J6QyxFQUFFVyxjQUN4QitCLHFCQUFzQjFDLEVBQUVXLGNBQ3hCZ0Msc0JBQXVCM0MsRUFBRUksU0FDekJ3QyxxQkFBc0I1QyxFQUFFSSxTQUN4QnlDLDZCQUE4QjdDLEVBQUVjLGlCQUdoQ2dDLGtDQUFtQzlDLEVBQUVRLFVBQ3JDdUMsa0NBQW1DL0MsRUFBRVEsVUFDckN3Qyx5QkFBMEJoRCxFQUFFSSxTQUM1QjZDLHNCQUF1QmpELEVBQUVJLFNBQ3pCOEMsdUJBQXdCbEQsRUFBRUksU0FDMUIrQyx5QkFBMEJuRCxFQUFFSSxTQUM1QmdELDJCQUE0QnBELEVBQUVJLFNBRzlCaUQsY0FBZXJELEVBQUVRLFVBQ2pCOEMsWUFBYXRELEVBQUVJLFNBQ2ZtRCxZQUFhdkQsRUFBRVcsY0FDZjZDLG9CQUFxQnhELEVBQUVXLGNBQ3ZCOEMsb0JBQXFCekQsRUFBRVEsVUFHdkJrRCxrQkFBbUIxRCxFQUFFSSxTQUNyQnVELGtCQUFtQjNELEVBQUVXLGNBQ3JCaUQscUJBQXNCNUQsRUFBRWMsaUJBR3hCK0MsNEJBQTZCN0QsRUFBRVEsVUFDL0JzRCxrQ0FBbUM5RCxFQUFFYyxpQkFDckNpRCw0QkFBNkIvRCxFQUFFYyxpQkFDL0JrRCwyQkFBNEJoRSxFQUFFYyxpQkFDOUJtRCxpQ0FBa0NqRSxFQUFFUSxVQUNwQzBELDhCQUErQmxFLEVBQUVJLFNBQ2pDK0QsZ0NBQWlDbkUsRUFBRUksU0FHbkNnRSxrQkFBbUJwRSxFQUFFUSxVQUNyQjZELGlCQUFrQnJFLEVBQUVRLFVBQ3BCOEQsZ0JBQWlCdEUsRUFBRVcsY0FDbkI0RCxxQkFBc0J2RSxFQUFFSSxTQUd4Qm9FLGlCQUFrQnhFLEVBQUVjLGlCQUNwQjJELGlCQUFrQnpFLEVBQUVjLGlCQUNwQjRELGdCQUFpQjFFLEVBQUVXLGNBQ25CZ0UscUJBQXNCM0UsRUFBRWMsaUJBQ3hCOEQsb0JBQXFCNUUsRUFBRWMsaUJBQ3ZCK0QscUJBQXNCN0UsRUFBRWMsaUJBQ3hCZ0Usa0JBQW1COUUsRUFBRWMsaUJBQ3JCaUUsMkJBQTRCL0UsRUFBRWMsaUJBQzlCa0UscUJBQXNCaEYsRUFBRWMsaUJBQ3hCbUUsa0JBQW1CakYsRUFBRVEsVUFHckIwRSxjQUFlL0UsSUFBQ0EsRUFDYkMsU0FDQS9LLE9BQ0FxTCxRQUNFbEssR0FDVyxLQUFWQSxJQUNFb0ssTUFBTUMsV0FBV3JLLEtBQ2pCcUssV0FBV3JLLElBQVUsR0FDckJxSyxXQUFXckssSUFBVSxJQUN4QkEsSUFBVyxDQUNWcUMsUUFBUyxtR0FBbUdyQyxTQUcvRzZKLFdBQVc3SixHQUFxQixLQUFWQSxFQUFlcUssV0FBV3JLLFFBQVMrQixJQUM1RDRNLGFBQWNuRixFQUFFSSxTQUNoQmdGLGFBQWNwRixFQUFFSSxTQUNoQmlGLG1CQUFvQnJGLEVBQUVRLFVBQ3RCOEUsZ0JBQWlCdEYsRUFBRVEsVUFHbkIrRSxVQUFXdkYsRUFBRVEsVUFDYmdGLFNBQVV4RixFQUFFSSxTQUdacUYsZUFBZ0J6RixFQUFFUyxLQUFLLENBQUMsY0FBZSxhQUFjLFNBQ3JEaUYsOEJBQStCMUYsRUFBRVEsVUFDakNtRixjQUFlM0YsRUFBRVEsVUFDakJvRixzQkFBdUI1RixFQUFFUSxVQUN6QnFGLHlCQUEwQjdGLEVBQUVRLFVBRzVCc0YsYUFBYzlGLEVBQUVRLFVBQ2hCdUYsZUFBZ0IvRixFQUFFUSxVQUNsQndGLGVBQWdCaEcsRUFBRVEsVUFDbEJ5Rix3QkFBeUJqRyxFQUFFUSxVQUMzQjBGLGFBQWNsRyxFQUFFUSxVQUNoQjJGLGNBQWVuRyxFQUFFYyxpQkFDakJzRixxQkFBc0JwRyxFQUFFVyxnQkFHYjBGLEtBQU90RixPQUFPdUYsVUFBVUMsTUFBTXBRLFFBQVFxUSxLQ3RPN0N2SyxjQUFnQndLLGFBQWE1TSxlQWU1QixTQUFTNk0sV0FBV0MsR0FBVSxHQUNuQyxPQUFPQSxFQUFVL1QsU0FBU3FKLGVBQWlCQSxhQUM3QyxDQWlCTyxTQUFTMkssY0FBY0MsRUFBWUYsR0FBVSxHQUVsRCxPQUFPRyxjQUFjSixXQUFXQyxHQUFVRSxFQUM1QyxDQXlETyxTQUFTRSxnQkFBZ0JDLEdBRTlCLE1BQU1ILEVBQWEsQ0FBQSxFQUduQixHQUFJclIsU0FBU3dSLEdBRVgsSUFBSyxNQUFPL1QsRUFBS3VELEtBQVV0RCxPQUFPK1QsUUFBUUQsR0FBYSxDQUVyRCxNQUFNRSxFQUFrQjVILFlBQVlyTSxHQUNoQ3FNLFlBQVlyTSxHQUFLZSxNQUFNLEtBQ3ZCLEdBSUprVCxFQUFnQkMsUUFDZCxDQUFDQyxFQUFLQyxFQUFNQyxJQUNURixFQUFJQyxHQUNISCxFQUFnQnRSLE9BQVMsSUFBTTBSLEVBQVE5USxFQUFRNFEsRUFBSUMsSUFBUyxJQUNoRVIsRUFFSCxNQUVEL08sSUFDRSxFQUNBLG1GQUtKLE9BQU8rTyxDQUNULENBb0JPLFNBQVNVLGdCQUNkN0gsT0FDQXhLLFVBQVcsRUFDWHNTLGdCQUFpQixHQUVqQixJQUVFLElBQUtoUyxTQUFTa0ssU0FBNkIsaUJBQVhBLE9BRTlCLE9BQU8sS0FJVCxNQUFNK0gsYUFDYyxpQkFBWC9ILE9BQ0g4SCxlQUNFRSxLQUFLLElBQUloSSxXQUNUaUksS0FBS3BCLE1BQU03RyxRQUNiQSxPQUdBa0ksbUJBQXFCQyxrQkFDekJKLGFBQ0FELGdCQUNBLEdBSUlNLGNBQWdCTixlQUNsQkcsS0FBS3BCLE1BQ0hzQixrQkFBa0JKLGFBQWNELGdCQUFnQixJQUNoRCxDQUFDTyxFQUFHdlIsUUFDZSxpQkFBVkEsT0FBc0JBLE1BQU1ZLFdBQVcsWUFDMUNzUSxLQUFLLElBQUlsUixVQUNUQSxRQUVSbVIsS0FBS3BCLE1BQU1xQixvQkFHZixPQUFPMVMsU0FBVzBTLG1CQUFxQkUsYUFDeEMsQ0FBQyxNQUFPcFAsR0FFUCxPQUFPLElBQ1IsQ0FDSCxDQThGQSxTQUFTK04sYUFBYS9HLEdBRXBCLE1BQU14RSxFQUFVLENBQUEsRUFHaEIsSUFBSyxNQUFPOE0sRUFBTXZTLEtBQVN2QyxPQUFPK1QsUUFBUXZILEdBQ3BDeE0sT0FBT0MsVUFBVUMsZUFBZUMsS0FBS29DLEVBQU0sY0FFbEI4QyxJQUF2QjhOLEtBQUs1USxFQUFLdUUsVUFBaUQsT0FBdkJxTSxLQUFLNVEsRUFBS3VFLFNBRWhEa0IsRUFBUThNLEdBQVEzQixLQUFLNVEsRUFBS3VFLFNBRzFCa0IsRUFBUThNLEdBQVF2UyxFQUFLZSxNQUl2QjBFLEVBQVE4TSxHQUFRdkIsYUFBYWhSLEdBS2pDLE9BQU95RixDQUNULENBWU8sU0FBUzRMLGNBQWNtQixFQUFpQnBCLEdBRTdDLEdBQUlyUixTQUFTeVMsSUFBb0J6UyxTQUFTcVIsR0FDeEMsSUFBSyxNQUFPNVQsRUFBS3VELEtBQVV0RCxPQUFPK1QsUUFBUUosR0FDeENvQixFQUFnQmhWLEdBQ2R1QyxTQUFTZ0IsS0FDUmdKLGNBQWM3TCxTQUFTVixTQUNDc0YsSUFBekIwUCxFQUFnQmhWLEdBQ1o2VCxjQUFjbUIsRUFBZ0JoVixHQUFNdUQsUUFDMUIrQixJQUFWL0IsRUFDRUEsRUFDQXlSLEVBQWdCaFYsSUFBUSxLQUtwQyxPQUFPZ1YsQ0FDVCxDQXNCTyxTQUFTSixrQkFBa0IzTSxFQUFTc00sRUFBZ0JVLEdBaUN6RCxPQUFPUCxLQUFLUSxVQUFVak4sR0FoQ0csQ0FBQzZNLEVBQUd2UixLQU8zQixHQUxxQixpQkFBVkEsSUFDVEEsRUFBUUEsRUFBTW5CLFFBS0csbUJBQVZtQixHQUNXLGlCQUFWQSxHQUNOQSxFQUFNWSxXQUFXLGFBQ2pCWixFQUFNVSxTQUFTLEtBQ2pCLENBRUEsR0FBSXNRLEVBRUYsT0FBT1UsRUFFSCxZQUFZMVIsRUFBUSxJQUFJNFIsV0FBVyxPQUFRLGVBRTNDLFdBQVc1UixFQUFRLElBQUk0UixXQUFXLE9BQVEsY0FHOUMsTUFBTSxJQUFJQyxLQUViLENBR0QsT0FBTzdSLENBQUssSUFJbUM0UixXQUMvQ0YsRUFBcUIseUJBQTJCLHFCQUNoRCxHQUVKLENDcllPSSxlQUFlQyxNQUFNQyxFQUFLQyxFQUFpQixJQUNoRCxPQUFPLElBQUlDLFNBQVEsQ0FBQzdULEVBQVM4VCxLQUMzQkMsbUJBQW1CSixHQUNoQkssSUFBSUwsRUFBS0MsR0FBaUJLLElBQ3pCLElBQUlDLEVBQWUsR0FHbkJELEVBQVNFLEdBQUcsUUFBU0MsSUFDbkJGLEdBQWdCRSxDQUFLLElBSXZCSCxFQUFTRSxHQUFHLE9BQU8sS0FDWkQsR0FDSEosRUFBTyxxQ0FFVEcsRUFBU0ksS0FBT0gsRUFDaEJsVSxFQUFRaVUsRUFBUyxHQUNqQixJQUVIRSxHQUFHLFNBQVV0USxJQUNaaVEsRUFBT2pRLEVBQU0sR0FDYixHQUVSLENBd0VBLFNBQVNrUSxtQkFBbUJKLEdBQzFCLE9BQU9BLEVBQUlwUixXQUFXLFNBQVcrUixNQUFRQyxJQUMzQyxDQ3BIQSxNQUFNQyxvQkFBb0JoQixNQVF4QixXQUFBaUIsQ0FBWXpRLEVBQVMwUSxHQUNuQkMsUUFFQUMsS0FBSzVRLFFBQVVBLEVBQ2Y0USxLQUFLM1EsYUFBZUQsRUFFaEIwUSxJQUNGRSxLQUFLRixXQUFhQSxFQUVyQixDQVNELFNBQUFHLENBQVVILEdBR1IsT0FGQUUsS0FBS0YsV0FBYUEsRUFFWEUsSUFDUixDQVVELFFBQUFFLENBQVNqUixHQWdCUCxPQWZBK1EsS0FBSy9RLE1BQVFBLEVBRVRBLEVBQU1zUCxPQUNSeUIsS0FBS3pCLEtBQU90UCxFQUFNc1AsTUFHaEJ0UCxFQUFNNlEsYUFDUkUsS0FBS0YsV0FBYTdRLEVBQU02USxZQUd0QjdRLEVBQU1LLFFBQ1IwUSxLQUFLM1EsYUFBZUosRUFBTUcsUUFDMUI0USxLQUFLMVEsTUFBUUwsRUFBTUssT0FHZDBRLElBQ1IsRUN4Q0gsTUFBTUcsTUFBUSxDQUNaclAsT0FBUSw4QkFDUnNQLGVBQWdCLENBQUUsRUFDbEJDLFFBQVMsR0FDVEMsVUFBVyxJQWVOekIsZUFBZTBCLG9CQUNwQkMsRUFDQUMsR0FFQSxJQUNFLElBQUlDLEVBR0osTUFBTTFQLEVBQVkyUCxlQUdaQyxFQUFlMVEsS0FBQUEsS0FBS2MsRUFBVyxpQkFDL0I2UCxFQUFhM1EsS0FBQUEsS0FBS2MsRUFBVyxjQU9uQyxJQUpDaEIsR0FBVUEsV0FBQ2dCLElBQWNmLEdBQVNBLFVBQUNlLEVBQVcsQ0FBRThQLFdBQVcsS0FJdkQ5USxHQUFBQSxXQUFXNFEsSUFBaUJKLEVBQWtCelAsV0FDakQxQyxJQUFJLEVBQUcseURBQ1BxUyxRQUF1QkssYUFDckJQLEVBQ0FDLEVBQ0FJLE9BRUcsQ0FDTCxJQUFJRyxHQUFnQixFQUdwQixNQUFNQyxFQUFXL0MsS0FBS3BCLE1BQU1wUCxHQUFZQSxhQUFDa1QsR0FBZSxRQUl4RCxHQUFJSyxFQUFTQyxTQUFXNVgsTUFBTUMsUUFBUTBYLEVBQVNDLFNBQVUsQ0FDdkQsTUFBTUMsRUFBWSxDQUFBLEVBQ2xCRixFQUFTQyxRQUFRL0ssU0FBU2lMLEdBQU9ELEVBQVVDLEdBQUssSUFDaERILEVBQVNDLFFBQVVDLENBQ3BCLENBR0QsTUFBTWxRLFlBQUVBLEVBQVdFLGNBQUVBLEVBQWFDLGlCQUFFQSxHQUNsQ29QLEVBQ0lhLEVBQ0pwUSxFQUFZOUUsT0FBU2dGLEVBQWNoRixPQUFTaUYsRUFBaUJqRixPQUszRDhVLEVBQVNwUSxVQUFZMlAsRUFBa0IzUCxTQUN6Q3hDLElBQ0UsRUFDQSx5RUFFRjJTLEdBQWdCLEdBRWhCdlgsT0FBT3lDLEtBQUsrVSxFQUFTQyxTQUFXLENBQUUsR0FBRS9VLFNBQVdrVixHQUUvQ2hULElBQ0UsRUFDQSwrRUFFRjJTLEdBQWdCLEdBR2hCQSxHQUFpQjdQLEdBQWlCLElBQUk5RSxNQUFNaVYsSUFDMUMsSUFBS0wsRUFBU0MsUUFBUUksR0FLcEIsT0FKQWpULElBQ0UsRUFDQSxlQUFlaVQsaURBRVYsQ0FDUixJQUtETixFQUNGTixRQUF1QkssYUFDckJQLEVBQ0FDLEVBQ0FJLElBR0Z4UyxJQUFJLEVBQUcsdURBR1A4UixNQUFNRSxRQUFVM1MsR0FBQUEsYUFBYW1ULEVBQVksUUFHekNILEVBQWlCTyxFQUFTQyxRQUcxQmYsTUFBTUcsVUFBWWlCLGVBQWVwQixNQUFNRSxTQUUxQyxPQUlLbUIsc0JBQXNCaEIsRUFBbUJFLEVBQ2hELENBQUMsTUFBT3pSLEdBQ1AsTUFBTSxJQUFJMlEsWUFDUiw4RUFDQSxLQUNBTSxTQUFTalIsRUFDWixDQUNILENBU08sU0FBU3dTLHVCQUNkLE9BQU90QixNQUFNRyxTQUNmLENBV096QixlQUFlNkMsd0JBQXdCQyxHQUU1QyxNQUFNbFEsRUFBVTBMLGNBQWMsQ0FDNUJ2TSxXQUFZLENBQ1ZDLFFBQVM4USxXQUtQcEIsb0JBQW9COU8sRUFBUWIsV0FBWWEsRUFBUXlCLE9BQU9NLE1BQy9ELENBV08sU0FBUytOLGVBQWVLLEdBQzdCLE9BQU9BLEVBQ0p2TCxVQUFVLEVBQUd1TCxFQUFhQyxRQUFRLE9BQ2xDNVgsUUFBUSxLQUFNLElBQ2RBLFFBQVEsS0FBTSxJQUNkQSxRQUFRLE1BQU8sSUFDZjJCLE1BQ0wsQ0FZTyxTQUFTa1csa0JBQWtCQyxHQUNoQyxPQUFPQSxFQUFXOVgsUUFDaEIscUVBQ0EsR0FFSixDQW9CTyxTQUFTMFcsZUFDZCxPQUFPclcsZ0JBQWdCMlMsYUFBYXJNLFdBQVdJLFVBQ2pELENBdUJBNk4sZUFBZW1ELHVCQUNiQyxFQUNBakQsRUFDQTBCLEVBQ0F3QixHQUFtQixHQUdmRCxFQUFPeFUsU0FBUyxTQUNsQndVLEVBQVNBLEVBQU81TCxVQUFVLEVBQUc0TCxFQUFPOVYsT0FBUyxJQUUvQ2tDLElBQUksRUFBRyw2QkFBNkI0VCxRQUdwQyxNQUFNNUMsUUFBaUJQLE1BQU0sR0FBR21ELE9BQWFqRCxHQUc3QyxHQUE0QixNQUF4QkssRUFBU1MsWUFBOEMsaUJBQWpCVCxFQUFTSSxLQUFrQixDQUNuRSxHQUFJaUIsRUFBZ0IsQ0FFbEJBLEVBRG1Cb0Isa0JBQWtCRyxJQUNSLENBQzlCLENBQ0QsT0FBTzVDLEVBQVNJLElBQ2pCLENBR0QsR0FBSXlDLEVBQ0YsTUFBTSxJQUFJdEMsWUFDUiwrQkFBK0JxQywyRUFBZ0Y1QyxFQUFTUyxlQUN4SCxLQUNBSSxTQUFTYixHQUVYaFIsSUFDRSxFQUNBLCtCQUErQjRULDZEQUdyQyxDQWlCQXBELGVBQWUyQyxzQkFBc0JoQixFQUFtQkUsRUFBaUIsSUFDdkUsTUFBTXlCLEVBQWMsQ0FDbEJ0UixRQUFTMlAsRUFBa0IzUCxRQUMzQnFRLFFBQVNSLEdBSVhQLE1BQU1DLGVBQWlCK0IsRUFFdkI5VCxJQUFJLEVBQUcsbUNBQ1AsSUFDRStULEdBQWFBLGNBQ1hsUyxVQUFLeVEsZUFBZ0IsaUJBQ3JCekMsS0FBS1EsVUFBVXlELEdBQ2YsT0FFSCxDQUFDLE1BQU9sVCxHQUNQLE1BQU0sSUFBSTJRLFlBQ1IsNENBQ0EsS0FDQU0sU0FBU2pSLEVBQ1osQ0FDSCxDQXVCQTRQLGVBQWV3RCxjQUNicFIsRUFDQUUsRUFDQUUsRUFDQW9QLEVBQ0FDLEdBR0EsSUFBSTRCLEVBQ0osTUFBTUMsRUFBWTlCLEVBQW1Cck4sS0FDL0JvUCxFQUFZL0IsRUFBbUJwTixLQUdyQyxHQUFJa1AsR0FBYUMsRUFDZixJQUNFRixFQUFhLElBQUlHLGdCQUFBQSxnQkFBZ0IsQ0FDL0JyUCxLQUFNbVAsRUFDTmxQLEtBQU1tUCxHQUVULENBQUMsTUFBT3ZULEdBQ1AsTUFBTSxJQUFJMlEsWUFDUiwwQ0FDQSxLQUNBTSxTQUFTalIsRUFDWixDQUlILE1BQU0rUCxFQUFpQnNELEVBQ25CLENBQ0VJLE1BQU9KLEVBQ1A3TyxRQUFTZ04sRUFBbUJoTixTQUU5QixHQUVFa1AsRUFBbUIsSUFDcEIxUixFQUFZNEYsS0FBS29MLEdBQ2xCRCx1QkFBdUIsR0FBR0MsSUFBVWpELEVBQWdCMEIsR0FBZ0IsUUFFbkV2UCxFQUFjMEYsS0FBS29MLEdBQ3BCRCx1QkFBdUIsR0FBR0MsSUFBVWpELEVBQWdCMEIsUUFFbkRyUCxFQUFjd0YsS0FBS29MLEdBQ3BCRCx1QkFBdUIsR0FBR0MsSUFBVWpELE1BS3hDLGFBRDZCQyxRQUFRMkQsSUFBSUQsSUFDbkJ6UyxLQUFLLE1BQzdCLENBb0JBMk8sZUFBZWtDLGFBQWFQLEVBQW1CQyxFQUFvQkksR0FFakUsTUFBTVAsRUFDMEIsV0FBOUJFLEVBQWtCM1AsUUFDZCxLQUNBLEdBQUcyUCxFQUFrQjNQLFVBR3JCQyxFQUFTMFAsRUFBa0IxUCxRQUFVcVAsTUFBTXJQLE9BRWpELElBQ0UsTUFBTTRQLEVBQWlCLENBQUEsRUF1Q3ZCLE9BckNBclMsSUFDRSxFQUNBLGlEQUFpRGlTLEdBQWEsYUFHaEVILE1BQU1FLGNBQWdCZ0MsY0FDcEIsSUFDSzdCLEVBQWtCdlAsWUFBWTRGLEtBQUtnTSxHQUNwQ3ZDLEVBQVksR0FBR3hQLEtBQVV3UCxLQUFhdUMsSUFBTSxHQUFHL1IsS0FBVStSLE9BRzdELElBQ0tyQyxFQUFrQnJQLGNBQWMwRixLQUFLdUssR0FDaEMsUUFBTkEsRUFDSWQsRUFDRSxHQUFHeFAsVUFBZXdQLGFBQXFCYyxJQUN2QyxHQUFHdFEsa0JBQXVCc1EsSUFDNUJkLEVBQ0UsR0FBR3hQLEtBQVV3UCxhQUFxQmMsSUFDbEMsR0FBR3RRLGFBQWtCc1EsU0FFMUJaLEVBQWtCcFAsaUJBQWlCeUYsS0FBS2lNLEdBQ3pDeEMsRUFDSSxHQUFHeFAsV0FBZ0J3UCxnQkFBd0J3QyxJQUMzQyxHQUFHaFMsc0JBQTJCZ1MsT0FHdEN0QyxFQUFrQm5QLGNBQ2xCb1AsRUFDQUMsR0FJRlAsTUFBTUcsVUFBWWlCLGVBQWVwQixNQUFNRSxTQUd2QytCLEdBQUFBLGNBQWN2QixFQUFZVixNQUFNRSxTQUN6QkssQ0FDUixDQUFDLE1BQU96UixHQUNQLE1BQU0sSUFBSTJRLFlBQ1IsdURBQ0EsS0FDQU0sU0FBU2pSLEVBQ1osQ0FDSCxDQ3BkTyxTQUFTOFQsa0JBQ2RDLFdBQVdDLFdBQWEsV0FDdEIsTUFBTyxDQUFFQyxTQUFVLEVBQ3ZCLENBQ0EsQ0FjT3JFLGVBQWVzRSxZQUFZQyxFQUFlQyxHQUUvQyxNQUFNcEcsV0FBRUEsRUFBVXFHLFdBQUVBLEVBQVVDLE1BQUVBLEVBQUtDLEtBQUVBLEdBQVNSLFdBSWhEQSxXQUFXUyxjQUFnQkYsR0FBTSxFQUFPLENBQUUsRUFBRXRHLEtBRzVDckosT0FBTzhQLGtCQUFtQixFQUMxQkYsRUFBS1IsV0FBV1csTUFBTWphLFVBQVcsUUFBUSxTQUFVa2EsRUFBU0MsRUFBYUMsS0FFdkVELEVBQWNOLEVBQU1NLEVBQWEsQ0FDL0JFLFVBQVcsQ0FDVEMsU0FBUyxHQUVYQyxZQUFhLENBQ1hDLE9BQVEsQ0FDTkMsTUFBTyxDQUNMSCxTQUFTLEtBT2ZJLFFBQVMsQ0FBRSxLQUdBRixRQUFVLElBQUkvTixTQUFRLFNBQVUrTixHQUMzQ0EsRUFBT0csV0FBWSxDQUN6QixJQUdTelEsT0FBTzBRLHFCQUNWMVEsT0FBTzBRLG1CQUFxQnRCLFdBQVd1QixTQUFTdkUsS0FBTSxVQUFVLEtBQzlEcE0sT0FBTzhQLGtCQUFtQixDQUFJLEtBSWxDRSxFQUFRL1UsTUFBTW1SLEtBQU0sQ0FBQzZELEVBQWFDLEdBQ3RDLElBRUVOLEVBQUtSLFdBQVd3QixPQUFPOWEsVUFBVyxRQUFRLFNBQVVrYSxFQUFTYSxFQUFPaFQsR0FDbEVtUyxFQUFRL1UsTUFBTW1SLEtBQU0sQ0FBQ3lFLEVBQU9oVCxHQUNoQyxJQUdFLE1BQU1pVCxFQUFvQixDQUN4QkQsTUFBTyxDQUVMSixXQUFXLEVBRVhyUyxPQUFRb1IsRUFBY3BSLE9BQ3RCQyxNQUFPbVIsRUFBY25SLE9BRXZCOFIsVUFBVyxDQUVUQyxTQUFTLElBS1BILEVBQWMsSUFBSWMsU0FBUyxVQUFVdkIsRUFBYzVSLFFBQXJDLEdBR2RpQixFQUFlLElBQUlrUyxTQUFTLFVBQVV2QixFQUFjM1EsZUFBckMsR0FHZkQsRUFBZ0IsSUFBSW1TLFNBQVMsVUFBVXZCLEVBQWM1USxnQkFBckMsR0FHaEJvUyxFQUFlckIsR0FDbkIsRUFDQTlRLEVBQ0FvUixFQUVBYSxHQUlJRyxFQUFnQnhCLEVBQW1CeFEsU0FDckMsSUFBSThSLFNBQVMsVUFBVXRCLEVBQW1CeFEsV0FBMUMsR0FDQSxLQUdBd1EsRUFBbUIvVixZQUNyQixJQUFJcVgsU0FBUyxVQUFXdEIsRUFBbUIvVixXQUEzQyxDQUF1RHVXLEdBSXJEclIsR0FDRjhRLEVBQVc5USxHQUlid1EsV0FBV0ksRUFBY3RaLFFBQVEsWUFBYThhLEVBQWNDLEdBRzVELE1BQU1DLEVBQWlCN0gsSUFHdkIsSUFBSyxNQUFNVyxLQUFRa0gsRUFDbUIsbUJBQXpCQSxFQUFlbEgsV0FDakJrSCxFQUFlbEgsR0FLMUIwRixFQUFXTixXQUFXUyxlQUd0QlQsV0FBV1MsY0FBZ0IsRUFDN0IsQ0M1SEEsTUFBTXNCLFNBQVdyWCxHQUFZQSxhQUMzQndDLFVBQUs1SCxZQUFXLFlBQWEsaUJBQzdCLFFBSUYsSUFBSTBjLFFBQVUsS0FtQ1BuRyxlQUFlb0csY0FBY0MsR0FFbEMsTUFBTTVQLE1BQUVBLEVBQUtOLE1BQUVBLEdBQVVpSSxjQUdqQjlKLE9BQVFnUyxLQUFpQkMsR0FBaUI5UCxFQUc1QytQLEVBQWdCLENBQ3BCOVAsVUFBVVAsRUFBTUssa0JBQW1CLFFBQ25DaVEsWUFBYSxNQUNiaFgsS0FBTTRXLEdBQWlCLEdBQ3ZCSyxjQUFjLEVBQ2RDLGVBQWUsRUFDZkMsY0FBYyxFQUNkQyxvQkFBb0IsRUFDcEJDLGdCQUFpQixRQUNiUixHQUFnQkMsR0FJdEIsSUFBS0osUUFBUyxDQUVaLElBQUlZLEVBQVcsRUFFZixNQUFNQyxFQUFPaEgsVUFDWCxJQUNFeFEsSUFDRSxFQUNBLHlEQUF5RHVYLE9BSTNEWixjQUFnQjNVLFVBQVV5VixPQUFPVCxFQUNsQyxDQUFDLE1BQU9wVyxHQVFQLEdBUEFELGFBQ0UsRUFDQUMsRUFDQSxvREFJRTJXLEVBQVcsSUFPYixNQUFNM1csRUFOTlosSUFBSSxFQUFHLHNDQUFzQ3VYLHVCQUd2QyxJQUFJM0csU0FBU0ksR0FBYTBHLFdBQVcxRyxFQUFVLGFBQy9Dd0csR0FJVCxHQUdILFVBQ1FBLElBR3lCLFVBQTNCUixFQUFjOVAsVUFDaEJsSCxJQUFJLEVBQUcsNkNBSUw4VyxHQUNGOVcsSUFBSSxFQUFHLDRDQUVWLENBQUMsTUFBT1ksR0FDUCxNQUFNLElBQUkyUSxZQUNSLGdFQUNBLEtBQ0FNLFNBQVNqUixFQUNaLENBRUQsSUFBSytWLFFBQ0gsTUFBTSxJQUFJcEYsWUFBWSwyQ0FBNEMsSUFFckUsQ0FHRCxPQUFPb0YsT0FDVCxDQVFPbkcsZUFBZW1ILGVBRWhCaEIsU0FBV0EsUUFBUWlCLGlCQUNmakIsUUFBUWtCLFFBRWhCbEIsUUFBVSxLQUNWM1csSUFBSSxFQUFHLGdDQUNULENBZ0JPd1EsZUFBZXNILFFBQVFDLEdBRTVCLElBQUtwQixVQUFZQSxRQUFRaUIsVUFDdkIsTUFBTSxJQUFJckcsWUFBWSwwQ0FBMkMsS0FnQm5FLEdBWkF3RyxFQUFhQyxXQUFhckIsUUFBUW1CLGdCQUc1QkMsRUFBYUMsS0FBS0MsaUJBQWdCLFNBR2xDQyxnQkFBZ0JILEVBQWFDLE1BR25DRyxlQUFlSixFQUFhQyxPQUd2QkQsRUFBYUMsTUFBUUQsRUFBYUMsS0FBS0ksV0FDMUMsTUFBTSxJQUFJN0csWUFBWSwyQ0FBNEMsSUFFdEUsQ0FrQk9mLGVBQWU2SCxVQUFVTixFQUFjTyxHQUFZLEdBQ3hELElBQ0UsR0FBSVAsRUFBYUMsT0FBU0QsRUFBYUMsS0FBS0ksV0FnQjFDLE9BZklFLFNBRUlQLEVBQWFDLEtBQUtPLEtBQUssY0FBZSxDQUMxQ0MsVUFBVywyQkFJUE4sZ0JBQWdCSCxFQUFhQyxhQUc3QkQsRUFBYUMsS0FBS1MsVUFBUyxLQUMvQnJlLFNBQVNzZSxLQUFLQyxVQUNaLDREQUE0RCxLQUczRCxDQUVWLENBQUMsTUFBTy9YLEdBQ1BELGFBQ0UsRUFDQUMsRUFDQSx5QkFBeUJtWCxFQUFhYSxtREFJeENiLEVBQWFjLFVBQVlqSyxhQUFhN0ksS0FBS0csVUFBWSxDQUN4RCxDQUNELE9BQU8sQ0FDVCxDQWlCT3NLLGVBQWVzSSxpQkFBaUJkLEVBQU1oRCxHQUUzQyxNQUFNK0QsRUFBb0IsR0FHcEJ0VSxFQUFZdVEsRUFBbUJ2USxVQUNyQyxHQUFJQSxFQUFXLENBQ2IsTUFBTXVVLEVBQWEsR0FVbkIsR0FQSXZVLEVBQVV3VSxJQUNaRCxFQUFXOVgsS0FBSyxDQUNkZ1ksUUFBU3pVLEVBQVV3VSxLQUtuQnhVLEVBQVUwVSxNQUNaLElBQUssTUFBTTdYLEtBQVFtRCxFQUFVMFUsTUFBTyxDQUNsQyxNQUFNQyxHQUFVOVgsRUFBS2hDLFdBQVcsUUFHaEMwWixFQUFXOVgsS0FDVGtZLEVBQ0ksQ0FDRUYsUUFBUzdaLEdBQUFBLGFBQWFwRCxnQkFBZ0JxRixHQUFPLFNBRS9DLENBQ0VvUCxJQUFLcFAsR0FHZCxDQUdILElBQUssTUFBTStYLEtBQWNMLEVBQ3ZCLElBQ0VELEVBQWtCN1gsV0FBVzhXLEVBQUtzQixhQUFhRCxHQUNoRCxDQUFDLE1BQU96WSxHQUNQRCxhQUFhLEVBQUdDLEVBQU8sOENBQ3hCLENBRUhvWSxFQUFXbGIsT0FBUyxFQUdwQixNQUFNeWIsRUFBYyxHQUNwQixHQUFJOVUsRUFBVStVLElBQUssQ0FDakIsSUFBSUMsRUFBYWhWLEVBQVUrVSxJQUFJRSxNQUFNLHVCQUNyQyxHQUFJRCxFQUVGLElBQUssSUFBSUUsS0FBaUJGLEVBQ3BCRSxJQUNGQSxFQUFnQkEsRUFDYi9kLFFBQVEsT0FBUSxJQUNoQkEsUUFBUSxVQUFXLElBQ25CQSxRQUFRLEtBQU0sSUFDZEEsUUFBUSxLQUFNLElBQ2RBLFFBQVEsSUFBSyxJQUNiQSxRQUFRLE1BQU8sSUFDZjJCLE9BR0NvYyxFQUFjcmEsV0FBVyxRQUMzQmlhLEVBQVlyWSxLQUFLLENBQ2Z3UCxJQUFLaUosSUFFRTNFLEVBQW1COVYsb0JBQzVCcWEsRUFBWXJZLEtBQUssQ0FDZnRFLEtBQU1YLGdCQUFnQjBkLE1BUWhDSixFQUFZclksS0FBSyxDQUNmZ1ksUUFBU3pVLEVBQVUrVSxJQUFJNWQsUUFBUSxzQkFBdUIsS0FBTyxNQUcvRCxJQUFLLE1BQU1nZSxLQUFlTCxFQUN4QixJQUNFUixFQUFrQjdYLFdBQVc4VyxFQUFLNkIsWUFBWUQsR0FDL0MsQ0FBQyxNQUFPaFosR0FDUEQsYUFDRSxFQUNBQyxFQUNBLCtDQUVILENBRUgyWSxFQUFZemIsT0FBUyxDQUN0QixDQUNGLENBQ0QsT0FBT2liLENBQ1QsQ0FlT3ZJLGVBQWVzSixtQkFBbUI5QixFQUFNZSxHQUM3QyxJQUNFLElBQUssTUFBTWdCLEtBQVloQixRQUNmZ0IsRUFBU0MsZ0JBSVhoQyxFQUFLUyxVQUFTLEtBRWxCLEdBQTBCLG9CQUFmOUQsV0FBNEIsQ0FFckMsTUFBTXNGLEVBQVl0RixXQUFXdUYsT0FHN0IsR0FBSWpmLE1BQU1DLFFBQVErZSxJQUFjQSxFQUFVbmMsT0FFeEMsSUFBSyxNQUFNcWMsS0FBWUYsRUFDckJFLEdBQVlBLEVBQVNDLFVBRXJCekYsV0FBV3VGLE9BQU8vZCxPQUd2QixDQUdELFNBQVVrZSxHQUFtQmpnQixTQUFTa2dCLHFCQUFxQixXQUVyRCxJQUFNQyxHQUFrQm5nQixTQUFTa2dCLHFCQUFxQixhQUVsREUsR0FBaUJwZ0IsU0FBU2tnQixxQkFBcUIsUUFHekQsSUFBSyxNQUFNRyxJQUFXLElBQ2pCSixLQUNBRSxLQUNBQyxHQUVIQyxFQUFRQyxRQUNULEdBRUosQ0FBQyxNQUFPOVosR0FDUEQsYUFBYSxFQUFHQyxFQUFPLDhDQUN4QixDQUNILENBWUE0UCxlQUFlMEgsZ0JBQWdCRixTQUV2QkEsRUFBSzJDLFdBQVdqRSxTQUFVLENBQUU4QixVQUFXLDJCQUd2Q1IsRUFBS3NCLGFBQWEsQ0FBRTFjLEtBQU1pRixLQUFJQSxLQUFDeVEsZUFBZ0Isc0JBRy9DMEYsRUFBS1MsU0FBUy9ELGdCQUN0QixDQVdBLFNBQVN5RCxlQUFlSCxHQUV0QixNQUFNL1EsTUFBRUEsR0FBVTJILGFBR2xCb0osRUFBSzlHLEdBQUcsYUFBYVYsVUFHZndILEVBQUtJLFVBRVIsSUFJQ25SLEVBQU1uQyxRQUFVbUMsRUFBTUcsaUJBQ3hCNFEsRUFBSzlHLEdBQUcsV0FBWW5RLElBQ2xCUixRQUFRUCxJQUFJLFdBQVdlLEVBQVFxUSxTQUFTLEdBRzlDLENDNWNBLElBQUF3SixZQUFlLElBQU0seVhDSU5DLFlBQUN4WCxHQUFRLDhMQVFsQnVYLDhFQUlFdlgsd0NDYURtTixlQUFlc0ssZ0JBQWdCOUMsRUFBTWpELEVBQWVDLEdBRXpELE1BQU0rRCxFQUFvQixHQUUxQixJQUNFLElBQUlnQyxHQUFRLEVBR1osR0FBSWhHLEVBQWMxUixJQUFLLENBSXJCLEdBSEFyRCxJQUFJLEVBQUcsbUNBR29CLFFBQXZCK1UsRUFBY2haLEtBQ2hCLE9BQU9nWixFQUFjMVIsSUFJdkIwWCxHQUFRLFFBR0YvQyxFQUFLMkMsV0FBV0UsWUFBWTlGLEVBQWMxUixLQUFNLENBQ3BEbVYsVUFBVyxvQkFFbkIsTUFDTXhZLElBQUksRUFBRywyQ0FHRGdZLEVBQUtTLFNBQVMzRCxZQUFhQyxFQUFlQyxHQU1sRCtELEVBQWtCN1gsY0FDTjRYLGlCQUFpQmQsRUFBTWhELElBSW5DLE1BQU1nRyxFQUFPRCxRQUNIL0MsRUFBS1MsVUFBVTVVLElBQ25CLE1BQU1vWCxFQUFhN2dCLFNBQVM4Z0IsY0FDMUIsc0NBSUlDLEVBQWNGLEVBQVd0WCxPQUFPeVgsUUFBUTFjLE1BQVFtRixFQUNoRHdYLEVBQWFKLEVBQVdyWCxNQUFNd1gsUUFBUTFjLE1BQVFtRixFQVVwRCxPQU5BekosU0FBU3NlLEtBQUs0QyxNQUFNQyxLQUFPMVgsRUFJM0J6SixTQUFTc2UsS0FBSzRDLE1BQU1FLE9BQVMsTUFFdEIsQ0FDTEwsY0FDQUUsYUFDRCxHQUNBdFMsV0FBV2dNLEVBQWNsUixjQUN0Qm1VLEVBQUtTLFVBQVMsS0FFbEIsTUFBTTBDLFlBQUVBLEVBQVdFLFdBQUVBLEdBQWU5VixPQUFPb1AsV0FBV3VGLE9BQU8sR0FPN0QsT0FGQTlmLFNBQVNzZSxLQUFLNEMsTUFBTUMsS0FBTyxFQUVwQixDQUNMSixjQUNBRSxhQUNELEtBSURJLEVBQUVBLEVBQUNDLEVBQUVBLFNBQVlDLGVBQWUzRCxHQUdoQzRELEVBQWlCL2MsS0FBS2dkLElBQzFCaGQsS0FBS2lkLEtBQUtkLEVBQUtHLGFBQWVwRyxFQUFjcFIsU0FJeENvWSxFQUFnQmxkLEtBQUtnZCxJQUN6QmhkLEtBQUtpZCxLQUFLZCxFQUFLSyxZQUFjdEcsRUFBY25SLFFBVTdDLElBQUlvWSxFQUVKLGFBUk1oRSxFQUFLaUUsWUFBWSxDQUNyQnRZLE9BQVFpWSxFQUNSaFksTUFBT21ZLEVBQ1BHLGtCQUFtQm5CLEVBQVEsRUFBSWhTLFdBQVdnTSxFQUFjbFIsU0FLbERrUixFQUFjaFosTUFDcEIsSUFBSyxNQUNIaWdCLFFBQWVHLFdBQVduRSxHQUMxQixNQUNGLElBQUssTUFDTCxJQUFLLE9BQ0hnRSxRQUFlSSxhQUNicEUsRUFDQWpELEVBQWNoWixLQUNkLENBQ0U2SCxNQUFPbVksRUFDUHBZLE9BQVFpWSxFQUNSSCxJQUNBQyxLQUVGM0csRUFBYzFRLHNCQUVoQixNQUNGLElBQUssTUFDSDJYLFFBQWVLLFdBQ2JyRSxFQUNBNEQsRUFDQUcsRUFDQWhILEVBQWMxUSxzQkFFaEIsTUFDRixRQUNFLE1BQU0sSUFBSWtOLFlBQ1IsdUNBQXVDd0QsRUFBY2haLFFBQ3JELEtBTU4sYUFETStkLG1CQUFtQjlCLEVBQU1lLEdBQ3hCaUQsQ0FDUixDQUFDLE1BQU9wYixHQUVQLGFBRE1rWixtQkFBbUI5QixFQUFNZSxHQUN4Qm5ZLENBQ1IsQ0FDSCxDQWNBNFAsZUFBZW1MLGVBQWUzRCxHQUM1QixPQUFPQSxFQUFLc0UsTUFBTSxvQkFBcUI3QixJQUNyQyxNQUFNZ0IsRUFBRUEsRUFBQ0MsRUFBRUEsRUFBQzlYLE1BQUVBLEVBQUtELE9BQUVBLEdBQVc4VyxFQUFROEIsd0JBQ3hDLE1BQU8sQ0FDTGQsSUFDQUMsSUFDQTlYLFFBQ0FELE9BQVE5RSxLQUFLMmQsTUFBTTdZLEVBQVMsRUFBSUEsRUFBUyxLQUMxQyxHQUVMLENBYUE2TSxlQUFlMkwsV0FBV25FLEdBQ3hCLE9BQU9BLEVBQUtzRSxNQUNWLGdDQUNDN0IsR0FBWUEsRUFBUWdDLFdBRXpCLENBa0JBak0sZUFBZTRMLGFBQWFwRSxFQUFNamMsRUFBTTJnQixFQUFNclksR0FDNUMsT0FBT3VNLFFBQVErTCxLQUFLLENBQ2xCM0UsRUFBSzRFLFdBQVcsQ0FDZDdnQixPQUNBMmdCLE9BQ0FHLFNBQVUsU0FDVkMsVUFBVSxFQUNWQyxrQkFBa0IsRUFDbEJDLHVCQUF1QixLQUNWLFFBQVRqaEIsRUFBaUIsQ0FBRWtoQixRQUFTLElBQU8sQ0FBQSxFQUV2Q0MsZUFBd0IsT0FBUm5oQixJQUVsQixJQUFJNlUsU0FBUSxDQUFDdU0sRUFBVXRNLElBQ3JCNkcsWUFDRSxJQUFNN0csRUFBTyxJQUFJVSxZQUFZLHdCQUF5QixPQUN0RGxOLEdBQXdCLFNBSWhDLENBaUJBbU0sZUFBZTZMLFdBQVdyRSxFQUFNclUsRUFBUUMsRUFBT1MsR0FFN0MsYUFETTJULEVBQUtvRixpQkFBaUIsVUFDckJwRixFQUFLcUYsSUFBSSxDQUVkMVosT0FBUUEsRUFBUyxFQUNqQkMsUUFDQWlaLFNBQVUsU0FDVnpYLFFBQVNmLEdBQXdCLE1BRXJDLENDblFBLElBQUkwQixLQUFPLEtBR1gsTUFBTXVYLFVBQVksQ0FDaEJDLGlCQUFrQixFQUNsQkMsaUJBQWtCLEVBQ2xCQyxlQUFnQixFQUNoQkMsZUFBZ0IsRUFDaEJDLG1CQUFvQixFQUNwQkMsdUJBQXdCLEVBQ3hCQywyQkFBNEIsRUFDNUJDLFVBQVcsRUFDWEMsaUJBQWtCLEdBcUJidk4sZUFBZXdOLFNBQVNDLEVBQWFwSCxTQUVwQ0QsY0FBY0MsR0FFcEIsSUFNRSxHQUxBN1csSUFDRSxFQUNBLDhDQUE4Q2llLEVBQVlqWSxtQkFBbUJpWSxFQUFZaFksZUFHdkZGLEtBS0YsWUFKQS9GLElBQ0UsRUFDQSx5RUFNQWllLEVBQVlqWSxXQUFhaVksRUFBWWhZLGFBQ3ZDZ1ksRUFBWWpZLFdBQWFpWSxFQUFZaFksWUFJdkNGLEtBQU8sSUFBSW1ZLEtBQUFBLEtBQUssSUFFWEMsU0FBU0YsR0FDWmhhLElBQUtnYSxFQUFZalksV0FDakI5QixJQUFLK1osRUFBWWhZLFdBQ2pCbVkscUJBQXNCSCxFQUFZOVgsZUFDbENrWSxvQkFBcUJKLEVBQVk3WCxjQUNqQ2tZLHFCQUFzQkwsRUFBWTVYLGVBQ2xDa1ksa0JBQW1CTixFQUFZM1gsWUFDL0JrWSwwQkFBMkJQLEVBQVkxWCxvQkFDdkNrWSxtQkFBb0JSLEVBQVl6WCxlQUNoQ2tZLHNCQUFzQixJQUl4QjNZLEtBQUttTCxHQUFHLFdBQVdWLE1BQU91SixJQUV4QixNQUFNNEUsUUFBb0J0RyxVQUFVMEIsR0FBVSxHQUM5Qy9aLElBQ0UsRUFDQSx5QkFBeUIrWixFQUFTbkIsZ0RBQWdEK0YsS0FDbkYsSUFHSDVZLEtBQUttTCxHQUFHLGtCQUFrQixDQUFDME4sRUFBVTdFLEtBQ25DL1osSUFDRSxFQUNBLHlCQUF5QitaLEVBQVNuQiwwQ0FFcENtQixFQUFTL0IsS0FBTyxJQUFJLElBR3RCLE1BQU02RyxFQUFtQixHQUV6QixJQUFLLElBQUlwSyxFQUFJLEVBQUdBLEVBQUl3SixFQUFZalksV0FBWXlPLElBQzFDLElBQ0UsTUFBTXNGLFFBQWlCaFUsS0FBSytZLFVBQVVDLFFBQ3RDRixFQUFpQjNkLEtBQUs2WSxFQUN2QixDQUFDLE1BQU9uWixHQUNQRCxhQUFhLEVBQUdDLEVBQU8sK0NBQ3hCLENBSUhpZSxFQUFpQi9XLFNBQVNpUyxJQUN4QmhVLEtBQUtpWixRQUFRakYsRUFBUyxJQUd4Qi9aLElBQ0UsRUFDQSw0QkFBMkI2ZSxFQUFpQi9nQixPQUFTLFNBQVMrZ0IsRUFBaUIvZ0Isb0NBQXNDLEtBRXhILENBQUMsTUFBTzhDLEdBQ1AsTUFBTSxJQUFJMlEsWUFDUiw2REFDQSxLQUNBTSxTQUFTalIsRUFDWixDQUNILENBWU80UCxlQUFleU8sV0FJcEIsR0FIQWpmLElBQUksRUFBRyw2REFHSCtGLEtBQU0sQ0FFUixJQUFLLE1BQU1tWixLQUFVblosS0FBS29aLEtBQ3hCcFosS0FBS2laLFFBQVFFLEVBQU9uRixVQUlqQmhVLEtBQUtxWixrQkFDRnJaLEtBQUtxVSxVQUNYcGEsSUFBSSxFQUFHLDRDQUVUK0YsS0FBTyxJQUNSLE9BR0s0UixjQUNSLENBbUJPbkgsZUFBZTZPLFNBQVNqYyxHQUM3QixJQUFJa2MsRUFFSixJQVlFLEdBWEF0ZixJQUFJLEVBQUcsZ0RBR0xzZCxVQUFVQyxpQkFHUm5hLEVBQVEyQyxLQUFLYixjQUNmcWEsZUFJR3haLEtBQ0gsTUFBTSxJQUFJd0wsWUFDUix1REFDQSxLQUtKLE1BQU1pTyxFQUFpQnJoQixjQUd2QixJQUNFNkIsSUFBSSxFQUFHLHFDQUdQc2YsUUFBcUJ2WixLQUFLK1ksVUFBVUMsUUFHaEMzYixFQUFReUIsT0FBT0ssY0FDakJsRixJQUNFLEVBQ0EsZ0JBQWVvRCxFQUFRcWMsVUFBWSxZQUFZcmMsRUFBUXFjLGdCQUFrQixJQUN6RSxrQ0FBa0NELFNBR3ZDLENBQUMsTUFBTzVlLEdBQ1AsTUFBTSxJQUFJMlEsWUFDUixVQUNFbk8sRUFBUXFjLFVBQVksWUFBWXJjLEVBQVFxYyxnQkFBa0IsMERBQ0pELFNBQ3hELEtBQ0EzTixTQUFTalIsRUFDWixDQUdELEdBRkFaLElBQUksRUFBRyxxQ0FFRnNmLEVBQWF0SCxLQUdoQixNQURBc0gsRUFBYXpHLFVBQVl6VixFQUFRMkMsS0FBS0csVUFBWSxFQUM1QyxJQUFJcUwsWUFDUixtRUFDQSxLQUtKLE1BQU1tTyxFQUFZbGlCLGlCQUVsQndDLElBQ0UsRUFDQSx5QkFBeUJzZixFQUFhMUcsMkNBSXhDLE1BQU0rRyxFQUFnQnhoQixjQUdoQjZkLFFBQWVsQixnQkFDbkJ3RSxFQUFhdEgsS0FDYjVVLEVBQVFILE9BQ1JHLEVBQVFrQixhQUlWLEdBQUkwWCxhQUFrQnpMLE1BbUJwQixLQU51QiwwQkFBbkJ5TCxFQUFPamIsVUFFVHVlLEVBQWF6RyxVQUFZelYsRUFBUTJDLEtBQUtHLFVBQVksRUFDbERvWixFQUFhdEgsS0FBTyxNQUlKLGlCQUFoQmdFLEVBQU85TCxNQUNZLDBCQUFuQjhMLEVBQU9qYixRQUVELElBQUl3USxZQUNSLFVBQ0VuTyxFQUFRcWMsVUFBWSxZQUFZcmMsRUFBUXFjLGdCQUFrQixtSEFFNUQ1TixTQUFTbUssR0FFTCxJQUFJekssWUFDUixVQUNFbk8sRUFBUXFjLFVBQVksWUFBWXJjLEVBQVFxYyxnQkFBa0Isc0NBQ3hCRSxVQUNwQzlOLFNBQVNtSyxHQUtYNVksRUFBUXlCLE9BQU9LLGNBQ2pCbEYsSUFDRSxFQUNBLGdCQUFlb0QsRUFBUXFjLFVBQVksWUFBWXJjLEVBQVFxYyxnQkFBa0IsSUFDekUsc0NBQXNDRSxVQUsxQzVaLEtBQUtpWixRQUFRTSxHQUliLE1BQ01NLEVBRFVwaUIsaUJBQ2FraUIsRUFTN0IsT0FQQXBDLFVBQVVRLFdBQWE4QixFQUN2QnRDLFVBQVVTLGlCQUNSVCxVQUFVUSxZQUFjUixVQUFVRSxpQkFFcEN4ZCxJQUFJLEVBQUcsNEJBQTRCNGYsUUFHNUIsQ0FDTDVELFNBQ0E1WSxVQUVILENBQUMsTUFBT3hDLEdBT1AsT0FORTBjLFVBQVVHLGVBRVI2QixHQUNGdlosS0FBS2laLFFBQVFNLEdBR1QxZSxDQUNQLENBQ0gsQ0FxQk8sU0FBU2lmLGVBQ2QsT0FBT3ZDLFNBQ1QsQ0FVTyxTQUFTd0Msa0JBQ2QsTUFBTyxDQUNMN2IsSUFBSzhCLEtBQUs5QixJQUNWQyxJQUFLNkIsS0FBSzdCLElBQ1ZpYixLQUFNcFosS0FBS2dhLFVBQ1hDLFVBQVdqYSxLQUFLa2EsVUFDaEJDLFdBQVluYSxLQUFLZ2EsVUFBWWhhLEtBQUtrYSxVQUNsQ0UsZ0JBQWlCcGEsS0FBS3FhLHFCQUN0QkMsZUFBZ0J0YSxLQUFLdWEsb0JBQ3JCQyxtQkFBb0J4YSxLQUFLeWEsd0JBQ3pCQyxnQkFBaUIxYSxLQUFLMGEsZ0JBQWdCM2lCLE9BQ3RDNGlCLFlBQ0UzYSxLQUFLZ2EsVUFDTGhhLEtBQUtrYSxVQUNMbGEsS0FBS3FhLHFCQUNMcmEsS0FBS3VhLG9CQUNMdmEsS0FBS3lhLHdCQUNMemEsS0FBSzBhLGdCQUFnQjNpQixPQUUzQixDQVNPLFNBQVN5aEIsY0FDZCxNQUFNdGIsSUFDSkEsRUFBR0MsSUFDSEEsRUFBR2liLEtBQ0hBLEVBQUlhLFVBQ0pBLEVBQVNFLFdBQ1RBLEVBQVVDLGdCQUNWQSxFQUFlRSxlQUNmQSxFQUFjRSxtQkFDZEEsRUFBa0JFLGdCQUNsQkEsRUFBZUMsWUFDZkEsR0FDRVosa0JBRUo5ZixJQUFJLEVBQUcsMkRBQTJEaUUsTUFDbEVqRSxJQUFJLEVBQUcsMkRBQTJEa0UsTUFDbEVsRSxJQUFJLEVBQUcsd0NBQXdDbWYsTUFDL0NuZixJQUFJLEVBQUcsd0NBQXdDZ2dCLE1BQy9DaGdCLElBQ0UsRUFDQSwrREFBK0RrZ0IsTUFFakVsZ0IsSUFDRSxFQUNBLDBEQUEwRG1nQixNQUU1RG5nQixJQUNFLEVBQ0EseURBQXlEcWdCLE1BRTNEcmdCLElBQ0UsRUFDQSwyREFBMkR1Z0IsTUFFN0R2Z0IsSUFDRSxFQUNBLDJEQUEyRHlnQixNQUU3RHpnQixJQUFJLEVBQUcsdUNBQXVDMGdCLEtBQ2hELENBV0EsU0FBU3ZDLFNBQVNGLEdBQ2hCLE1BQU8sQ0FjTDBDLE9BQVFuUSxVQUVOLE1BQU11SCxFQUFlLENBQ25CYSxHQUFJZ0ksS0FBQUEsS0FFSi9ILFVBQVdoYSxLQUFLRSxNQUFNRixLQUFLZ2lCLFVBQVk1QyxFQUFZL1gsVUFBWSxLQUdqRSxJQUVFLE1BQU00YSxFQUFZdGpCLGlCQWNsQixhQVhNc2EsUUFBUUMsR0FHZC9YLElBQ0UsRUFDQSx5QkFBeUIrWCxFQUFhYSw2Q0FDcENwYixpQkFBbUJzakIsUUFLaEIvSSxDQUNSLENBQUMsTUFBT25YLEdBS1AsTUFKQVosSUFDRSxFQUNBLHlCQUF5QitYLEVBQWFhLHFEQUVsQ2hZLENBQ1AsR0FnQkhtZ0IsU0FBVXZRLE1BQU91SCxHQWlCVkEsRUFBYUMsS0FTZEQsRUFBYUMsS0FBS0ksWUFDcEJwWSxJQUNFLEVBQ0EseUJBQXlCK1gsRUFBYWEseURBRWpDLEdBSUxiLEVBQWFDLEtBQUtnSixZQUFZQyxVQUNoQ2poQixJQUNFLEVBQ0EseUJBQXlCK1gsRUFBYWEsd0RBRWpDLEtBS1BxRixFQUFZL1gsYUFDVjZSLEVBQWFjLFVBQVlvRixFQUFZL1gsYUFFdkNsRyxJQUNFLEVBQ0EseUJBQXlCK1gsRUFBYWEseUNBQXlDcUYsRUFBWS9YLHlDQUV0RixJQWxDUGxHLElBQ0UsRUFDQSx5QkFBeUIrWCxFQUFhYSxzREFFakMsR0E4Q1h3QixRQUFTNUosTUFBT3VILElBTWQsR0FMQS9YLElBQ0UsRUFDQSx5QkFBeUIrWCxFQUFhYSw4QkFHcENiLEVBQWFDLE9BQVNELEVBQWFDLEtBQUtJLFdBQzFDLElBRUVMLEVBQWFDLEtBQUtrSixtQkFBbUIsYUFDckNuSixFQUFhQyxLQUFLa0osbUJBQW1CLFdBQ3JDbkosRUFBYUMsS0FBS2tKLG1CQUFtQix1QkFHL0JuSixFQUFhQyxLQUFLSCxPQUN6QixDQUFDLE1BQU9qWCxHQUtQLE1BSkFaLElBQ0UsRUFDQSx5QkFBeUIrWCxFQUFhYSxtREFFbENoWSxDQUNQLENBQ0YsRUFHUCxDQ3hrQk8sU0FBU3VnQixTQUFTbGtCLEdBRXZCLE1BQU1zSSxFQUFTLElBQUk2YixNQUFBQSxNQUFNLElBQUk3YixPQU03QixPQUhlOGIsVUFBVTliLEdBR1g0YixTQUFTbGtCLEVBQU8sQ0FBRXFrQixTQUFVLENBQUMsa0JBQzdDLENDREEsSUFBSS9jLG9CQUFxQixFQXFCbEJpTSxlQUFlK1EsYUFBYW5lLEdBRWpDLElBQUlBLElBQVdBLEVBQVFILE9Bd0NyQixNQUFNLElBQUlzTyxZQUNSLGtLQUNBLFdBeENJaVEsWUFDSixDQUFFdmUsT0FBUUcsRUFBUUgsT0FBUXFCLFlBQWFsQixFQUFRa0IsY0FDL0NrTSxNQUFPNVAsRUFBTzZnQixLQUVaLEdBQUk3Z0IsRUFDRixNQUFNQSxFQUlSLE1BQU02QyxJQUFFQSxFQUFHekgsUUFBRUEsRUFBT0QsS0FBRUEsR0FBUzBsQixFQUFLcmUsUUFBUUgsT0FHNUMsSUFDTVEsRUFFRnNRLEdBQWFBLGNBQ1gsR0FBRy9YLEVBQVFFLE1BQU0sS0FBS0MsU0FBVyxjQUNqQ2EsVUFBVXlrQixFQUFLekYsT0FBUWpnQixJQUl6QmdZLEdBQWFBLGNBQ1gvWCxHQUFXLFNBQVNELElBQ1gsUUFBVEEsRUFBaUJtQixPQUFPQyxLQUFLc2tCLEVBQUt6RixPQUFRLFVBQVl5RixFQUFLekYsT0FHaEUsQ0FBQyxNQUFPcGIsR0FDUCxNQUFNLElBQUkyUSxZQUNSLHNDQUNBLEtBQ0FNLFNBQVNqUixFQUNaLE9BR0txZSxVQUFVLEdBU3hCLENBc0JPek8sZUFBZWtSLFlBQVl0ZSxHQUVoQyxLQUFJQSxHQUFXQSxFQUFRSCxRQUFVRyxFQUFRSCxPQUFPSyxPQTRFOUMsTUFBTSxJQUFJaU8sWUFDUiwrR0FDQSxLQTlFbUQsQ0FFckQsTUFBTW9RLEVBQWlCLEdBR3ZCLElBQUssSUFBSUMsS0FBUXhlLEVBQVFILE9BQU9LLE1BQU1wSCxNQUFNLE1BQVEsR0FDbEQwbEIsRUFBT0EsRUFBSzFsQixNQUFNLEtBQ0UsSUFBaEIwbEIsRUFBSzlqQixPQUNQNmpCLEVBQWV6Z0IsS0FDYnNnQixZQUNFLENBQ0V2ZSxPQUFRLElBQ0hHLEVBQVFILE9BQ1hDLE9BQVEwZSxFQUFLLEdBQ2I1bEIsUUFBUzRsQixFQUFLLElBRWhCdGQsWUFBYWxCLEVBQVFrQixjQUV2QixDQUFDMUQsRUFBTzZnQixLQUVOLEdBQUk3Z0IsRUFDRixNQUFNQSxFQUlSLE1BQU02QyxJQUFFQSxFQUFHekgsUUFBRUEsRUFBT0QsS0FBRUEsR0FBUzBsQixFQUFLcmUsUUFBUUgsT0FHNUMsSUFDTVEsRUFFRnNRLEdBQWFBLGNBQ1gsR0FBRy9YLEVBQVFFLE1BQU0sS0FBS0MsU0FBVyxjQUNqQ2EsVUFBVXlrQixFQUFLekYsT0FBUWpnQixJQUl6QmdZLEdBQWFBLGNBQ1gvWCxFQUNTLFFBQVRELEVBQ0ltQixPQUFPQyxLQUFLc2tCLEVBQUt6RixPQUFRLFVBQ3pCeUYsRUFBS3pGLE9BR2QsQ0FBQyxNQUFPcGIsR0FDUCxNQUFNLElBQUkyUSxZQUNSLHNDQUNBLEtBQ0FNLFNBQVNqUixFQUNaLE1BS1BaLElBQUksRUFBRyx1REFLWCxNQUFNNmhCLFFBQXFCalIsUUFBUWtSLFdBQVdILFNBR3hDMUMsV0FHTjRDLEVBQWEvWixTQUFRLENBQUNrVSxFQUFReE0sS0FFeEJ3TSxFQUFPK0YsUUFDVHBoQixhQUNFLEVBQ0FxYixFQUFPK0YsT0FDUCwrQkFBK0J2UyxFQUFRLHNDQUUxQyxHQUVQLENBTUEsQ0FvQ09nQixlQUFlZ1IsWUFBWVEsRUFBY0MsR0FDOUMsSUFFRSxJQUFLdmtCLFNBQVNza0IsR0FDWixNQUFNLElBQUl6USxZQUNSLGlGQUNBLEtBS0osTUFBTW5PLEVBQVUwTCxjQUNkLENBQ0U3TCxPQUFRK2UsRUFBYS9lLE9BQ3JCcUIsWUFBYTBkLEVBQWExZCxjQUU1QixHQUlJeVEsRUFBZ0IzUixFQUFRSCxPQU05QixHQUhBakQsSUFBSSxFQUFHLDJDQUdzQixPQUF6QitVLEVBQWM3UixPQUFpQixDQUdqQyxJQUFJZ2YsRUFGSmxpQixJQUFJLEVBQUcsbURBR1AsSUFFRWtpQixFQUFjN2lCLEdBQVlBLGFBQ3hCcEQsZ0JBQWdCOFksRUFBYzdSLFFBQzlCLE9BRUgsQ0FBQyxNQUFPdEMsR0FDUCxNQUFNLElBQUkyUSxZQUNSLG1EQUNBLEtBQ0FNLFNBQVNqUixFQUNaLENBR0QsR0FBSW1VLEVBQWM3UixPQUFPOUQsU0FBUyxRQUVoQzJWLEVBQWMxUixJQUFNNmUsTUFDZixLQUFJbk4sRUFBYzdSLE9BQU85RCxTQUFTLFNBSXZDLE1BQU0sSUFBSW1TLFlBQ1Isa0RBQ0EsS0FKRndELEVBQWM1UixNQUFRK2UsQ0FNdkIsQ0FDRixDQUdELEdBQTBCLE9BQXRCbk4sRUFBYzFSLElBQWMsQ0FDOUJyRCxJQUFJLEVBQUcscURBR0w2ZixlQUFlakMsdUJBR2pCLE1BQU01QixRQUFlbUcsZUFDbkJoQixTQUFTcE0sRUFBYzFSLEtBQ3ZCRCxHQU9GLFFBSEV5YyxlQUFlbkMsZUFHVnVFLEVBQVksS0FBTWpHLEVBQzFCLENBR0QsR0FBNEIsT0FBeEJqSCxFQUFjNVIsT0FBNEMsT0FBMUI0UixFQUFjM1IsUUFBa0IsQ0FDbEVwRCxJQUFJLEVBQUcsc0RBR0w2ZixlQUFlaEMsMkJBR2pCLE1BQU03QixRQUFlb0csbUJBQ25Cck4sRUFBYzVSLE9BQVM0UixFQUFjM1IsUUFDckNBLEdBT0YsUUFIRXljLGVBQWVsQyxtQkFHVnNFLEVBQVksS0FBTWpHLEVBQzFCLENBR0QsT0FBT2lHLEVBQ0wsSUFBSTFRLFlBQ0YsZ0pBQ0EsS0FHTCxDQUFDLE1BQU8zUSxHQUNQLE9BQU9xaEIsRUFBWXJoQixFQUNwQixDQUNILENBU08sU0FBU3loQix3QkFDZCxPQUFPOWQsa0JBQ1QsQ0FVTyxTQUFTK2Qsc0JBQXNCNWpCLEdBQ3BDNkYsbUJBQXFCN0YsQ0FDdkIsQ0FrQkE4UixlQUFlMlIsZUFBZUksRUFBZW5mLEdBRTNDLEdBQzJCLGlCQUFsQm1mLElBQ05BLEVBQWMvTyxRQUFRLFNBQVcsR0FBSytPLEVBQWMvTyxRQUFRLFVBQVksR0FZekUsT0FWQXhULElBQUksRUFBRyxpQ0FHUG9ELEVBQVFILE9BQU9JLElBQU1rZixFQUdyQm5mLEVBQVFILE9BQU9FLE1BQVEsS0FDdkJDLEVBQVFILE9BQU9HLFFBQVUsS0FHbEJvZixlQUFlcGYsR0FFdEIsTUFBTSxJQUFJbU8sWUFBWSxtQ0FBb0MsSUFFOUQsQ0FrQkFmLGVBQWU0UixtQkFBbUJHLEVBQWVuZixHQUMvQ3BELElBQUksRUFBRyx1Q0FHUCxNQUFNOFAsRUFBcUJMLGdCQUN6QjhTLEdBQ0EsRUFDQW5mLEVBQVFrQixZQUFZQyxvQkFJdEIsR0FDeUIsT0FBdkJ1TCxHQUM4QixpQkFBdkJBLElBQ05BLEVBQW1CeFEsV0FBVyxPQUM5QndRLEVBQW1CMVEsU0FBUyxLQUU3QixNQUFNLElBQUltUyxZQUNSLG9QQUNBLEtBV0osT0FOQW5PLEVBQVFILE9BQU9FLE1BQVEyTSxFQUd2QjFNLEVBQVFILE9BQU9JLElBQU0sS0FHZG1mLGVBQWVwZixFQUN4QixDQWNBb04sZUFBZWdTLGVBQWVwZixHQUM1QixNQUFRSCxPQUFROFIsRUFBZXpRLFlBQWEwUSxHQUF1QjVSLEVBa0NuRSxPQS9CQTJSLEVBQWNoWixLQUFPSyxRQUFRMlksRUFBY2haLEtBQU1nWixFQUFjL1ksU0FHL0QrWSxFQUFjL1ksUUFBVUYsV0FBV2laLEVBQWNoWixLQUFNZ1osRUFBYy9ZLFNBR3JFK1ksRUFBY3RaLE9BQVNELFVBQVV1WixFQUFjdFosUUFHL0N1RSxJQUNFLEVBQ0EsK0JBQStCZ1YsRUFBbUJ6USxtQkFBcUIsVUFBWSxpQkFJckZrZSxtQkFBbUJ6TixFQUFvQkEsRUFBbUJ6USxvQkFHMURtZSxzQkFDRTNOLEVBQ0FDLEVBQW1COVYsbUJBQ25COFYsRUFBbUJ6USxvQkFJckJuQixFQUFRSCxPQUFTLElBQ1o4UixLQUNBNE4sZUFBZTVOLElBSWJzSyxTQUFTamMsRUFDbEIsQ0FxQkEsU0FBU3VmLGVBQWU1TixHQUV0QixNQUFRcUIsTUFBT3dNLEVBQWNsTixVQUFXbU4sR0FDdEM5TixFQUFjM1IsU0FBV3FNLGdCQUFnQnNGLEVBQWM1UixTQUFVLEdBRzNEaVQsTUFBTzBNLEVBQW9CcE4sVUFBV3FOLEdBQzVDdFQsZ0JBQWdCc0YsRUFBYzVRLGlCQUFrQixHQUcxQ2lTLE1BQU80TSxFQUFtQnROLFVBQVd1TixHQUMzQ3hULGdCQUFnQnNGLEVBQWMzUSxnQkFBaUIsRUFNM0NQLEVBQVFwRixZQUNaSSxLQUFLcUYsSUFDSCxHQUNBckYsS0FBS29GLElBQ0g4USxFQUFjbFIsT0FDWmdmLEdBQWtCaGYsT0FDbEJrZixHQUF3QmxmLE9BQ3hCb2YsR0FBdUJwZixPQUN2QmtSLEVBQWMvUSxjQUNkLEVBQ0YsSUFHSixHQTRCSWdYLEVBQU8sQ0FBRXJYLE9BdkJib1IsRUFBY3BSLFFBQ2RrZixHQUFrQkssY0FDbEJOLEdBQWNqZixRQUNkb2YsR0FBd0JHLGNBQ3hCSixHQUFvQm5mLFFBQ3BCc2YsR0FBdUJDLGNBQ3ZCRixHQUFtQnJmLFFBQ25Cb1IsRUFBY2pSLGVBQ2QsSUFlcUJGLE1BWHJCbVIsRUFBY25SLE9BQ2RpZixHQUFrQk0sYUFDbEJQLEdBQWNoZixPQUNkbWYsR0FBd0JJLGFBQ3hCTCxHQUFvQmxmLE9BQ3BCcWYsR0FBdUJFLGFBQ3ZCSCxHQUFtQnBmLE9BQ25CbVIsRUFBY2hSLGNBQ2QsSUFHNEJGLFNBRzlCLElBQUssSUFBS3VmLEVBQU8xa0IsS0FBVXRELE9BQU8rVCxRQUFRNkwsR0FDeENBLEVBQUtvSSxHQUNjLGlCQUFWMWtCLEdBQXNCQSxFQUFNOUMsUUFBUSxTQUFVLElBQU04QyxFQUkvRCxPQUFPc2MsQ0FDVCxDQWtCQSxTQUFTeUgsbUJBQW1Cek4sRUFBb0J6USxHQUU5QyxHQUFJQSxFQUFvQixDQUV0QixHQUE0QyxpQkFBakN5USxFQUFtQnZRLFVBRTVCdVEsRUFBbUJ2USxVQUFZNGUsaUJBQzdCck8sRUFBbUJ2USxVQUNuQnVRLEVBQW1COVYsb0JBQ25CLFFBRUcsSUFBSzhWLEVBQW1CdlEsVUFDN0IsSUFFRXVRLEVBQW1CdlEsVUFBWTRlLGlCQUM3QmhrQixHQUFBQSxhQUFhcEQsZ0JBQWdCLGtCQUFtQixRQUNoRCtZLEVBQW1COVYsb0JBQ25CLEVBRUgsQ0FBQyxNQUFPMEIsR0FDUFosSUFBSSxFQUFHLDREQUNSLENBSUgsSUFFRWdWLEVBQW1CL1YsV0FBYUQsV0FDOUJnVyxFQUFtQi9WLFdBQ25CK1YsRUFBbUI5VixtQkFFdEIsQ0FBQyxNQUFPMEIsR0FDUEQsYUFBYSxFQUFHQyxFQUFPLDhDQUd2Qm9VLEVBQW1CL1YsV0FBYSxJQUNqQyxDQUdELElBRUUrVixFQUFtQnhRLFNBQVd4RixXQUM1QmdXLEVBQW1CeFEsU0FDbkJ3USxFQUFtQjlWLG9CQUNuQixFQUVILENBQUMsTUFBTzBCLEdBQ1BELGFBQWEsRUFBR0MsRUFBTyw0Q0FHdkJvVSxFQUFtQnhRLFNBQVcsSUFDL0IsQ0FHRyxDQUFDLFVBQU0vRCxHQUFXNUUsU0FBU21aLEVBQW1CL1YsYUFDaERlLElBQUksRUFBRyx1REFJTCxDQUFDLFVBQU1TLEdBQVc1RSxTQUFTbVosRUFBbUJ4USxXQUNoRHhFLElBQUksRUFBRyxxREFJTCxDQUFDLFVBQU1TLEdBQVc1RSxTQUFTbVosRUFBbUJ2USxZQUNoRHpFLElBQUksRUFBRyxxREFFYixNQUlJLEdBQ0VnVixFQUFtQnhRLFVBQ25Cd1EsRUFBbUJ2USxXQUNuQnVRLEVBQW1CL1YsV0FRbkIsTUFMQStWLEVBQW1CeFEsU0FBVyxLQUM5QndRLEVBQW1CdlEsVUFBWSxLQUMvQnVRLEVBQW1CL1YsV0FBYSxLQUcxQixJQUFJc1MsWUFDUixvR0FDQSxJQUlSLENBa0JBLFNBQVM4UixpQkFDUDVlLEVBQVksS0FDWnZGLEVBQ0FxRixHQUdBLE1BQU0rZSxFQUFlLENBQUMsS0FBTSxNQUFPLFNBRW5DLElBQUlDLEVBQW1COWUsRUFDbkIrZSxHQUFtQixFQUd2QixHQUFJdGtCLEdBQXNCdUYsRUFBVXJGLFNBQVMsU0FDM0MsSUFDRW1rQixFQUFtQjlULGdCQUNqQnBRLEdBQUFBLGFBQWFwRCxnQkFBZ0J3SSxHQUFZLFNBQ3pDLEVBQ0FGLEVBRVIsQ0FBTSxNQUNBLE9BQU8sSUFDUixNQUdEZ2YsRUFBbUI5VCxnQkFBZ0JoTCxHQUFXLEVBQU9GLEdBR2pEZ2YsSUFBcUJya0IsVUFDaEJxa0IsRUFBaUJwSyxNQUs1QixJQUFLLE1BQU1zSyxLQUFZRixFQUNoQkQsRUFBYXpuQixTQUFTNG5CLEdBRWZELElBQ1ZBLEdBQW1CLFVBRlpELEVBQWlCRSxHQU81QixPQUFLRCxHQUtERCxFQUFpQnBLLFFBQ25Cb0ssRUFBaUJwSyxNQUFRb0ssRUFBaUJwSyxNQUFNM1EsS0FBSzdLLEdBQVNBLEVBQUtKLFdBQzlEZ21CLEVBQWlCcEssT0FBU29LLEVBQWlCcEssTUFBTXJiLFFBQVUsV0FDdkR5bEIsRUFBaUJwSyxPQUtyQm9LLEdBWkUsSUFhWCxDQW9CQSxTQUFTYixzQkFDUDNOLEVBQ0E3VixFQUNBcUYsR0FHQSxDQUFDLGdCQUFpQixnQkFBZ0J1RCxTQUFTNGIsSUFDekMsSUFFTTNPLEVBQWMyTyxLQUdkeGtCLEdBQ3NDLGlCQUEvQjZWLEVBQWMyTyxJQUNyQjNPLEVBQWMyTyxHQUFhdGtCLFNBQVMsU0FHcEMyVixFQUFjMk8sR0FBZWpVLGdCQUMzQnBRLEdBQUFBLGFBQWFwRCxnQkFBZ0I4WSxFQUFjMk8sSUFBZSxTQUMxRCxFQUNBbmYsR0FJRndRLEVBQWMyTyxHQUFlalUsZ0JBQzNCc0YsRUFBYzJPLElBQ2QsRUFDQW5mLEdBSVAsQ0FBQyxNQUFPM0QsR0FDUEQsYUFDRSxFQUNBQyxFQUNBLGlCQUFpQjhpQix5QkFJbkIzTyxFQUFjMk8sR0FBZSxJQUM5QixLQUlDLENBQUMsVUFBTWpqQixHQUFXNUUsU0FBU2taLEVBQWM1USxnQkFDM0NuRSxJQUFJLEVBQUcsMERBSUwsQ0FBQyxVQUFNUyxHQUFXNUUsU0FBU2taLEVBQWMzUSxlQUMzQ3BFLElBQUksRUFBRyx3REFFWCxDQ2wwQkEsTUFBTTJqQixTQUFXLEdBU1YsU0FBU0MsU0FBU2hMLEdBQ3ZCK0ssU0FBU3ppQixLQUFLMFgsRUFDaEIsQ0FRTyxTQUFTaUwsaUJBQ2Q3akIsSUFBSSxFQUFHLDJEQUNQLElBQUssTUFBTTRZLEtBQU0rSyxTQUNmRyxjQUFjbEwsR0FDZG1MLGFBQWFuTCxFQUVqQixDQ2ZBLFNBQVNvTCxtQkFBbUJwakIsRUFBT3FqQixFQUFTalQsRUFBVWtULEdBVXBELE9BUkF2akIsYUFBYSxFQUFHQyxHQUdtQixnQkFBL0JnTyxhQUFhakksTUFBTUMsZ0JBQ2RoRyxFQUFNSyxNQUlSaWpCLEVBQUt0akIsRUFDZCxDQVlBLFNBQVN1akIsc0JBQXNCdmpCLEVBQU9xakIsRUFBU2pULEVBQVVrVCxHQUV2RCxNQUFNbmpCLFFBQUVBLEVBQU9FLE1BQUVBLEdBQVVMLEVBR3JCNlEsRUFBYTdRLEVBQU02USxZQUFjLElBR3ZDVCxFQUFTb1QsT0FBTzNTLEdBQVk0UyxLQUFLLENBQUU1UyxhQUFZMVEsVUFBU0UsU0FDMUQsQ0FPZSxTQUFTcWpCLGdCQUFnQkMsR0FFdENBLEVBQUlDLElBQUlSLG9CQUdSTyxFQUFJQyxJQUFJTCxzQkFDVixDQzVDZSxTQUFTTSx1QkFBdUJGLEVBQUtHLEdBQ2xELElBRUUsR0FBSUgsR0FBT0csRUFBb0I1ZixPQUFRLENBQ3JDLE1BQU0vRCxFQUNKLHlFQUdJNGpCLEVBQWMsQ0FDbEJwZixPQUFRbWYsRUFBb0JuZixRQUFVLEVBQ3RDRCxZQUFhb2YsRUFBb0JwZixhQUFlLEdBQ2hERSxNQUFPa2YsRUFBb0JsZixPQUFTLEVBQ3BDQyxXQUFZaWYsRUFBb0JqZixhQUFjLEVBQzlDQyxRQUFTZ2YsRUFBb0JoZixTQUFXLEtBQ3hDQyxVQUFXK2UsRUFBb0IvZSxXQUFhLE1BSTFDZ2YsRUFBWWxmLFlBQ2Q4ZSxFQUFJemYsT0FBTyxlQUliLE1BQU04ZixFQUFVQyxVQUFVLENBRXhCQyxTQUErQixHQUFyQkgsRUFBWXBmLE9BQWMsSUFFcEN3ZixNQUFPSixFQUFZcmYsWUFFbkIwZixRQUFTTCxFQUFZbmYsTUFDckJ5ZixRQUFTLENBQUNoQixFQUFTalQsS0FDakJBLEVBQVNrVSxPQUFPLENBQ2RiLEtBQU0sS0FDSnJULEVBQVNvVCxPQUFPLEtBQUtlLEtBQUssQ0FBRXBrQixXQUFVLEVBRXhDcWtCLFFBQVMsS0FDUHBVLEVBQVNvVCxPQUFPLEtBQUtlLEtBQUtwa0IsRUFBUSxHQUVwQyxFQUVKc2tCLEtBQU9wQixHQUdxQixPQUF4QlUsRUFBWWpmLFNBQ2MsT0FBMUJpZixFQUFZaGYsV0FDWnNlLEVBQVFxQixNQUFNbnFCLE1BQVF3cEIsRUFBWWpmLFNBQ2xDdWUsRUFBUXFCLE1BQU1DLGVBQWlCWixFQUFZaGYsWUFFM0MzRixJQUFJLEVBQUcsMkNBQ0EsS0FPYnVrQixFQUFJQyxJQUFJSSxHQUVSNWtCLElBQ0UsRUFDQSw4Q0FBOEMya0IsRUFBWXJmLDRCQUE0QnFmLEVBQVlwZiw4Q0FBOENvZixFQUFZbGYsY0FFL0osQ0FDRixDQUFDLE1BQU83RSxHQUNQLE1BQU0sSUFBSTJRLFlBQ1IseUVBQ0EsS0FDQU0sU0FBU2pSLEVBQ1osQ0FDSCxDQ3pEQSxTQUFTNGtCLHNCQUFzQnZCLEVBQVNqVCxFQUFVa1QsR0FDaEQsSUFFRSxNQUFNdUIsRUFBY3hCLEVBQVF5QixRQUFRLGlCQUFtQixHQUd2RCxJQUNHRCxFQUFZNXBCLFNBQVMsc0JBQ3JCNHBCLEVBQVk1cEIsU0FBUyx1Q0FDckI0cEIsRUFBWTVwQixTQUFTLHVCQUV0QixNQUFNLElBQUkwVixZQUNSLGlIQUNBLEtBS0osT0FBTzJTLEdBQ1IsQ0FBQyxNQUFPdGpCLEdBQ1AsT0FBT3NqQixFQUFLdGpCLEVBQ2IsQ0FDSCxDQW1CQSxTQUFTK2tCLHNCQUFzQjFCLEVBQVNqVCxFQUFVa1QsR0FDaEQsSUFFRSxNQUFNeEwsRUFBT3VMLEVBQVF2TCxLQUdmK0csRUFBWW1CLEtBQUFBLEtBR2xCLElBQUtsSSxHQUFROWEsY0FBYzhhLEdBUXpCLE1BUEExWSxJQUNFLEVBQ0EseUJBQXlCeWYseUJBQ3ZCd0UsRUFBUXlCLFFBQVEsb0JBQXNCekIsRUFBUTJCLFdBQVdDLDJEQUl2RCxJQUFJdFUsWUFDUix5QkFBeUJrTyw4SkFDekIsS0FLSixNQUFNbGIsRUFBcUI4ZCx3QkFHckJsZixFQUFRc00sZ0JBRVppSixFQUFLdlYsT0FBU3VWLEVBQUt0VixTQUFXc1YsRUFBS3hWLFFBQVV3VixFQUFLK0ksTUFFbEQsRUFFQWxkLEdBSUYsR0FBYyxPQUFWcEIsSUFBbUJ1VixFQUFLclYsSUFRMUIsTUFQQXJELElBQ0UsRUFDQSx5QkFBeUJ5Zix5QkFDdkJ3RSxFQUFReUIsUUFBUSxvQkFBc0J6QixFQUFRMkIsV0FBV0MsMkZBQ21CaFcsS0FBS1EsVUFBVXFJLE9BR3pGLElBQUluSCxZQUNSLFlBQVlrTyxzUkFDWixLQUtKLEdBQUkvRyxFQUFLclYsS0FBT3RGLHVCQUF1QjJhLEVBQUtyVixLQUMxQyxNQUFNLElBQUlrTyxZQUNSLFlBQVlrTyxpTUFDWixLQTBDSixPQXJDQXdFLEVBQVE2QixpQkFBbUIsQ0FFekJyRyxZQUNBeGMsT0FBUSxDQUNORSxRQUNBRSxJQUFLcVYsRUFBS3JWLElBQ1ZySCxRQUNFMGMsRUFBSzFjLFNBQ0wsR0FBR2lvQixFQUFROEIsT0FBT0MsVUFBWSxXQUFXdE4sRUFBSzNjLE1BQVEsUUFDeERBLEtBQU0yYyxFQUFLM2MsS0FDWE4sT0FBUWlkLEVBQUtqZCxPQUNiZ0ksSUFBS2lWLEVBQUtqVixJQUNWQyxXQUFZZ1YsRUFBS2hWLFdBQ2pCQyxPQUFRK1UsRUFBSy9VLE9BQ2JDLE1BQU84VSxFQUFLOVUsTUFDWkMsTUFBTzZVLEVBQUs3VSxNQUNaTSxjQUFlc0wsZ0JBQ2JpSixFQUFLdlUsZUFDTCxFQUNBSSxHQUVGSCxhQUFjcUwsZ0JBQ1ppSixFQUFLdFUsY0FDTCxFQUNBRyxJQUdKRCxZQUFhLENBQ1hDLHFCQUNBckYsb0JBQW9CLEVBQ3BCRCxXQUFZeVosRUFBS3paLFdBQ2pCdUYsU0FBVWtVLEVBQUtsVSxTQUNmQyxVQUFXZ0wsZ0JBQWdCaUosRUFBS2pVLFdBQVcsRUFBTUYsS0FLOUMyZixHQUNSLENBQUMsTUFBT3RqQixHQUNQLE9BQU9zakIsRUFBS3RqQixFQUNiLENBQ0gsQ0FPZSxTQUFTcWxCLHFCQUFxQjFCLEdBRTNDQSxFQUFJMkIsS0FBSyxDQUFDLElBQUssY0FBZVYsdUJBRzlCakIsRUFBSTJCLEtBQUssQ0FBQyxJQUFLLGNBQWVQLHNCQUNoQyxDQzdLQSxNQUFNUSxhQUFlLENBQ25CQyxJQUFLLFlBQ0xDLEtBQU0sYUFDTkMsSUFBSyxZQUNMakosSUFBSyxrQkFDTGhhLElBQUssaUJBZ0JQbU4sZUFBZStWLGNBQWN0QyxFQUFTalQsRUFBVWtULEdBQzlDLElBRUUsTUFBTXNDLEVBQWlCcm9CLGNBR3ZCLElBQUlzb0IsR0FBb0IsRUFDeEJ4QyxFQUFReUMsT0FBT3hWLEdBQUcsU0FBVXlWLElBQ3RCQSxJQUNGRixHQUFvQixFQUNyQixJQUlILE1BQU05VixFQUFpQnNULEVBQVE2QixpQkFHekJyRyxFQUFZOU8sRUFBZThPLFVBR2pDemYsSUFBSSxFQUFHLHFCQUFxQnlmLDRDQUd0QitCLFlBQVk3USxHQUFnQixDQUFDL1AsRUFBTzZnQixLQUt4QyxHQUhBd0MsRUFBUXlDLE9BQU94RixtQkFBbUIsU0FHOUJ1RixFQUNGem1CLElBQ0UsRUFDQSxxQkFBcUJ5ZixtRkFIekIsQ0FTQSxHQUFJN2UsRUFDRixNQUFNQSxFQUlSLElBQUs2Z0IsSUFBU0EsRUFBS3pGLE9BU2pCLE1BUkFoYyxJQUNFLEVBQ0EscUJBQXFCeWYscUJBQ25Cd0UsRUFBUXlCLFFBQVEsb0JBQ2hCekIsRUFBUTJCLFdBQVdDLG1EQUNpQnBFLEVBQUt6RixXQUd2QyxJQUFJekssWUFDUixxQkFBcUJrTyx5R0FDckIsS0FLSixHQUFJZ0MsRUFBS3pGLE9BQVEsQ0FDZmhjLElBQ0UsRUFDQSxxQkFBcUJ5Zix5Q0FBaUQrRyxVQUl4RSxNQUFNenFCLEtBQUVBLEVBQUkwSCxJQUFFQSxFQUFHQyxXQUFFQSxFQUFVMUgsUUFBRUEsR0FBWXlsQixFQUFLcmUsUUFBUUgsT0FHeEQsT0FBSVEsRUFDS3VOLEVBQVNtVSxLQUFLbm9CLFVBQVV5a0IsRUFBS3pGLE9BQVFqZ0IsS0FJOUNpVixFQUFTNFYsT0FBTyxlQUFnQlQsYUFBYXBxQixJQUFTLGFBR2pEMkgsR0FDSHNOLEVBQVM2VixXQUFXN3FCLEdBSU4sUUFBVEQsRUFDSGlWLEVBQVNtVSxLQUFLMUQsRUFBS3pGLFFBQ25CaEwsRUFBU21VLEtBQUtqb0IsT0FBT0MsS0FBS3NrQixFQUFLekYsT0FBUSxXQUM1QyxDQWxEQSxDQWtEQSxHQUVKLENBQUMsTUFBT3BiLEdBQ1AsT0FBT3NqQixFQUFLdGpCLEVBQ2IsQ0FDSCxDQVNlLFNBQVNrbUIsYUFBYXZDLEdBS25DQSxFQUFJMkIsS0FBSyxJQUFLSyxlQU1kaEMsRUFBSTJCLEtBQUssYUFBY0ssY0FDekIsQ0NwSUEsTUFBTVEsZ0JBQWtCLElBQUl6cEIsS0FHdEIwcEIsWUFBY25YLEtBQUtwQixNQUN2QnBQLEdBQUFBLGFBQWF3QyxLQUFBQSxLQUFLNUgsWUFBVyxnQkFBaUIsU0FJMUNndEIsYUFBZSxHQUdmQyxlQUFpQixJQUdqQkMsV0FBYSxHQVVuQixTQUFTQywwQkFDUCxPQUFPSCxhQUFhNVgsUUFBTyxDQUFDZ1ksRUFBR0MsSUFBTUQsRUFBSUMsR0FBRyxHQUFLTCxhQUFhbnBCLE1BQ2hFLENBVUEsU0FBU3lwQixvQkFDUCxPQUFPQyxhQUFZLEtBQ2pCLE1BQU1DLEVBQVE1SCxlQUNSNkgsRUFDdUIsSUFBM0JELEVBQU1sSyxpQkFDRixFQUNDa0ssRUFBTWpLLGlCQUFtQmlLLEVBQU1sSyxpQkFBb0IsSUFFMUQwSixhQUFhL2xCLEtBQUt3bUIsR0FDZFQsYUFBYW5wQixPQUFTcXBCLFlBQ3hCRixhQUFhOXFCLE9BQ2QsR0FDQStxQixlQUNMLENBU2UsU0FBU1MsYUFBYXBELEdBR25DWCxTQUFTMkQscUJBS1RoRCxFQUFJeFQsSUFBSSxXQUFXLENBQUNrVCxFQUFTalQsRUFBVWtULEtBQ3JDLElBQ0Vsa0IsSUFBSSxFQUFHLHFDQUVQLE1BQU15bkIsRUFBUTVILGVBQ1IrSCxFQUFTWCxhQUFhbnBCLE9BQ3RCK3BCLEVBQWdCVCwwQkFHdEJwVyxFQUFTbVUsS0FBSyxDQUVaZixPQUFRLEtBQ1IwRCxTQUFVZixnQkFDVmdCLE9BQVEsR0FBR2xwQixLQUFLbXBCLE9BQU94cUIsaUJBQW1CdXBCLGdCQUFnQnRwQixXQUFhLElBQU8sY0FHOUV3cUIsY0FBZWpCLFlBQVl4a0IsUUFDM0IwbEIsa0JBQW1COVUsdUJBR25CK1Usa0JBQW1CVixFQUFNMUosaUJBQ3pCcUssaUJBQWtCWCxFQUFNbEssaUJBQ3hCOEssaUJBQWtCWixFQUFNakssaUJBQ3hCOEssY0FBZWIsRUFBTWhLLGVBQ3JCOEssWUFBY2QsRUFBTWpLLGlCQUFtQmlLLEVBQU1sSyxpQkFBb0IsSUFHakV4WCxLQUFNK1osa0JBR044SCxTQUNBQyxnQkFDQTltQixRQUNFK0gsTUFBTStlLEtBQW1CWixhQUFhbnBCLE9BQ2xDLG9FQUNBLFFBQVE4cEIsbUNBQXdDQyxFQUFjVyxRQUFRLE9BRzVFQyxXQUFZaEIsRUFBTS9KLGVBQ2xCZ0wsWUFBYWpCLEVBQU05SixtQkFDbkJnTCxtQkFBb0JsQixFQUFNN0osdUJBQzFCZ0wsb0JBQXFCbkIsRUFBTTVKLDRCQUU5QixDQUFDLE1BQU9qZCxHQUNQLE9BQU9zakIsRUFBS3RqQixFQUNiLElBRUwsQ0M5R2UsU0FBU2lvQixTQUFTdEUsR0FJL0JBLEVBQUl4VCxJQUFJbkMsYUFBYW5JLEdBQUdDLE9BQVMsS0FBSyxDQUFDdWQsRUFBU2pULEVBQVVrVCxLQUN4RCxJQUNFbGtCLElBQUksRUFBRyxxQ0FFUGdSLEVBQVM4WCxTQUFTam5CLEtBQUlBLEtBQUM1SCxZQUFXLFNBQVUsY0FBZSxDQUN6RDh1QixjQUFjLEdBRWpCLENBQUMsTUFBT25vQixHQUNQLE9BQU9zakIsRUFBS3RqQixFQUNiLElBRUwsQ0NmZSxTQUFTb29CLG9CQUFvQnpFLEdBSzFDQSxFQUFJMkIsS0FBSywrQkFBK0IxVixNQUFPeVQsRUFBU2pULEVBQVVrVCxLQUNoRSxJQUNFbGtCLElBQUksRUFBRywwQ0FHUCxNQUFNaXBCLEVBQWExYSxLQUFLL0UsdUJBR3hCLElBQUt5ZixJQUFlQSxFQUFXbnJCLE9BQzdCLE1BQU0sSUFBSXlULFlBQ1IsaUhBQ0EsS0FLSixNQUFNMlgsRUFBUWpGLEVBQVFsVCxJQUFJLFdBRzFCLElBQUttWSxHQUFTQSxJQUFVRCxFQUN0QixNQUFNLElBQUkxWCxZQUNSLDJFQUNBLEtBS0osSUFBSStCLEVBQWEyUSxFQUFROEIsT0FBT3pTLFdBQ2hDLElBQUlBLEVBbUJGLE1BQU0sSUFBSS9CLFlBQVkscUNBQXNDLEtBbEI1RCxVQUVROEIsd0JBQXdCQyxFQUMvQixDQUFDLE1BQU8xUyxHQUNQLE1BQU0sSUFBSTJRLFlBQ1IsNkJBQTZCM1EsRUFBTUcsVUFDbkMsS0FDQThRLFNBQVNqUixFQUNaLENBR0RvUSxFQUFTb1QsT0FBTyxLQUFLZSxLQUFLLENBQ3hCMVQsV0FBWSxJQUNaeVcsa0JBQW1COVUsdUJBQ25CclMsUUFBUywrQ0FBK0N1UyxNQU03RCxDQUFDLE1BQU8xUyxHQUNQLE9BQU9zakIsRUFBS3RqQixFQUNiLElBRUwsQ0MxQ0EsTUFBTXVvQixjQUFnQixJQUFJQyxJQUdwQjdFLElBQU04RSxVQXNCTDdZLGVBQWU4WSxZQUFZQyxHQUNoQyxJQUVFLE1BQU1ubUIsRUFBVTBMLGNBQWMsQ0FDNUJqSyxPQUFRMGtCLElBT1YsS0FIQUEsRUFBZ0JubUIsRUFBUXlCLFFBR0xDLFNBQVd5ZixJQUM1QixNQUFNLElBQUloVCxZQUNSLG1GQUNBLEtBTUosTUFBTWlZLEVBQStDLEtBQTVCRCxFQUFjdGtCLFlBQXFCLEtBR3REd2tCLEVBQVVDLE9BQU9DLGdCQUdqQkMsRUFBU0YsT0FBTyxDQUNwQkQsVUFDQUksT0FBUSxDQUNOQyxVQUFXTixLQTJDZixHQXRDQWpGLElBQUl3RixRQUFRLGdCQUdaeEYsSUFBSUMsSUFDRndGLEtBQUssQ0FDSEMsUUFBUyxDQUFDLE9BQVEsTUFBTyxjQU03QjFGLElBQUlDLEtBQUksQ0FBQ1AsRUFBU2pULEVBQVVrVCxLQUMxQmxULEVBQVNrWixJQUFJLGdCQUFpQixRQUM5QmhHLEdBQU0sSUFJUkssSUFBSUMsSUFDRjZFLFFBQVFoRixLQUFLLENBQ1hVLE1BQU95RSxLQUtYakYsSUFBSUMsSUFDRjZFLFFBQVFjLFdBQVcsQ0FDakJDLFVBQVUsRUFDVnJGLE1BQU95RSxLQUtYakYsSUFBSUMsSUFBSW9GLEVBQU9TLFFBR2Y5RixJQUFJQyxJQUFJNkUsUUFBUWlCLE9BQU96b0IsS0FBSUEsS0FBQzVILFlBQVcsYUFHbENzdkIsRUFBYzNqQixJQUFJQyxNQUFPLENBRTVCLE1BQU0wa0IsRUFBYWpaLEtBQUtrWixhQUFhakcsS0FHckNrRywyQkFBMkJGLEdBRzNCQSxFQUFXRyxPQUFPbkIsRUFBY3ZrQixLQUFNdWtCLEVBQWN4a0IsTUFBTSxLQUV4RG9rQixjQUFjZSxJQUFJWCxFQUFjdmtCLEtBQU11bEIsR0FFdEN2cUIsSUFDRSxFQUNBLG1DQUFtQ3VwQixFQUFjeGtCLFFBQVF3a0IsRUFBY3ZrQixRQUN4RSxHQUVKLENBR0QsR0FBSXVrQixFQUFjM2pCLElBQUlkLE9BQVEsQ0FFNUIsSUFBSTNKLEVBQUt3dkIsRUFFVCxJQUVFeHZCLEVBQU1rRSxHQUFZQSxhQUNoQndDLEtBQUlBLEtBQUM1RixnQkFBZ0JzdEIsRUFBYzNqQixJQUFJRSxVQUFXLGNBQ2xELFFBSUY2a0IsRUFBT3RyQixHQUFZQSxhQUNqQndDLEtBQUlBLEtBQUM1RixnQkFBZ0JzdEIsRUFBYzNqQixJQUFJRSxVQUFXLGNBQ2xELE9BRUgsQ0FBQyxNQUFPbEYsR0FDUFosSUFDRSxFQUNBLHFEQUFxRHVwQixFQUFjM2pCLElBQUlFLHNEQUUxRSxDQUVELEdBQUkzSyxHQUFPd3ZCLEVBQU0sQ0FFZixNQUFNQyxFQUFjdlosTUFBTW1aLGFBQWEsQ0FBRXJ2QixNQUFLd3ZCLFFBQVFwRyxLQUd0RGtHLDJCQUEyQkcsR0FHM0JBLEVBQVlGLE9BQU9uQixFQUFjM2pCLElBQUlaLEtBQU11a0IsRUFBY3hrQixNQUFNLEtBRTdEb2tCLGNBQWNlLElBQUlYLEVBQWMzakIsSUFBSVosS0FBTTRsQixHQUUxQzVxQixJQUNFLEVBQ0Esb0NBQW9DdXBCLEVBQWN4a0IsUUFBUXdrQixFQUFjM2pCLElBQUlaLFFBQzdFLEdBRUosQ0FDRixDQUdEeWYsdUJBQXVCRixJQUFLZ0YsRUFBY2xrQixjQUcxQzRnQixxQkFBcUIxQixLQUdyQnVDLGFBQWF2QyxLQUNib0QsYUFBYXBELEtBQ2JzRSxTQUFTdEUsS0FDVHlFLG9CQUFvQnpFLEtBR3BCRCxnQkFBZ0JDLElBQ2pCLENBQUMsTUFBTzNqQixHQUNQLE1BQU0sSUFBSTJRLFlBQ1IscURBQ0EsS0FDQU0sU0FBU2pSLEVBQ1osQ0FDSCxDQU9PLFNBQVNpcUIsZUFFZCxHQUFJMUIsY0FBY25PLEtBQU8sRUFBRyxDQUMxQmhiLElBQUksRUFBRyxpQ0FHUCxJQUFLLE1BQU9nRixFQUFNSCxLQUFXc2tCLGNBQzNCdGtCLEVBQU9nVCxPQUFNLEtBQ1hzUixjQUFjMkIsT0FBTzlsQixHQUNyQmhGLElBQUksRUFBRyxtQ0FBbUNnRixLQUFRLEdBR3ZELENBQ0gsQ0FTTyxTQUFTK2xCLGFBQ2QsT0FBTzVCLGFBQ1QsQ0FTTyxTQUFTNkIsYUFDZCxPQUFPM0IsT0FDVCxDQVNPLFNBQVM0QixTQUNkLE9BQU8xRyxHQUNULENBWU8sU0FBUzJHLG1CQUFtQnhHLEdBRWpDLE1BQU10aEIsRUFBVTBMLGNBQWMsQ0FDNUJqSyxPQUFRLENBQ05RLGFBQWNxZixLQUtsQkQsdUJBQXVCRixJQUFLbmhCLEVBQVF5QixPQUFPNmYsb0JBQzdDLENBVU8sU0FBU0YsSUFBSTVuQixLQUFTdXVCLEdBQzNCNUcsSUFBSUMsSUFBSTVuQixLQUFTdXVCLEVBQ25CLENBVU8sU0FBU3BhLElBQUluVSxLQUFTdXVCLEdBQzNCNUcsSUFBSXhULElBQUluVSxLQUFTdXVCLEVBQ25CLENBVU8sU0FBU2pGLEtBQUt0cEIsS0FBU3V1QixHQUM1QjVHLElBQUkyQixLQUFLdHBCLEtBQVN1dUIsRUFDcEIsQ0FTQSxTQUFTViwyQkFBMkI1bEIsR0FDbENBLEVBQU9xTSxHQUFHLGVBQWUsQ0FBQ3RRLEVBQU84bEIsS0FDL0IvbEIsYUFDRSxFQUNBQyxFQUNBLDBCQUEwQkEsRUFBTUcsK0JBRWxDMmxCLEVBQU90TSxTQUFTLElBR2xCdlYsRUFBT3FNLEdBQUcsU0FBVXRRLElBQ2xCRCxhQUFhLEVBQUdDLEVBQU8sMEJBQTBCQSxFQUFNRyxVQUFVLElBR25FOEQsRUFBT3FNLEdBQUcsY0FBZXdWLElBQ3ZCQSxFQUFPeFYsR0FBRyxTQUFVdFEsSUFDbEJELGFBQWEsRUFBR0MsRUFBTywwQkFBMEJBLEVBQU1HLFVBQVUsR0FDakUsR0FFTixDQUVBLElBQWU4RCxPQUFBLENBQ2J5a0Isd0JBQ0F1QiwwQkFDQUUsc0JBQ0FDLHNCQUNBQyxjQUNBQyxzQ0FDQTFHLFFBQ0F6VCxRQUNBbVYsV0N2VksxVixlQUFlNGEsZ0JBQWdCQyxFQUFXLFNBRXpDemEsUUFBUWtSLFdBQVcsQ0FFdkIrQixpQkFHQWdILGVBR0E1TCxhQUlGNWdCLFFBQVFpdEIsS0FBS0QsRUFDZixDQ1NPN2EsZUFBZSthLFdBQVdDLEdBRS9CLE1BQU1wb0IsRUFBVTBMLGNBQWMwYyxHQUc5QmxKLHNCQUFzQmxmLEVBQVFrQixZQUFZQyxvQkFHMUNwRCxZQUFZaUMsRUFBUTVELFNBR2hCNEQsRUFBUXVELE1BQU1FLHNCQUNoQjRrQixvQ0FJSXZaLG9CQUFvQjlPLEVBQVFiLFdBQVlhLEVBQVF5QixPQUFPTSxhQUd2RDZZLFNBQVM1YSxFQUFRMkMsS0FBTTNDLEVBQVFwQixVQUFVL0IsS0FDakQsQ0FTQSxTQUFTd3JCLDhCQUNQenJCLElBQUksRUFBRyxzREFHUDNCLFFBQVE2UyxHQUFHLFFBQVN3YSxJQUNsQjFyQixJQUFJLEVBQUcsc0NBQXNDMHJCLEtBQVEsSUFJdkRydEIsUUFBUTZTLEdBQUcsVUFBVVYsTUFBT04sRUFBTXdiLEtBQ2hDMXJCLElBQUksRUFBRyxpQkFBaUJrUSxzQkFBeUJ3YixZQUMzQ04saUJBQWlCLElBSXpCL3NCLFFBQVE2UyxHQUFHLFdBQVdWLE1BQU9OLEVBQU13YixLQUNqQzFyQixJQUFJLEVBQUcsaUJBQWlCa1Esc0JBQXlCd2IsWUFDM0NOLGlCQUFpQixJQUl6Qi9zQixRQUFRNlMsR0FBRyxVQUFVVixNQUFPTixFQUFNd2IsS0FDaEMxckIsSUFBSSxFQUFHLGlCQUFpQmtRLHNCQUF5QndiLFlBQzNDTixpQkFBaUIsSUFJekIvc0IsUUFBUTZTLEdBQUcscUJBQXFCVixNQUFPNVAsRUFBT3NQLEtBQzVDdlAsYUFBYSxFQUFHQyxFQUFPLGlCQUFpQnNQLGtCQUNsQ2tiLGdCQUFnQixFQUFFLEdBRTVCLENBRUEsSUFBZTViLE1BQUEsSUFFVjNLLE9BR0grSixzQkFDQUUsNEJBQ0FHLGdDQUdBc2Msc0JBQ0FoSywwQkFDQUcsd0JBQ0FGLHdCQUdBdkMsa0JBQ0FtTSxnQ0FHQXByQixRQUNBVywwQkFDQVksWUFBYSxTQUFVbkIsR0FTckJtQixZQVBnQnVOLGNBQWMsQ0FDNUJ0UCxRQUFTLENBQ1BZLFdBS2dCWixRQUFRWSxNQUM3QixFQUNEb0IscUJBQXNCLFNBQVUvQixHQVM5QitCLHFCQVBnQnNOLGNBQWMsQ0FDNUJ0UCxRQUFTLENBQ1BDLGVBS3lCRCxRQUFRQyxVQUN0QyxFQUNEZ0Msa0JBQW1CLFNBQVVKLEVBQU1DLEVBQU01QixHQUV2QyxNQUFNMEQsRUFBVTBMLGNBQWMsQ0FDNUJ0UCxRQUFTLENBQ1A2QixPQUNBQyxPQUNBNUIsWUFLSitCLGtCQUNFMkIsRUFBUTVELFFBQVE2QixLQUNoQitCLEVBQVE1RCxRQUFROEIsS0FDaEI4QixFQUFRNUQsUUFBUUUsT0FFbkIifQ== +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIG5vcm1hbGl6ZSwgcmVzb2x2ZSB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBkaXJlY3RvcnkgcGF0aFxyXG5leHBvcnQgY29uc3QgX19kaXJuYW1lID0gZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuLi8uJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFuZCBzdGFuZGFyZGl6ZXMgdGV4dCBieSByZXBsYWNpbmcgbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZVxyXG4gKiBjaGFyYWN0ZXJzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIHRyaW1taW5nIGFueSBsZWFkaW5nIG9yIHRyYWlsaW5nXHJcbiAqIHdoaXRlc3BhY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhclRleHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyB0aGUgJy9cXHNcXHMrL2cnIFJlZ0V4cC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtyZXBsYWNlcj0nICddIC0gVGhlIHN0cmluZyB1c2VkIHRvIHJlcGxhY2UgbXVsdGlwbGVcclxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgJyAnIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyVGV4dCh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpIHtcclxuICByZXR1cm4gdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBkZWVwQ29weVxyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8QXJyYXkpfSBvYmpBcnIgLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fEFycmF5KX0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDb3B5KG9iakFycikge1xyXG4gIC8vIElmIHRoZSBgb2JqQXJyYCBpcyBudWxsIG9yIG5vdCBvZiB0aGUgYG9iamVjdGAgdHlwZSwgcmV0dXJuIGl0XHJcbiAgaWYgKG9iakFyciA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqQXJyICE9PSAnb2JqZWN0Jykge1xyXG4gICAgcmV0dXJuIG9iakFycjtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZWl0aGVyIGEgbmV3IGFycmF5IG9yIGEgbmV3IG9iamVjdFxyXG4gIGNvbnN0IG9iakFyckNvcHkgPSBBcnJheS5pc0FycmF5KG9iakFycikgPyBbXSA6IHt9O1xyXG5cclxuICAvLyBSZWN1cnNpdmVseSBjb3B5IGVhY2ggcHJvcGVydHlcclxuICBmb3IgKGNvbnN0IGtleSBpbiBvYmpBcnIpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqQXJyLCBrZXkpKSB7XHJcbiAgICAgIG9iakFyckNvcHlba2V5XSA9IGRlZXBDb3B5KG9iakFycltrZXldKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY29waWVkIG9iamVjdFxyXG4gIHJldHVybiBvYmpBcnJDb3B5O1xyXG59XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZXhwQmFja29mZlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBmdW5jdGlvbiB0byBiZSByZXRyaWVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYDBgLlxyXG4gKiBAcGFyYW0gey4uLnVua25vd259IGFyZ3MgLSBBcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSByZXN1bHRcclxuICogb2YgdGhlIGZ1bmN0aW9uIGlmIHN1Y2Nlc3NmdWwuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCBpZiB0aGUgbWF4aW11bSBudW1iZXIgb2YgYXR0ZW1wdHNcclxuICogaXMgcmVhY2hlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleHBCYWNrb2ZmKGZuLCBhdHRlbXB0ID0gMCwgLi4uYXJncykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBUcnkgdG8gY2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIHJldHVybiBhd2FpdCBmbiguLi5hcmdzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gQ2FsY3VsYXRlIGRlbGF5IGluIG1zXHJcbiAgICBjb25zdCBkZWxheUluTXMgPSAyICoqIGF0dGVtcHQgKiAxMDAwO1xyXG5cclxuICAgIC8vIElmIHRoZSBhdHRlbXB0IGV4Y2VlZHMgdGhlIG1heGltdW0gYXR0ZW1wdHMgb2YgcmVwZWF0LCB0aHJvdyBhbiBlcnJvclxyXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXYWl0IGdpdmVuIGFtb3VudCBvZiB0aW1lXHJcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIGRlbGF5SW5NcykpO1xyXG5cclxuICAgIC8vLyBUTyBETzogQ29ycmVjdFxyXG4gICAgLy8gLy8gSW5mb3JtYXRpb24gYWJvdXQgdGhlIHJlc291cmNlIHRpbWVvdXRcclxuICAgIC8vIGxvZyhcclxuICAgIC8vICAgMyxcclxuICAgIC8vICAgYFt1dGlsc10gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIG9mIElEOiAke2FyZ3NbMF19LmBcclxuICAgIC8vICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRqdXN0cyB0aGUgY29uc3RydWN0b3IgbmFtZSBieSB0cmFuc2Zvcm1pbmcgYW5kIG5vcm1hbGl6aW5nIGl0IGJhc2VkXHJcbiAqIG9uIGNvbW1vbiBjaGFydCB0eXBlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeENvbnN0clxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY29uc3RyIC0gVGhlIG9yaWdpbmFsIGNvbnN0cnVjdG9yIG5hbWUgdG8gYmUgZml4ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IgbmFtZSwgb3IgJ2NoYXJ0JyBpZiB0aGUgaW5wdXRcclxuICogaXMgbm90IHJlY29nbml6ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4Q29uc3RyKGNvbnN0cikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBGaXggdGhlIGNvbnN0cnVjdG9yIGJ5IGxvd2VyaW5nIGNhc2luZ1xyXG4gICAgY29uc3QgZml4ZWRDb25zdHIgPSBgJHtjb25zdHIudG9Mb3dlckNhc2UoKS5yZXBsYWNlKCdjaGFydCcsICcnKX1DaGFydGA7XHJcblxyXG4gICAgLy8gSGFuZGxlIHRoZSBjYXNlIHdoZXJlIHRoZSByZXN1bHQgaXMganVzdCAnQ2hhcnQnXHJcbiAgICBpZiAoZml4ZWRDb25zdHIgPT09ICdDaGFydCcpIHtcclxuICAgICAgZml4ZWRDb25zdHIudG9Mb3dlckNhc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZXR1cm4gdGhlIGNvcnJlY3RlZCBjb25zdHJ1Y3Rvciwgb3RoZXJ3aXNlIGRlZmF1bHQgdG8gJ2NoYXJ0J1xyXG4gICAgcmV0dXJuIFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10uaW5jbHVkZXMoXHJcbiAgICAgIGZpeGVkQ29uc3RyXHJcbiAgICApXHJcbiAgICAgID8gZml4ZWRDb25zdHJcclxuICAgICAgOiAnY2hhcnQnO1xyXG4gIH0gY2F0Y2gge1xyXG4gICAgLy8gRGVmYXVsdCB0byAnY2hhcnQnIGluIGNhc2Ugb2YgYW55IGVycm9yXHJcbiAgICByZXR1cm4gJ2NoYXJ0JztcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGaXhlcyB0aGUgb3V0ZmlsZSBiYXNlZCBvbiBwcm92aWRlZCB0eXBlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZml4T3V0ZmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSBvcmlnaW5hbCBleHBvcnQgdHlwZS5cclxuICogQHBhcmFtIHtzdHJpbmd9IG91dGZpbGUgLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgb3V0ZmlsZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhPdXRmaWxlKHR5cGUsIG91dGZpbGUpIHtcclxuICAvLyBHZXQgdGhlIGZpbGUgbmFtZSBmcm9tIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgY29uc3QgZmlsZU5hbWUgPSBnZXRBYnNvbHV0ZVBhdGgob3V0ZmlsZSB8fCAnY2hhcnQnKVxyXG4gICAgLnNwbGl0KCcuJylcclxuICAgIC5zaGlmdCgpO1xyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IG91dGZpbGVcclxuICByZXR1cm4gYCR7ZmlsZU5hbWV9LiR7dHlwZX1gO1xyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIGV4cG9ydCB0eXBlIGJhc2VkIG9uIE1JTUUgdHlwZXMgYW5kIGZpbGUgZXh0ZW5zaW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeFR5cGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbb3V0ZmlsZT1udWxsXSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYG51bGxgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeFR5cGUodHlwZSwgb3V0ZmlsZSA9IG51bGwpIHtcclxuICAvLyBNSU1FIHR5cGVzXHJcbiAgY29uc3QgbWltZVR5cGVzID0ge1xyXG4gICAgJ2ltYWdlL3BuZyc6ICdwbmcnLFxyXG4gICAgJ2ltYWdlL2pwZWcnOiAnanBlZycsXHJcbiAgICAnYXBwbGljYXRpb24vcGRmJzogJ3BkZicsXHJcbiAgICAnaW1hZ2Uvc3ZnK3htbCc6ICdzdmcnXHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IGZvcm1hdHNcclxuICBjb25zdCBmb3JtYXRzID0gT2JqZWN0LnZhbHVlcyhtaW1lVHlwZXMpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICAvLyBTdXBwb3J0IHRoZSBKUEcgdHlwZVxyXG4gICAgaWYgKG91dFR5cGUgPT09ICdqcGcnKSB7XHJcbiAgICAgIHR5cGUgPSAnanBlZyc7XHJcbiAgICB9IGVsc2UgaWYgKGZvcm1hdHMuaW5jbHVkZXMob3V0VHlwZSkgJiYgdHlwZSAhPT0gb3V0VHlwZSkge1xyXG4gICAgICB0eXBlID0gb3V0VHlwZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGNvcnJlY3QgdHlwZVxyXG4gIHJldHVybiBtaW1lVHlwZXNbdHlwZV0gfHwgZm9ybWF0cy5maW5kKCh0KSA9PiB0ID09PSB0eXBlKSB8fCAncG5nJztcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gcGF0aCBpcyByZWxhdGl2ZSBvciBhYnNvbHV0ZSBhbmQgcmV0dXJucyB0aGUgY29ycmVjdGVkLFxyXG4gKiBhYnNvbHV0ZSBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBYnNvbHV0ZVBhdGhcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byBiZSBjaGVja2VkIG9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgYWJzb2x1dGUgcGF0aC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBYnNvbHV0ZVBhdGgocGF0aCkge1xyXG4gIHJldHVybiBpc0Fic29sdXRlKHBhdGgpID8gbm9ybWFsaXplKHBhdGgpIDogcmVzb2x2ZShwYXRoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGlucHV0IGRhdGEgdG8gYSBCYXNlNjQgc3RyaW5nIGJhc2VkIG9uIHRoZSBleHBvcnQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJhc2U2NFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgaW5wdXQgdG8gYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZvcm1hdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBCYXNlNjQgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRCYXNlNjQoaW5wdXQsIHR5cGUpIHtcclxuICAvLyBGb3IgcGRmIGFuZCBzdmcgdHlwZXMgdGhlIGlucHV0IG11c3QgYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZyb20gYSBidWZmZXJcclxuICBpZiAodHlwZSA9PT0gJ3BkZicgfHwgdHlwZSA9PSAnc3ZnJykge1xyXG4gICAgcmV0dXJuIEJ1ZmZlci5mcm9tKGlucHV0LCAndXRmOCcpLnRvU3RyaW5nKCdiYXNlNjQnKTtcclxuICB9XHJcblxyXG4gIC8vIEZvciBwbmcgYW5kIGpwZWcgaW5wdXQgaXMgYWxyZWFkeSBhIEJhc2U2NCBzdHJpbmdcclxuICByZXR1cm4gaW5wdXQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHN0cmluZ2lmaWVkIGRhdGUgd2l0aG91dCB0aGUgR01UIHRleHQgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3RGF0ZSgpIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIHJldHVybiBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBzdG9yZWQgdGltZSB2YWx1ZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlVGltZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGVUaW1lKCkge1xyXG4gIHJldHVybiBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gaXRlbSBpcyBhbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgaXRlbSBpcyBhbiBvYmplY3QsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdChpdGVtKSB7XHJcbiAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChpdGVtKSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIG9iamVjdCBpcyBlbXB0eS5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW0gLSBUaGUgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdEVtcHR5KGl0ZW0pIHtcclxuICByZXR1cm4gKFxyXG4gICAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgICAhQXJyYXkuaXNBcnJheShpdGVtKSAmJlxyXG4gICAgaXRlbSAhPT0gbnVsbCAmJlxyXG4gICAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kLCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChpdGVtKSB7XHJcbiAgY29uc3QgcmVnZXhQYXR0ZXJucyA9IFtcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT9sb2NhbGhvc3RcXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEwXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMjdcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE3MlxcLigxWzYtOV18MlswLTldfDNbMC0xXSlcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xOTJcXC4xNjhcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiL1xyXG4gIF07XHJcblxyXG4gIHJldHVybiByZWdleFBhdHRlcm5zLnNvbWUoKHBhdHRlcm4pID0+IHBhdHRlcm4udGVzdChpdGVtKSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVdGlsaXR5IHRvIG1lYXN1cmUgZWxhcHNlZCB0aW1lIHVzaW5nIHRoZSBOb2RlLmpzIGBwcm9jZXNzLmhydGltZSgpYCBtZXRob2QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtZWFzdXJlVGltZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IEEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBlbGFwc2VkIHRpbWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1lYXN1cmVUaW1lKCkge1xyXG4gIGNvbnN0IHN0YXJ0ID0gcHJvY2Vzcy5ocnRpbWUuYmlnaW50KCk7XHJcbiAgcmV0dXJuICgpID0+IE51bWJlcihwcm9jZXNzLmhydGltZS5iaWdpbnQoKSAtIHN0YXJ0KSAvIDEwMDAwMDA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSb3VuZHMgYSBudW1iZXIgdG8gdGhlIHNwZWNpZmllZCBwcmVjaXNpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByb3VuZE51bWJlclxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBwcmVjaXNpb24gLSBUaGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIHRvIHJvdW5kIHRvLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgcm91bmRlZCBudW1iZXIuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcm91bmROdW1iZXIodmFsdWUsIHByZWNpc2lvbiA9IDEpIHtcclxuICBjb25zdCBtdWx0aXBsaWVyID0gTWF0aC5wb3coMTAsIHByZWNpc2lvbiB8fCAwKTtcclxuICByZXR1cm4gTWF0aC5yb3VuZCgrdmFsdWUgKiBtdWx0aXBsaWVyKSAvIG11bHRpcGxpZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBhIHZhbHVlIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHRvQm9vbGVhblxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSBib29sZWFuIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dCB2YWx1ZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB0b0Jvb2xlYW4oaXRlbSkge1xyXG4gIHJldHVybiBbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTicsICcwJywgJyddLmluY2x1ZGVzKGl0ZW0pXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6ICEhaXRlbTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFdyYXBzIGN1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgaXQgc2FmZWx5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gd3JhcEFyb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tQ29kZSAtIFRoZSBjdXN0b20gY29kZSB0byBiZSB3cmFwcGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEZsYWcgdG8gYWxsb3cgbG9hZGluZyBjb2RlIGZyb20gYSBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtpc0NhbGxiYWNrPWZhbHNlXSAtIEZsYWcgdGhhdCBpbmRpY2F0ZXMgdGhlIHJldHVybmVkIGNvZGVcclxuICogbXVzdCBiZSBpbiBhIGNhbGxiYWNrIGZvcm1hdC5cclxuICpcclxuICogQHJldHVybnMgeyhzdHJpbmd8bnVsbCl9IFRoZSB3cmFwcGVkIGN1c3RvbSBjb2RlIG9yIG51bGwgaWYgd3JhcHBpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gd3JhcEFyb3VuZChjdXN0b21Db2RlLCBhbGxvd0ZpbGVSZXNvdXJjZXMsIGlzQ2FsbGJhY2sgPSBmYWxzZSkge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICAvLyBMb2FkIGEgZmlsZSBpZiB0aGUgZmlsZSByZXNvdXJjZXMgYXJlIGFsbG93ZWRcclxuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICAgID8gd3JhcEFyb3VuZChcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChjdXN0b21Db2RlKSwgJ3V0ZjgnKSxcclxuICAgICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgICBpc0NhbGxiYWNrXHJcbiAgICAgICAgICApXHJcbiAgICAgICAgOiBudWxsO1xyXG4gICAgfSBlbHNlIGlmIChcclxuICAgICAgIWlzQ2FsbGJhY2sgJiZcclxuICAgICAgKGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24oKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbiAoKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpID0+JykpXHJcbiAgICApIHtcclxuICAgICAgLy8gVHJlYXQgYSBmdW5jdGlvbiBhcyBhIHNlbGYtaW52b2tpbmcgZXhwcmVzc2lvblxyXG4gICAgICByZXR1cm4gYCgke2N1c3RvbUNvZGV9KSgpYDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBPciByZXR1cm4gYXMgYSBzdHJpbmdpZmllZCBjb2RlXHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGRlZXBDb3B5LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4Q29uc3RyLFxyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIGdldE5ld0RhdGUsXHJcbiAgZ2V0TmV3RGF0ZVRpbWUsXHJcbiAgaXNPYmplY3QsXHJcbiAgaXNPYmplY3RFbXB0eSxcclxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxyXG4gIG1lYXN1cmVUaW1lLFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHRvQm9vbGVhbixcclxuICB3cmFwQXJvdW5kXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSBmb3IgbWFuYWdpbmcgbG9nZ2luZyBmdW5jdGlvbmFsaXR5IHdpdGggY3VzdG9taXphYmxlXHJcbiAqIGxvZyBsZXZlbHMsIGNvbnNvbGUgYW5kIGZpbGUgbG9nZ2luZyBvcHRpb25zLCBhbmQgZXJyb3IgaGFuZGxpbmcgc3VwcG9ydC5cclxuICogVGhlIG1vZHVsZSBhbHNvIGVuc3VyZXMgdGhhdCBmaWxlLWJhc2VkIGxvZ3MgYXJlIHN0b3JlZCBpbiBhIHN0cnVjdHVyZWRcclxuICogZGlyZWN0b3J5LCBjcmVhdGluZyB0aGUgbmVjZXNzYXJ5IHBhdGhzIGF1dG9tYXRpY2FsbHkgaWYgdGhleSBkbyBub3QgZXhpc3QuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYXBwZW5kRmlsZSwgZXhpc3RzU3luYywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGgsIGdldE5ld0RhdGUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmNvbnN0IGxvZ2dpbmcgPSB7XHJcbiAgLy8gRmxhZ3MgZm9yIGxvZ2dpbmcgc3RhdHVzXHJcbiAgdG9Db25zb2xlOiB0cnVlLFxyXG4gIHRvRmlsZTogZmFsc2UsXHJcbiAgcGF0aENyZWF0ZWQ6IGZhbHNlLFxyXG4gIC8vIEZ1bGwgcGF0aCB0byB0aGUgbG9nIGZpbGVcclxuICBwYXRoVG9Mb2c6ICcnLFxyXG4gIC8vIExvZyBsZXZlbHNcclxuICBsZXZlbHNEZXNjOiBbXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnZXJyb3InLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzBdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3dhcm5pbmcnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzFdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ25vdGljZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMl1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAndmVyYm9zZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbM11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnYmVuY2htYXJrJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1s0XVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGEgbWVzc2FnZSB3aXRoIGEgc3BlY2lmaWVkIGxvZyBsZXZlbC4gQWNjZXB0cyBhIHZhcmlhYmxlIG51bWJlclxyXG4gKiBvZiBhcmd1bWVudHMuIFRoZSBhcmd1bWVudHMgYWZ0ZXIgdGhlIGBsZXZlbGAgYXJlIHBhc3NlZCB0byBgY29uc29sZS5sb2dgXHJcbiAqIGFuZC9vciB1c2VkIHRvIGNvbnN0cnVjdCBhbmQgYXBwZW5kIG1lc3NhZ2VzIHRvIGEgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dcclxuICpcclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQW4gYXJyYXkgb2YgYXJndW1lbnRzIHdoZXJlIHRoZSBmaXJzdCBpcyB0aGUgbG9nXHJcbiAqIGxldmVsIGFuZCB0aGUgcmVtYWluaW5nIGFyZSBzdHJpbmdzIHVzZWQgdG8gYnVpbGQgdGhlIGxvZyBtZXNzYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nKC4uLmFyZ3MpIHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbHNEZXNjLCBsZXZlbCB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlIG9yIGlzIGl0IGEgYmVuY2htYXJrIGxvZ1xyXG4gIGlmIChcclxuICAgIG5ld0xldmVsICE9PSA1ICYmXHJcbiAgICAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKVxyXG4gICkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdCh0ZXh0cylcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIGFsb25nIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ1dpdGhTdGFja1xyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbG9nIGxldmVsLlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QgY29udGFpbmluZyB0aGUgc3RhY2sgdHJhY2UuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21NZXNzYWdlIC0gQW4gb3B0aW9uYWwgY3VzdG9tIG1lc3NhZ2UgdG8gYmUgaW5jbHVkZWRcclxuICogaW4gdGhlIGxvZyBhbG9uZ3NpZGUgdGhlIGVycm9yLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nV2l0aFN0YWNrKG5ld0xldmVsLCBlcnJvciwgY3VzdG9tTWVzc2FnZSkge1xyXG4gIC8vIEdldCB0aGUgbWFpbiBtZXNzYWdlXHJcbiAgY29uc3QgbWFpbk1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlIHx8IChlcnJvciAmJiBlcnJvci5tZXNzYWdlKSB8fCAnJztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQWRkIHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXHJcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID0gZXJyb3IgJiYgZXJyb3Iuc3RhY2s7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2UsIGlmIGV4aXN0c1xyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlXTtcclxuICBpZiAoc3RhY2tNZXNzYWdlKSB7XHJcbiAgICB0ZXh0cy5wdXNoKCdcXG4nLCBzdGFja01lc3NhZ2UpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcclxuICAgICAgICB0ZXh0cy5zaGlmdCgpW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAuLi50ZXh0c1xyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpbml0TG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgbG9nZ2luZ2Agb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpbml0TG9nZ2luZyhsb2dnaW5nT3B0aW9ucykge1xyXG4gIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGBsb2dnaW5nT3B0aW9uc2Agb2JqZWN0XHJcbiAgY29uc3QgeyBsZXZlbCwgZGVzdCwgZmlsZSwgdG9Db25zb2xlLCB0b0ZpbGUgfSA9IGxvZ2dpbmdPcHRpb25zO1xyXG5cclxuICAvLyBSZXNldCBmbGFncyB0byB0aGUgZGVmYXVsdCB2YWx1ZXNcclxuICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgbG9nZ2luZy5wYXRoVG9Mb2cgPSAnJztcclxuXHJcbiAgLy8gU2V0IHRoZSBsb2dnaW5nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobGV2ZWwpO1xyXG5cclxuICAvLyBTZXQgdGhlIGNvbnNvbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nKHRvQ29uc29sZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgZmlsZSBsb2dnaW5nXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGxvZyBsZXZlbCB0byB0aGUgc3BlY2lmaWVkIHZhbHVlLiBMb2cgbGV2ZWxzIGFyZSAoYDBgID0gbm8gbG9nZ2luZyxcclxuICogYDFgID0gZXJyb3IsIGAyYCA9IHdhcm5pbmcsIGAzYCA9IG5vdGljZSwgYDRgID0gdmVyYm9zZSwgb3IgYDVgID0gYmVuY2htYXJrKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldExvZ0xldmVsXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsb2cgbGV2ZWwgdG8gYmUgc2V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldExvZ0xldmVsKGxldmVsKSB7XHJcbiAgaWYgKFxyXG4gICAgTnVtYmVyLmlzSW50ZWdlcihsZXZlbCkgJiZcclxuICAgIGxldmVsID49IDAgJiZcclxuICAgIGxldmVsIDw9IGxvZ2dpbmcubGV2ZWxzRGVzYy5sZW5ndGhcclxuICApIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgbGV2ZWxgIG9wdGlvblxyXG4gICAgbG9nZ2luZy5sZXZlbCA9IGxldmVsO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgY29uc29sZSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB0b0NvbnNvbGUgLSBUaGUgZmxhZyBmb3Igc2V0dGluZyB0aGUgbG9nZ2luZyB0byB0aGUgY29uc29sZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpIHtcclxuICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYHRvQ29uc29sZWAgb3B0aW9uXHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSAhIXRvQ29uc29sZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUgbmFtZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBkZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggd2hlcmUgdGhlIGxvZyBmaWxlIHNob3VsZFxyXG4gKiBiZSBzYXZlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGZpbGUgLSBUaGUgbmFtZSBvZiB0aGUgbG9nIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9GaWxlIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2dnaW5nIHNob3VsZFxyXG4gKiBiZSBkaXJlY3RlZCB0byBhIGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0ZpbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9GaWxlID0gISF0b0ZpbGU7XHJcblxyXG4gIC8vIFNldCB0aGUgYGRlc3RgIGFuZCBgZmlsZWAgb3B0aW9ucyBvbmx5IGlmIHRoZSBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZFxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ID0gZGVzdCB8fCAnJztcclxuICAgIGxvZ2dpbmcuZmlsZSA9IGZpbGUgfHwgJyc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyB0aGUgcHJvdmlkZWQgdGV4dHMgdG8gYSBmaWxlLCBpZiBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZC4gSXQgY3JlYXRlc1xyXG4gKiB0aGUgbmVjZXNzYXJ5IGRpcmVjdG9yeSBzdHJ1Y3R1cmUgaWYgbm90IGFscmVhZHkgY3JlYXRlZCBhbmQgYXBwZW5kc1xyXG4gKiB0aGUgY29udGVudCwgaW5jbHVkaW5nIGFuIG9wdGlvbmFsIHByZWZpeCwgdG8gdGhlIHNwZWNpZmllZCBsb2cgZmlsZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2dUb0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBBbiBvcHRpb25hbCBwcmVmaXggdG8gYmUgYWRkZWQgdG8gZWFjaCBsb2cgZW50cnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpIHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpICYmXHJcbiAgICAgIG1rZGlyU3luYyhnZXRBYnNvbHV0ZVBhdGgobG9nZ2luZy5kZXN0KSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBmdWxsIHBhdGhcclxuICAgIGxvZ2dpbmcucGF0aFRvTG9nID0gZ2V0QWJzb2x1dGVQYXRoKGpvaW4obG9nZ2luZy5kZXN0LCBsb2dnaW5nLmZpbGUpKTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yICYmIGxvZ2dpbmcudG9GaWxlICYmIGxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29uZmlndXJhdGlvbiBtYW5hZ2VtZW50IG1vZHVsZSBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci5cclxuICogUHJvdmlkZXMgZGVmYXVsdCBjb25maWd1cmF0aW9ucyB0aGF0IHN1cHBvcnQgZW52aXJvbm1lbnQgdmFyaWFibGVzLCBDTElcclxuICogYXJndW1lbnRzLCBhbmQgaW50ZXJhY3RpdmUgcHJvbXB0cyBmb3IgY3VzdG9taXphdGlvbiBvZiBvcHRpb25zIGFuZCBmZWF0dXJlcy5cclxuICogQWRkaXRpb25hbGx5LCBpdCBtYXBzIGxlZ2FjeSBvcHRpb25zIHRvIG1vZGVybiBzdHJ1Y3R1cmVzLCBnZW5lcmF0ZXMgbmVzdGVkXHJcbiAqIGFyZ3VtZW50IG1hcHBpbmdzLCBhbmQgZGlzcGxheXMgQ0xJIHVzYWdlIGluZm9ybWF0aW9uLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBhbGwgYXZhaWxhYmxlIG9wdGlvbnMsIG9yZ2FuaXplZFxyXG4gKiBieSBzZWN0aW9ucy5cclxuICpcclxuICogVGhpcyBvYmplY3QgaW5jbHVkZXM6XHJcbiAqIC0gRGVmYXVsdCB2YWx1ZXMgZm9yIGVhY2ggb3B0aW9uXHJcbiAqIC0gRGF0YSB0eXBlcyBmb3IgdmFsaWRhdGlvblxyXG4gKiAtIE5hbWVzIG9mIGNvcnJlc3BvbmRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIC0gRGVzY3JpcHRpb25zIG9mIGVhY2ggcHJvcGVydHlcclxuICogLSBJbmZvcm1hdGlvbiB1c2VkIGZvciBwcm9tcHRzIGluIGludGVyYWN0aXZlIGNvbmZpZ3VyYXRpb25cclxuICogLSBbT3B0aW9uYWxdIENvcnJlc3BvbmRpbmcgQ0xJIGFyZ3VtZW50IG5hbWVzIGZvciBDTEkgdXNhZ2VcclxuICogLSBbT3B0aW9uYWxdIExlZ2FjeSBuYW1lcyBmcm9tIHRoZSBwcmV2aW91cyBQaGFudG9tSlMtYmFzZWQgc2VydmVyXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IHtcclxuICAgIGFyZ3M6IHtcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnLS1hbGxvdy1ydW5uaW5nLWluc2VjdXJlLWNvbnRlbnQnLFxyXG4gICAgICAgICctLWFzaC1uby1udWRnZXMnLFxyXG4gICAgICAgICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICAgICAgICctLWJsb2NrLW5ldy13ZWItY29udGVudHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYWNjZWxlcmF0ZWQtMmQtY2FudmFzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtbmV0d29ya2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLXRpbWVyLXRocm90dGxpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZGluZy1vY2NsdWRlZC13aW5kb3dzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJyZWFrcGFkJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNoZWNrZXItaW1hZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jbGllbnQtc2lkZS1waGlzaGluZy1kZXRlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LWV4dGVuc2lvbnMtd2l0aC1iYWNrZ3JvdW5kLXBhZ2VzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICAgICAgICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAgICAgICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZmVhdHVyZXM9Q2FsY3VsYXRlTmF0aXZlV2luT2NjbHVzaW9uLEludGVyZXN0RmVlZENvbnRlbnRTdWdnZXN0aW9ucyxXZWJPVFAnLFxyXG4gICAgICAgICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWlwYy1mbG9vZGluZy1wcm90ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWxvZ2dpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtbm90aWZpY2F0aW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1vZmZlci1zdG9yZS11bm1hc2tlZC13YWxsZXQtY2FyZHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtcG9wdXAtYmxvY2tpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJpbnQtcHJldmlldycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcm9tcHQtb24tcmVwb3N0JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXJlbmRlcmVyLWJhY2tncm91bmRpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2VhcmNoLWVuZ2luZS1jaG9pY2Utc2NyZWVuJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlc3Npb24tY3Jhc2hlZC1idWJibGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2l0ZS1pc29sYXRpb24tdHJpYWxzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICAgICAgICctLWRpc2FibGUtc3luYycsXHJcbiAgICAgICAgJy0tZW5hYmxlLXVuc2FmZS13ZWJncHUnLFxyXG4gICAgICAgICctLWhpZGUtY3Jhc2gtcmVzdG9yZS1idWJibGUnLFxyXG4gICAgICAgICctLWhpZGUtc2Nyb2xsYmFycycsXHJcbiAgICAgICAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgICAgICAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgICAgICAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAgICAgICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICAgICAgICctLW5vLXBpbmdzJyxcclxuICAgICAgICAnLS1uby1zYW5kYm94JyxcclxuICAgICAgICAnLS1uby1zdGFydHVwLXdpbmRvdycsXHJcbiAgICAgICAgJy0tbm8tenlnb3RlJyxcclxuICAgICAgICAnLS1wYXNzd29yZC1zdG9yZT1iYXNpYycsXHJcbiAgICAgICAgJy0tcHJvY2Vzcy1wZXItdGFiJyxcclxuICAgICAgICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuICAgICAgXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ1BVUFBFVEVFUl9BUkdTJyxcclxuICAgICAgY2xpTmFtZTogJ3B1cHBldGVlckFyZ3MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FycmF5IG9mIFB1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfVkVSU0lPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2RuVXJsOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NETl9VUkwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0NETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGbGFnIHRvIHJlZmV0Y2ggc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdG9yeSBwYXRoIGZvciBjYWNoZWQgSGlnaGNoYXJ0cyBzY3JpcHRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DT1JFX1NDUklQVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtb2R1bGVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ3N0b2NrJyxcclxuICAgICAgICAnbWFwJyxcclxuICAgICAgICAnZ2FudHQnLFxyXG4gICAgICAgICdleHBvcnRpbmcnLFxyXG4gICAgICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAgICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgICAgIC8vICdhbm5vdGF0aW9ucy1hZHZhbmNlZCcsXHJcbiAgICAgICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAgICAgJ2Jvb3N0JyxcclxuICAgICAgICAnZGF0YScsXHJcbiAgICAgICAgJ2RhdGEtdG9vbHMnLFxyXG4gICAgICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICAgICAnc3RhdGljLXNjYWxlJyxcclxuICAgICAgICAnYnJva2VuLWF4aXMnLFxyXG4gICAgICAgICdoZWF0bWFwJyxcclxuICAgICAgICAndGlsZW1hcCcsXHJcbiAgICAgICAgJ3RpbGVkd2VibWFwJyxcclxuICAgICAgICAndGltZWxpbmUnLFxyXG4gICAgICAgICd0cmVlbWFwJyxcclxuICAgICAgICAndHJlZWdyYXBoJyxcclxuICAgICAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgICAgICdkcmlsbGRvd24nLFxyXG4gICAgICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICAgICAnYnVsbGV0JyxcclxuICAgICAgICAnZnVubmVsJyxcclxuICAgICAgICAnZnVubmVsM2QnLFxyXG4gICAgICAgICdnZW9oZWF0bWFwJyxcclxuICAgICAgICAncHlyYW1pZDNkJyxcclxuICAgICAgICAnbmV0d29ya2dyYXBoJyxcclxuICAgICAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAgICAgJ3BhcmV0bycsXHJcbiAgICAgICAgJ3BhdHRlcm4tZmlsbCcsXHJcbiAgICAgICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAgICAgJ3ByaWNlLWluZGljYXRvcicsXHJcbiAgICAgICAgJ3NhbmtleScsXHJcbiAgICAgICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICAgICAnZGVwZW5kZW5jeS13aGVlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1sYWJlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1vbi1wb2ludCcsXHJcbiAgICAgICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICAgICAnc29uaWZpY2F0aW9uJyxcclxuICAgICAgICAvLyAnc3RvY2stdG9vbHMnLFxyXG4gICAgICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAgICAgJ3N1bmJ1cnN0JyxcclxuICAgICAgICAndmFyaWFibGUtcGllJyxcclxuICAgICAgICAndmFyaXdpZGUnLFxyXG4gICAgICAgICd2ZWN0b3InLFxyXG4gICAgICAgICd2ZW5uJyxcclxuICAgICAgICAnd2luZGJhcmInLFxyXG4gICAgICAgICd3b3JkY2xvdWQnLFxyXG4gICAgICAgICd4cmFuZ2UnLFxyXG4gICAgICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgICAgICdkcmFnLXBhbmVzJyxcclxuICAgICAgICAnZGVidWdnZXInLFxyXG4gICAgICAgICdkdW1iYmVsbCcsXHJcbiAgICAgICAgJ2xvbGxpcG9wJyxcclxuICAgICAgICAnY3lsaW5kZXInLFxyXG4gICAgICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgICAgICdkb3RwbG90JyxcclxuICAgICAgICAnbWFya2VyLWNsdXN0ZXJzJyxcclxuICAgICAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgICAgICdoZWlraW5hc2hpJyxcclxuICAgICAgICAnZmxvd21hcCcsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAnbmF2aWdhdG9yJyxcclxuICAgICAgICAndGV4dHBhdGgnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIG1vZHVsZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbmRpY2F0b3JTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2luZGljYXRvcnMtYWxsJ10sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGluZGljYXRvciBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjMwLjEvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuNDUvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBZGRpdGlvbmFsIGN1c3RvbSBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgICAgc2VwYXJhdG9yOiAnOydcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5GSUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0IGZpbGVuYW1lIHdpdGggdHlwZSwgZm9ybWF0dGVkIGNvcnJlY3RseSBhcyBKU09OIG9yIFNWRycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9JTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdmVycmlkZXMgdGhlIGBpbmZpbGVgIHdpdGggSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgU1ZHIGlucHV0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgb3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWxpYXMgZm9yIHRoZSBgaW5zdHJgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHN2Zzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TVkcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1NWRyBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IHRvIHJlbmRlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJhdGNoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0JBVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0JhdGNoIGpvYiBzdHJpbmcgd2l0aCBpbnB1dC9vdXRwdXQgcGFpcnM6IFwiaW49b3V0O2luPW91dDsuLi5cIicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG91dGZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1VURklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdXRwdXQgZmlsZW5hbWUgd2l0aCB0eXBlLiBDYW4gYmUganBlZywgcG5nLCBwZGYsIG9yIHN2ZyBhbmQgaWdub3JlcyBgdHlwZWAgb3B0aW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdHlwZToge1xyXG4gICAgICB2YWx1ZTogJ3BuZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1RZUEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0ZpbGUgZXhwb3J0IGZvcm1hdC4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3NlbGVjdCcsXHJcbiAgICAgICAgaGludDogJ0RlZmF1bHQ6IHBuZycsXHJcbiAgICAgICAgY2hvaWNlczogWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvbnN0cjoge1xyXG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfQ09OU1RSJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NoYXJ0IGNvbnN0cnVjdG9yLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBjaGFydCcsXHJcbiAgICAgICAgY2hvaWNlczogWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYjY0OiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0I2NCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byB0aGUgY2hhcnQgc2hvdWxkIGJlIHJlY2VpdmVkIGluIEJhc2U2NCBmb3JtYXQgaW5zdGVhZCBvZiBiaW5hcnknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vRG93bmxvYWQ6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfTk9fRE9XTkxPQUQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gaW5jbHVkZSBvciBleGNsdWRlIGF0dGFjaG1lbnQgaGVhZGVycyBpbiB0aGUgcmVzcG9uc2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgd2lkdGg6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfV0lEVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1dpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGVzIGNoYXJ0IHNldHRpbmdzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzY2FsZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TQ0FMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdTY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncy4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdEhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX0hFSUdIVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRXaWR0aDoge1xyXG4gICAgICB2YWx1ZTogNjAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWZhdWx0IHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0U2NhbGU6IHtcclxuICAgICAgdmFsdWU6IDEsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGVmYXVsdCBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldC4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBtaW46IDAuMSxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGdsb2JhbE9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0dMT0JBTF9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCBnbG9iYWwgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdGhlbWVPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9USEVNRV9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCB0aGVtZSBvcHRpb25zIGZvciBIaWdoY2hhcnRzLnNldE9wdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXN0ZXJpemF0aW9uVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMTUwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaWxsaXNlY29uZHMgdG8gd2FpdCBmb3Igd2VicGFnZSByZW5kZXJpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgY3VzdG9tTG9naWM6IHtcclxuICAgIGFsbG93Q29kZUV4ZWN1dGlvbjoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbGxvd3Mgb3IgZGlzYWxsb3dzIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBkdXJpbmcgZXhwb3J0aW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBpbmplY3Rpb24gb2YgZmlsZXN5c3RlbSByZXNvdXJjZXMgKGRpc2FibGVkIGluIHNlcnZlciBtb2RlKScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tQ29kZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DVVNUT01fQ09ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDdXN0b20gY29kZSB0byBleGVjdXRlIGJlZm9yZSBjaGFydCBpbml0aWFsaXphdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24sIGNvZGUgd3JhcHBlZCBpbiBhIGZ1bmN0aW9uLCBvciBhIC5qcyBmaWxlbmFtZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhbGxiYWNrOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0NBTExCQUNLJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0phdmFTY3JpcHQgY29kZSB0byBydW4gZHVyaW5nIGNvbnN0cnVjdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24gb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX1JFU09VUkNFUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBZGRpdGlvbmFsIHJlc291cmNlcyBhcyBKU09OLCBzdHJpbmdpZmllZCBKU09OLCBvciBmaWxlbmFtZSwgY29udGFpbmluZyBmaWxlcywganMsIGFuZCBjc3Mgc2VjdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsb2FkQ29uZmlnOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHJyxcclxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIHdpdGggYSBwcmUtZGVmaW5lZCBjb25maWd1cmF0aW9uIHRvIHVzZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZUNvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1Byb21wdC1iYXNlZCBvcHRpb24gc2V0dGluZywgc2F2ZWQgdG8gYSBwcm92aWRlZCBjb25maWcgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU3RhcnRzIHRoZSBzZXJ2ZXIgd2hlbiB0cnVlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIHZhbHVlOiAnMC4wLjAuMCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hvc3RuYW1lIG9mIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA3ODAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG51bWJlciBmb3IgdGhlIHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdXBsb2FkTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDMsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX1VQTE9BRF9MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSByZXF1ZXN0IGJvZHkgc2l6ZSBpbiBNQicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGlzcGxheXMgb3Igbm90IGFjdGlvbiBkdXJhdGlvbnMgaW4gbWlsbGlzZWNvbmRzIGR1cmluZyBzZXJ2ZXIgcmVxdWVzdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHByb3h5OiB7XHJcbiAgICAgIGhvc3Q6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfSE9TVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5SG9zdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdIb3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcG9ydDoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgb2YgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRpbWVvdXQ6IHtcclxuICAgICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX1RJTUVPVVQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVRpbWVvdXQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJhdGVMaW1pdGluZzoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdlbmFibGVSYXRlTGltaXRpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzZXJ2ZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBtYXhSZXF1ZXN0czoge1xyXG4gICAgICAgIHZhbHVlOiAxMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAncmF0ZUxpbWl0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgcGVyIG1pbnV0ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIHZhbHVlOiAxLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1cnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGltZSB3aW5kb3cgaW4gbWludXRlcyBmb3IgcmF0ZSBsaW1pdGluZycsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGRlbGF5OiB7XHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdEZWxheSBkdXJhdGlvbiBiZXR3ZWVuIHN1Y2Nlc3NpdmUgcmVxdWVzdHMgYmVmb3JlIHJlYWNoaW5nIHRoZSBsaW1pdCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRydXN0UHJveHk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTZXQgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBza2lwS2V5OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnS2V5IHRvIGJ5cGFzcyB0aGUgcmF0ZSBsaW1pdGVyLCB1c2VkIHdpdGggYHNraXBUb2tlbmAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTicsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUb2tlbiB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwS2V5YCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgU1NMIHByb3RvY29sJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgZm9yY2U6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0ZPUkNFJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsRm9yY2UnLFxyXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdzc2xPbmx5JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0ZvcmNlcyB0aGUgc2VydmVyIHRvIHVzZSBIVFRQUyBvbmx5IHdoZW4gdHJ1ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogNDQzLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX1BPUlQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgZm9yIHRoZSBTU0wgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgY2VydFBhdGg6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0NFUlRfUEFUSCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbENlcnRQYXRoJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsUGF0aCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHRoZSBTU0wgY2VydGlmaWNhdGUva2V5IGZpbGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX01JTl9XT1JLRVJTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaW5pbXVtIGFuZCBpbml0aWFsIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG1heFdvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDgsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NQVhfV09SS0VSUycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICd3b3JrZXJzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdvcmtMaW1pdDoge1xyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9XT1JLX0xJTUlUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdOdW1iZXIgb2YgdGFza3MgYSB3b3JrZXIgY2FuIGhhbmRsZSBiZWZvcmUgcmVzdGFydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWNxdWlyZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBhY3F1aXJpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3JlYXRlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGlkbGVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiAzMDAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0lETEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGRlc3Ryb3lpbmcgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWw6IHtcclxuICAgICAgdmFsdWU6IDIwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYmVmb3JlIHJldHJ5aW5nIHJlc291cmNlIGNyZWF0aW9uIG9uIGZhaWx1cmUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0ludGVydmFsIGluIG1pbGxpc2Vjb25kcyB0byBjaGVjayBhbmQgZGVzdHJveSBpZGxlIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAncG9vbEJlbmNobWFya2luZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU2hvd3Mgc3RhdGlzdGljcyBmb3IgdGhlIHBvb2wgb2YgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfTEVWRUwnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0xvZ2dpbmcgdmVyYm9zaXR5IGxldmVsJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIHJvdW5kOiAwLFxyXG4gICAgICAgIG1pbjogMCxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgdmFsdWU6ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdMb2cgZmlsZSBuYW1lLiBSZXF1aXJlcyBgbG9nVG9GaWxlYCBhbmQgYGxvZ0Rlc3RgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Q6IHtcclxuICAgICAgdmFsdWU6ICdsb2cnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfREVTVCcsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgdG8gYmUgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9Db25zb2xlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0NPTlNPTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9Db25zb2xlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGNvbnNvbGUgbG9nZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9GaWxlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGxvZ2dpbmcgdG8gYSBmaWxlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcm91dGU6IHtcclxuICAgICAgdmFsdWU6ICcvJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9ST1VURScsXHJcbiAgICAgIGNsaU5hbWU6ICd1aVJvdXRlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgZW5kcG9pbnQgcm91dGUgZm9yIHRoZSBVSScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgb3RoZXI6IHtcclxuICAgIG5vZGVFbnY6IHtcclxuICAgICAgdmFsdWU6ICdwcm9kdWN0aW9uJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT0RFX0VOVicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIE5vZGUuanMgZW52aXJvbm1lbnQgdHlwZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvUHJvY2Vzc0V4aXRzOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gYXR0YWNoIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbm9Mb2dvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9fTE9HTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGlzcGxheSBvciBza2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIHN0YXJ0dXAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhhcmRSZXNldFBhZ2U6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9IQVJEX1JFU0VUX1BBR0UnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHJlc2V0IHRoZSBwYWdlIGNvbnRlbnQgZW50aXJlbHknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJyb3dzZXJTaGVsbE1vZGU6IHtcclxuICAgICAgdmFsdWU6IHRydWUsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX0JST1dTRVJfU0hFTExfTU9ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBzaGVsbCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGRlYnVnOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlRGVidWcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIHVuZGVybHlpbmcgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaGVhZGxlc3M6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19IRUFETEVTUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byBzZXQgdGhlIGJyb3dzZXIgdG8gcnVuIGluIGhlYWRsZXNzIG1vZGUgZHVyaW5nIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGV2dG9vbHM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19ERVZUT09MUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBEZXZUb29scyBpbiBoZWFkZnVsIG1vZGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0xJU1RFTl9UT19DT05TT0xFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VuYWJsZXMgb3IgZGlzYWJsZXMgbGlzdGVuaW5nIHRvIGNvbnNvbGUgbWVzc2FnZXMgZnJvbSB0aGUgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZHVtcGlvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRFVNUElPJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1JlZGlyZWN0cyBvciBub3QgYnJvd3NlciBzdGRvdXQgYW5kIHN0ZGVyciB0byBwcm9jZXNzLnN0ZG91dCBhbmQgcHJvY2Vzcy5zdGRlcnInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNsb3dNbzoge1xyXG4gICAgICB2YWx1ZTogMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19TTE9XX01PJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWxheXMgUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBtaWxsaXNlY29uZHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlYnVnZ2luZ1BvcnQ6IHtcclxuICAgICAgdmFsdWU6IDkyMjIsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVCVUdHSU5HX1BPUlQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgdXNlZCBmb3IgZGVidWdnaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vLyBQcm9wZXJ0aWVzIG5lc3RpbmcgbGV2ZWwgb2YgYWxsIG9wdGlvbnNcclxuZXhwb3J0IGNvbnN0IG5lc3RlZFByb3BzID0gX2NyZWF0ZU5lc3RlZFByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLy8gUHJvcGVydGllcyBuYW1lcyB0aGF0IHNob3VsZCBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkXHJcbmV4cG9ydCBjb25zdCBhYnNvbHV0ZVByb3BzID0gX2NyZWF0ZUFic29sdXRlUHJvcHMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgZ2VuZXJhdGVzIGEgbWFwcGluZyBvZiBuZXN0ZWQgYXJndW1lbnQgY2hhaW5zIGZyb20gYSBuZXN0ZWRcclxuICogY29uZmlnIG9iamVjdC4gVGhpcyBmdW5jdGlvbiB0cmF2ZXJzZXMgYSBuZXN0ZWQgb2JqZWN0IGFuZCBjcmVhdGVzIGEgbWFwcGluZ1xyXG4gKiB3aGVyZSBlYWNoIGtleSBpcyBhbiBhcmd1bWVudCBuYW1lIChlaXRoZXIgZnJvbSBgY2xpTmFtZWAsIGBsZWdhY3lOYW1lYCxcclxuICogb3IgdGhlIG9yaWdpbmFsIGtleSkgYW5kIGVhY2ggdmFsdWUgaXMgYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBjaGFpblxyXG4gKiBvZiBuZXN0ZWQgcHJvcGVydGllcyBsZWFkaW5nIHRvIHRoYXQgYXJndW1lbnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtuZXN0ZWRQcm9wcz17fV0gLSBUaGUgYWNjdW11bGF0b3Igb2JqZWN0IGZvciBzdG9yaW5nXHJcbiAqIHRoZSByZXN1bHRpbmcgYXJndW1lbnRzIGNoYWlucy4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtwcm9wQ2hhaW49JyddIC0gVGhlIGN1cnJlbnQgY2hhaW4gb2YgbmVzdGVkIHByb3BlcnRpZXMsXHJcbiAqIHVzZWQgaW50ZXJuYWxseSBkdXJpbmcgcmVjdXJzaW9uLiAgVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgc3RyaW5nLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgbWFwcGluZyBhcmd1bWVudCBuYW1lcyB0byB0aGVpciBjb3JyZXNwb25kaW5nXHJcbiAqIG5lc3RlZCBwcm9wZXJ0eSBjaGFpbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHMoY29uZmlnLCBuZXN0ZWRQcm9wcyA9IHt9LCBwcm9wQ2hhaW4gPSAnJykge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHMgb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBfY3JlYXRlTmVzdGVkUHJvcHMoZW50cnksIG5lc3RlZFByb3BzLCBgJHtwcm9wQ2hhaW59LiR7a2V5fWApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIHRoZSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzXHJcbiAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmNsaU5hbWUgfHwga2V5XSA9IGAke3Byb3BDaGFpbn0uJHtrZXl9YC5zdWJzdHJpbmcoMSk7XHJcblxyXG4gICAgICAvLyBTdXBwb3J0IGZvciB0aGUgbGVnYWN5LCBQaGFudG9tSlMgcHJvcGVydGllcyBuYW1lc1xyXG4gICAgICBpZiAoZW50cnkubGVnYWN5TmFtZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgbmVzdGVkUHJvcHNbZW50cnkubGVnYWN5TmFtZV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb2JqZWN0IHdpdGggbmVzdGVkIGFyZ3VtZW50IGNoYWluc1xyXG4gIHJldHVybiBuZXN0ZWRQcm9wcztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdhdGhlcnMgdGhlIG5hbWVzIG9mIHByb3BlcnRpZXMgZnJvbSBhIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRoYXRcclxuICogY2FuIGJlIHRyZWF0ZWQgYXMgYWJzb2x1dGUgcHJvcGVydGllcy4gVGhlc2UgcHJvcGVydGllcyBoYXZlIHZhbHVlcyB0aGF0XHJcbiAqIGFyZSBvYmplY3RzIGFuZCBkbyBub3QgY29udGFpbiBmdXJ0aGVyIG5lc3RlZCBkZXB0aCB3aGVuIG1lcmdpbmcgYW4gb2JqZWN0XHJcbiAqIGNvbnRhaW5pbmcgdGhlc2Ugb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVBYnNvbHV0ZVByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IFthYnNvbHV0ZVByb3BzPVtdXSAtIEFuIGFycmF5IHRvIGNvbGxlY3QgdGhlIG5hbWVzXHJcbiAqIG9mIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPHN0cmluZz59IEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG5hbWVzIG9mIGFic29sdXRlXHJcbiAqIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wcyhjb25maWcsIGFic29sdXRlUHJvcHMgPSBbXSkge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS50eXBlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHNcclxuICAgICAgX2NyZWF0ZUFic29sdXRlUHJvcHMoZW50cnksIGFic29sdXRlUHJvcHMpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSWYgdGhlIG9wdGlvbiBjYW4gYmUgYW4gb2JqZWN0LCBzYXZlIGl0cyB0eXBlIGluIHRoZSBhcnJheVxyXG4gICAgICBpZiAoZW50cnkudHlwZXMuaW5jbHVkZXMoJ09iamVjdCcpKSB7XHJcbiAgICAgICAgYWJzb2x1dGVQcm9wcy5wdXNoKGtleSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBhcnJheSB3aXRoIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzXHJcbiAgcmV0dXJuIGFic29sdXRlUHJvcHM7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBkZWZhdWx0Q29uZmlnLFxyXG4gIG5lc3RlZFByb3BzLFxyXG4gIGFic29sdXRlUHJvcHNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgZmlsZSBpcyByZXNwb25zaWJsZSBmb3IgcGFyc2luZyB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIHdpdGggdGhlICd6b2QnIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkXHJcbiAqIHRvIGJlIHVzZWQgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIGBlbnZzYC4gV2Ugc2hvdWxkIG5vdCB1c2UgdGhlIGBwcm9jZXNzLmVudmBcclxuICogZGlyZWN0bHkgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuXHJcbiAqIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lclxyXG4gKiBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICovXHJcblxyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5cclxuaW1wb3J0IHsgZGVmYXVsdENvbmZpZyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuLy8gTG9hZCAuZW52IGludG8gZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbi8vIE9iamVjdCB3aXRoIGN1c3RvbSB2YWxpZGF0b3JzIGFuZCB0cmFuc2Zvcm1lcnMsIHRvIGF2b2lkIHJlcGV0aXRpb25cclxuLy8gaW4gdGhlIENvbmZpZyBvYmplY3RcclxuY29uc3QgdiA9IHtcclxuICAvLyBTcGxpdHMgc3RyaW5nIHZhbHVlIGludG8gZWxlbWVudHMgaW4gYW4gYXJyYXksIHRyaW1zIGV2ZXJ5IGVsZW1lbnQsIGNoZWNrc1xyXG4gIC8vIGlmIGFuIGFycmF5IGlzIGNvcnJlY3QsIGlmIGl0IGlzIGVtcHR5LCBhbmQgaWYgaXQgaXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgYXJyYXk6IChmaWx0ZXJBcnJheSkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlXHJcbiAgICAgICAgICAuc3BsaXQoJywnKVxyXG4gICAgICAgICAgLm1hcCgodmFsdWUpID0+IHZhbHVlLnRyaW0oKSlcclxuICAgICAgICAgIC5maWx0ZXIoKHZhbHVlKSA9PiBmaWx0ZXJBcnJheS5pbmNsdWRlcyh2YWx1ZSkpXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZS5sZW5ndGggPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3Mgb25seSB0cnVlLCBmYWxzZSBhbmQgY29ycmVjdGx5IHBhcnNlIHRoZSB2YWx1ZSB0byBib29sZWFuXHJcbiAgLy8gb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSB1bmRlZmluZWRcclxuICBib29sZWFuOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbJ3RydWUnLCAnZmFsc2UnLCAnJ10pXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgPT09ICd0cnVlJyA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3MgcGFzc2VkIHZhbHVlcyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsXHJcbiAgLy8gYmUgdW5kZWZpbmVkXHJcbiAgZW51bTogKHZhbHVlcykgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWy4uLnZhbHVlcywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIFRyaW1zIHRoZSBzdHJpbmcgdmFsdWUgYW5kIGNoZWNrcyBpZiBpdCBpcyBlbXB0eSBvciBjb250YWlucyBzdHJpbmdpZmllZFxyXG4gIC8vIHZhbHVlcyBzdWNoIGFzIGZhbHNlLCB1bmRlZmluZWQsIG51bGwsIE5hTiwgaWYgaXQgZG9lcywgcmV0dXJucyB1bmRlZmluZWRcclxuICBzdHJpbmc6ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgIVsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJ10uaW5jbHVkZXModmFsdWUpIHx8XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSBzdHJpbmcgY29udGFpbnMgZm9yYmlkZGVuIHZhbHVlcywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBvc2l0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIHBvc2l0aXZlTnVtOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBwb3NpdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG5vbi1uZWdhdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlXHJcbiAgLy8gd2lsbCBiZSB1bmRlZmluZWRcclxuICBub25OZWdhdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCksXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSB2YWx1ZSBtdXN0IGJlIG51bWVyaWMgYW5kIG5vbi1uZWdhdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKVxyXG59O1xyXG5cclxuZXhwb3J0IGNvbnN0IENvbmZpZyA9IHoub2JqZWN0KHtcclxuICAvLyBwdXBwZXRlZXJcclxuICBQVVBQRVRFRVJfQVJHUzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEhJR0hDSEFSVFNfVkVSU0lPTiBtdXN0IGJlICdsYXRlc3QnLCBhIG1ham9yIHZlcnNpb24sIG9yIGluIHRoZSBmb3JtIFhYLllZLlpaLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfQ0ROX1VSTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHA6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcbiAgSElHSENIQVJUU19GT1JDRV9GRVRDSDogdi5ib29sZWFuKCksXHJcbiAgSElHSENIQVJUU19DQUNIRV9QQVRIOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQURNSU5fVE9LRU46IHYuc3RyaW5nKCksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNvcmVTY3JpcHRzLnZhbHVlKSxcclxuICBISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLm1vZHVsZVNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuaW5kaWNhdG9yU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcbiAgSElHSENIQVJUU19DVVNUT01fU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jdXN0b21TY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuXHJcbiAgLy8gZXhwb3J0XHJcbiAgRVhQT1JUX0lORklMRTogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfSU5TVFI6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1NWRzogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfQkFUQ0g6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09VVEZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RZUEU6IHYuZW51bShbJ2pwZWcnLCAncG5nJywgJ3BkZicsICdzdmcnXSksXHJcbiAgRVhQT1JUX0NPTlNUUjogdi5lbnVtKFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10pLFxyXG4gIEVYUE9SVF9CNjQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9OT19ET1dOTE9BRDogdi5ib29sZWFuKCksXHJcbiAgRVhQT1JUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1dJRFRIOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfR0xPQkFMX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RIRU1FX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG5cclxuICAvLyBjdXN0b21cclxuICBDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT046IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUzogdi5ib29sZWFuKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NVU1RPTV9DT0RFOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19DQUxMQkFDSzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfUkVTT1VSQ0VTOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gc2VydmVyXHJcbiAgU0VSVkVSX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX0hPU1Q6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfVVBMT0FEX0xJTUlUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX0JFTkNITUFSS0lORzogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIHNlcnZlciBwcm94eVxyXG4gIFNFUlZFUl9QUk9YWV9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QUk9YWV9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1BST1hZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gc2VydmVyIHJhdGUgbGltaXRpbmdcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFk6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU46IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlciBzc2xcclxuICBTRVJWRVJfU1NMX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9GT1JDRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1NTTF9DRVJUX1BBVEg6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHBvb2xcclxuICBQT09MX01JTl9XT1JLRVJTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9NQVhfV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfV09SS19MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfREVTVFJPWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfUkVBUEVSX0lOVEVSVkFMOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBsb2dnZXJcclxuICBMT0dHSU5HX0xFVkVMOiB6XHJcbiAgICAuc3RyaW5nKClcclxuICAgIC50cmltKClcclxuICAgIC5yZWZpbmUoXHJcbiAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZSA9PT0gJycgfHxcclxuICAgICAgICAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPD0gNSksXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgTE9HR0lOR19MRVZFTC4gV2Ugb25seSBhY2NlcHQgdmFsdWVzIGZyb20gMCB0byA1IGFzIGxvZ2dpbmcgbGV2ZWxzLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpLFxyXG4gIExPR0dJTkdfRklMRTogdi5zdHJpbmcoKSxcclxuICBMT0dHSU5HX0RFU1Q6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19UT19DT05TT0xFOiB2LmJvb2xlYW4oKSxcclxuICBMT0dHSU5HX1RPX0ZJTEU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyB1aVxyXG4gIFVJX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgVUlfUk9VVEU6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIG90aGVyXHJcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcclxuICBPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfTk9fTE9HTzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcclxuICBPVEhFUl9CUk9XU0VSX1NIRUxMX01PREU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBkZWJ1Z2dlclxyXG4gIERFQlVHX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfSEVBRExFU1M6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RFVlRPT0xTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19MSVNURU5fVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfRFVNUElPOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19TTE9XX01POiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgREVCVUdfREVCVUdHSU5HX1BPUlQ6IHYucG9zaXRpdmVOdW0oKVxyXG59KTtcclxuXHJcbmV4cG9ydCBjb25zdCBlbnZzID0gQ29uZmlnLnBhcnRpYWwoKS5wYXJzZShwcm9jZXNzLmVudik7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBNYW5hZ2VzIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgYnkgbG9hZGluZ1xyXG4gKiBhbmQgbWVyZ2luZyBvcHRpb25zIGZyb20gbXVsdGlwbGUgc291cmNlcywgc3VjaCBhcyBkZWZhdWx0IHNldHRpbmdzLFxyXG4gKiBlbnZpcm9ubWVudCB2YXJpYWJsZXMsIHVzZXItcHJvdmlkZWQgb3B0aW9ucywgYW5kIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqIEVuc3VyZXMgdGhlIGdsb2JhbCBvcHRpb25zIGFyZSB1cC10by1kYXRlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHkgdmFsdWVzLlxyXG4gKiBQcm92aWRlcyBmdW5jdGlvbnMgZm9yIGFjY2Vzc2luZyBhbmQgdXBkYXRpbmcgY29uZmlndXJhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBkZWVwQ29weSwgZ2V0QWJzb2x1dGVQYXRoLCBpc09iamVjdCB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IHsgYWJzb2x1dGVQcm9wcywgbmVzdGVkUHJvcHMsIGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0T3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PXRydWVdIC0gU3BlY2lmaWVzIHdoZXRoZXIgdG8gcmV0dXJuIGEgY29waWVkXHJcbiAqIG9iamVjdCBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgKGB0cnVlYCkgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCBvciBhIHJlZmVyZW5jZVxyXG4gKiB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoZ2V0Q29weSA9IHRydWUpIHtcclxuICByZXR1cm4gZ2V0Q29weSA/IGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpIDogZ2xvYmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXBkYXRlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBuZXcgb3B0aW9ucyB0byBiZVxyXG4gKiBtZXJnZWQgaW50byB0aGUgZ2xvYmFsIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldENvcHk9ZmFsc2VdIC0gRGV0ZXJtaW5lcyB3aGV0aGVyIHRvIG1lcmdlIHRoZSBuZXdcclxuICogb3B0aW9ucyBpbnRvIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IChgdHJ1ZWApIG9yIGRpcmVjdGx5IGludG9cclxuICogdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0LCBlaXRoZXIgdGhlIG1vZGlmaWVkIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9yIGEgbW9kaWZpZWQgY29weSwgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGBnZXRDb3B5YC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVPcHRpb25zKG5ld09wdGlvbnMsIGdldENvcHkgPSBmYWxzZSkge1xyXG4gIC8vIE1lcmdlIG5ldyBvcHRpb25zIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyBvciBpdHMgY29weSBhbmQgcmV0dXJuIHRoZSByZXN1bHRcclxuICByZXR1cm4gX21lcmdlT3B0aW9ucyhnZXRPcHRpb25zKGdldENvcHkpLCBuZXdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggdmFsdWVzIHByb3ZpZGVkIHRocm91Z2ggdGhlIENMSSwga2VlcGluZ1xyXG4gKiB0aGUgcHJpbmNpcGxlIG9mIG9wdGlvbnMgbG9hZCBwcmlvcml0eS4gVGhpcyBmdW5jdGlvbiBhY2NlcHRzIGEgYGNsaUFyZ3NgXHJcbiAqIGFycmF5IGNvbnRhaW5pbmcgYXJndW1lbnRzIGZyb20gdGhlIENMSSwgd2hpY2ggd2lsbCBiZSB2YWxpZGF0ZWQgYW5kIGFwcGxpZWRcclxuICogaWYgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIFRoZSBwcmlvcml0eSBvcmRlciBmb3Igc2V0dGluZyB2YWx1ZXMgaXM6XHJcbiAqXHJcbiAqIDEuIFZhbHVlcyBmcm9tIGEgY3VzdG9tIEpTT04gZmlsZSAobG9hZGVkIGJ5IHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24pLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSB0aGUgY29tbWFuZCBsaW5lIGludGVyZmFjZSAoQ0xJKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldENsaU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQgbGluZSBhcmd1bWVudHMgdXNlZFxyXG4gKiBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHJlZmxlY3RpbmcgdGhlIG1lcmdlZFxyXG4gKiBjb25maWd1cmF0aW9uIGZyb20gc291cmNlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEkuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0Q2xpT3B0aW9ucyhjbGlBcmdzKSB7XHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzICYmIEFycmF5LmlzQXJyYXkoY2xpQXJncykgJiYgY2xpQXJncy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGxvYWRlZCB2aWEgdGhlIGAtLWxvYWRDb25maWdgXHJcbiAgICBjb25zdCBjb25maWdPcHRpb25zID0gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNvbmZpZ09wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNvbmZpZ09wdGlvbnMpO1xyXG5cclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIENMSVxyXG4gICAgY29uc3QgY2xpT3B0aW9ucyA9IF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncyk7XHJcblxyXG4gICAgLy8gVXBkYXRlIGdsb2JhbCBvcHRpb25zIHdpdGggdGhlIHZhbHVlcyBmcm9tIHRoZSBgY2xpT3B0aW9uc2BcclxuICAgIHVwZGF0ZU9wdGlvbnMoY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnZXRPcHRpb25zKGZhbHNlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG9sZE9wdGlvbnMpKSB7XHJcbiAgICAvLyBJdGVyYXRlIG92ZXIgZWFjaCBrZXktdmFsdWUgcGFpciBpbiB0aGUgb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgdGhlcmUgaXMgYSBuZXN0ZWQgbWFwcGluZywgc3BsaXQgaXQgaW50byBhIHByb3BlcnRpZXMgY2hhaW5cclxuICAgICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNba2V5XVxyXG4gICAgICAgID8gbmVzdGVkUHJvcHNba2V5XS5zcGxpdCgnLicpXHJcbiAgICAgICAgOiBbXTtcclxuXHJcbiAgICAgIC8vIElmIGl0IGlzIHRoZSBsYXN0IHByb3BlcnR5IGluIHRoZSBjaGFpbiwgYXNzaWduIHRoZSB2YWx1ZSwgb3RoZXJ3aXNlLFxyXG4gICAgICAvLyBjcmVhdGUgb3IgcmV1c2UgdGhlIG5lc3RlZCBvYmplY3RcclxuICAgICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cclxuICAgICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgICAgbmV3T3B0aW9uc1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgICdbY29uZmlnXSBObyBjb3JyZWN0IG9iamVjdCB3aXRoIG9wdGlvbnMgd2FzIHByb3ZpZGVkLiBSZXR1cm5pbmcgYW4gZW1wdHkgYXJyYXkuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgbmV3LCBzdHJ1Y3R1cmVkIG9wdGlvbnMgb2JqZWN0XHJcbiAgcmV0dXJuIG5ld09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBWYWxpZGF0ZXMsIHBhcnNlcywgYW5kIGNoZWNrcyBpZiB0aGUgcHJvdmlkZWQgY29uZmlnIGlzIGFsbG93ZWQgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0FsbG93ZWRDb25maWdcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBjb25maWcgLSBUaGUgY29uZmlnIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkIGFzIGEgc2V0XHJcbiAqIG9mIG9wdGlvbnMuIE11c3QgYmUgZWl0aGVyIGFuIG9iamVjdCBvciBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBbdG9TdHJpbmc9ZmFsc2VdIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCB2ZXJzaW9uXHJcbiAqIG9mIHRoZSBwYXJzZWQgY29uZmlnLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFthbGxvd0Z1bmN0aW9ucz1mYWxzZV0gLSBXaGV0aGVyIHRvIGFsbG93IGZ1bmN0aW9uc1xyXG4gKiBpbiB0aGUgcGFyc2VkIGNvbmZpZy4gSWYgdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWQuIE90aGVyd2lzZSwgd2hlblxyXG4gKiBhIGZ1bmN0aW9uIGlzIGZvdW5kLCBudWxsIGlzIHJldHVybmVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFZlcnNpb24obm9Mb2dvID0gZmFsc2UpIHtcclxuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGAuZW52YCBvciBmcm9tIGBwYWNrYWdlLmpzb25gXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuICApLnZlcnNpb247XHJcblxyXG4gIC8vIFByaW50IHRleHQgb25seVxyXG4gIGlmIChub0xvZ28pIHtcclxuICAgIGNvbnNvbGUubG9nKGBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259YCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFByaW50IHRoZSBsb2dvXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAnbXNnJywgJ3N0YXJ0dXAubXNnJyksICd1dGY4JykudG9TdHJpbmcoKVxyXG4gICAgICAgIC5ib2xkLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IGJhc2VkIG9uIHRoZSBwcm92aWRlZFxyXG4gKiBjb25maWd1cmF0aW9uLCBzZXR0aW5nIHZhbHVlcyBmcm9tIG5lc3RlZCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5LlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIChkZWZhdWx0cykuXHJcbiAqIDIuIFZhbHVlcyBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcyAoc3BlY2lmaWVkIGluIHRoZSBgLmVudmAgZmlsZSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaW5pdE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogdGhlIGdsb2JhbCBvcHRpb25zLiBJdCBzaG91bGQgaW5jbHVkZSBuZXN0ZWQgcHJvcGVydGllcyB3aXRoIGEgYHZhbHVlYFxyXG4gKiBhbmQgYW4gYGVudkxpbmtgIGZvciBsaW5raW5nIHRvIGVudmlyb25tZW50IHZhcmlhYmxlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGluaXRpYWxpemVkIGdsb2JhbCBvcHRpb25zIG9iamVjdCwgcG9wdWxhdGVkIHdpdGhcclxuICogdmFsdWVzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHlcclxuICogb3JkZXIuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaW5pdE9wdGlvbnMoY29uZmlnKSB7XHJcbiAgLy8gSW5pdCB0aGUgb2JqZWN0IGZvciBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBTdGFydCBpbml0aWFsaXppbmcgdGhlIGBvcHRpb25zYCBvYmplY3QgcmVjdXJzaXZlbHlcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBpdGVtXSBvZiBPYmplY3QuZW50cmllcyhjb25maWcpKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY29ycmVjdCB2YWx1ZSBiYXNlZCBvbiB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHkgb3JkZXJcclxuICAgICAgaWYgKGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gdW5kZWZpbmVkICYmIGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gbnVsbCkge1xyXG4gICAgICAgIC8vIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdmFsdWVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gZW52c1tpdGVtLmVudkxpbmtdO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRoZSB2YWx1ZSBmcm9tIHRoZSBjb25maWcgZmlsZVxyXG4gICAgICAgIG9wdGlvbnNbbmFtZV0gPSBpdGVtLnZhbHVlO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDcmVhdGUgYSBzZWN0aW9uIGluIHRoZSBvcHRpb25zXHJcbiAgICAgIG9wdGlvbnNbbmFtZV0gPSBfaW5pdE9wdGlvbnMoaXRlbSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNyZWF0ZWQgYG9wdGlvbnNgIG9iamVjdFxyXG4gIHJldHVybiBvcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogTWVyZ2VzIHR3byBzZXRzIG9mIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgY29uc2lkZXJpbmcgYWJzb2x1dGUgcHJvcGVydGllcy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9tZXJnZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9yaWdpbmFsT3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTWVyZ2VkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9ucywgbmV3T3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgb3JpZ2luYWxPcHRpb25zYCBhbmQgYG5ld09wdGlvbnNgIGFyZSBjb3JyZWN0IG9iamVjdHNcclxuICBpZiAoaXNPYmplY3Qob3JpZ2luYWxPcHRpb25zKSAmJiBpc09iamVjdChuZXdPcHRpb25zKSkge1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobmV3T3B0aW9ucykpIHtcclxuICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gPVxyXG4gICAgICAgIGlzT2JqZWN0KHZhbHVlKSAmJlxyXG4gICAgICAgICFhYnNvbHV0ZVByb3BzLmluY2x1ZGVzKGtleSkgJiZcclxuICAgICAgICBvcmlnaW5hbE9wdGlvbnNba2V5XSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICA/IF9tZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zW2tleV0sIHZhbHVlKVxyXG4gICAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgICAgOiBvcmlnaW5hbE9wdGlvbnNba2V5XSB8fCBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBvcmlnaW5hbCAobW9kaWZpZWQgb3Igbm90KSBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIHRoZSBwcm92aWRlZCBvcHRpb25zIG9iamVjdCB0byBhIEpTT04tZm9ybWF0dGVkIHN0cmluZ1xyXG4gKiB3aXRoIHRoZSBvcHRpb24gdG8gcHJlc2VydmUgZnVuY3Rpb25zLiBJbiBvcmRlciBmb3IgYSBmdW5jdGlvblxyXG4gKiB0byBiZSBwcmVzZXJ2ZWQsIGl0IG5lZWRzIHRvIGZvbGxvdyB0aGUgZm9ybWF0IGBmdW5jdGlvbiAoLi4uKSB7Li4ufWAuXHJcbiAqIFN1Y2ggYSBmdW5jdGlvbiBjYW4gYWxzbyBiZSBzdHJpbmdpZmllZC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9vcHRpb25zU3RyaW5naWZ5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IHRvIGJlIGNvbnZlcnRlZCB0byBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0Z1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHByZXNlcnZlZFxyXG4gKiBpbiB0aGUgb3V0cHV0LiBPdGhlcndpc2UgYW4gZXJyb3IgaXMgdGhyb3duLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHN0cmluZ2lmeUZ1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHNhdmVkXHJcbiAqIGFzIHN0cmluZ3MuIFRoZSBgYWxsb3dGdW5jdGlvbnNgIG11c3QgYmUgc2V0IHRvIHRydWUgYXMgd2VsbCBmb3IgdGhpcyB0byB0YWtlXHJcbiAqIGFuIGVmZmVjdC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCB3aGVuIGZ1bmN0aW9ucyBhcmUgbm90IGFsbG93ZWQgYnV0IGFyZVxyXG4gKiBmb3VuZCBpbiBwcm92aWRlZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeShvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucywgc3RyaW5naWZ5RnVuY3Rpb25zKSB7XHJcbiAgY29uc3QgcmVwbGFjZXJDYWxsYmFjayA9IChfLCB2YWx1ZSkgPT4ge1xyXG4gICAgLy8gVHJpbSBzdHJpbmcgdmFsdWVzXHJcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xyXG4gICAgICB2YWx1ZSA9IHZhbHVlLnRyaW0oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBJZiB2YWx1ZSBpcyBhIGZ1bmN0aW9uIG9yIHN0cmluZ2lmaWVkIGZ1bmN0aW9uXHJcbiAgICBpZiAoXHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJyB8fFxyXG4gICAgICAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJykgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIElmIGFsbG93RnVuY3Rpb25zIGlzIHNldCB0byB0cnVlLCBwcmVzZXJ2ZSBmdW5jdGlvbnNcclxuICAgICAgaWYgKGFsbG93RnVuY3Rpb25zKSB7XHJcbiAgICAgICAgLy8gQmFzZWQgb24gdGhlIGBzdHJpbmdpZnlGdW5jdGlvbnNgIG9wdGlvbnMsIHNldCBmdW5jdGlvbiB2YWx1ZXNcclxuICAgICAgICByZXR1cm4gc3RyaW5naWZ5RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IC8vIEFzIHN0cmluZ2lmaWVkIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgXCJFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5cImBcclxuICAgICAgICAgIDogLy8gQXMgZnVuY3Rpb25zXHJcbiAgICAgICAgICAgIGBFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5gO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRocm93IGFuIGVycm9yIG90aGVyd2lzZVxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcigpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSW4gYWxsIG90aGVyIGNhc2VzLCBzaW1wbHkgcmV0dXJuIHRoZSB2YWx1ZVxyXG4gICAgcmV0dXJuIHZhbHVlO1xyXG4gIH07XHJcblxyXG4gIC8vIFN0cmluZ2lmeSBvcHRpb25zIGFuZCBpZiByZXF1aXJlZCwgcmVwbGFjZSBzcGVjaWFsIGZ1bmN0aW9ucyBtYXJrc1xyXG4gIHJldHVybiBKU09OLnN0cmluZ2lmeShvcHRpb25zLCByZXBsYWNlckNhbGxiYWNrKS5yZXBsYWNlQWxsKFxyXG4gICAgc3RyaW5naWZ5RnVuY3Rpb25zID8gL1xcXFxcIkVYUF9GVU58RVhQX0ZVTlxcXFxcIi9nIDogL1wiRVhQX0ZVTnxFWFBfRlVOXCIvZyxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgcHJvdmlkZWQgdmlhXHJcbiAqIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gaW4gdGhlIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgdG8gc2VhcmNoXHJcbiAqIGZvciB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncykge1xyXG4gIC8vIEdldCB0aGUgYWxsb3cgZmxhZ3MgZm9yIHRoZSBjdXN0b20gbG9naWMgY2hlY2tcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiwgYWxsb3dGaWxlUmVzb3VyY2VzIH0gPSBnZXRPcHRpb25zKCkuY3VzdG9tTG9naWM7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiB2YWx1ZVxyXG4gIGNvbnN0IGNvbmZpZ0ZpbGVOYW1lID0gY29uZmlnSW5kZXggPiAtMSAmJiBjbGlBcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lICYmIGFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICByZXR1cm4gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7Y29uZmlnRmlsZU5hbWV9IGZpbGUuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gYWRkaXRpb25hbCBvcHRpb25zIHRvIHJldHVyblxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhcnNlcyBjb21tYW5kLWxpbmUgYXJndW1lbnRzIGFuZCBwYWlycyBlYWNoIGFyZ3VtZW50IHdpdGggaXRzIGNvcnJlc3BvbmRpbmdcclxuICogb3B0aW9uIGluIHRoZSBjb25maWd1cmF0aW9uLiBUaGUgdmFsdWVzIGFyZSBzdHJ1Y3R1cmVkIGludG8gYSBuZXN0ZWQgb3B0aW9uc1xyXG4gKiBvYmplY3QsIGJhc2VkIG9uIHByZWRlZmluZWQgbWFwcGluZ3MuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gbmVzdGVkUHJvcHMgLSBBbiBhcnJheSBvZiBuZXN0aW5nIGxldmVsIGZvciBhbGxcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQtbGluZSBhcmd1bWVudHNcclxuICogY29udGFpbmluZyBvcHRpb25zIGFuZCB0aGVpciBhc3NvY2lhdGVkIHZhbHVlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gdXBkYXRlZCBvcHRpb25zIG9iamVjdCB3aGVyZSBlYWNoIG9wdGlvbiBmcm9tXHJcbiAqIHRoZSBjb21tYW5kLWxpbmUgaXMgcGFpcmVkIHdpdGggaXRzIHZhbHVlLCBzdHJ1Y3R1cmVkIGludG8gbmVzdGVkIG9iamVjdHNcclxuICogYXMgZGVmaW5lZC5cclxuICovXHJcbmZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncykge1xyXG4gIC8vIEFuIGVtcHR5IG9iamVjdCB0byBjb2xsZWN0IGFuZCBzdHJ1Y3R1cml6ZSBkYXRhIGZyb20gdGhlIGFyZ3NcclxuICBjb25zdCBjbGlPcHRpb25zID0ge307XHJcblxyXG4gIC8vIEN5Y2xlIHRocm91Z2ggYWxsIENMSSBhcmdzIGFuZCBmaWx0ZXIgdGhlbVxyXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2xpQXJncy5sZW5ndGg7IGkrKykge1xyXG4gICAgY29uc3Qgb3B0aW9uID0gY2xpQXJnc1tpXS5yZXBsYWNlKC8tL2csICcnKTtcclxuXHJcbiAgICAvLyBGaW5kIHRoZSByaWdodCBwbGFjZSBmb3IgcHJvcGVydHkncyB2YWx1ZVxyXG4gICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZFByb3BzW29wdGlvbl0uc3BsaXQoJy4nKVxyXG4gICAgICA6IFtdO1xyXG5cclxuICAgIC8vIENyZWF0ZSBvcHRpb25zIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIENMSSBmb3IgbGF0ZXIgcGFyc2luZyBhbmQgbWVyZ2luZ1xyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZSgob2JqLCBwcm9wLCBpbmRleCkgPT4ge1xyXG4gICAgICBpZiAocHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4KSB7XHJcbiAgICAgICAgY29uc3QgdmFsdWUgPSBjbGlBcmdzWysraV07XHJcbiAgICAgICAgaWYgKCF2YWx1ZSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NvbmZpZ10gTWlzc2luZyB2YWx1ZSBmb3IgdGhlIENMSSAnLS0ke29wdGlvbn0nIGFyZ3VtZW50LiBVc2luZyB0aGUgZGVmYXVsdCB2YWx1ZS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgICBvYmpbcHJvcF0gPSB2YWx1ZSB8fCBudWxsO1xyXG4gICAgICB9IGVsc2UgaWYgKG9ialtwcm9wXSA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgb2JqW3Byb3BdID0ge307XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcclxuICAgIH0sIGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHBhcnNlZCBDTEkgb3B0aW9uc1xyXG4gIHJldHVybiBjbGlPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgdHJhdmVyc2VzIHRoZSBvcHRpb25zIG9iamVjdCB0byBwcmludCB0aGUgdXNhZ2UgaW5mb3JtYXRpb25cclxuICogZm9yIGVhY2ggb3B0aW9uIGNhdGVnb3J5IGFuZCBpbmRpdmlkdWFsIG9wdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBDTEkgb3B0aW9ucy4gSXQgbWF5XHJcbiAqIGluY2x1ZGUgbmVzdGVkIGNhdGVnb3JpZXMgYW5kIGluZGl2aWR1YWwgb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9ucykge1xyXG4gIGZvciAoY29uc3QgW25hbWUsIG9wdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcclxuICAgIC8vIElmIHRoZSBjdXJyZW50IGVudHJ5IGlzIGEgY2F0ZWdvcnkgYW5kIG5vdCBhIGxlYWYgb3B0aW9uLCByZWN1cnNlIGludG8gaXRcclxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9wdGlvbiwgJ3ZhbHVlJykpIHtcclxuICAgICAgX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb24pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gUHJlcGFyZSBkZXNjcmlwdGlvblxyXG4gICAgICBjb25zdCBkZXNjTmFtZSA9IGAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9YDtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdmFsdWVcclxuICAgICAgbGV0IG9wdGlvblZhbHVlID0gb3B0aW9uLnZhbHVlO1xyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGFycmF5IG9mIHN0cmluZ3NcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nW10nKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID1cclxuICAgICAgICAgICdbJyArIG9wdGlvblZhbHVlLm1hcCgoaXRlbSkgPT4gYCcke2l0ZW19J2ApLmpvaW4oJywgJykgKyAnXSc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhIHN0cmluZ1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmcnKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID0gYCcke29wdGlvblZhbHVlfSdgO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgIGRlc2NOYW1lLmdyZWVuLFxyXG4gICAgICAgIGAkeygnPCcgKyBvcHRpb24udHlwZXMuam9pbignfCcpICsgJz4nKS55ZWxsb3d9YCxcclxuICAgICAgICBgJHtTdHJpbmcob3B0aW9uVmFsdWUpLmJvbGR9YC5ibHVlLFxyXG4gICAgICAgIGAtICR7b3B0aW9uLmRlc2NyaXB0aW9ufS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIHNldENsaU9wdGlvbnMsXHJcbiAgbWFwVG9OZXdPcHRpb25zLFxyXG4gIGlzQWxsb3dlZENvbmZpZyxcclxuICBwcmludExpY2Vuc2UsXHJcbiAgcHJpbnRVc2FnZSxcclxuICBwcmludFZlcnNpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhUVFAgdXRpbGl0eSBtb2R1bGUgZm9yIGZldGNoaW5nIGFuZCBwb3N0aW5nIGRhdGEuIFN1cHBvcnRzIGJvdGhcclxuICogSFRUUCBhbmQgSFRUUFMgcHJvdG9jb2xzLCBwcm92aWRpbmcgbWV0aG9kcyB0byBtYWtlIEdFVCBhbmQgUE9TVCByZXF1ZXN0c1xyXG4gKiB3aXRoIGN1c3RvbWl6YWJsZSBvcHRpb25zLiBJbmNsdWRlcyBwcm90b2NvbCBkZXRlcm1pbmF0aW9uIGJhc2VkIG9uIFVSTFxyXG4gKiBhbmQgYXVnbWVudHMgcmVzcG9uc2Ugb2JqZWN0cyB3aXRoIGEgJ3RleHQnIHByb3BlcnR5IGZvciBlYXNpZXIgZGF0YSBhY2Nlc3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBkYXRhIGZyb20gdGhlIHNwZWNpZmllZCBVUkwgdXNpbmcgZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZmV0Y2hcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZmV0Y2ggZGF0YSBmcm9tLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZldGNoKHVybCwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAuZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIGlmICghcmVzcG9uc2VEYXRhKSB7XHJcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgUE9TVCByZXF1ZXN0IHRvIHRoZSBzcGVjaWZpZWQgVVJMIHdpdGggdGhlIHByb3ZpZGVkIEpTT04gYm9keSB1c2luZ1xyXG4gKiBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIHNlbmQgdGhlIFBPU1QgcmVxdWVzdCB0by5cclxuICogQHBhcmFtIHtPYmplY3R9IFtib2R5PXt9XSAtIFRoZSBKU09OIGJvZHkgdG8gaW5jbHVkZSBpbiB0aGUgUE9TVCByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcG9zdCh1cmwsIGJvZHkgPSB7fSwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoYm9keSk7XHJcblxyXG4gICAgLy8gU2V0IGRlZmF1bHQgaGVhZGVycyBhbmQgbWVyZ2Ugd2l0aCByZXF1ZXN0T3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oXHJcbiAgICAgIHtcclxuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcclxuICAgICAgICBoZWFkZXJzOiB7XHJcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxyXG4gICAgICAgICAgJ0NvbnRlbnQtTGVuZ3RoJzogZGF0YS5sZW5ndGhcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zXHJcbiAgICApO1xyXG5cclxuICAgIGNvbnN0IHJlcXVlc3QgPSBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAucmVxdWVzdCh1cmwsIG9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgIC8vIFdyaXRlIHRoZSByZXF1ZXN0IGJvZHkgYW5kIGVuZCB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC53cml0ZShkYXRhKTtcclxuICAgIHJlcXVlc3QuZW5kKCk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgVVJMLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGRldGVybWluZSB0aGUgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSAoaHR0cCBvciBodHRwcykuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKSB7XHJcbiAgcmV0dXJuIHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZmV0Y2gsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBlcnJvciBjbGFzcyBmb3IgaGFuZGxpbmcgZXhwb3J0LXJlbGF0ZWQgZXJyb3JzLiBFeHRlbmRzIHRoZSBuYXRpdmVcclxuICogYEVycm9yYCBjbGFzcyB0byBpbmNsdWRlIGFkZGl0aW9uYWwgcHJvcGVydGllcyBsaWtlIHN0YXR1cyBjb2RlIGFuZCBzdGFja1xyXG4gKiB0cmFjZSBkZXRhaWxzLlxyXG4gKi9cclxuY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gVGhlIGVycm9yIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkLlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gT3B0aW9uYWwgSFRUUCBzdGF0dXMgY29kZSBhc3NvY2lhdGVkXHJcbiAgICogd2l0aCB0aGUgZXJyb3IgKGUuZy4sIDQwMCwgNTAwKS5cclxuICAgKi9cclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXNDb2RlKSB7XHJcbiAgICBzdXBlcigpO1xyXG5cclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcblxyXG4gICAgaWYgKHN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgb3IgdXBkYXRlcyB0aGUgSFRUUCBzdGF0dXMgY29kZSBmb3IgdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBUaGUgSFRUUCBzdGF0dXMgY29kZSB0byBhc3NpZ24gdG8gdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgYWRkaXRpb25hbCBlcnJvciBkZXRhaWxzIGJhc2VkIG9uIGFuIGV4aXN0aW5nIGVycm9yIG9iamVjdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gQW4gZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgZGV0YWlscyB0byBwb3B1bGF0ZVxyXG4gICAqIHRoZSBgRXhwb3J0RXJyb3JgIGluc3RhbmNlLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG5cclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoZSBjYWNoZSBtYW5hZ2VyIGlzIHJlc3BvbnNpYmxlIGZvciBoYW5kbGluZyBhbmQgbWFuYWdpbmdcclxuICogdGhlIEhpZ2hjaGFydHMgbGlicmFyeSBhbG9uZyB3aXRoIGl0cyBkZXBlbmRlbmNpZXMuIEl0IGVuc3VyZXMgdGhhdCB0aGVzZVxyXG4gKiByZXNvdXJjZXMgYXJlIHN0b3JlZCBhbmQgcmV0cmlldmVkIGVmZmljaWVudGx5IHRvIG9wdGltaXplIHBlcmZvcm1hbmNlXHJcbiAqIGFuZCByZWR1Y2UgcmVkdW5kYW50IG5ldHdvcmsgcmVxdWVzdHMuIFRoZSBjYWNoZSBpcyBzdG9yZWQgaW4gdGhlIGAuY2FjaGVgXHJcbiAqIGRpcmVjdG9yeSBieSBkZWZhdWx0LCB3aGljaCBzZXJ2ZXMgYXMgYSBkZWRpY2F0ZWQgZm9sZGVyIGZvciBrZWVwaW5nIGNhY2hlZFxyXG4gKiBmaWxlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnNcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNhY2hlIHBhdGhcclxuICAgIGNvbnN0IGNhY2hlUGF0aCA9IGdldENhY2hlUGF0aCgpO1xyXG5cclxuICAgIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgICBjb25zdCBtYW5pZmVzdFBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKTtcclxuICAgIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ3NvdXJjZXMuanMnKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcclxuXHJcbiAgICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIHRoZSBgbWFuaWZlc3QuanNvbmAgZG9lcyBub3QgZXhpc3RcclxuICAgIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICAgIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGhpZ2hjaGFydHNPcHRpb25zLmZvcmNlRmV0Y2gpIHtcclxuICAgICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAgIC8vIFJlYWQgdGhlIG1hbmlmZXN0IEpTT05cclxuICAgICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpLCAndXRmOCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG1vZHVsZXMgaXMgYW4gYXJyYXksIGlmIHNvLCB3ZSByZXdyaXRlIGl0IHRvIGEgbWFwIHRvIG1ha2VcclxuICAgICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIGFjdHVhbCBudW1iZXIgb2Ygc2NyaXB0cyB0byBiZSBmZXRjaGVkXHJcbiAgICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9XHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnM7XHJcbiAgICAgIGNvbnN0IG51bWJlck9mTW9kdWxlcyA9XHJcbiAgICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcclxuICAgICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24pIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAgIE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzXHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgX3VwZGF0ZUNhY2hlKFxyXG4gICAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgICBzb3VyY2VQYXRoXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xyXG5cclxuICAgICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEZpbmFsbHksIHNhdmUgdGhlIG5ldyBtYW5pZmVzdCwgd2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjdXJyZW50IGNvbmZpZ1xyXG4gICAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgICBhd2FpdCBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY29uZmlndXJlIGNhY2hlIGFuZCBjcmVhdGUgb3IgdXBkYXRlIHRoZSBjb25maWcgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIFVwZGF0ZSB0byB0aGUgbmV3IHZlcnNpb25cclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICBoaWdoY2hhcnRzOiB7XHJcbiAgICAgIHZlcnNpb246IG5ld1ZlcnNpb25cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgsICd1dGY4Jyk7IC8vICM1NjJcclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBgZmV0Y2hlZE1vZHVsZXNgIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxyXG4gKiB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxyXG4gKiBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuIFRoZSBkZWZhdWx0XHJcbiAqIHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbVxyXG4gKiB3aXRoIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxyXG4gIHNjcmlwdCxcclxuICByZXF1ZXN0T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlcyxcclxuICBzaG91bGRUaHJvd0Vycm9yID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgLmpzIGZyb20gdGhlIGN1c3RvbSBzdHJpbmdzXHJcbiAgaWYgKHNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgIHNjcmlwdCA9IHNjcmlwdC5zdWJzdHJpbmcoMCwgc2NyaXB0Lmxlbmd0aCAtIDMpO1xyXG4gIH1cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gIH1cclxuXHJcbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYCxcclxuICAgICAgNDA0XHJcbiAgICApLnNldEVycm9yKHJlc3BvbnNlKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uLmBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3RcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbZmV0Y2hlZE1vZHVsZXM9e31dIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzT3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMgPSB7fSkge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzXHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihnZXRDYWNoZVBhdGgoKSwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgYHNjcmlwdHNgIGFuZCBgY3VzdG9tU2NyaXB0c2AgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNvcmVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBjb3JlIHNjcmlwdHMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG1vZHVsZVNjcmlwdHMgLSBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGN1c3RvbVNjcmlwdHMgLSBDdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsXHJcbiAqIFVSTHMpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmZXRjaGVkIHNjcmlwdHNcclxuICogY29udGVudCBqb2luZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogc2V0dGluZyBhbiBIVFRQIEFnZW50IGZvciBwcm94eS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHMoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pIHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gc2VydmVyUHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gc2VydmVyUHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiBleGlzdHMsIGFkZCBwcm94eSBhZ2VudCB0byByZXF1ZXN0IG9wdGlvbnNcclxuICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHByb3h5QWdlbnRcclxuICAgID8ge1xyXG4gICAgICAgIGFnZW50OiBwcm94eUFnZW50LFxyXG4gICAgICAgIHRpbWVvdXQ6IHNlcnZlclByb3h5T3B0aW9ucy50aW1lb3V0XHJcbiAgICAgIH1cclxuICAgIDoge307XHJcblxyXG4gIGNvbnN0IGFsbEZldGNoUHJvbWlzZXMgPSBbXHJcbiAgICAuLi5jb3JlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcylcclxuICAgICksXHJcbiAgICAuLi5jdXN0b21TY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucylcclxuICAgIClcclxuICBdO1xyXG5cclxuICBjb25zdCBmZXRjaGVkU2NyaXB0cyA9IGF3YWl0IFByb21pc2UuYWxsKGFsbEZldGNoUHJvbWlzZXMpO1xyXG4gIHJldHVybiBmZXRjaGVkU2NyaXB0cy5qb2luKCc7XFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgcmVwcmVzZW50aW5nXHJcbiAqIHRoZSBmZXRjaGVkIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF91cGRhdGVDYWNoZShoaWdoY2hhcnRzT3B0aW9ucywgc2VydmVyUHJveHlPcHRpb25zLCBzb3VyY2VQYXRoKSB7XHJcbiAgLy8gR2V0IEhpZ2hjaGFydHMgdmVyc2lvbiBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGhjVmVyc2lvbiA9XHJcbiAgICBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uID09PSAnbGF0ZXN0J1xyXG4gICAgICA/IG51bGxcclxuICAgICAgOiBgJHtoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9ufWA7XHJcblxyXG4gIC8vIEdldCB0aGUgQ0ROIHVybCBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGNkblVybCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVybCB8fCBjYWNoZS5jZG5Vcmw7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5zb3VyY2VzID0gYXdhaXQgX2ZldGNoU2NyaXB0cyhcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoYykgPT5cclxuICAgICAgICAgIGhjVmVyc2lvbiA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259LyR7Y31gIDogYCR7Y2RuVXJsfS8ke2N9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vbWFwcy8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tYXBzL21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgOiBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICksXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGkpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb25cclxuICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L3N0b2NrLyR7aGNWZXJzaW9ufS9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9zdG9jay9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldEhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIGV4dHJhY3RWZXJzaW9uLFxyXG4gIGV4dHJhY3RNb2R1bGVOYW1lLFxyXG4gIGdldENhY2hlLFxyXG4gIGdldENhY2hlUGF0aFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWV0aG9kcyBmb3IgaW5pdGlhbGl6aW5nIEhpZ2hjaGFydHMgd2l0aCBjdXN0b21pemVkXHJcbiAqIGFuaW1hdGlvbiBzZXR0aW5ncyBhbmQgdHJpZ2dlcmluZyB0aGUgY3JlYXRpb24gb2YgSGlnaGNoYXJ0cyBjaGFydHMgd2l0aFxyXG4gKiBleHBvcnQtc3BlY2lmaWMgY29uZmlndXJhdGlvbnMgaW4gdGhlIHBhZ2UgY29udGV4dC4gU3VwcG9ydHMgZHluYW1pYyBvcHRpb25cclxuICogbWVyZ2luZywgY3VzdG9tIGxvZ2ljIGluamVjdGlvbiwgYW5kIGNvbnRyb2wgb3ZlciByZW5kZXJpbmcgYmVoYXZpb3JzLiBVc2VkXHJcbiAqIGJ5IHRoZSBQdXBwZXRlZXIgcGFnZS5cclxuICovXHJcblxyXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xyXG5cclxuLyoqXHJcbiAqIFNldHRpbmcgdGhlIGBIaWdoY2hhcnRzLmFuaW1PYmplY3RgIGZ1bmN0aW9uLiBDYWxsZWQgd2hlbiBpbml0aW5nIHRoZSBwYWdlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xyXG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcclxuICAgIHJldHVybiB7IGR1cmF0aW9uOiAwIH07XHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBIaWdoY2hhcnRzIGNoYXJ0IG9uIGEgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVDaGFydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQ2hhcnQoZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgc2V0T3B0aW9ucywgbWVyZ2UsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHRoZSBgdXNlck9wdGlvbnNgIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB0aGUgYHVzZXJPcHRpb25zYCB3aGVuIGBmb3JFeHBvcnRgIGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBTb21lIG1hbmRhdG9yeSBhZGRpdGlvbmFsIGBjaGFydGAgYW5kIGBleHBvcnRpbmdgIG9wdGlvbnNcclxuICBjb25zdCBhZGRpdGlvbmFsT3B0aW9ucyA9IHtcclxuICAgIGNoYXJ0OiB7XHJcbiAgICAgIC8vIEJ5IGRlZmF1bHQgYW5pbWF0aW9uIGlzIGRpc2FibGVkXHJcbiAgICAgIGFuaW1hdGlvbjogZmFsc2UsXHJcbiAgICAgIC8vIEdldCB0aGUgcmlnaHQgc2l6ZSB2YWx1ZXNcclxuICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcclxuICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmluc3RyfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYHRoZW1lT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgdGhlbWVPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9uc31gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb25cclxuICBjb25zdCBnbG9iYWxPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gTWVyZ2UgdGhlIGZvbGxvd2luZyBvcHRpb25zIG9iamVjdHMgdG8gY3JlYXRlIGZpbmFsIG9wdGlvbnNcclxuICBjb25zdCBmaW5hbE9wdGlvbnMgPSBtZXJnZShcclxuICAgIGZhbHNlLFxyXG4gICAgdGhlbWVPcHRpb25zLFxyXG4gICAgdXNlck9wdGlvbnMsXHJcbiAgICAvLyBQbGFjZWQgaXQgaGVyZSBpbnN0ZWFkIGluIHRoZSBpbml0IGJlY2F1c2Ugb2YgdGhlIHNpemUgaXNzdWVzXHJcbiAgICBhZGRpdGlvbmFsT3B0aW9uc1xyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjYWxsYmFja2Agb3B0aW9uXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke2N1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja31gKSgpXHJcbiAgICA6IG51bGw7XHJcblxyXG4gIC8vIFRyaWdnZXIgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbignb3B0aW9ucycsIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbZXhwb3J0T3B0aW9ucy5jb25zdHJdKCdjb250YWluZXInLCBmaW5hbE9wdGlvbnMsIGZpbmFsQ2FsbGJhY2spO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBgc2V0T3B0aW9uc2Agd2FzIHVzZWQgaW4gdGhlIGBjdXN0b21Db2RlYClcclxuICBmb3IgKGNvbnN0IHByb3AgaW4gZGVmYXVsdE9wdGlvbnMpIHtcclxuICAgIGlmICh0eXBlb2YgZGVmYXVsdE9wdGlvbnNbcHJvcF0gIT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgZGVsZXRlIGRlZmF1bHRPcHRpb25zW3Byb3BdO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBkZWZhdWx0IG9wdGlvbnMgYmFja1xyXG4gIHNldE9wdGlvbnMoSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqKTtcclxuXHJcbiAgLy8gRW1wdHkgdGhlIGN1c3RvbSBnbG9iYWwgb3B0aW9ucyBvYmplY3RcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSB7fTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNldHVwSGlnaGNoYXJ0cyxcclxuICBjcmVhdGVDaGFydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZSwgY3JlYXRpbmcgYW5kIGNsZWFyaW5nIHBhZ2VzLCBpbmplY3RpbmcgY3VzdG9tIHJlc291cmNlcyxcclxuICogYW5kIHNldHRpbmcgdXAgSGlnaGNoYXJ0cyBmb3Igc2VydmVyLXNpZGUgcmVuZGVyaW5nLiBUaGUgbW9kdWxlIGVuc3VyZXNcclxuICogdGhhdCByZXNvdXJjZXMgYXJlIGNvcnJlY3RseSBtYW5hZ2VkIGFuZCBjYW4gaGFuZGxlIGZhaWx1cmVzIGR1cmluZ1xyXG4gKiBvcGVyYXRpb25zIGxpa2UgbGF1bmNoaW5nIHRoZSBicm93c2VyIG9yIGNyZWF0aW5nIG5ldyBwYWdlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciBwYWdlc1xyXG5jb25zdCB0ZW1wbGF0ZSA9IHJlYWRGaWxlU3luYyhcclxuICBqb2luKF9fZGlybmFtZSwgJ3RlbXBsYXRlcycsICd0ZW1wbGF0ZS5odG1sJyksXHJcbiAgJ3V0ZjgnXHJcbik7XHJcblxyXG4vLyBUbyBzYXZlIHRoZSBicm93c2VyXHJcbmxldCBicm93c2VyID0gbnVsbDtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QnJvd3NlclxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBubyB2YWxpZCBicm93c2VyXHJcbiAqIGhhcyBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QnJvd3NlcigpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nLCA1MDApO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXJcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgY3JlYXRlZCBQdXBwZXRlZXJcclxuICogYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG1heCByZXRyaWVzIHRvIG9wZW5cclxuICogYSBicm93c2VyIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyXHJcbiAqIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2AgYW5kIGBvdGhlcmAgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWdPcHRpb25zIH0gPSBkZWJ1ZztcclxuXHJcbiAgLy8gTGF1bmNoIG9wdGlvbnMgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgY29uc3QgbGF1bmNoT3B0aW9ucyA9IHtcclxuICAgIGhlYWRsZXNzOiBvdGhlci5icm93c2VyU2hlbGxNb2RlID8gJ3NoZWxsJyA6IHRydWUsXHJcbiAgICB1c2VyRGF0YURpcjogJ3RtcCcsXHJcbiAgICBhcmdzOiBwdXBwZXRlZXJBcmdzIHx8IFtdLFxyXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHSFVQOiBmYWxzZSxcclxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXHJcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXHJcbiAgICAuLi4oZW5hYmxlZERlYnVnICYmIGRlYnVnT3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICAvLyBBIGNvdW50ZXIgZm9yIHRoZSBicm93c2VyJ3MgbGF1bmNoIHJldHJpZXNcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFticm93c2VyXSBBdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeSAkeysrdHJ5Q291bnR9KS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gTGF1bmNoIHRoZSBicm93c2VyXHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgJ1ticm93c2VyXSBGYWlsZWQgdG8gbGF1bmNoIGEgYnJvd3NlciBpbnN0YW5jZS4nXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0cnkgdG8gbGF1bmNoIGJyb3dzZXIgdW50aWwgcmVhY2hpbmcgbWF4IGF0dGVtcHRzXHJcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcclxuICAgICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIFJldHJ5IHRvIG9wZW4gYSBicm93c2VyICgke3RyeUNvdW50fSBvdXQgb2YgMjUpLmApO1xyXG5cclxuICAgICAgICAgIC8vIFdhaXQgZm9yIGEgNCBzZWNvbmRzIGJlZm9yZSB0cnlpbmcgYWdhaW5cclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG5cclxuICAgICAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGxhdW5jaE9wdGlvbnMuaGVhZGxlc3MgPT09ICdzaGVsbCcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIHNoZWxsIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERlYnVnIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChlbmFibGVkRGVidWcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIGRlYnVnIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2Jyb3dzZXJdIE1heGltdW0gcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlciBpbnN0YW5jZSByZWFjaGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nLCA1MDApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xvc2VCcm93c2VyXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2VCcm93c2VyKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIgJiYgYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgYnJvd3NlciA9IG51bGw7XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgcGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHRoZSBgX3NldFBhZ2VDb250ZW50KClgLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBuZXdQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIGBpZGAsXHJcbiAqIGB3b3JrQ291bnRgLCBhbmQgYHBhZ2VgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjb25uZWN0ZWQgb3IgaWYgYSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG5ld1BhZ2UocG9vbFJlc291cmNlKSB7XHJcbiAgLy8gRXJyb3IgaW4gY2FzZSBvZiBubyBjb25uZWN0ZWQgYnJvd3NlclxyXG4gIGlmICghYnJvd3NlciB8fCAhYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW2Jyb3dzZXJdIEJyb3dzZXIgaXMgbm90IHlldCBjb25uZWN0ZWQuYCwgNTAwKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBwb29sUmVzb3VyY2UucGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBEaXNhYmxlIGNhY2hlXHJcbiAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXHJcbiAgX3NldFBhZ2VFdmVudHMocG9vbFJlc291cmNlLnBhZ2UpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgcGFnZSBpcyBjb3JyZWN0bHkgY3JlYXRlZFxyXG4gIGlmICghcG9vbFJlc291cmNlLnBhZ2UgfHwgcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgdGhlIGNvbnRlbnQgb2YgYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG1vZGUuIExvZ3NcclxuICogdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIG9mIGEgcGFnZSdzIGNvbnRlbnQgZmFpbHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIHBhZ2UgYW5kIGlkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtoYXJkUmVzZXQ9ZmFsc2VdIC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gYGFib3V0OmJsYW5rYCBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgd2hlbiBwYWdlXHJcbiAqIGlzIGNvcnJlY3RseSBjbGVhcmVkIGFuZCBmYWxzZSB3aGVuIGl0IGlzIG5vdC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2UocG9vbFJlc291cmNlLCBoYXJkUmVzZXQgPSBmYWxzZSkge1xyXG4gIHRyeSB7XHJcbiAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGBhYm91dDpibGFua2BcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHtcclxuICAgICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBfc2V0UGFnZUNvbnRlbnQocG9vbFJlc291cmNlLnBhZ2UpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gQ29udGVudCBvZiB0aGUgcGFnZSBjb3VsZCBub3QgYmUgY2xlYXJlZC5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICBwb29sUmVzb3VyY2Uud29ya0NvdW50ID0gZ2V0T3B0aW9ucygpLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICB9XHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyBjdXN0b20gSlMgYW5kIENTUyByZXNvdXJjZXMgdG8gYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYWRkUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9IGZpbGUuc3RhcnRzV2l0aCgnaHR0cCcpID8gZmFsc2UgOiB0cnVlO1xyXG5cclxuICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gc2NyaXB0IGZyb20gcmVzb3VyY2VzJyBmaWxlc1xyXG4gICAgICAgIGluamVjdGVkSnMucHVzaChcclxuICAgICAgICAgIGlzTG9jYWxcclxuICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICBjb250ZW50OiByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGZpbGUpLCAndXRmOCcpXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA6IHtcclxuICAgICAgICAgICAgICAgIHVybDogZmlsZVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgZm9yIChjb25zdCBqc1Jlc291cmNlIG9mIGluamVjdGVkSnMpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU2NyaXB0VGFnKGpzUmVzb3VyY2UpKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gVGhlIEpTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGluamVjdGVkSnMubGVuZ3RoID0gMDtcclxuXHJcbiAgICAvLyBMb2FkIENTU1xyXG4gICAgY29uc3QgaW5qZWN0ZWRDc3MgPSBbXTtcclxuICAgIGlmIChyZXNvdXJjZXMuY3NzKSB7XHJcbiAgICAgIGxldCBjc3NJbXBvcnRzID0gcmVzb3VyY2VzLmNzcy5tYXRjaCgvQGltcG9ydFxccyooW147XSopOy9nKTtcclxuICAgICAgaWYgKGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAvLyBIYW5kbGUgY3NzIHNlY3Rpb25cclxuICAgICAgICBmb3IgKGxldCBjc3NJbXBvcnRQYXRoIG9mIGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoKSB7XHJcbiAgICAgICAgICAgIGNzc0ltcG9ydFBhdGggPSBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ3VybCgnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgnQGltcG9ydCcsICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvJy9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvOy8sICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cXCkvZywgJycpXHJcbiAgICAgICAgICAgICAgLnRyaW0oKTtcclxuXHJcbiAgICAgICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBjc3MgZnJvbSByZXNvdXJjZXNcclxuICAgICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGguc3RhcnRzV2l0aCgnaHR0cCcpKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICBwYXRoOiBnZXRBYnNvbHV0ZVBhdGgoY3NzSW1wb3J0UGF0aClcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVGhlIHJlc3Qgb2YgdGhlIENTUyBzZWN0aW9uIHdpbGwgYmUgY29udGVudCBieSBub3dcclxuICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmNzcy5yZXBsYWNlKC9AaW1wb3J0XFxzKihbXjtdKik7L2csICcnKSB8fCAnICdcclxuICAgICAgfSk7XHJcblxyXG4gICAgICBmb3IgKGNvbnN0IGNzc1Jlc291cmNlIG9mIGluamVjdGVkQ3NzKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyhjc3NSZXNvdXJjZSkpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICBgW2Jyb3dzZXJdIFRoZSBDU1MgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBpbmplY3RlZENzcy5sZW5ndGggPSAwO1xyXG4gICAgfVxyXG4gIH1cclxuICByZXR1cm4gaW5qZWN0ZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgb3V0IGFsbCBzdGF0ZSBzZXQgb24gdGhlIHBhZ2Ugd2l0aCBgYWRkU2NyaXB0VGFnYCBhbmQgYGFkZFN0eWxlVGFnYC5cclxuICogUmVtb3ZlcyBpbmplY3RlZCByZXNvdXJjZXMgYW5kIHJlc2V0cyBDU1MgYW5kIHNjcmlwdCB0YWdzIG9uIHRoZSBwYWdlLlxyXG4gKiBBZGRpdGlvbmFsbHksIGl0IGRlc3Ryb3lzIHByZXZpb3VzbHkgZXhpc3RpbmcgY2hhcnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgZnJvbSB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxPYmplY3Q+fSBpbmplY3RlZFJlc291cmNlcyAtIEFycmF5IG9mIGluamVjdGVkIHJlc291cmNlc1xyXG4gKiB0byBiZSBjbGVhcmVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcykge1xyXG4gIHRyeSB7XHJcbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlc291cmNlLmRpc3Bvc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IG9sZCBjaGFydHMgYWZ0ZXIgZXhwb3J0IGlzIGRvbmUgYW5kIHJlc2V0IGFsbCBDU1MgYW5kIHNjcmlwdCB0YWdzXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgLy8gV2UgYXJlIG5vdCBndWFyYW50ZWVkIHRoYXQgSGlnaGNoYXJ0cyBpcyBsb2FkZWQsIHdoZW4gZG9pbmcgU1ZHIGV4cG9ydHNcclxuICAgICAgaWYgKHR5cGVvZiBIaWdoY2hhcnRzICE9PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgIGNvbnN0IG9sZENoYXJ0cyA9IEhpZ2hjaGFydHMuY2hhcnRzO1xyXG5cclxuICAgICAgICAvLyBDaGVjayBpbiBhbnkgYWxyZWFkeSBleGlzdGluZyBjaGFydHNcclxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShvbGRDaGFydHMpICYmIG9sZENoYXJ0cy5sZW5ndGgpIHtcclxuICAgICAgICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0c1xyXG4gICAgICAgICAgZm9yIChjb25zdCBvbGRDaGFydCBvZiBvbGRDaGFydHMpIHtcclxuICAgICAgICAgICAgb2xkQ2hhcnQgJiYgb2xkQ2hhcnQuZGVzdHJveSgpO1xyXG4gICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgICAgSGlnaGNoYXJ0cy5jaGFydHMuc2hpZnQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gQ291bGQgbm90IGNsZWFyIHBhZ2UncyByZXNvdXJjZXMuYCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgY29udGVudCBmb3IgYSBQdXBwZXRlZXIgUGFnZSB1c2luZyBhIHByZWRlZmluZWQgdGVtcGxhdGVcclxuICogYW5kIGFkZGl0aW9uYWwgc2NyaXB0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBjb250ZW50XHJcbiAqIGlzIGJlaW5nIHNldC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zZXRQYWdlQ29udGVudChwYWdlKSB7XHJcbiAgLy8gU2V0IHRoZSBpbml0aWFsIHBhZ2UgY29udGVudFxyXG4gIGF3YWl0IHBhZ2Uuc2V0Q29udGVudCh0ZW1wbGF0ZSwgeyB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJyB9KTtcclxuXHJcbiAgLy8gQWRkIGFsbCByZWdpc3RlcmVkIEhpZ2NoYXJ0cyBzY3JpcHRzLCBxdWl0ZSBkZW1hbmRpbmdcclxuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdzb3VyY2VzLmpzJykgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBgYW5pbU9iamVjdGAgZm9yIEhpZ2hjaGFydHNcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKHNldHVwSGlnaGNoYXJ0cyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgZXZlbnRzIChsaWtlIGBwYWdlZXJyb3JgIGFuZCBgY29uc29sZWApIGZvciBhIFB1cHBldGVlciBQYWdlIGluIG9yZGVyXHJcbiAqIHRvIGNhdGNoIGFuZCBkaXNwbGF5IGVycm9ycyBhbmQgY29uc29sZSBsb2dzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VFdmVudHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBsaXN0ZW5lcnNcclxuICogYXJlIGJlaW5nIHNldC5cclxuICovXHJcbmZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzKHBhZ2UpIHtcclxuICAvLyBHZXQgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBkZWJ1ZyB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBTZXQgdGhlIGBwYWdlZXJyb3JgIGxpc3RlbmVyXHJcbiAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKCkgPT4ge1xyXG4gICAgLy8gSXQgd291bGQgc2VlbSBsaWtlIHRoaXMgbWF5IGZpcmUgYXQgdGhlIHNhbWUgdGltZSBvciBzaG9ydGx5IGJlZm9yZVxyXG4gICAgLy8gYSBwYWdlIGlzIGNsb3NlZC5cclxuICAgIGlmIChwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGBjb25zb2xlYCBsaXN0ZW5lciwgaWYgbmVlZGVkXHJcbiAgaWYgKGRlYnVnLmVuYWJsZSAmJiBkZWJ1Zy5saXN0ZW5Ub0NvbnNvbGUpIHtcclxuICAgIHBhZ2Uub24oJ2NvbnNvbGUnLCAobWVzc2FnZSkgPT4ge1xyXG4gICAgICBjb25zb2xlLmxvZyhgW2RlYnVnXSAke21lc3NhZ2UudGV4dCgpfWApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0QnJvd3NlcixcclxuICBjcmVhdGVCcm93c2VyLFxyXG4gIGNsb3NlQnJvd3NlcixcclxuICBuZXdQYWdlLFxyXG4gIGNsZWFyUGFnZSxcclxuICBhZGRQYWdlUmVzb3VyY2VzLFxyXG4gIGNsZWFyUGFnZVJlc291cmNlc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgQ1NTIHRvIGJlIHVzZWQgb24gdGhlIGV4cG9ydGVkIHBhZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBDU1MgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0ICgpID0+IGBcclxuXHJcbmh0bWwsIGJvZHkge1xyXG4gIG1hcmdpbjogMDtcclxuICBwYWRkaW5nOiAwO1xyXG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XHJcbn1cclxuXHJcbiN0YWJsZS1kaXYsICNzbGlkZXJzLCAjZGF0YXRhYmxlLCAjY29udHJvbHMsIC5sZC1yb3cge1xyXG4gIGRpc3BsYXk6IG5vbmU7XHJcbiAgaGVpZ2h0OiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyIHtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG4gIG1hcmdpbjogMDtcclxuICBvdmVyZmxvdzogYXV0bztcclxuICBmb250LXNpemU6IDA7XHJcbn1cclxuXHJcbiNjaGFydC1jb250YWluZXIgPiBmaWd1cmUsIGRpdiB7XHJcbiAgbWFyZ2luLXRvcDogMCAhaW1wb3J0YW50O1xyXG4gIG1hcmdpbi1ib3R0b206IDAgIWltcG9ydGFudDtcclxufVxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY3NzVGVtcGxhdGUgZnJvbSAnLi9jc3MuanMnO1xyXG5cclxuLyoqXHJcbiAqIFRoZSBTVkcgdGVtcGxhdGUgdG8gdXNlIHdoZW4gbG9hZGluZyBTVkcgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHN2ZyAtIFRoZSBTVkcgaW5wdXQgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIFNWRyB0ZW1wbGF0ZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChzdmcpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNoYXJ0cyBFeHBvcnQ8L3RpdGxlPlxyXG4gIDwvaGVhZD5cclxuICA8c3R5bGU+XHJcbiAgICAke2Nzc1RlbXBsYXRlKCl9XHJcbiAgPC9zdHlsZT5cclxuICA8Ym9keT5cclxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cclxuICAgICAgJHtzdmd9XHJcbiAgICA8L2Rpdj5cclxuICA8L2JvZHk+XHJcbjwvaHRtbD5cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBoYW5kbGVzIGNoYXJ0IGV4cG9ydCBmdW5jdGlvbmFsaXR5IHVzaW5nIFB1cHBldGVlci5cclxuICogSXQgc3VwcG9ydHMgZXhwb3J0aW5nIGNoYXJ0cyBhcyBTVkcsIFBORywgSlBFRywgYW5kIFBERiBmb3JtYXRzLiBUaGUgbW9kdWxlXHJcbiAqIG1hbmFnZXMgcGFnZSByZXNvdXJjZXMsIHNldHMgdXAgdGhlIGV4cG9ydCBlbnZpcm9ubWVudCwgYW5kIHByb2Nlc3NlcyBjaGFydFxyXG4gKiBjb25maWd1cmF0aW9ucyBvciBTVkcgaW5wdXRzIGZvciByZW5kZXJpbmcuIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZVxyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYWRkUGFnZVJlc291cmNlcywgY2xlYXJQYWdlUmVzb3VyY2VzIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgY3JlYXRlQ2hhcnQgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgc3ZnVGVtcGxhdGUgZnJvbSAnLi4vdGVtcGxhdGVzL3N2Z0V4cG9ydC9zdmdFeHBvcnQuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2UgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTwoc3RyaW5nfEJ1ZmZlcnxFeHBvcnRFcnJvcik+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlc1xyXG4gKiB0byB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgZXhwb3J0IHRvIGFuIHVuc3VwcG9ydGVkXHJcbiAqIG91dHB1dCBmb3JtYXQgb2NjdXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydChwYWdlLCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXkgKGFkZGl0aW9uYWwgSlMgYW5kIENTUylcclxuICBjb25zdCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICB0cnkge1xyXG4gICAgbGV0IGlzU1ZHID0gZmFsc2U7XHJcblxyXG4gICAgLy8gRGVjaWRlIG9uIHRoZSBleHBvcnQgbWV0aG9kXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy5zdmcpIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgYHR5cGVgIGlzIGFsc28gU1ZHLCByZXR1cm4gdGhlIGlucHV0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGV4cG9ydE9wdGlvbnMuc3ZnO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBNYXJrIGFzIFNWRyBleHBvcnQgZm9yIHRoZSBsYXRlciBzaXplIGNvcnJlY3Rpb25zXHJcbiAgICAgIGlzU1ZHID0gdHJ1ZTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRcclxuICAgICAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKGV4cG9ydE9wdGlvbnMuc3ZnKSwge1xyXG4gICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgIH0pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBKU09OIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoY3JlYXRlQ2hhcnQsIGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXHJcbiAgICAvLyBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAuLi4oYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBjdXN0b21Mb2dpY09wdGlvbnMpKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZSBhbmQgc2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XHJcbiAgICBjb25zdCBzaXplID0gaXNTVkdcclxuICAgICAgPyBhd2FpdCBwYWdlLmV2YWx1YXRlKChzY2FsZSkgPT4ge1xyXG4gICAgICAgICAgY29uc3Qgc3ZnRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXHJcbiAgICAgICAgICAgICcjY2hhcnQtY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJ1xyXG4gICAgICAgICAgKTtcclxuXHJcbiAgICAgICAgICAvLyBHZXQgdGhlIHZhbHVlcyBjb3JyZWN0bHkgc2NhbGVkXHJcbiAgICAgICAgICBjb25zdCBjaGFydEhlaWdodCA9IHN2Z0VsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0V2lkdGggPSBzdmdFbGVtZW50LndpZHRoLmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBJbiBjYXNlIG9mIFNWRyB0aGUgem9vbSBtdXN0IGJlIHNldCBkaXJlY3RseSBmb3IgYm9keSBhcyBzY2FsZVxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBTZXQgdGhlIG1hcmdpbiB0byAwcHhcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcclxuXHJcbiAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICBjaGFydEhlaWdodCxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aFxyXG4gICAgICAgICAgfTtcclxuICAgICAgICB9LCBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpKVxyXG4gICAgICA6IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBjb25zdCB7IGNoYXJ0SGVpZ2h0LCBjaGFydFdpZHRoIH0gPSB3aW5kb3cuSGlnaGNoYXJ0cy5jaGFydHNbMF07XHJcblxyXG4gICAgICAgICAgLy8gTm8gbmVlZCBmb3Igc3VjaCBzY2FsZSBtYW5pcHVsYXRpb24gaW4gY2FzZSBvZiBvdGhlciB0eXBlc1xyXG4gICAgICAgICAgLy8gb2YgZXhwb3J0cy4gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjbGlwIHJlZ2lvbiBmb3IgdGhlIHBhZ2VcclxuICAgIGNvbnN0IHsgeCwgeSB9ID0gYXdhaXQgX2dldENsaXBSZWdpb24ocGFnZSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGBoZWlnaHRgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRIZWlnaHQgfHwgZXhwb3J0T3B0aW9ucy5oZWlnaHQpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgd2lkdGhgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRXaWR0aCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcclxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgIH0pO1xyXG5cclxuICAgIGxldCByZXN1bHQ7XHJcbiAgICAvLyBSYXN0ZXJpemF0aW9uIHByb2Nlc3NcclxuICAgIHN3aXRjaCAoZXhwb3J0T3B0aW9ucy50eXBlKSB7XHJcbiAgICAgIGNhc2UgJ3N2Zyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVNWRyhwYWdlKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncG5nJzpcclxuICAgICAgY2FzZSAnanBlZyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZUltYWdlKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMudHlwZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIHgsXHJcbiAgICAgICAgICAgIHlcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncGRmJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlUERGKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBkZWZhdWx0OlxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBVbnN1cHBvcnRlZCBvdXRwdXQgZm9ybWF0OiAke2V4cG9ydE9wdGlvbnMudHlwZX0uYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2xlYXIgcHJldmlvdXNseSBpbmplY3RlZCBKUyBhbmQgQ1NTIHJlc291cmNlc1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMgb2YgdGhlIHNwZWNpZmllZCBwYWdlIGVsZW1lbnRcclxuICogd2l0aCB0aGUgJ2NoYXJ0LWNvbnRhaW5lcicgaWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2dldENsaXBSZWdpb25cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGB4YCwgYHlgLCBgd2lkdGhgLCBhbmQgYGhlaWdodGAgcHJvcGVydGllcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XHJcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB4LFxyXG4gICAgICB5LFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXHJcbiAgICB9O1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBTVkcgYnkgZXZhbHVhdGluZyB0aGUgYG91dGVySFRNTGAgb2YgdGhlIGZpcnN0ICdzdmcnIGVsZW1lbnRcclxuICogaW5zaWRlIGFuIGVsZW1lbnQgd2l0aCB0aGUgaWQgJ2NvbnRhaW5lcicuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVNWR1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFNWRyBzdHJpbmcuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlU1ZHKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbChcclxuICAgICcjY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJyxcclxuICAgIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIGltYWdlIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgYHNjcmVlbnNob3RgIGZ1bmN0aW9uYWxpdHkgd2l0aFxyXG4gKiBzcGVjaWZpZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlSW1hZ2VcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gSW1hZ2UgdHlwZS5cclxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgaW1hZ2UgYnVmZmVyXHJcbiAqIG9yIHJlamVjdGluZyB3aXRoIGFuIGBFeHBvcnRFcnJvcmAgZm9yIHRpbWVvdXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlSW1hZ2UocGFnZSwgdHlwZSwgY2xpcCwgcmFzdGVyaXphdGlvblRpbWVvdXQpIHtcclxuICByZXR1cm4gUHJvbWlzZS5yYWNlKFtcclxuICAgIHBhZ2Uuc2NyZWVuc2hvdCh7XHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGNsaXAsXHJcbiAgICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgICAgZnVsbFBhZ2U6IGZhbHNlLFxyXG4gICAgICBvcHRpbWl6ZUZvclNwZWVkOiB0cnVlLFxyXG4gICAgICBjYXB0dXJlQmV5b25kVmlld3BvcnQ6IHRydWUsXHJcbiAgICAgIC4uLih0eXBlICE9PSAncG5nJyA/IHsgcXVhbGl0eTogODAgfSA6IHt9KSxcclxuICAgICAgLy8gQWx3YXlzIHJlbmRlciBvbiBhIHRyYW5zcGFyZW50IHBhZ2UgaWYgdGhlIGV4cGVjdGVkIHR5cGUgZm9ybWF0IGlzIFBOR1xyXG4gICAgICBvbWl0QmFja2dyb3VuZDogdHlwZSA9PSAncG5nJyAvLyAjNDQ3LCAjNDYzXHJcbiAgICB9KSxcclxuICAgIG5ldyBQcm9taXNlKChfcmVzb2x2ZSwgcmVqZWN0KSA9PlxyXG4gICAgICBzZXRUaW1lb3V0KFxyXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcsIDQwOCkpLFxyXG4gICAgICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICAgICAgKVxyXG4gICAgKVxyXG4gIF0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIFBERiB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBwZGZgIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlUERGXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0IC0gUERGIGhlaWdodC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoIC0gUERGIHdpZHRoLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFBERiBidWZmZXIuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlUERGKHBhZ2UsIGhlaWdodCwgd2lkdGgsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgYXdhaXQgcGFnZS5lbXVsYXRlTWVkaWFUeXBlKCdzY3JlZW4nKTtcclxuICByZXR1cm4gcGFnZS5wZGYoe1xyXG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXHJcbiAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICB3aWR0aCxcclxuICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgIHRpbWVvdXQ6IHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHB1cHBldGVlckV4cG9ydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgYSB3b3JrZXIgcG9vbCBpbXBsZW1lbnRhdGlvbiBmb3IgbWFuYWdpbmdcclxuICogdGhlIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHBhZ2VzLCBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIHVzZSB3aXRoXHJcbiAqIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuIEl0IG9wdGltaXplcyByZXNvdXJjZXMgdXNhZ2UgYW5kIHBlcmZvcm1hbmNlXHJcbiAqIGJ5IG1haW50YWluaW5nIGEgcG9vbCBvZiB3b3JrZXJzIHRoYXQgY2FuIGhhbmRsZSBjb25jdXJyZW50IGV4cG9ydCB0YXNrc1xyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgUG9vbCB9IGZyb20gJ3Rhcm4nO1xyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBjcmVhdGVCcm93c2VyLCBjbG9zZUJyb3dzZXIsIG5ld1BhZ2UsIGNsZWFyUGFnZSB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IHB1cHBldGVlckV4cG9ydCB9IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldE5ld0RhdGVUaW1lLCBtZWFzdXJlVGltZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBwb29sIGluc3RhbmNlXHJcbmxldCBwb29sID0gbnVsbDtcclxuXHJcbi8vIFBvb2wgc3RhdGlzdGljc1xyXG5jb25zdCBwb29sU3RhdHMgPSB7XHJcbiAgZXhwb3J0c0F0dGVtcHRlZDogMCxcclxuICBleHBvcnRzUGVyZm9ybWVkOiAwLFxyXG4gIGV4cG9ydHNEcm9wcGVkOiAwLFxyXG4gIGV4cG9ydHNGcm9tU3ZnOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uczogMCxcclxuICBleHBvcnRzRnJvbVN2Z0F0dGVtcHRzOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzOiAwLFxyXG4gIHRpbWVTcGVudDogMCxcclxuICB0aW1lU3BlbnRBdmVyYWdlOiAwXHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwb29sIHdpdGggdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24sIGNyZWF0aW5nXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhbmQgc2V0dGluZyB1cCB3b3JrZXIgcmVzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGluaXRQb29sXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlclxyXG4gKiBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBlbmRpbmcgdGhlIGZ1bmN0aW9uXHJcbiAqIGV4ZWN1dGlvbiB3aGVuIGFuIGFscmVhZHkgaW5pdGlhbGl6ZWQgcG9vbCBvZiByZXNvdXJjZXMgaXMgZm91bmQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY3JlYXRlIHRoZSBwb29sXHJcbiAqIG9mIHdvcmtlcnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdFBvb2wocG9vbE9wdGlvbnMsIHB1cHBldGVlckFyZ3MpIHtcclxuICAvLyBDcmVhdGUgYSBicm93c2VyIGluc3RhbmNlIHdpdGggdGhlIHB1cHBldGVlciBhcmd1bWVudHNcclxuICBhd2FpdCBjcmVhdGVCcm93c2VyKHB1cHBldGVlckFyZ3MpO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIEluaXRpYWxpemluZyBwb29sIHdpdGggd29ya2VyczogbWluICR7cG9vbE9wdGlvbnMubWluV29ya2Vyc30sIG1heCAke3Bvb2xPcHRpb25zLm1heFdvcmtlcnN9LmBcclxuICAgICk7XHJcblxyXG4gICAgaWYgKHBvb2wpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXAgYW4gZXllIG9uIGEgY29ycmVjdCBtaW4gYW5kIG1heCB3b3JrZXJzIG51bWJlclxyXG4gICAgaWYgKHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzKSB7XHJcbiAgICAgIHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPSBwb29sT3B0aW9ucy5tYXhXb3JrZXJzO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENyZWF0ZSBhIHBvb2wgYWxvbmcgd2l0aCBhIG1pbmltYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgcG9vbCA9IG5ldyBQb29sKHtcclxuICAgICAgLy8gR2V0IHRoZSBgY3JlYXRlYCwgYHZhbGlkYXRlYCwgYW5kIGBkZXN0cm95YCBmdW5jdGlvbnNcclxuICAgICAgLi4uX2ZhY3RvcnkocG9vbE9wdGlvbnMpLFxyXG4gICAgICBtaW46IHBvb2xPcHRpb25zLm1pbldvcmtlcnMsXHJcbiAgICAgIG1heDogcG9vbE9wdGlvbnMubWF4V29ya2VycyxcclxuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmFjcXVpcmVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVUaW1lb3V0LFxyXG4gICAgICBkZXN0cm95VGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuZGVzdHJveVRpbWVvdXQsXHJcbiAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5pZGxlVGltZW91dCxcclxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlUmV0cnlJbnRlcnZhbCxcclxuICAgICAgcmVhcEludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5yZWFwZXJJbnRlcnZhbCxcclxuICAgICAgcHJvcGFnYXRlQ3JlYXRlRXJyb3I6IGZhbHNlXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBTZXQgZXZlbnRzXHJcbiAgICBwb29sLm9uKCdyZWxlYXNlJywgYXN5bmMgKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIENsZWFyIHBhZ2VcclxuICAgICAgY29uc3QgY2xlYXJTdGF0dXMgPSBhd2FpdCBjbGVhclBhZ2UocmVzb3VyY2UsIGZhbHNlKTtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBSZWxlYXNpbmcgYSB3b3JrZXIuIENsZWFyIHBhZ2Ugc3RhdHVzOiAke2NsZWFyU3RhdHVzfS5gXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChfZXZlbnRJZCwgcmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBEZXN0cm95ZWQgYSB3b3JrZXIgc3VjY2Vzc2Z1bGx5LmBcclxuICAgICAgKTtcclxuICAgICAgcmVzb3VyY2UucGFnZSA9IG51bGw7XHJcbiAgICB9KTtcclxuXHJcbiAgICBjb25zdCBpbml0aWFsUmVzb3VyY2VzID0gW107XHJcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBvb2xPcHRpb25zLm1pbldvcmtlcnM7IGkrKykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuICAgICAgICBpbml0aWFsUmVzb3VyY2VzLnB1c2gocmVzb3VyY2UpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIGFuIGluaXRpYWwgcmVzb3VyY2UuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXMgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgaW5pdGlhbFJlc291cmNlcy5mb3JFYWNoKChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICBwb29sLnJlbGVhc2UocmVzb3VyY2UpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIFRoZSBwb29sIGlzIHJlYWR5JHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aCA/IGAgd2l0aCAke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RofSBpbml0aWFsIHJlc291cmNlcyB3YWl0aW5nLmAgOiAnLid9YFxyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3Bvb2xdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogVGVybWluYXRlcyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24ga2lsbFBvb2xcclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgYWxsIHdvcmtlcnMgYXJlXHJcbiAqIHRlcm1pbmF0ZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIHN1Y2Nlc3NmdWxseSBjbG9zZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XHJcbiAgbG9nKDMsICdbcG9vbF0gS2lsbGluZyBwb29sIHdpdGggYWxsIHdvcmtlcnMgYW5kIGNsb3NpbmcgYnJvd3Nlci4nKTtcclxuXHJcbiAgLy8gSWYgc3RpbGwgYWxpdmUsIGRlc3Ryb3kgdGhlIHBvb2wgb2YgcGFnZXMgYmVmb3JlIGNsb3NpbmcgYSBicm93c2VyXHJcbiAgaWYgKHBvb2wpIHtcclxuICAgIC8vIEZyZWUgdXAgbm90IHJlbGVhc2VkIHdvcmtlcnNcclxuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHBvb2wudXNlZCkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VyLnJlc291cmNlKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IHRoZSBwb29sIGlmIGl0IGlzIHN0aWxsIGF2YWlsYWJsZVxyXG4gICAgaWYgKCFwb29sLmRlc3Ryb3llZCkge1xyXG4gICAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gRGVzdHJveWVkIHRoZSBwb29sIG9mIHJlc291cmNlcy4nKTtcclxuICAgIH1cclxuICAgIHBvb2wgPSBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICBhd2FpdCBjbG9zZUJyb3dzZXIoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByb2Nlc3NlcyB0aGUgZXhwb3J0IHdvcmsgdXNpbmcgYSB3b3JrZXIgZnJvbSB0aGUgcG9vbC4gQWNxdWlyZXMgYSB3b3JrZXJcclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcclxuICogdGhlIHdvcmtlciBoYW5kbGUgYmFjayB0byB0aGUgcG9vbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0V29ya1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZXhwb3J0IHJlc3VsdFxyXG4gKiBhbmQgb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3RXb3JrKG9wdGlvbnMpIHtcclxuICBsZXQgd29ya2VySGFuZGxlO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgICAvLyBBbiBleHBvcnQgYXR0ZW1wdCBjb3VudGVkXHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzQXR0ZW1wdGVkO1xyXG5cclxuICAgIC8vIERpc3BsYXkgdGhlIHBvb2wgaW5mb3JtYXRpb24gaWYgbmVlZGVkXHJcbiAgICBpZiAob3B0aW9ucy5wb29sLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGluIGNhc2Ugb2YgbGFja2luZyB0aGUgcG9vbCBpbnN0YW5jZVxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhlIGFjcXVpcmUgY291bnRlclxyXG4gICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFRyeSB0byBhY3F1aXJlIHRoZSB3b3JrZXIgYWxvbmcgd2l0aCB0aGUgaWQsIHdvcmtzIGNvdW50IGFuZCBwYWdlXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgICAgLy8gQWNxdWlyZSBhIHBvb2wgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxyXG4gICAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgICAgYEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUgdG9vayAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFJlc29sdmVkIHdvcmtlciBwYWdlIGlzIGludmFsaWQ6IHRoZSBwb29sIHNldHVwIGlzIHdvbmt5LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgY29uc3Qgd29ya1N0YXJ0ID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHt3b3JrZXJIYW5kbGUuaWR9XSAtIFN0YXJ0aW5nIHdvcmsgb24gdGhpcyBwb29sIGVudHJ5LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU3RhcnQgbWVhc3VyaW5nIGV4cG9ydCB0aW1lXHJcbiAgICBjb25zdCBleHBvcnRDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KFxyXG4gICAgICB3b3JrZXJIYW5kbGUucGFnZSxcclxuICAgICAgb3B0aW9ucy5leHBvcnQsXHJcbiAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKHJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIElmIHRoZXJlJ3MgYSByYXN0ZXJpemF0aW9uIHRpbWVvdXQsIHdlIHdhbnQgbmVlZCB0byBmbHVzaCB0aGUgcGFnZS5cclxuICAgICAgLy8gVGhpcyBpcyBiZWNhdXNlIHRoZSBwYWdlIG1heSBiZSBpbiBhIHN0YXRlIHdoZXJlIGl0J3Mgd2FpdGluZyBmb3JcclxuICAgICAgLy8gdGhlIHNjcmVlbnNob3QgdG8gZmluaXNoIGV2ZW4gdGhvdWdoIHRoZSB0aW1lb3V0IGhhcyBvY2N1cmVkLlxyXG4gICAgICAvLyBXaGljaCBvZiBjb3Vyc2UgY2F1c2VzIGEgbG90IG9mIGlzc3VlcyB3aXRoIHRoZSBldmVudCBzeXN0ZW0sXHJcbiAgICAgIC8vIGFuZCBwYWdlIGNvbnNpc3RlbmN5LlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBPbmx5IHBhZ2Uuc2NyZWVuc2hvdCB3aWxsIHRocm93IHRoaXMsIHRpbWVvdXRzIGZvciBQREYncyBhcmVcclxuICAgICAgLy8gaGFuZGxlZCBieSB0aGUgcGFnZS5wZGYgZnVuY3Rpb24gaXRzZWxmLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyAuLi55ZXMsIHRoaXMgaXMgdWdseS5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlID0gbnVsbDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJlc3VsdC5uYW1lID09PSAnVGltZW91dEVycm9yJyB8fFxyXG4gICAgICAgIHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0J1xyXG4gICAgICApIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfVJhc3Rlcml6YXRpb24gdGltZW91dDogeW91ciBjaGFydCBtYXkgYmUgdG9vIGNvbXBsZXggb3IgbGFyZ2UsIGFuZCBmYWlsZWQgdG8gcmVuZGVyIHdpdGhpbiB0aGUgYWxsb3R0ZWQgdGltZS5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGV4cG9ydDogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIHRoZSBQdXBwZXRlZXIgZXhwb3J0IHRpbWVcclxuICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDUsXHJcbiAgICAgICAgYFtiZW5jaG1hcmtdICR7b3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnfWAsXHJcbiAgICAgICAgYEV4cG9ydGluZyBhIGNoYXJ0IHN1Y2Vzc2Z1bGx5IHRvb2sgJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIHJlc291cmNlIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG5cclxuICAgIC8vIFVzZWQgZm9yIHN0YXRpc3RpY3MgaW4gYXZlcmFnZVRpbWUgYW5kIHByb2Nlc3NlZFdvcmtDb3VudCwgd2hpY2hcclxuICAgIC8vIGluIHR1cm4gaXMgdXNlZCBieSB0aGUgL2hlYWx0aCByb3V0ZS5cclxuICAgIGNvbnN0IHdvcmtFbmQgPSBnZXROZXdEYXRlVGltZSgpO1xyXG4gICAgY29uc3QgZXhwb3J0VGltZSA9IHdvcmtFbmQgLSB3b3JrU3RhcnQ7XHJcblxyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UgPVxyXG4gICAgICBwb29sU3RhdHMudGltZVNwZW50IC8gKytwb29sU3RhdHMuZXhwb3J0c1BlcmZvcm1lZDtcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBXb3JrIGNvbXBsZXRlZCBpbiAke2V4cG9ydFRpbWV9bXMuYCk7XHJcblxyXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICByZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICsrcG9vbFN0YXRzLmV4cG9ydHNEcm9wcGVkO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhyb3cgZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fG51bGwpfSBUaGUgY3VycmVudCBwb29sIGluc3RhbmNlIGlmIGluaXRpYWxpemVkLCBvciBudWxsXHJcbiAqIGlmIHRoZSBwb29sIGhhcyBub3QgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2woKSB7XHJcbiAgcmV0dXJuIHBvb2w7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBzdGF0aXN0aWMgb2YgYSBwb29sIGluc3RhY2UgYWJvdXQgZXhwb3J0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xTdGF0c1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY3VycmVudCBwb29sIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbFN0YXRzKCkge1xyXG4gIHJldHVybiBwb29sU3RhdHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgcG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdCwgaW5jbHVkaW5nIG1pbmltdW0gYW5kIG1heGltdW1cclxuICogd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OKCkge1xyXG4gIHJldHVybiB7XHJcbiAgICBtaW46IHBvb2wubWluLFxyXG4gICAgbWF4OiBwb29sLm1heCxcclxuICAgIHVzZWQ6IHBvb2wubnVtVXNlZCgpLFxyXG4gICAgYXZhaWxhYmxlOiBwb29sLm51bUZyZWUoKSxcclxuICAgIGFsbENyZWF0ZWQ6IHBvb2wubnVtVXNlZCgpICsgcG9vbC5udW1GcmVlKCksXHJcbiAgICBwZW5kaW5nQWNxdWlyZXM6IHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCksXHJcbiAgICBwZW5kaW5nQ3JlYXRlczogcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zOiBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzOiBwb29sLnBlbmRpbmdEZXN0cm95cy5sZW5ndGgsXHJcbiAgICBhYnNvbHV0ZUFsbDpcclxuICAgICAgcG9vbC5udW1Vc2VkKCkgK1xyXG4gICAgICBwb29sLm51bUZyZWUoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdDcmVhdGVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpICtcclxuICAgICAgcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoXHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHBvb2wsIGluY2x1ZGluZyB0aGUgbWluaW11bVxyXG4gKiBhbmQgbWF4aW11bSB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmVcclxuICogcmVxdWVzdHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sSW5mb1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvKCkge1xyXG4gIGNvbnN0IHtcclxuICAgIG1pbixcclxuICAgIG1heCxcclxuICAgIHVzZWQsXHJcbiAgICBhdmFpbGFibGUsXHJcbiAgICBhbGxDcmVhdGVkLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXMsXHJcbiAgICBwZW5kaW5nVmFsaWRhdGlvbnMsXHJcbiAgICBwZW5kaW5nRGVzdHJveXMsXHJcbiAgICBhYnNvbHV0ZUFsbFxyXG4gIH0gPSBnZXRQb29sSW5mb0pTT04oKTtcclxuXHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1pbmltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWlufS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttYXh9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgdXNlZCByZXNvdXJjZXM6ICR7dXNlZH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBmcmVlIHJlc291cmNlczogJHthdmFpbGFibGV9LmApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIGNyZWF0ZWQgKHVzZWQgYW5kIGZyZWUpIHJlc291cmNlczogJHthbGxDcmVhdGVkfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGFjcXVpcmVkOiAke3BlbmRpbmdBY3F1aXJlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBjcmVhdGVkOiAke3BlbmRpbmdDcmVhdGVzfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIHZhbGlkYXRlZDogJHtwZW5kaW5nVmFsaWRhdGlvbnN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgZGVzdHJveWVkOiAke3BlbmRpbmdEZXN0cm95c30uYFxyXG4gICk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgcmVzb3VyY2VzOiAke2Fic29sdXRlQWxsfS5gKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZhY3RvcnkgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGFuIG9iamVjdCB3aXRoIGBjcmVhdGVgLCBgdmFsaWRhdGVgLFxyXG4gKiBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9ucyBmb3IgdGhlIHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmFjdG9yeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9mYWN0b3J5KHBvb2xPcHRpb25zKSB7XHJcbiAgcmV0dXJuIHtcclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyB3b3JrZXIgcGFnZSBmb3IgdGhlIGV4cG9ydCBwb29sLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGNyZWF0ZVxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdFxyXG4gICAgICogY29udGFpbmluZyB0aGUgd29ya2VyIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgaW5pdGlhbFxyXG4gICAgICogd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gZXJyb3IgZHVyaW5nXHJcbiAgICAgKiB0aGUgY3JlYXRpb24gb2YgdGhlIG5ldyBwYWdlLlxyXG4gICAgICovXHJcbiAgICBjcmVhdGU6IGFzeW5jICgpID0+IHtcclxuICAgICAgLy8gSW5pdCB0aGUgcmVzb3VyY2Ugd2l0aCB1bmlxdWUgaWQgYW5kIHdvcmsgY291bnRcclxuICAgICAgY29uc3QgcG9vbFJlc291cmNlID0ge1xyXG4gICAgICAgIGlkOiB1dWlkKCksXHJcbiAgICAgICAgLy8gVHJ5IHRvIGRpc3RyaWJ1dGUgdGhlIGluaXRpYWwgd29yayBjb3VudFxyXG4gICAgICAgIHdvcmtDb3VudDogTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogKHBvb2xPcHRpb25zLndvcmtMaW1pdCAvIDIpKVxyXG4gICAgICB9O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBTdGFydCBtZWFzdXJpbmcgYSBwYWdlIGNyZWF0aW9uIHRpbWVcclxuICAgICAgICBjb25zdCBzdGFydERhdGUgPSBnZXROZXdEYXRlVGltZSgpO1xyXG5cclxuICAgICAgICAvLyBDcmVhdGUgYSBuZXcgcGFnZVxyXG4gICAgICAgIGF3YWl0IG5ld1BhZ2UocG9vbFJlc291cmNlKTtcclxuXHJcbiAgICAgICAgLy8gTWVhc3VyZSB0aGUgdGltZSBvZiBmdWxsIGNyZWF0aW9uIGFuZCBjb25maWd1cmF0aW9uIG9mIGEgcGFnZVxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBhIHdvcmtlciwgdG9vayAke1xyXG4gICAgICAgICAgICBnZXROZXdEYXRlVGltZSgpIC0gc3RhcnREYXRlXHJcbiAgICAgICAgICB9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHVybiByZWFkeSBwb29sIHJlc291cmNlXHJcbiAgICAgICAgcmV0dXJuIHBvb2xSZXNvdXJjZTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBjcmVhdGluZyBhIG5ldyBwYWdlLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcclxuICAgICAqIHRoZSB3b3JrIGxpbWl0LlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIHZhbGlkYXRlXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHBvb2xSZXNvdXJjZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAgICogdGhlIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBpZiB0aGUgd29ya2VyXHJcbiAgICAgKiBpcyB2YWxpZCBhbmQgd2l0aGluIHRoZSB3b3JrIGxpbWl0OyBvdGhlcndpc2UsIHRvIGZhbHNlLlxyXG4gICAgICovXHJcbiAgICB2YWxpZGF0ZTogYXN5bmMgKHBvb2xSZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJbiBjZXJ0YWluIGNhc2VzIGFjcXVpcmluZyB0aHJvd3MgYSBUYXJnZXRDbG9zZUVycm9yLCB3aGljaCBtYXlcclxuICAgICAgLy8gYmUgY2F1c2VkIGJ5IHR3byB0aGluZ3M6XHJcbiAgICAgIC8vIC0gVGhlIHBhZ2UgaXMgY2xvc2VkIGFuZCBhdHRlbXB0ZWQgdG8gYmUgcmV1c2VkLlxyXG4gICAgICAvLyAtIExvc3QgY29udGFjdCB3aXRoIHRoZSBicm93c2VyLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBXaGF0IHdlJ3JlIHNlZWluZyBpbiBsb2dzIGlzIHRoYXQgc3VjY2Vzc2l2ZSBleHBvcnRzIHR5cGljYWxseVxyXG4gICAgICAvLyBzdWNjZWVkcywgYW5kIHRoZSBzZXJ2ZXIgcmVjb3ZlcnMsIGluZGljYXRpbmcgdGhhdCBpdCdzIGxpa2VseVxyXG4gICAgICAvLyB0aGUgZmlyc3QgY2FzZS4gVGhpcyBpcyBhbiBhdHRlbXB0IGF0IGFsbGlldmF0aW5nIHRoZSBpc3N1ZSBieVxyXG4gICAgICAvLyBzaW1wbHkgbm90IHZhbGlkYXRpbmcgdGhlIHdvcmtlciBpZiB0aGUgcGFnZSBpcyBudWxsIG9yIGNsb3NlZC5cclxuICAgICAgLy9cclxuICAgICAgLy8gVGhlIGFjdHVhbCByZXN1bHQgZnJvbSB3aGVuIHRoaXMgaGFwcGVuZWQsIHdhcyB0aGF0IGEgd29ya2VyIHdvdWxkXHJcbiAgICAgIC8vIGJlIGNvbXBsZXRlbHkgbG9ja2VkLCBzdG9wcGluZyBpdCBmcm9tIGJlaW5nIGFjcXVpcmVkIHVudGlsXHJcbiAgICAgIC8vIGl0cyB3b3JrIGNvdW50IHJlYWNoZWQgdGhlIGxpbWl0LlxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBwYWdlYCBpcyB2YWxpZFxyXG4gICAgICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChubyB2YWxpZCBwYWdlIGlzIGZvdW5kKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgY2xvc2VkXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlIGlzIGNsb3NlZCBvciBpbnZhbGlkKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgbWFpbkZyYW1lYCBpcyBkZXRhY2hlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UubWFpbkZyYW1lKCkuZGV0YWNoZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKHBhZ2UncyBmcmFtZSBpcyBkZXRhY2hlZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHdvcmtMaW1pdGAgaXMgZXhjZWVkZWRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHBvb2xPcHRpb25zLndvcmtMaW1pdCAmJlxyXG4gICAgICAgICsrcG9vbFJlc291cmNlLndvcmtDb3VudCA+IHBvb2xPcHRpb25zLndvcmtMaW1pdFxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKGV4Y2VlZGVkIHRoZSAke3Bvb2xPcHRpb25zLndvcmtMaW1pdH0gd29ya3MgcGVyIHJlc291cmNlIGxpbWl0KS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSBgcG9vbFJlc291cmNlYCBpcyB2YWxpZGF0ZWRcclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gZGVzdHJveVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKi9cclxuICAgIGRlc3Ryb3k6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRGVzdHJveWluZyBhIHdvcmtlci5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgLy8gUmVtb3ZlIGFsbCBhdHRhY2hlZCBldmVudCBsaXN0ZW5lcnMgZnJvbSB0aGUgcmVzb3VyY2VcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygncGFnZWVycm9yJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2NvbnNvbGUnKTtcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygnZnJhbWVkZXRhY2hlZCcpO1xyXG5cclxuICAgICAgICAgIC8vIFdlIG5lZWQgdG8gd2FpdCBhcm91bmQgZm9yIHRoaXNcclxuICAgICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gUGFnZSBjb3VsZCBub3QgYmUgY2xvc2VkIHVwb24gZGVzdHJveWluZy5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGluaXRQb29sLFxyXG4gIGtpbGxQb29sLFxyXG4gIHBvc3RXb3JrLFxyXG4gIGdldFBvb2wsXHJcbiAgZ2V0UG9vbFN0YXRzLFxyXG4gIGdldFBvb2xJbmZvLFxyXG4gIGdldFBvb2xJbmZvSlNPTlxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVXNlZCB0byBzYW5pdGl6ZSB0aGUgc3RyaW5ncyBjb21pbmcgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZVxyXG4gKiB0byBwcmV2ZW50IFhTUyBhdHRhY2tzICh3aXRoIHRoZSBET01QdXJpZnkgbGlicmFyeSkuXHJcbiAqL1xyXG5cclxuaW1wb3J0IERPTVB1cmlmeSBmcm9tICdkb21wdXJpZnknO1xyXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcclxuXHJcbi8qKlxyXG4gKiBTYW5pdGl6ZXMgYSBnaXZlbiBIVE1MIHN0cmluZyBieSByZW1vdmluZyA8c2NyaXB0PiB0YWdzLiBUaGlzIGZ1bmN0aW9uIHVzZXNcclxuICogYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gZmluZCBhbmQgcmVtb3ZlIGFsbCBvY2N1cnJlbmNlcyBvZiA8c2NyaXB0Pjwvc2NyaXB0PlxyXG4gKiB0YWdzIGFuZCBhbnkgY29udGVudCB3aXRoaW4gdGhlbS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNhbml0aXplXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCAtIFRoZSBIVE1MIHN0cmluZyB0byBiZSBzYW5pdGl6ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTCBzdHJpbmcuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXQpIHtcclxuICAvLyBHZXQgdGhlIHZpcnR1YWwgRE9NXHJcbiAgY29uc3Qgd2luZG93ID0gbmV3IEpTRE9NKCcnKS53aW5kb3c7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHB1cmlmeWluZyBpbnN0YW5jZVxyXG4gIGNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cpO1xyXG5cclxuICAvLyBSZXR1cm4gc2FuaXRpemVkIGlucHV0XHJcbiAgcmV0dXJuIHB1cmlmeS5zYW5pdGl6ZShpbnB1dCwgeyBBRERfVEFHUzogWydmb3JlaWduT2JqZWN0J10gfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzYW5pdGl6ZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIHRvIHByZXBhcmUgZm9yIHRoZSBleHBvcnRpbmcgY2hhcnRzXHJcbiAqIGludG8gdmFyaW91cyBpbWFnZSBvdXRwdXQgZm9ybWF0cyBzdWNoIGFzIEpQRUcsIFBORywgUERGLCBhbmQgU1ZHcy5cclxuICogSXQgc3VwcG9ydHMgc2luZ2xlIGFuZCBiYXRjaCBleHBvcnQgb3BlcmF0aW9ucyBhbmQgYWxsb3dzIGN1c3RvbWl6YXRpb25cclxuICogdGhyb3VnaCBvcHRpb25zIHBhc3NlZCBmcm9tIGNvbmZpZ3VyYXRpb25zIG9yIEFQSXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnLCB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBpc09iamVjdCxcclxuICByb3VuZE51bWJlcixcclxuICB3cmFwQXJvdW5kXHJcbn0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGdsb2JhbCBmbGFnIGZvciB0aGUgY29kZSBleGVjdXRpb24gcGVybWlzc2lvblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucyBhbmQgc2F2ZXNcclxuICogdGhlIHJlc3VsdGluZyBpbWFnZSB0byB0aGUgcHJvdmlkZWQgb3V0cHV0IGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIFRoZSBvYmplY3QgbXVzdCBjb250YWluIGF0IGxlYXN0IG9uZVxyXG4gKiBvZiB0aGUgZm9sbG93aW5nIGBleHBvcnRgIHByb3BlcnRpZXM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgXHJcbiAqIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpbmdsZUV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0KSB7XHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQoXHJcbiAgICAgIHsgZXhwb3J0OiBvcHRpb25zLmV4cG9ydCwgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWMgfSxcclxuICAgICAgYXN5bmMgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSBvbmUgb2YgdGhlIGZvbGxvd2luZyBvcHRpb25zOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIGluZm9ybWF0aW9uXHJcbiAqIHByb3ZpZGVkIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZ1xyXG4gKiBmb3JtYXQ6IFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzXHJcbiAqIGFyZSBzYXZlZCB0byB0aGUgc3BlY2lmaWVkIG91dHB1dCBmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIGZyb21cclxuICogdGhlIGBleHBvcnRgIHNlY3Rpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3NlcyBhcmUgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJhdGNoRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQgJiYgb3B0aW9ucy5leHBvcnQuYmF0Y2gpIHtcclxuICAgIC8vIEFuIGFycmF5IGZvciBjb2xsZWN0aW5nIGJhdGNoIGV4cG9ydHNcclxuICAgIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIGBiYXRjaGAgYXJndW1lbnRzXHJcbiAgICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykgfHwgW10pIHtcclxuICAgICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgaW1hZ2VPcHRpb25zYCBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXRcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgcGFydGlhbFxyXG4gKiBvcHRpb25zIGFyZSBwcm92aWRlZCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICpcclxuICogVGhlIGBlbmRDYWxsYmFja2AgZnVuY3Rpb24gaXMgaW52b2tlZCB1cG9uIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBleHBvcnQsXHJcbiAqIGVpdGhlciBzdWNjZXNzZnVsbHkgb3Igd2l0aCBhbiBlcnJvci4gVGhlIGBlcnJvcmAgb2JqZWN0IGlzIHByb3ZpZGVkXHJcbiAqIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIGNvbnRhaW5pbmdcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5XHJcbiAqIGFuZCB0aGUgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW1hZ2VPcHRpb25zIC0gVGhlIGBpbWFnZU9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkXHJcbiAqIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgdGhlIHByb3ZpZGVkXHJcbiAqIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHRoZSBleHBvcnQgcHJvY2VzcyBvciB1cG9uIGVuY291bnRlcmluZyBhbiBlcnJvci4gVGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGlzIHRoZSBgZXJyb3JgIG9iamVjdCwgYW5kIHRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIGBkYXRhYCBvYmplY3QsXHJcbiAqIHdoaWNoIGluY2x1ZGVzIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YFxyXG4gKiBwcm9wZXJ0eSBhbmQgdGhlIGZ1bGwgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGltYWdlT3B0aW9ucywgZW5kQ2FsbGJhY2spIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgb3B0aW9ucyBhcmUgaW4gYW4gb2JqZWN0XHJcbiAgICBpZiAoIWlzT2JqZWN0KGltYWdlT3B0aW9ucykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgcHJvdmlkZWQgYGltYWdlT3B0aW9uc2AuIE5lZWRzIHRvIGJlIGFuIG9iamVjdC4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE1lcmdlIGFkZGl0aW9uYWwgb3B0aW9ucyB0byB0aGUgY29weSBvZiB0aGUgaW5zdGFuY2Ugb3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoXHJcbiAgICAgIHtcclxuICAgICAgICBleHBvcnQ6IGltYWdlT3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgY3VzdG9tTG9naWM6IGltYWdlT3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICByZXR1cm4gcG9zdFdvcmsob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBmb3IgY2hhcnQgZXhwb3J0cyBiYXNlZFxyXG4gKiBvbiB0aGUgcHJvdmlkZWQgZXhwb3J0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICogMS4gVGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBgc2NhbGVgIGZyb20gdGhlIGBleHBvcnRPcHRpb25zYC5cclxuICogMi4gT3B0aW9ucyBmcm9tIHRoZSBjaGFydCBjb25maWd1cmF0aW9uIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBnbG9iYWwgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgdGhlbWUgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCBzZWN0aW9ucykuXHJcbiAqIDUuIEZhbGxiYWNrIGRlZmF1bHQgdmFsdWVzIChgaGVpZ2h0ID0gNDAwYCwgYHdpZHRoID0gNjAwYCwgYHNjYWxlID0gMWApLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZpbmRDaGFydFNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb2RlIGV4ZWN1dGlvblxyXG4gKiBpcyBub3QgYWxsb3dlZCBidXQgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYXJlIHN0aWxsIHByb3ZpZGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgLy8gSW4gY2FzZSBvZiBhbGxvd2luZyBjb2RlIGV4ZWN1dGlvblxyXG4gIGlmIChhbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIC8vIFByb2Nlc3MgdGhlIGByZXNvdXJjZXNgIG9wdGlvblxyXG4gICAgaWYgKHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID09PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBDdXN0b20gc3RyaW5naWZpZWQgcmVzb3VyY2VzXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gTG9hZCB0aGUgZGVmYXVsdCBvbmVcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoJ3Jlc291cmNlcy5qc29uJyksICd1dGY4JyksXHJcbiAgICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IGByZXNvdXJjZXMuanNvbmAgZmlsZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGN1c3RvbSBjb2RlIGFuZCB3cmFwIGFyb3VuZCBpdCBpbiBhIHNlbGYgaW52b2tpbmcgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1tjaGFydF0gVGhlIGBjdXN0b21Db2RlYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gVHJ5IHRvIGxvYWQgY2FsbGJhY2sgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2ssXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY2FsbGJhY2tgIGNhbm5vdCBiZSBsb2FkZWQuJyk7XHJcblxyXG4gICAgICAvLyBJbiBjYXNlIG9mIGFuIGVycm9yLCBzZXQgdGhlIG9wdGlvbiB3aXRoIG51bGxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGN1c3RvbUNvZGVgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGN1c3RvbUNvZGVgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGNhbGxiYWNrYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgY2FsbGJhY2tgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHJlc291cmNlc2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gSWYgdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIGZsYWcgaXMgc2V0IHRvIGZhbHNlLCB3ZSBzaG91bGQgcmVmdXNlXHJcbiAgICAvLyB0aGUgdXNhZ2Ugb2YgdGhlIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgLCBhbmQgYGN1c3RvbUNvZGVgIG9wdGlvbnMuXHJcbiAgICAvLyBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbCByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LlxyXG4gICAgaWYgKFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFJlc2V0IGFsbCBjdXN0b20gY29kZSBvcHRpb25zXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcblxyXG4gICAgICAvLyBTZW5kIGEgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydCB0aGVzZSBzZXR0aW5nc1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnLCBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gLFxyXG4gICAgICAgIDQwM1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZnJvbSB0aGUgYHJlc291cmNlc2Agb3B0aW9uIGZvciBleHBvcnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFtyZXNvdXJjZXM9bnVsbF0gLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuXHJcbiAqIENhbiBiZSBlaXRoZXIgYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiwgYSBwYXRoIHRvIGEgSlNPTiBmaWxlLFxyXG4gKiBvciBudWxsLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgbnVsbGAuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2FkaW5nXHJcbiAqIHJlc291cmNlcyBmcm9tIGZpbGVzIGlzIGFsbG93ZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGhhbmRsZWQgcmVzb3VyY2VzIG9yIG51bGwgaWYgbm8gdmFsaWQgcmVzb3VyY2VzXHJcbiAqIGFyZSBmb3VuZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgcmVzb3VyY2VzID0gbnVsbCxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIExpc3Qgb2YgYWxsb3dlZCBzZWN0aW9ucyBpbiB0aGUgcmVzb3VyY2VzIEpTT05cclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKHJlc291cmNlcyksICd1dGY4JyksXHJcbiAgICAgICAgZmFsc2UsXHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxyXG4gICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhyZXNvdXJjZXMsIGZhbHNlLCBhbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIGZpbGVzIHNlY3Rpb25cclxuICAgIGlmIChoYW5kbGVkUmVzb3VyY2VzICYmICFhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaWx0ZXIgZnJvbSB1bm5lY2Vzc2FyeSBwcm9wZXJ0aWVzXHJcbiAgZm9yIChjb25zdCBwcm9wTmFtZSBpbiBoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXNbcHJvcE5hbWVdO1xyXG4gICAgfSBlbHNlIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiBhbGxvd2VkIHByb3BlcnRpZXMgaXMgcHJlc2VudFxyXG4gIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgbG9hZGluZyBhbmQgdmFsaWRhdGlvbiBvZiB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYFxyXG4gKiBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIElmIHRoZSBvcHRpb24gaXMgYSBzdHJpbmcgYW5kIHJlZmVyZW5jZXMgYSBKU09OIGZpbGVcclxuICogKHdoZW4gdGhlIGBhbGxvd0ZpbGVSZXNvdXJjZXNgIGlzIHRydWUpLCBpdCByZWFkcyBhbmQgcGFyc2VzIHRoZSBmaWxlLlxyXG4gKiBPdGhlcndpc2UsIGl0IGF0dGVtcHRzIHRvIHBhcnNlIHRoZSBzdHJpbmcgb3Igb2JqZWN0IGFzIEpTT04uIElmIGFueSBlcnJvcnNcclxuICogb2NjdXIgZHVyaW5nIHRoaXMgcHJvY2VzcywgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC4gSWYgdGhlcmUgaXMgYW4gZXJyb3JcclxuICogbG9hZGluZyBvciBwYXJzaW5nIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3IgYHRoZW1lT3B0aW9uc2AsIHRoZSBlcnJvciBpcyBsb2dnZWRcclxuICogYW5kIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBlbmFibGluZyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzcGVjaWZpZWQgRXhwcmVzcyBhcHAuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXRcclxuICogdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCByYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWQgYW5kIHRoZSBhcHAgZXhpc3RzXHJcbiAgICBpZiAoYXBwICYmIHJhdGVMaW1pdGluZ09wdGlvbnMuZW5hYmxlKSB7XHJcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPVxyXG4gICAgICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgICAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICAgICAgd2luZG93OiByYXRlTGltaXRpbmdPcHRpb25zLndpbmRvdyB8fCAxLFxyXG4gICAgICAgIG1heFJlcXVlc3RzOiByYXRlTGltaXRpbmdPcHRpb25zLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgICAgIGRlbGF5OiByYXRlTGltaXRpbmdPcHRpb25zLmRlbGF5IHx8IDAsXHJcbiAgICAgICAgdHJ1c3RQcm94eTogcmF0ZUxpbWl0aW5nT3B0aW9ucy50cnVzdFByb3h5IHx8IGZhbHNlLFxyXG4gICAgICAgIHNraXBLZXk6IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcEtleSB8fCBudWxsLFxyXG4gICAgICAgIHNraXBUb2tlbjogcmF0ZUxpbWl0aW5nT3B0aW9ucy5za2lwVG9rZW4gfHwgbnVsbFxyXG4gICAgICB9O1xyXG5cclxuICAgICAgLy8gU2V0IGlmIGJlaGluZCBhIHByb3h5XHJcbiAgICAgIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICAgICAgYXBwLmVuYWJsZSgndHJ1c3QgcHJveHknKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gICAgICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcclxuICAgICAgICAvLyBUaW1lIGZyYW1lIGZvciB3aGljaCByZXF1ZXN0cyBhcmUgY2hlY2tlZCBhbmQgcmVtZW1iZXJlZFxyXG4gICAgICAgIHdpbmRvd01zOiByYXRlT3B0aW9ucy53aW5kb3cgKiA2MCAqIDEwMDAsXHJcbiAgICAgICAgLy8gTGltaXQgZWFjaCBJUCB0byAxMDAgcmVxdWVzdHMgcGVyIGB3aW5kb3dNc2BcclxuICAgICAgICBsaW1pdDogcmF0ZU9wdGlvbnMubWF4UmVxdWVzdHMsXHJcbiAgICAgICAgLy8gRGlzYWJsZSBkZWxheWluZywgZnVsbCBzcGVlZCB1bnRpbCB0aGUgbWF4IGxpbWl0IGlzIHJlYWNoZWRcclxuICAgICAgICBkZWxheU1zOiByYXRlT3B0aW9ucy5kZWxheSxcclxuICAgICAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlLmZvcm1hdCh7XHJcbiAgICAgICAgICAgIGpzb246ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZSB9KTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgICAgIC8vIEFsbG93IGJ5cGFzc2luZyB0aGUgbGltaXRlciBpZiBhIHZhbGlkIGtleS90b2tlbiBoYXMgYmVlbiBzZW50XHJcbiAgICAgICAgICBpZiAoXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IG51bGwgJiZcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkuYWNjZXNzX3Rva2VuID09PSByYXRlT3B0aW9ucy5za2lwVG9rZW5cclxuICAgICAgICAgICkge1xyXG4gICAgICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcblxyXG4gICAgICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gICAgICBhcHAudXNlKGxpbWl0ZXIpO1xyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtyYXRlIGxpbWl0aW5nXSBFbmFibGVkIHJhdGUgbGltaXRpbmcgd2l0aCAke3JhdGVPcHRpb25zLm1heFJlcXVlc3RzfSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcmF0ZSBsaW1pdGluZ10gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciB2YWxpZGF0aW5nIGluY29taW5nIEhUVFAgcmVxdWVzdHNcclxuICogaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi4gVGhpcyBtb2R1bGUgZW5zdXJlcyB0aGF0IHJlcXVlc3RzIGNvbnRhaW5cclxuICogYXBwcm9wcmlhdGUgY29udGVudCB0eXBlcyBhbmQgdmFsaWQgcmVxdWVzdCBib2RpZXMsIGluY2x1ZGluZyBwcm9wZXIgSlNPTlxyXG4gKiBzdHJ1Y3R1cmVzIGFuZCBjaGFydCBkYXRhIGZvciBleHBvcnRzLiBJdCBjaGVja3MgZm9yIHBvdGVudGlhbCBpc3N1ZXMgc3VjaFxyXG4gKiBhcyBtaXNzaW5nIG9yIG1hbGZvcm1lZCBkYXRhLCBwcml2YXRlIHJhbmdlIFVSTHMgaW4gU1ZHIHBheWxvYWRzLCBhbmQgYWxsb3dzXHJcbiAqIGZvciBmbGV4aWJsZSBvcHRpb25zIHZhbGlkYXRpb24uIFRoZSBtaWRkbGV3YXJlIGxvZ3MgZGV0YWlsZWQgaW5mb3JtYXRpb25cclxuICogYW5kIGhhbmRsZXMgZXJyb3JzIHJlbGF0ZWQgdG8gaW5jb3JyZWN0IHBheWxvYWRzLCBjaGFydCBkYXRhLCBhbmQgcHJpdmF0ZSBVUkxcclxuICogdXNhZ2UuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uIH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpc09iamVjdEVtcHR5LCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgY29udGVudC10eXBlIGhlYWRlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBjb250ZW50LXR5cGVcclxuICogaXMgbm90IGNvcnJlY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSBjb250ZW50IHR5cGUgaGVhZGVyXHJcbiAgICBjb25zdCBjb250ZW50VHlwZSA9IHJlcXVlc3QuaGVhZGVyc1snY29udGVudC10eXBlJ10gfHwgJyc7XHJcblxyXG4gICAgLy8gQWxsb3cgb25seSBKU09OLCBVUkwtZW5jb2RlZCBhbmQgZm9ybSBkYXRhIHdpdGhvdXQgZmlsZXMgdHlwZXMgb2YgZGF0YVxyXG4gICAgaWYgKFxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL2pzb24nKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnbXVsdGlwYXJ0L2Zvcm0tZGF0YScpXHJcbiAgICApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbdmFsaWRhdGlvbl0gQ29udGVudC1UeXBlIG11c3QgYmUgYXBwbGljYXRpb24vanNvbiwgYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkLCBvciBtdWx0aXBhcnQvZm9ybS1kYXRhLicsXHJcbiAgICAgICAgNDE1XHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYHJlcXVlc3RCb2R5TWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIHJlcXVlc3QncyBib2R5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGJvZHkgaXMgbm90IGNvcnJlY3QuXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY2hhcnQgZGF0YSBmcm9tIHRoZSBib2R5XHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaW4gY2FzZSBvZiB0aGUgcHJpdmF0ZSByYW5nZVxyXG4gKiB1cmwgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGJvZHlcclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHV1aWQoKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgZW1wdHkuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGJvZHkgaXMgcmVxdWlyZWQuIFBsZWFzZSBlbnN1cmUgdGhhdCB5b3VyIENvbnRlbnQtVHlwZSBoZWFkZXIgaXMgY29ycmVjdC4gQWNjZXB0ZWQgdHlwZXMgYXJlICdhcHBsaWNhdGlvbi9qc29uJyBhbmQgJ211bHRpcGFydC9mb3JtLWRhdGEnLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uIGZvciB0aGUgc2VydmVyXHJcbiAgICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb24gPSBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKTtcclxuXHJcbiAgICAvLyBGaW5kIGEgY29ycmVjdCBjaGFydCBvcHRpb25zXHJcbiAgICBjb25zdCBpbnN0ciA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgLy8gVXNlIG9uZSBvZiB0aGUgYmVsb3dcclxuICAgICAgYm9keS5pbnN0ciB8fCBib2R5Lm9wdGlvbnMgfHwgYm9keS5pbmZpbGUgfHwgYm9keS5kYXRhLFxyXG4gICAgICAvLyBTdHJpbmdpZnkgb3B0aW9uc1xyXG4gICAgICB0cnVlLFxyXG4gICAgICAvLyBBbGxvdyBvciBkaXNhbGxvdyBmdW5jdGlvbnNcclxuICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRoZXJlIGlzIG5vIGNvcnJlY3QgY2hhcnQgZGF0YVxyXG4gICAgaWYgKGluc3RyID09PSBudWxsICYmICFib2R5LnN2Zykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIG1pc3NpbmcgY29ycmVjdCBjaGFydCBkYXRhIGZvciBleHBvcnQ6ICR7SlNPTi5zdHJpbmdpZnkoYm9keSl9LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGVzdCBvZiB4bGluazpocmVmIGVsZW1lbnRzIGZyb20gcGF5bG9hZCdzIFNWRyBmYWlsc1xyXG4gICAgaWYgKGJvZHkuc3ZnICYmIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoYm9keS5zdmcpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBTVkcgcG90ZW50aWFsbHkgY29udGFpbiBhdCBsZWFzdCBvbmUgZm9yYmlkZGVuIFVSTCBpbiAneGxpbms6aHJlZicgZWxlbWVudC4gUGxlYXNlIHJldmlldyB0aGUgU1ZHIGNvbnRlbnQgYW5kIGVuc3VyZSB0aGF0IGFsbCByZWZlcmVuY2VkIFVSTHMgY29tcGx5IHdpdGggc2VjdXJpdHkgcG9saWNpZXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3Qgb3B0aW9ucyBhbmQgc3RvcmUgcGFyc2VkIHN0cnVjdHVyZSBpbiB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC52YWxpZGF0ZWRPcHRpb25zID0ge1xyXG4gICAgICAvLyBTZXQgdGhlIGNyZWF0ZWQgSUQgYXMgYSBgcmVxdWVzdElkYCBwcm9wZXJ0eSBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICByZXF1ZXN0SWQsXHJcbiAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIHN2ZzogYm9keS5zdmcsXHJcbiAgICAgICAgb3V0ZmlsZTpcclxuICAgICAgICAgIGJvZHkub3V0ZmlsZSB8fFxyXG4gICAgICAgICAgYCR7cmVxdWVzdC5wYXJhbXMuZmlsZW5hbWUgfHwgJ2NoYXJ0J30uJHtib2R5LnR5cGUgfHwgJ3BuZyd9YCxcclxuICAgICAgICB0eXBlOiBib2R5LnR5cGUsXHJcbiAgICAgICAgY29uc3RyOiBib2R5LmNvbnN0cixcclxuICAgICAgICBiNjQ6IGJvZHkuYjY0LFxyXG4gICAgICAgIG5vRG93bmxvYWQ6IGJvZHkubm9Eb3dubG9hZCxcclxuICAgICAgICBoZWlnaHQ6IGJvZHkuaGVpZ2h0LFxyXG4gICAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxyXG4gICAgICAgIHNjYWxlOiBib2R5LnNjYWxlLFxyXG4gICAgICAgIGdsb2JhbE9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkuZ2xvYmFsT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApLFxyXG4gICAgICAgIHRoZW1lT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS50aGVtZU9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKVxyXG4gICAgICB9LFxyXG4gICAgICBjdXN0b21Mb2dpYzoge1xyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvbixcclxuICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IGZhbHNlLFxyXG4gICAgICAgIGN1c3RvbUNvZGU6IGJvZHkuY3VzdG9tQ29kZSxcclxuICAgICAgICBjYWxsYmFjazogYm9keS5jYWxsYmFjayxcclxuICAgICAgICByZXNvdXJjZXM6IGlzQWxsb3dlZENvbmZpZyhib2R5LnJlc291cmNlcywgdHJ1ZSwgYWxsb3dDb2RlRXhlY3V0aW9uKVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIC8vIENhbGwgdGhlIG5leHQgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApIHtcclxuICAvLyBBZGQgY29udGVudCB0eXBlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIGNvbnRlbnRUeXBlTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCByZXF1ZXN0IGJvZHkgcmVxdWVzdCB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCByZXF1ZXN0Qm9keU1pZGRsZXdhcmUpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIHRoZSBleHBvcnQgcm91dGVzIGFuZCBsb2dpYyBmb3IgaGFuZGxpbmcgY2hhcnQgZXhwb3J0XHJcbiAqIHJlcXVlc3RzIGluIGFuIEV4cHJlc3Mgc2VydmVyLiBUaGlzIG1vZHVsZSBwcm9jZXNzZXMgaW5jb21pbmcgcmVxdWVzdHNcclxuICogdG8gZXhwb3J0IGNoYXJ0cyBpbiB2YXJpb3VzIGZvcm1hdHMgKGUuZy4gSlBFRywgUE5HLCBQREYsIFNWRykuIEl0IGludGVncmF0ZXNcclxuICogd2l0aCBIaWdoY2hhcnRzJyBjb3JlIGZ1bmN0aW9uYWxpdGllcyBhbmQgc3VwcG9ydHMgYm90aCBpbW1lZGlhdGUgZG93bmxvYWRcclxuICogcmVzcG9uc2VzIGFuZCBCYXNlNjQtZW5jb2RlZCBjb250ZW50IHJldHVybnMuIFRoZSBjb2RlIGFsc28gZmVhdHVyZXNcclxuICogYmVuY2htYXJraW5nIGZvciBwZXJmb3JtYW5jZSBtb25pdG9yaW5nLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHN0YXJ0RXhwb3J0IH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRCYXNlNjQsIG1lYXN1cmVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBSZXZlcnNlZCBNSU1FIHR5cGVzXHJcbmNvbnN0IHJldmVyc2VkTWltZSA9IHtcclxuICBwbmc6ICdpbWFnZS9wbmcnLFxyXG4gIGpwZWc6ICdpbWFnZS9qcGVnJyxcclxuICBnaWY6ICdpbWFnZS9naWYnLFxyXG4gIHBkZjogJ2FwcGxpY2F0aW9uL3BkZicsXHJcbiAgc3ZnOiAnaW1hZ2Uvc3ZnK3htbCdcclxufTtcclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleHBvcnQgcmVxdWVzdHMgZnJvbSB0aGUgY2xpZW50LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnQocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gU3RhcnQgY291bnRpbmcgdGltZSBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gSW4gY2FzZSB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQsIGZvcmNlIHRvIGFib3J0IGZ1cnRoZXIgYWN0aW9uc1xyXG4gICAgbGV0IGNvbm5lY3Rpb25BYm9ydGVkID0gZmFsc2U7XHJcbiAgICByZXF1ZXN0LnNvY2tldC5vbignY2xvc2UnLCAoaGFkRXJyb3JzKSA9PiB7XHJcbiAgICAgIGlmIChoYWRFcnJvcnMpIHtcclxuICAgICAgICBjb25uZWN0aW9uQWJvcnRlZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgb3B0aW9ucyBwcmV2aW91c2x5IHZhbGlkYXRlZCBpbiB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucztcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3QgaWRcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHJlcXVlc3RPcHRpb25zLnJlcXVlc3RJZDtcclxuXHJcbiAgICAvLyBJbmZvIGFib3V0IGFuIGluY29taW5nIHJlcXVlc3Qgd2l0aCBjb3JyZWN0IGRhdGFcclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3QuYCk7XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChyZXF1ZXN0T3B0aW9ucywgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgIC8vIFJlbW92ZSB0aGUgY2xvc2UgZXZlbnQgZnJvbSB0aGUgc29ja2V0XHJcbiAgICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBjb25uZWN0aW9uIHdhcyBjbG9zZWQsIGRvIG5vdGhpbmdcclxuICAgICAgaWYgKGNvbm5lY3Rpb25BYm9ydGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gYmVmb3JlIHRoZSBjaGFydCBmaW5pc2hlZCBwcm9jZXNzaW5nLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZXJyb3IsIGxvZyBpdCBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCBsb2cgdGhlIG1lc3NhZ2UgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKCFkYXRhIHx8ICFkYXRhLnJlc3VsdCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8XHJcbiAgICAgICAgICAgIHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHJlc3VsdCBpcyAke2RhdGEucmVzdWx0fS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBVbmV4cGVjdGVkIHJldHVybiBvZiB0aGUgZXhwb3J0IHJlc3VsdCBmcm9tIHRoZSBjaGFydCBnZW5lcmF0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciByZXF1ZXN0IGRhdGEuYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFJldHVybiB0aGUgcmVzdWx0IGluIGFuIGFwcHJvcHJpYXRlIGZvcm1hdFxyXG4gICAgICBpZiAoZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3MgdG9vayAke3JlcXVlc3RDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYHR5cGVgLCBgYjY0YCwgYG5vRG93bmxvYWRgLCBhbmQgYG91dGZpbGVgIGZyb20gb3B0aW9uc1xyXG4gICAgICAgIGNvbnN0IHsgdHlwZSwgYjY0LCBub0Rvd25sb2FkLCBvdXRmaWxlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBJZiBvbmx5IEJhc2U2NCBpcyByZXF1aXJlZCwgcmV0dXJuIGl0XHJcbiAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTZXQgY29ycmVjdCBjb250ZW50IHR5cGVcclxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XHJcblxyXG4gICAgICAgIC8vIERlY2lkZSB3aGV0aGVyIHRvIGRvd25sb2FkIG9yIG5vdCBjaGFydCBmaWxlXHJcbiAgICAgICAgaWYgKCFub0Rvd25sb2FkKSB7XHJcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KG91dGZpbGUpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgU1ZHLCByZXR1cm4gcGxhaW4gY29udGVudCwgb3RoZXJ3aXNlIGEgYjY0IHN0cmluZyBmcm9tIGEgYnVmZmVyXHJcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXHJcbiAgICAgICAgICA/IHJlc3BvbnNlLnNlbmQoZGF0YS5yZXN1bHQpXHJcbiAgICAgICAgICA6IHJlc3BvbnNlLnNlbmQoQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYGV4cG9ydGAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZXhwb3J0Um91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gZXhwb3J0Um91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy8nIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyBhdCB0aGUgcm9vdFxyXG4gICAqIGVuZHBvaW50LlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvJywgcmVxdWVzdEV4cG9ydCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy86ZmlsZW5hbWUnIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyB3aXRoXHJcbiAgICogYSBzcGVjaWZpZWQgZmlsZW5hbWUgcGFyYW1ldGVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgcmVxdWVzdEV4cG9ydCk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmVyIGhlYWx0aCBtb25pdG9yaW5nLCBpbmNsdWRpbmdcclxuICogdXB0aW1lLCBzdWNjZXNzIHJhdGVzLCBhbmQgb3RoZXIgc2VydmVyIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBnZXRQb29sSW5mb0pTT04gfSBmcm9tICcuLi8uLi9wb29sLmpzJztcclxuaW1wb3J0IHsgYWRkVGltZXIgfSBmcm9tICcuLi8uLi90aW1lci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0TmV3RGF0ZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vLyBTZXQgdGhlIHN0YXJ0IGRhdGUgb2YgdGhlIHNlcnZlclxyXG5jb25zdCBzZXJ2ZXJTdGFydFRpbWUgPSBuZXcgRGF0ZSgpO1xyXG5cclxuLy8gR2V0IHRoZSBgcGFja2FnZS5qc29uYCBjb250ZW50XHJcbmNvbnN0IHBhY2thZ2VGaWxlID0gSlNPTi5wYXJzZShcclxuICByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICdwYWNrYWdlLmpzb24nKSwgJ3V0ZjgnKVxyXG4pO1xyXG5cclxuLy8gQW4gYXJyYXkgZm9yIHN1Y2Nlc3MgcmF0ZSByYXRpb3NcclxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XHJcblxyXG4vLyBSZWNvcmQgZXZlcnkgbWludXRlXHJcbmNvbnN0IHJlY29yZEludGVydmFsID0gNjAgKiAxMDAwO1xyXG5cclxuLy8gMzAgbWludXRlc1xyXG5jb25zdCB3aW5kb3dTaXplID0gMzA7XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgYHN1Y2Nlc3NSYXRlc2BcclxuICogYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBBIG1vdmluZyBhdmVyYWdlIGZvciBzdWNjZXNzIHJhdGlvIG9mIHRoZSBzZXJ2ZXIgZXhwb3J0cy5cclxuICovXHJcbmZ1bmN0aW9uIF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCkge1xyXG4gIHJldHVybiBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIHRoZSBpbnRlcnZhbCByZXNwb25zaWJsZSBmb3IgY2FsY3VsYXRpbmcgY3VycmVudCBzdWNjZXNzIHJhdGUgcmF0aW9cclxuICogYW5kIGNvbGxlY3RzIHJlY29yZHMgdG8gdGhlIGBzdWNjZXNzUmF0ZXNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3N0YXJ0U3VjY2Vzc1JhdGVcclxuICpcclxuICogQHJldHVybnMge05vZGVKUy5UaW1lb3V0fSBJZCBvZiBhbiBpbnRlcnZhbC5cclxuICovXHJcbmZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlKCkge1xyXG4gIHJldHVybiBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICBjb25zdCBzdGF0cyA9IGdldFBvb2xTdGF0cygpO1xyXG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID1cclxuICAgICAgc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCA9PT0gMFxyXG4gICAgICAgID8gMVxyXG4gICAgICAgIDogKHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQgLyBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkKSAqIDEwMDtcclxuXHJcbiAgICBzdWNjZXNzUmF0ZXMucHVzaChzdWNjZXNzUmF0aW8pO1xyXG4gICAgaWYgKHN1Y2Nlc3NSYXRlcy5sZW5ndGggPiB3aW5kb3dTaXplKSB7XHJcbiAgICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xyXG4gICAgfVxyXG4gIH0sIHJlY29yZEludGVydmFsKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBoZWFsdGhgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGhlYWx0aFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGhlYWx0aFJvdXRlcyhhcHApIHtcclxuICAvLyBTdGFydCBwcm9jZXNzaW5nIHN1Y2Nlc3MgcmF0ZSByYXRpbyBpbnRlcnZhbCBhbmQgc2F2ZSBpdHMgaWQgdG8gdGhlIGFycmF5XHJcbiAgLy8gZm9yIHRoZSBncmFjZWZ1bCBjbGVhcmluZyBvbiBzaHV0ZG93biB3aXRoIGluamVjdGVkIGBhZGRUaW1lcmAgZnVudGlvblxyXG4gIGFkZFRpbWVyKF9zdGFydFN1Y2Nlc3NSYXRlKCkpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy9oZWFsdGgnIC0gQSByb3V0ZSBmb3IgZ2V0dGluZyB0aGUgYmFzaWMgc3RhdHMgb2YgdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KCcvaGVhbHRoJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1toZWFsdGhdIFJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgICAgY29uc3QgbW92aW5nQXZlcmFnZSA9IF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgICAvLyBTZW5kIHRoZSBzZXJ2ZXIncyBzdGF0aXN0aWNzXHJcbiAgICAgIHJlc3BvbnNlLnNlbmQoe1xyXG4gICAgICAgIC8vIFN0YXR1cyBhbmQgdGltZXNcclxuICAgICAgICBzdGF0dXM6ICdPSycsXHJcbiAgICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcclxuICAgICAgICB1cHRpbWU6IGAke01hdGguZmxvb3IoKGdldE5ld0RhdGVUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MCl9IG1pbnV0ZXNgLFxyXG5cclxuICAgICAgICAvLyBWZXJzaW9uc1xyXG4gICAgICAgIHNlcnZlclZlcnNpb246IHBhY2thZ2VGaWxlLnZlcnNpb24sXHJcbiAgICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IGdldEhpZ2hjaGFydHNWZXJzaW9uKCksXHJcblxyXG4gICAgICAgIC8vIEV4cG9ydHNcclxuICAgICAgICBhdmVyYWdlRXhwb3J0VGltZTogc3RhdHMudGltZVNwZW50QXZlcmFnZSxcclxuICAgICAgICBhdHRlbXB0ZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkLFxyXG4gICAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQsXHJcbiAgICAgICAgZmFpbGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0Ryb3BwZWQsXHJcbiAgICAgICAgc3VjZXNzUmF0aW86IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDAsXHJcblxyXG4gICAgICAgIC8vIFBvb2xcclxuICAgICAgICBwb29sOiBnZXRQb29sSW5mb0pTT04oKSxcclxuXHJcbiAgICAgICAgLy8gTW92aW5nIGF2ZXJhZ2VcclxuICAgICAgICBwZXJpb2QsXHJcbiAgICAgICAgbW92aW5nQXZlcmFnZSxcclxuICAgICAgICBtZXNzYWdlOlxyXG4gICAgICAgICAgaXNOYU4obW92aW5nQXZlcmFnZSkgfHwgIXN1Y2Nlc3NSYXRlcy5sZW5ndGhcclxuICAgICAgICAgICAgPyAnVG9vIGVhcmx5IHRvIHJlcG9ydC4gTm8gZXhwb3J0cyBtYWRlIHlldC4gUGxlYXNlIGNoZWNrIGJhY2sgc29vbi4nXHJcbiAgICAgICAgICAgIDogYExhc3QgJHtwZXJpb2R9IG1pbnV0ZXMgaGFkIGEgc3VjY2VzcyByYXRlIG9mICR7bW92aW5nQXZlcmFnZS50b0ZpeGVkKDIpfSUuYCxcclxuXHJcbiAgICAgICAgLy8gU1ZHIGFuZCBKU09OIGV4cG9ydHNcclxuICAgICAgICBzdmdFeHBvcnRzOiBzdGF0cy5leHBvcnRzRnJvbVN2ZyxcclxuICAgICAgICBqc29uRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zLFxyXG4gICAgICAgIHN2Z0V4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyxcclxuICAgICAgICBqc29uRXhwb3J0c0F0dGVtcHRzOiBzdGF0cy5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0c1xyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciBzZXJ2aW5nIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXJcclxuICogd2hlbiBlbmFibGVkLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHVpYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1aVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHVpUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIEdFVCAnLycgLSBBIHJvdXRlIGZvciBhIFVJIHdoZW4gZW5hYmxlZCBvbiB0aGUgZXhwb3J0IHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KGdldE9wdGlvbnMoKS51aS5yb3V0ZSB8fCAnLycsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdWldIFJldHVybmluZyBVSSBmb3IgdGhlIGV4cG9ydC4nKTtcclxuXHJcbiAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwge1xyXG4gICAgICAgIGFjY2VwdFJhbmdlczogZmFsc2VcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3IgdXBkYXRpbmcgdGhlIEhpZ2hjaGFydHMgdmVyc2lvblxyXG4gKiBvbiB0aGUgc2VydmVyLCB3aXRoIGF1dGhlbnRpY2F0aW9uIGFuZCB2YWxpZGF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGdldEhpZ2hjaGFydHNWZXJzaW9uLCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHZlcnNpb25fY2hhbmdlYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nIC0gQSByb3V0ZSBmb3IgY2hhbmdpbmdcclxuICAgKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicsIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdmVyc2lvbl0gQ2hhbmdpbmcgSGlnaGNoYXJ0cyB2ZXJzaW9uLicpO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBkaXJlY3RseSBmcm9tIGVudnNcclxuICAgICAgY29uc3QgYWRtaW5Ub2tlbiA9IGVudnMuSElHSENIQVJUU19BRE1JTl9UT0tFTjtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBleGlzdGVuY2Ugb2YgdGhlIHRva2VuXHJcbiAgICAgIGlmICghYWRtaW5Ub2tlbiB8fCAhYWRtaW5Ub2tlbi5sZW5ndGgpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIFRoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBmcm9tIHRoZSBoYy1hdXRoIGhlYWRlclxyXG4gICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbdmVyc2lvbl0gSW52YWxpZCBvciBtaXNzaW5nIHRva2VuOiBTZXQgdGhlIHRva2VuIGluIHRoZSBoYy1hdXRoIGhlYWRlci4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xyXG4gICAgICBsZXQgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XHJcbiAgICAgIGlmIChuZXdWZXJzaW9uKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFVwZGF0ZSB2ZXJzaW9uXHJcbiAgICAgICAgICBhd2FpdCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICBgW3ZlcnNpb25dIFZlcnNpb24gY2hhbmdlOiAke2Vycm9yLm1lc3NhZ2V9YCxcclxuICAgICAgICAgICAgNDAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFN1Y2Nlc3NcclxuICAgICAgICByZXNwb25zZS5zdGF0dXMoMjAwKS5zZW5kKHtcclxuICAgICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcclxuICAgICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG4gICAgICAgICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIEhpZ2hjaGFydHMgdG8gdmVyc2lvbjogJHtuZXdWZXJzaW9ufS5gXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gTm8gdmVyc2lvbiBzcGVjaWZpZWRcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1t2ZXJzaW9uXSBObyBuZXcgdmVyc2lvbiBzdXBwbGllZC4nLCA0MDApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEEgbW9kdWxlIHRoYXQgc2V0cyB1cCBhbmQgbWFuYWdlcyBIVFRQIGFuZCBIVFRQUyBzZXJ2ZXJzXHJcbiAqIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBoYW5kbGVzIHNlcnZlciBpbml0aWFsaXphdGlvbixcclxuICogY29uZmlndXJhdGlvbiwgZXJyb3IgaGFuZGxpbmcsIG1pZGRsZXdhcmUgc2V0dXAsIHJvdXRlIGRlZmluaXRpb24sIGFuZCByYXRlXHJcbiAqIGxpbWl0aW5nLiBUaGUgbW9kdWxlIGV4cG9ydHMgZnVuY3Rpb25zIHRvIHN0YXJ0LCBzdG9wLCBhbmQgbWFuYWdlIHNlcnZlclxyXG4gKiBpbnN0YW5jZXMsIGFzIHdlbGwgYXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIGRlZmluaW5nIHJvdXRlcyBhbmQgYXR0YWNoaW5nXHJcbiAqIG1pZGRsZXdhcmVzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xyXG5cclxuaW1wb3J0IHsgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgZXJyb3JNaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvZXJyb3IuanMnO1xyXG5pbXBvcnQgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3JhdGVMaW1pdGluZy5qcyc7XHJcbmltcG9ydCB2YWxpZGF0aW9uTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3ZhbGlkYXRpb24uanMnO1xyXG5cclxuaW1wb3J0IGV4cG9ydFJvdXRlcyBmcm9tICcuL3JvdXRlcy9leHBvcnQuanMnO1xyXG5pbXBvcnQgaGVhbHRoUm91dGVzIGZyb20gJy4vcm91dGVzL2hlYWx0aC5qcyc7XHJcbmltcG9ydCB1aVJvdXRlcyBmcm9tICcuL3JvdXRlcy91aS5qcyc7XHJcbmltcG9ydCB2ZXJzaW9uQ2hhbmdlUm91dGVzIGZyb20gJy4vcm91dGVzL3ZlcnNpb25DaGFuZ2UuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBBcnJheSBvZiBhbiBhY3RpdmUgc2VydmVyc1xyXG5jb25zdCBhY3RpdmVTZXJ2ZXJzID0gbmV3IE1hcCgpO1xyXG5cclxuLy8gQ3JlYXRlIGV4cHJlc3MgYXBwXHJcbmNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gSFRUUCBhbmQvb3IgSFRUUFMgc2VydmVyIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLlxyXG4gKiBUaGUgYHNlcnZlck9wdGlvbnNgIG9iamVjdCBjb250YWlucyBzZXJ2ZXItcmVsYXRlZCBwcm9wZXJ0aWVzIChyZWZlclxyXG4gKiB0byB0aGUgYHNlcnZlcmAgc2VjdGlvbiBpbiB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIGZvciBkZXRhaWxzKS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydFNlcnZlclxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBzZXJ2ZXJgXHJcbiAqIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2YgdGhlIGBzZXJ2ZXJgXHJcbiAqIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlcyB3aWxsIGRlZmF1bHRcclxuICogdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgZWl0aGVyXHJcbiAqIG5vdCBlbmFibGVkIG9yIG5vIHZhbGlkIEV4cHJlc3MgYXBwIGlzIGZvdW5kLCBzaWduYWxpbmcgdGhlIGVuZCBvZiB0aGVcclxuICogZnVuY3Rpb24ncyBleGVjdXRpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgc2VydmVyIGNhbm5vdFxyXG4gKiBiZSBjb25maWd1cmVkIGFuZCBzdGFydGVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0U2VydmVyKHNlcnZlck9wdGlvbnMpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBzZXJ2ZXI6IHNlcnZlck9wdGlvbnNcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFVzZSB2YWxpZGF0ZWQgb3B0aW9uc1xyXG4gICAgc2VydmVyT3B0aW9ucyA9IG9wdGlvbnMuc2VydmVyO1xyXG5cclxuICAgIC8vIFN0b3AgaWYgbm90IGVuYWJsZWRcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5lbmFibGUgfHwgIWFwcCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tzZXJ2ZXJdIFNlcnZlciBjYW5ub3QgYmUgc3RhcnRlZCAobm90IGVuYWJsZWQgb3Igbm8gY29ycmVjdCBFeHByZXNzIGFwcCBmb3VuZCkuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUb28gYmlnIGxpbWl0cyBsZWFkIHRvIHRpbWVvdXRzIGluIHRoZSBleHBvcnQgcHJvY2VzcyB3aGVuXHJcbiAgICAvLyB0aGUgcmFzdGVyaXphdGlvbiB0aW1lb3V0IGlzIHNldCB0b28gbG93XHJcbiAgICBjb25zdCB1cGxvYWRMaW1pdEJ5dGVzID0gc2VydmVyT3B0aW9ucy51cGxvYWRMaW1pdCAqIDEwMjQgKiAxMDI0O1xyXG5cclxuICAgIC8vIE1lbW9yeSBzdG9yYWdlIGZvciBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XHJcblxyXG4gICAgLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3QgdXBsb2FkID0gbXVsdGVyKHtcclxuICAgICAgc3RvcmFnZSxcclxuICAgICAgbGltaXRzOiB7XHJcbiAgICAgICAgZmllbGRTaXplOiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIERpc2FibGUgdGhlIFgtUG93ZXJlZC1CeSBoZWFkZXJcclxuICAgIGFwcC5kaXNhYmxlKCd4LXBvd2VyZWQtYnknKTtcclxuXHJcbiAgICAvLyBFbmFibGUgQ09SUyBzdXBwb3J0XHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBjb3JzKHtcclxuICAgICAgICBtZXRob2RzOiBbJ1BPU1QnLCAnR0VUJywgJ09QVElPTlMnXVxyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXR0aW5nIGEgbG90IG9mIGBSYW5nZU5vdFNhdGlzZmlhYmxlRXJyb3JgIGV4Y2VwdGlvbnMgKGV2ZW4gdGhvdWdoIHRoaXNcclxuICAgIC8vIGlzIGEgZGVwcmVjYXRlZCBvcHRpb25zLCBsZXQncyB0cnkgdG8gc2V0IGl0IHRvIGZhbHNlKVxyXG4gICAgYXBwLnVzZSgocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgICAgcmVzcG9uc2Uuc2V0KCdBY2NlcHQtUmFuZ2VzJywgJ25vbmUnKTtcclxuICAgICAgbmV4dCgpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBKU09OIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MuanNvbih7XHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBVUkwtZW5jb2RlZCBmb3JtIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MudXJsZW5jb2RlZCh7XHJcbiAgICAgICAgZXh0ZW5kZWQ6IHRydWUsXHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gVXNlIG9ubHkgbm9uLWZpbGUgbXVsdGlwYXJ0IGZvcm0gZmllbGRzXHJcbiAgICBhcHAudXNlKHVwbG9hZC5ub25lKCkpO1xyXG5cclxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcclxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQIHNlcnZlclxyXG4gICAgaWYgKCFzZXJ2ZXJPcHRpb25zLnNzbC5mb3JjZSkge1xyXG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcCk7XHJcblxyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKGh0dHBTZXJ2ZXIpO1xyXG5cclxuICAgICAgLy8gTGlzdGVuXHJcbiAgICAgIGh0dHBTZXJ2ZXIubGlzdGVuKHNlcnZlck9wdGlvbnMucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFAgc2VydmVyXHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5zZXQoc2VydmVyT3B0aW9ucy5wb3J0LCBodHRwU2VydmVyKTtcclxuXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyT3B0aW9ucy5ob3N0fToke3NlcnZlck9wdGlvbnMucG9ydH0uYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQUyBzZXJ2ZXJcclxuICAgIGlmIChzZXJ2ZXJPcHRpb25zLnNzbC5lbmFibGUpIHtcclxuICAgICAgLy8gU2V0IHVwIGFuIFNTTCBzZXJ2ZXIgYWxzb1xyXG4gICAgICBsZXQga2V5LCBjZXJ0O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBrZXlcclxuICAgICAgICBrZXkgPSByZWFkRmlsZVN5bmMoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIua2V5JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBjZXJ0aWZpY2F0ZVxyXG4gICAgICAgIGNlcnQgPSByZWFkRmlsZVN5bmMoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIuY3J0JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gVW5hYmxlIHRvIGxvYWQga2V5L2NlcnRpZmljYXRlIGZyb20gdGhlICcke3NlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRofScgcGF0aC4gQ291bGQgbm90IHJ1biBzZWN1cmVkIGxheWVyIHNlcnZlci5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKGtleSAmJiBjZXJ0KSB7XHJcbiAgICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFBTKVxyXG4gICAgICAgIGNvbnN0IGh0dHBzU2VydmVyID0gaHR0cHMuY3JlYXRlU2VydmVyKHsga2V5LCBjZXJ0IH0sIGFwcCk7XHJcblxyXG4gICAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgIC8vIExpc3RlblxyXG4gICAgICAgIGh0dHBzU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBzZXJ2ZXJPcHRpb25zLmhvc3QsICgpID0+IHtcclxuICAgICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQUyBzZXJ2ZXJcclxuICAgICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMuc3NsLnBvcnQsIGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0fS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBzZXJ2ZXJPcHRpb25zLnJhdGVMaW1pdGluZyk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSB2YWxpZGF0aW9uIGhhbmRsZXJcclxuICAgIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHJvdXRlc1xyXG4gICAgZXhwb3J0Um91dGVzKGFwcCk7XHJcbiAgICBoZWFsdGhSb3V0ZXMoYXBwKTtcclxuICAgIHVpUm91dGVzKGFwcCk7XHJcbiAgICB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXHJcbiAgICBlcnJvck1pZGRsZXdhcmUoYXBwKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbG9zZVNlcnZlcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbG9zZVNlcnZlcnMoKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgYXJlIHNlcnZlcnMgd29ya2luZ1xyXG4gIGlmIChhY3RpdmVTZXJ2ZXJzLnNpemUgPiAwKSB7XHJcbiAgICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NpbmcgYWxsIHNlcnZlcnMuYCk7XHJcblxyXG4gICAgLy8gQ2xvc2UgZWFjaCBvbmUgb2Ygc2VydmVyc1xyXG4gICAgZm9yIChjb25zdCBbcG9ydCwgc2VydmVyXSBvZiBhY3RpdmVTZXJ2ZXJzKSB7XHJcbiAgICAgIHNlcnZlci5jbG9zZSgoKSA9PiB7XHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5kZWxldGUocG9ydCk7XHJcbiAgICAgICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zZWQgc2VydmVyIG9uIHBvcnQ6ICR7cG9ydH0uYCk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRTZXJ2ZXJzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtBcnJheS48T2JqZWN0Pn0gU2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VydmVycygpIHtcclxuICByZXR1cm4gYWN0aXZlU2VydmVycztcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEV4cHJlc3NcclxuICpcclxuICogQHJldHVybnMge0V4cHJlc3N9IFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEV4cHJlc3MoKSB7XHJcbiAgcmV0dXJuIGV4cHJlc3M7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QXBwXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXBwKCkge1xyXG4gIHJldHVybiBhcHA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXRcclxuICogb2YgdGhlIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlc1xyXG4gKiB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlUmF0ZUxpbWl0aW5nKHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgc2VydmVyOiB7XHJcbiAgICAgIHJhdGVMaW1pdGluZzogcmF0ZUxpbWl0aW5nT3B0aW9uc1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9uc1xyXG4gIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBvcHRpb25zLnNlcnZlci5yYXRlTGltaXRpbmdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFwcGx5IG1pZGRsZXdhcmUocykgdG8gYSBzcGVjaWZpYyBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXNlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggR0VUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIFBPU1QgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5wb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEF0dGFjaCBlcnJvciBoYW5kbGVycyB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnNcclxuICpcclxuICogQHBhcmFtIHsoaHR0cC5TZXJ2ZXJ8aHR0cHMuU2VydmVyKX0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoc2VydmVyKSB7XHJcbiAgc2VydmVyLm9uKCdjbGllbnRFcnJvcicsIChlcnJvciwgc29ja2V0KSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDEsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9LCBkZXN0cm95aW5nIHNvY2tldC5gXHJcbiAgICApO1xyXG4gICAgc29ja2V0LmRlc3Ryb3koKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignY29ubmVjdGlvbicsIChzb2NrZXQpID0+IHtcclxuICAgIHNvY2tldC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGNsb3NlU2VydmVycyxcclxuICBnZXRTZXJ2ZXJzLFxyXG4gIGdldEV4cHJlc3MsXHJcbiAgZ2V0QXBwLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhhbmRsZXMgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciwgZW5zdXJpbmdcclxuICogcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzIHN1Y2ggYXMgYnJvd3NlciwgcGFnZXMsIHNlcnZlcnMsIGFuZCB0aW1lcnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBjbGVhckFsbFRpbWVycyB9IGZyb20gJy4vdGltZXIuanMnO1xyXG5cclxuaW1wb3J0IHsgY2xvc2VTZXJ2ZXJzIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBjbGVhbnVwIG9wZXJhdGlvbnMgdG8gZW5zdXJlIGEgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIHByb2Nlc3MuXHJcbiAqIFRoaXMgaW5jbHVkZXMgY2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgdGltZW91dHMvaW50ZXJ2YWxzLCBjbG9zaW5nIGFjdGl2ZVxyXG4gKiBzZXJ2ZXJzLCB0ZXJtaW5hdGluZyByZXNvdXJjZXMgKHBhZ2VzKSBvZiB0aGUgcG9vbCwgcG9vbCBpdHNlbGYsIGFuZCBjbG9zaW5nXHJcbiAqIHRoZSBicm93c2VyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbZXhpdENvZGU9MF0gLSBUaGUgZXhpdCBjb2RlIHRvIHVzZSB3aXRoIGBwcm9jZXNzLmV4aXQoKWAuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGAwYC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXAoZXhpdENvZGUgPSAwKSB7XHJcbiAgLy8gQXdhaXQgZnJlZWluZyBhbGwgcmVzb3VyY2VzXHJcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcclxuICAgIC8vIENsZWFyIGFsbCBvbmdvaW5nIGludGVydmFsc1xyXG4gICAgY2xlYXJBbGxUaW1lcnMoKSxcclxuXHJcbiAgICAvLyBHZXQgYXZhaWxhYmxlIHNlcnZlciBpbnN0YW5jZXMgKEhUVFAvSFRUUFMpIGFuZCBjbG9zZSB0aGVtXHJcbiAgICBjbG9zZVNlcnZlcnMoKSxcclxuXHJcbiAgICAvLyBDbG9zZSBhbiBhY3RpdmUgcG9vbCBhbG9uZyB3aXRoIGl0cyB3b3JrZXJzIGFuZCB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gICAga2lsbFBvb2woKVxyXG4gIF0pO1xyXG5cclxuICAvLyBFeGl0IHByb2Nlc3Mgd2l0aCBhIGNvcnJlY3QgY29kZVxyXG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaHV0ZG93bkNsZWFuVXBcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvcmUgbW9kdWxlIGZvciBpbml0aWFsaXppbmcgYW5kIG1hbmFnaW5nIHRoZSBIaWdoY2hhcnRzIEV4cG9ydFxyXG4gKiBTZXJ2ZXIuIFByb3ZpZGVzIGZ1bmN0aW9uYWxpdGllcyBmb3IgY29uZmlndXJpbmcgZXhwb3J0cywgc2V0dGluZyB1cCBzZXJ2ZXJcclxuICogb3BlcmF0aW9ucywgbG9nZ2luZywgc2NyaXB0cyBjYWNoaW5nLCByZXNvdXJjZSBwb29saW5nLCBhbmQgZ3JhY2VmdWwgcHJvY2Vzc1xyXG4gKiBjbGVhbnVwLlxyXG4gKi9cclxuXHJcbmltcG9ydCAnY29sb3JzJztcclxuXHJcbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMsIG1hcFRvTmV3T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHtcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIGluaXRMb2dnaW5nLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG4gIHNldExvZ0xldmVsXHJcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpbml0UG9vbCwga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzaHV0ZG93bkNsZWFuVXAgfSBmcm9tICcuL3Jlc291cmNlUmVsZWFzZS5qcyc7XHJcblxyXG5pbXBvcnQgc2VydmVyIGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwcm9jZXNzLiBUYXNrcyBzdWNoIGFzIGNvbmZpZ3VyaW5nIGxvZ2dpbmcsIGNoZWNraW5nXHJcbiAqIHRoZSBjYWNoZSBhbmQgc291cmNlcywgYW5kIGluaXRpYWxpemluZyB0aGUgcmVzb3VyY2UgcG9vbCBvY2N1ciBkdXJpbmcgdGhpc1xyXG4gKiBzdGFnZS5cclxuICpcclxuICogVGhpcyBmdW5jdGlvbiBtdXN0IGJlIGNhbGxlZCBiZWZvcmUgYXR0ZW1wdGluZyB0byBleHBvcnQgY2hhcnRzIG9yIHNldFxyXG4gKiB1cCBhIHNlcnZlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpbml0T3B0aW9ucyAtIFRoZSBgaW5pdE9wdGlvbnNgIG9iamVjdCwgd2hpY2ggbWF5XHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmdcclxuICogdmFsdWVzIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0RXhwb3J0KGluaXRPcHRpb25zKSB7XHJcbiAgLy8gSW5pdCBhbmQgdXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKGluaXRPcHRpb25zKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBwZXIgZXhwb3J0IG1vZHVsZSBzY29wZVxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gIC8vIEluaXQgdGhlIGxvZ2dpbmdcclxuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAob3B0aW9ucy5vdGhlci5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXHJcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMsIG9wdGlvbnMuc2VydmVyLnByb3h5KTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgcG9vbFxyXG4gIGF3YWl0IGluaXRQb29sKG9wdGlvbnMucG9vbCwgb3B0aW9ucy5wdXBwZXRlZXIuYXJncyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXHJcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nXHJcbiAqIGFuZCAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVyc1xyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCkge1xyXG4gIGxvZygzLCAnW3Byb2Nlc3NdIEF0dGFjaGluZyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdleGl0J1xyXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gUHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0uYCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSU5UJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdURVJNJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICd1bmNhdWdodEV4Y2VwdGlvbidcclxuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGVycm9yLmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDEpO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgLy8gU2VydmVyXHJcbiAgLi4uc2VydmVyLFxyXG5cclxuICAvLyBPcHRpb25zXHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuXHJcbiAgLy8gRXhwb3J0aW5nXHJcbiAgaW5pdEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcblxyXG4gIC8vIFJlbGVhc2VcclxuICBraWxsUG9vbCxcclxuICBzaHV0ZG93bkNsZWFuVXAsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsOiBmdW5jdGlvbiAobGV2ZWwpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGxldmVsXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICBzZXRMb2dMZXZlbChvcHRpb25zLmxvZ2dpbmcubGV2ZWwpO1xyXG4gIH0sXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmc6IGZ1bmN0aW9uICh0b0NvbnNvbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIHRvQ29uc29sZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlQ29uc29sZUxvZ2dpbmcob3B0aW9ucy5sb2dnaW5nLnRvQ29uc29sZSk7XHJcbiAgfSxcclxuICBlbmFibGVGaWxlTG9nZ2luZzogZnVuY3Rpb24gKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBsb2dnaW5nOiB7XHJcbiAgICAgICAgZGVzdCxcclxuICAgICAgICBmaWxlLFxyXG4gICAgICAgIHRvRmlsZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy5kZXN0LFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZmlsZSxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLnRvRmlsZVxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcbiJdLCJuYW1lcyI6WyJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInRhZ05hbWUiLCJ0b1VwcGVyQ2FzZSIsInNyYyIsImJhc2VVUkkiLCJkZWVwQ29weSIsIm9iakFyciIsIm9iakFyckNvcHkiLCJBcnJheSIsImlzQXJyYXkiLCJrZXkiLCJPYmplY3QiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJmaXhDb25zdHIiLCJjb25zdHIiLCJmaXhlZENvbnN0ciIsInRvTG93ZXJDYXNlIiwicmVwbGFjZSIsImluY2x1ZGVzIiwiZml4T3V0ZmlsZSIsInR5cGUiLCJvdXRmaWxlIiwiZ2V0QWJzb2x1dGVQYXRoIiwic3BsaXQiLCJzaGlmdCIsImZpeFR5cGUiLCJtaW1lVHlwZXMiLCJmb3JtYXRzIiwidmFsdWVzIiwib3V0VHlwZSIsInBvcCIsImZpbmQiLCJ0IiwicGF0aCIsImlzQWJzb2x1dGUiLCJub3JtYWxpemUiLCJyZXNvbHZlIiwiZ2V0QmFzZTY0IiwiaW5wdXQiLCJCdWZmZXIiLCJmcm9tIiwidG9TdHJpbmciLCJnZXROZXdEYXRlIiwiRGF0ZSIsInRyaW0iLCJnZXROZXdEYXRlVGltZSIsImdldFRpbWUiLCJpc09iamVjdCIsIml0ZW0iLCJpc09iamVjdEVtcHR5Iiwia2V5cyIsImxlbmd0aCIsImlzUHJpdmF0ZVJhbmdlVXJsRm91bmQiLCJzb21lIiwicGF0dGVybiIsInRlc3QiLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwicHJvY2VzcyIsImhydGltZSIsImJpZ2ludCIsIk51bWJlciIsInJvdW5kTnVtYmVyIiwidmFsdWUiLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwiTWF0aCIsInBvdyIsInJvdW5kIiwid3JhcEFyb3VuZCIsImN1c3RvbUNvZGUiLCJhbGxvd0ZpbGVSZXNvdXJjZXMiLCJpc0NhbGxiYWNrIiwiZW5kc1dpdGgiLCJyZWFkRmlsZVN5bmMiLCJzdGFydHNXaXRoIiwiY29sb3JzIiwibG9nZ2luZyIsInRvQ29uc29sZSIsInRvRmlsZSIsInBhdGhDcmVhdGVkIiwicGF0aFRvTG9nIiwibGV2ZWxzRGVzYyIsInRpdGxlIiwiY29sb3IiLCJsb2ciLCJhcmdzIiwibmV3TGV2ZWwiLCJ0ZXh0cyIsImxldmVsIiwicHJlZml4IiwiX2xvZ1RvRmlsZSIsImNvbnNvbGUiLCJhcHBseSIsInVuZGVmaW5lZCIsImNvbmNhdCIsImxvZ1dpdGhTdGFjayIsImVycm9yIiwiY3VzdG9tTWVzc2FnZSIsIm1haW5NZXNzYWdlIiwibWVzc2FnZSIsInN0YWNrTWVzc2FnZSIsInN0YWNrIiwicHVzaCIsImluaXRMb2dnaW5nIiwibG9nZ2luZ09wdGlvbnMiLCJkZXN0IiwiZmlsZSIsInNldExvZ0xldmVsIiwiZW5hYmxlQ29uc29sZUxvZ2dpbmciLCJlbmFibGVGaWxlTG9nZ2luZyIsImlzSW50ZWdlciIsImV4aXN0c1N5bmMiLCJta2RpclN5bmMiLCJqb2luIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiX2luaXRPcHRpb25zIiwiZ2V0T3B0aW9ucyIsImdldENvcHkiLCJ1cGRhdGVPcHRpb25zIiwibmV3T3B0aW9ucyIsIl9tZXJnZU9wdGlvbnMiLCJtYXBUb05ld09wdGlvbnMiLCJvbGRPcHRpb25zIiwiZW50cmllcyIsInByb3BlcnRpZXNDaGFpbiIsInJlZHVjZSIsIm9iaiIsInByb3AiLCJpbmRleCIsImlzQWxsb3dlZENvbmZpZyIsImFsbG93RnVuY3Rpb25zIiwib2JqZWN0Q29uZmlnIiwiZXZhbCIsIkpTT04iLCJzdHJpbmdpZmllZE9wdGlvbnMiLCJfb3B0aW9uc1N0cmluZ2lmeSIsInBhcnNlZE9wdGlvbnMiLCJfIiwibmFtZSIsIm9yaWdpbmFsT3B0aW9ucyIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJFcnJvciIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZWplY3QiLCJfZ2V0UHJvdG9jb2xNb2R1bGUiLCJnZXQiLCJyZXNwb25zZSIsInJlc3BvbnNlRGF0YSIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiaHR0cHMiLCJodHRwIiwiRXhwb3J0RXJyb3IiLCJjb25zdHJ1Y3RvciIsInN0YXR1c0NvZGUiLCJzdXBlciIsInRoaXMiLCJzZXRTdGF0dXMiLCJzZXRFcnJvciIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsImkiLCJzZXR1cEhpZ2hjaGFydHMiLCJIaWdoY2hhcnRzIiwiYW5pbU9iamVjdCIsImR1cmF0aW9uIiwiY3JlYXRlQ2hhcnQiLCJleHBvcnRPcHRpb25zIiwiY3VzdG9tTG9naWNPcHRpb25zIiwic2V0T3B0aW9ucyIsIm1lcmdlIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJpc1JlbmRlckNvbXBsZXRlIiwiQ2hhcnQiLCJwcm9jZWVkIiwidXNlck9wdGlvbnMiLCJjYiIsImV4cG9ydGluZyIsImVuYWJsZWQiLCJwbG90T3B0aW9ucyIsInNlcmllcyIsImxhYmVsIiwidG9vbHRpcCIsImFuaW1hdGlvbiIsIm9uSGlnaGNoYXJ0c1JlbmRlciIsImFkZEV2ZW50IiwiU2VyaWVzIiwiY2hhcnQiLCJhZGRpdGlvbmFsT3B0aW9ucyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImRlZmF1bHRPcHRpb25zIiwidGVtcGxhdGUiLCJicm93c2VyIiwiY3JlYXRlQnJvd3NlciIsInB1cHBldGVlckFyZ3MiLCJlbmFibGVkRGVidWciLCJkZWJ1Z09wdGlvbnMiLCJsYXVuY2hPcHRpb25zIiwidXNlckRhdGFEaXIiLCJoYW5kbGVTSUdJTlQiLCJoYW5kbGVTSUdURVJNIiwiaGFuZGxlU0lHSFVQIiwid2FpdEZvckluaXRpYWxQYWdlIiwiZGVmYXVsdFZpZXdwb3J0IiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwic2V0VGltZW91dCIsImNsb3NlQnJvd3NlciIsImNvbm5lY3RlZCIsImNsb3NlIiwibmV3UGFnZSIsInBvb2xSZXNvdXJjZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJfc2V0UGFnZUNvbnRlbnQiLCJfc2V0UGFnZUV2ZW50cyIsImlzQ2xvc2VkIiwiY2xlYXJQYWdlIiwiaGFyZFJlc2V0IiwiZ290byIsIndhaXRVbnRpbCIsImV2YWx1YXRlIiwiYm9keSIsImlubmVySFRNTCIsImlkIiwid29ya0NvdW50IiwiYWRkUGFnZVJlc291cmNlcyIsImluamVjdGVkUmVzb3VyY2VzIiwiaW5qZWN0ZWRKcyIsImpzIiwiY29udGVudCIsImZpbGVzIiwiaXNMb2NhbCIsImpzUmVzb3VyY2UiLCJhZGRTY3JpcHRUYWciLCJpbmplY3RlZENzcyIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJjc3NSZXNvdXJjZSIsImFkZFN0eWxlVGFnIiwiY2xlYXJQYWdlUmVzb3VyY2VzIiwicmVzb3VyY2UiLCJkaXNwb3NlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJlbGVtZW50IiwicmVtb3ZlIiwic2V0Q29udGVudCIsImNzc1RlbXBsYXRlIiwic3ZnVGVtcGxhdGUiLCJwdXBwZXRlZXJFeHBvcnQiLCJpc1NWRyIsInNpemUiLCJzdmdFbGVtZW50IiwicXVlcnlTZWxlY3RvciIsImNoYXJ0SGVpZ2h0IiwiYmFzZVZhbCIsImNoYXJ0V2lkdGgiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ4IiwieSIsIl9nZXRDbGlwUmVnaW9uIiwidmlld3BvcnRIZWlnaHQiLCJhYnMiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsInJlc3VsdCIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJfY3JlYXRlU1ZHIiwiX2NyZWF0ZUltYWdlIiwiX2NyZWF0ZVBERiIsIiRldmFsIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidHJ1bmMiLCJvdXRlckhUTUwiLCJjbGlwIiwicmFjZSIsInNjcmVlbnNob3QiLCJlbmNvZGluZyIsImZ1bGxQYWdlIiwib3B0aW1pemVGb3JTcGVlZCIsImNhcHR1cmVCZXlvbmRWaWV3cG9ydCIsInF1YWxpdHkiLCJvbWl0QmFja2dyb3VuZCIsIl9yZXNvbHZlIiwiZW11bGF0ZU1lZGlhVHlwZSIsInBkZiIsInBvb2xTdGF0cyIsImV4cG9ydHNBdHRlbXB0ZWQiLCJleHBvcnRzUGVyZm9ybWVkIiwiZXhwb3J0c0Ryb3BwZWQiLCJleHBvcnRzRnJvbVN2ZyIsImV4cG9ydHNGcm9tT3B0aW9ucyIsImV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMiLCJleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cyIsInRpbWVTcGVudCIsInRpbWVTcGVudEF2ZXJhZ2UiLCJpbml0UG9vbCIsInBvb2xPcHRpb25zIiwiUG9vbCIsIl9mYWN0b3J5IiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsImNsZWFyU3RhdHVzIiwiX2V2ZW50SWQiLCJpbml0aWFsUmVzb3VyY2VzIiwiYWNxdWlyZSIsInByb21pc2UiLCJyZWxlYXNlIiwia2lsbFBvb2wiLCJ3b3JrZXIiLCJ1c2VkIiwiZGVzdHJveWVkIiwicG9zdFdvcmsiLCJ3b3JrZXJIYW5kbGUiLCJnZXRQb29sSW5mbyIsImFjcXVpcmVDb3VudGVyIiwicmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsImV4cG9ydFRpbWUiLCJnZXRQb29sU3RhdHMiLCJnZXRQb29sSW5mb0pTT04iLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwibnVtRnJlZSIsImFsbENyZWF0ZWQiLCJwZW5kaW5nQWNxdWlyZXMiLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwZW5kaW5nQ3JlYXRlcyIsIm51bVBlbmRpbmdDcmVhdGVzIiwicGVuZGluZ1ZhbGlkYXRpb25zIiwibnVtUGVuZGluZ1ZhbGlkYXRpb25zIiwicGVuZGluZ0Rlc3Ryb3lzIiwiYWJzb2x1dGVBbGwiLCJjcmVhdGUiLCJ1dWlkIiwicmFuZG9tIiwic3RhcnREYXRlIiwidmFsaWRhdGUiLCJtYWluRnJhbWUiLCJkZXRhY2hlZCIsInJlbW92ZUFsbExpc3RlbmVycyIsInNhbml0aXplIiwiSlNET00iLCJET01QdXJpZnkiLCJBRERfVEFHUyIsInNpbmdsZUV4cG9ydCIsInN0YXJ0RXhwb3J0IiwiZGF0YSIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiYmF0Y2hSZXN1bHRzIiwiYWxsU2V0dGxlZCIsInJlYXNvbiIsImltYWdlT3B0aW9ucyIsImVuZENhbGxiYWNrIiwiZmlsZUNvbnRlbnQiLCJfZXhwb3J0RnJvbVN2ZyIsIl9leHBvcnRGcm9tT3B0aW9ucyIsImdldEFsbG93Q29kZUV4ZWN1dGlvbiIsInNldEFsbG93Q29kZUV4ZWN1dGlvbiIsImlucHV0VG9FeHBvcnQiLCJfcHJlcGFyZUV4cG9ydCIsIl9oYW5kbGVDdXN0b21Mb2dpYyIsIl9oYW5kbGVHbG9iYWxBbmRUaGVtZSIsIl9maW5kQ2hhcnRTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidGltZXJJZHMiLCJhZGRUaW1lciIsImNsZWFyQWxsVGltZXJzIiwiY2xlYXJJbnRlcnZhbCIsImNsZWFyVGltZW91dCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcXVlc3QiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RhdHVzIiwianNvbiIsImVycm9yTWlkZGxld2FyZSIsImFwcCIsInVzZSIsInJhdGVMaW1pdGluZ01pZGRsZXdhcmUiLCJyYXRlTGltaXRpbmdPcHRpb25zIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwicmF0ZUxpbWl0Iiwid2luZG93TXMiLCJsaW1pdCIsImRlbGF5TXMiLCJoYW5kbGVyIiwiZm9ybWF0Iiwic2VuZCIsImRlZmF1bHQiLCJza2lwIiwicXVlcnkiLCJhY2Nlc3NfdG9rZW4iLCJjb250ZW50VHlwZU1pZGRsZXdhcmUiLCJjb250ZW50VHlwZSIsImhlYWRlcnMiLCJyZXF1ZXN0Qm9keU1pZGRsZXdhcmUiLCJjb25uZWN0aW9uIiwicmVtb3RlQWRkcmVzcyIsInZhbGlkYXRlZE9wdGlvbnMiLCJwYXJhbXMiLCJmaWxlbmFtZSIsInZhbGlkYXRpb25NaWRkbGV3YXJlIiwicG9zdCIsInJldmVyc2VkTWltZSIsInBuZyIsImpwZWciLCJnaWYiLCJyZXF1ZXN0RXhwb3J0IiwicmVxdWVzdENvdW50ZXIiLCJjb25uZWN0aW9uQWJvcnRlZCIsInNvY2tldCIsImhhZEVycm9ycyIsImhlYWRlciIsImF0dGFjaG1lbnQiLCJleHBvcnRSb3V0ZXMiLCJzZXJ2ZXJTdGFydFRpbWUiLCJwYWNrYWdlRmlsZSIsInN1Y2Nlc3NSYXRlcyIsInJlY29yZEludGVydmFsIiwid2luZG93U2l6ZSIsIl9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlIiwiYSIsImIiLCJfc3RhcnRTdWNjZXNzUmF0ZSIsInNldEludGVydmFsIiwic3RhdHMiLCJzdWNjZXNzUmF0aW8iLCJoZWFsdGhSb3V0ZXMiLCJwZXJpb2QiLCJtb3ZpbmdBdmVyYWdlIiwiYm9vdFRpbWUiLCJ1cHRpbWUiLCJmbG9vciIsInNlcnZlclZlcnNpb24iLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VFeHBvcnRUaW1lIiwiYXR0ZW1wdGVkRXhwb3J0cyIsInBlcmZvcm1lZEV4cG9ydHMiLCJmYWlsZWRFeHBvcnRzIiwic3VjZXNzUmF0aW8iLCJ0b0ZpeGVkIiwic3ZnRXhwb3J0cyIsImpzb25FeHBvcnRzIiwic3ZnRXhwb3J0c0F0dGVtcHRzIiwianNvbkV4cG9ydHNBdHRlbXB0cyIsInVpUm91dGVzIiwic2VuZEZpbGUiLCJhY2NlcHRSYW5nZXMiLCJ2ZXJzaW9uQ2hhbmdlUm91dGVzIiwiYWRtaW5Ub2tlbiIsInRva2VuIiwiYWN0aXZlU2VydmVycyIsIk1hcCIsImV4cHJlc3MiLCJzdGFydFNlcnZlciIsInNlcnZlck9wdGlvbnMiLCJ1cGxvYWRMaW1pdEJ5dGVzIiwic3RvcmFnZSIsIm11bHRlciIsIm1lbW9yeVN0b3JhZ2UiLCJ1cGxvYWQiLCJsaW1pdHMiLCJmaWVsZFNpemUiLCJkaXNhYmxlIiwiY29ycyIsIm1ldGhvZHMiLCJzZXQiLCJ1cmxlbmNvZGVkIiwiZXh0ZW5kZWQiLCJub25lIiwic3RhdGljIiwiaHR0cFNlcnZlciIsImNyZWF0ZVNlcnZlciIsIl9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwibGlzdGVuIiwiY2VydCIsImh0dHBzU2VydmVyIiwiY2xvc2VTZXJ2ZXJzIiwiZGVsZXRlIiwiZ2V0U2VydmVycyIsImdldEV4cHJlc3MiLCJnZXRBcHAiLCJlbmFibGVSYXRlTGltaXRpbmciLCJtaWRkbGV3YXJlcyIsInNodXRkb3duQ2xlYW5VcCIsImV4aXRDb2RlIiwiZXhpdCIsImluaXRFeHBvcnQiLCJpbml0T3B0aW9ucyIsIl9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyIsImNvZGUiXSwibWFwcGluZ3MiOiJ3bEJBMkJPLE1BQU1BLFlBQVlDLElBQWFBLGNBQUMsSUFBSUMsSUFBSSxPQUFRLG9CQUFBQyxTQUFBQyxRQUFBLE9BQUFDLGNBQUFDLFlBQUFDLEtBQUFDLHdCQUFBLFdBQUFBLHVCQUFBQyxRQUFBQyxlQUFBRix1QkFBQUcsS0FBQSxJQUFBVCxJQUFBLFlBQUFDLFNBQUFTLFNBQUFMLE9BK0JoRCxTQUFTTSxTQUFTQyxHQUV2QixHQUFlLE9BQVhBLEdBQXFDLGlCQUFYQSxFQUM1QixPQUFPQSxFQUlULE1BQU1DLEVBQWFDLE1BQU1DLFFBQVFILEdBQVUsR0FBSyxHQUdoRCxJQUFLLE1BQU1JLEtBQU9KLEVBQ1pLLE9BQU9DLFVBQVVDLGVBQWVDLEtBQUtSLEVBQVFJLEtBQy9DSCxFQUFXRyxHQUFPTCxTQUFTQyxFQUFPSSxLQUt0QyxPQUFPSCxDQUNULENBMkRPLFNBQVNRLFVBQVVDLEdBQ3hCLElBRUUsTUFBTUMsRUFBYyxHQUFHRCxFQUFPRSxjQUFjQyxRQUFRLFFBQVMsV0FRN0QsTUFMb0IsVUFBaEJGLEdBQ0ZBLEVBQVlDLGNBSVAsQ0FBQyxRQUFTLGFBQWMsV0FBWSxjQUFjRSxTQUN2REgsR0FFRUEsRUFDQSxPQUNSLENBQUksTUFFQSxNQUFPLE9BQ1IsQ0FDSCxDQVlPLFNBQVNJLFdBQVdDLEVBQU1DLEdBTy9CLE1BQU8sR0FMVUMsZ0JBQWdCRCxHQUFXLFNBQ3pDRSxNQUFNLEtBQ05DLFdBR21CSixHQUN4QixDQWFPLFNBQVNLLFFBQVFMLEVBQU1DLEVBQVUsTUFFdEMsTUFBTUssRUFBWSxDQUNoQixZQUFhLE1BQ2IsYUFBYyxPQUNkLGtCQUFtQixNQUNuQixnQkFBaUIsT0FJYkMsRUFBVWxCLE9BQU9tQixPQUFPRixHQUc5QixHQUFJTCxFQUFTLENBQ1gsTUFBTVEsRUFBVVIsRUFBUUUsTUFBTSxLQUFLTyxNQUduQixRQUFaRCxFQUNGVCxFQUFPLE9BQ0VPLEVBQVFULFNBQVNXLElBQVlULElBQVNTLElBQy9DVCxFQUFPUyxFQUVWLENBR0QsT0FBT0gsRUFBVU4sSUFBU08sRUFBUUksTUFBTUMsR0FBTUEsSUFBTVosS0FBUyxLQUMvRCxDQVlPLFNBQVNFLGdCQUFnQlcsR0FDOUIsT0FBT0MsS0FBQUEsV0FBV0QsR0FBUUUsS0FBQUEsVUFBVUYsR0FBUUcsS0FBQUEsUUFBUUgsRUFDdEQsQ0FZTyxTQUFTSSxVQUFVQyxFQUFPbEIsR0FFL0IsTUFBYSxRQUFUQSxHQUEwQixPQUFSQSxFQUNibUIsT0FBT0MsS0FBS0YsRUFBTyxRQUFRRyxTQUFTLFVBSXRDSCxDQUNULENBT08sU0FBU0ksYUFFZCxPQUFPLElBQUlDLE1BQU9GLFdBQVdsQixNQUFNLEtBQUssR0FBR3FCLE1BQzdDLENBT08sU0FBU0MsaUJBQ2QsT0FBTyxJQUFJRixNQUFPRyxTQUNwQixDQVdPLFNBQVNDLFNBQVNDLEdBQ3ZCLE1BQWdELG9CQUF6Q3ZDLE9BQU9DLFVBQVUrQixTQUFTN0IsS0FBS29DLEVBQ3hDLENBV08sU0FBU0MsY0FBY0QsR0FDNUIsTUFDa0IsaUJBQVRBLElBQ04xQyxNQUFNQyxRQUFReUMsSUFDTixPQUFUQSxHQUM2QixJQUE3QnZDLE9BQU95QyxLQUFLRixHQUFNRyxNQUV0QixDQVdPLFNBQVNDLHVCQUF1QkosR0FTckMsTUFSc0IsQ0FDcEIsbURBQ0EsdUVBQ0Esd0VBQ0EsdUZBQ0EscUVBR21CSyxNQUFNQyxHQUFZQSxFQUFRQyxLQUFLUCxJQUN0RCxDQVNPLFNBQVNRLGNBQ2QsTUFBTUMsRUFBUUMsUUFBUUMsT0FBT0MsU0FDN0IsTUFBTyxJQUFNQyxPQUFPSCxRQUFRQyxPQUFPQyxTQUFXSCxHQUFTLEdBQ3pELENBWU8sU0FBU0ssWUFBWUMsRUFBT0MsRUFBWSxHQUM3QyxNQUFNQyxFQUFhQyxLQUFLQyxJQUFJLEdBQUlILEdBQWEsR0FDN0MsT0FBT0UsS0FBS0UsT0FBT0wsRUFBUUUsR0FBY0EsQ0FDM0MsQ0E2Qk8sU0FBU0ksV0FBV0MsRUFBWUMsRUFBb0JDLEdBQWEsR0FDdEUsR0FBSUYsR0FBb0MsaUJBQWZBLEVBR3ZCLE9BRkFBLEVBQWFBLEVBQVcxQixRQUVUNkIsU0FBUyxPQUVmRixFQUNIRixXQUNFSyxHQUFBQSxhQUFhcEQsZ0JBQWdCZ0QsR0FBYSxRQUMxQ0MsRUFDQUMsR0FFRixNQUVIQSxJQUNBRixFQUFXSyxXQUFXLGVBQ3JCTCxFQUFXSyxXQUFXLGdCQUN0QkwsRUFBV0ssV0FBVyxTQUN0QkwsRUFBV0ssV0FBVyxVQUdqQixJQUFJTCxPQUlOQSxFQUFXckQsUUFBUSxLQUFNLEdBRXBDLENDdlhBLE1BQU0yRCxPQUFTLENBQUMsTUFBTyxTQUFVLE9BQVEsT0FBUSxTQUczQ0MsUUFBVSxDQUVkQyxXQUFXLEVBQ1hDLFFBQVEsRUFDUkMsYUFBYSxFQUViQyxVQUFXLEdBRVhDLFdBQVksQ0FDVixDQUNFQyxNQUFPLFFBQ1BDLE1BQU9SLE9BQU8sSUFFaEIsQ0FDRU8sTUFBTyxVQUNQQyxNQUFPUixPQUFPLElBRWhCLENBQ0VPLE1BQU8sU0FDUEMsTUFBT1IsT0FBTyxJQUVoQixDQUNFTyxNQUFPLFVBQ1BDLE1BQU9SLE9BQU8sSUFFaEIsQ0FDRU8sTUFBTyxZQUNQQyxNQUFPUixPQUFPLE1Ba0JiLFNBQVNTLE9BQU9DLEdBQ3JCLE1BQU9DLEtBQWFDLEdBQVNGLEdBR3ZCSixXQUFFQSxFQUFVTyxNQUFFQSxHQUFVWixRQUc5QixHQUNlLElBQWJVLElBQ2MsSUFBYkEsR0FBa0JBLEVBQVdFLEdBQVNBLEVBQVFQLEVBQVcvQixRQUUxRCxPQUlGLE1BQU11QyxFQUFTLEdBQUdoRCxpQkFBaUJ3QyxFQUFXSyxFQUFXLEdBQUdKLFdBR3hETixRQUFRRSxRQUNWWSxXQUFXSCxFQUFPRSxHQUloQmIsUUFBUUMsV0FDVmMsUUFBUVAsSUFBSVEsV0FDVkMsRUFDQSxDQUFDSixFQUFPakQsV0FBV29DLFFBQVFLLFdBQVdLLEVBQVcsR0FBR0gsUUFBUVcsT0FBT1AsR0FHekUsQ0FnQk8sU0FBU1EsYUFBYVQsRUFBVVUsRUFBT0MsR0FFNUMsTUFBTUMsRUFBY0QsR0FBa0JELEdBQVNBLEVBQU1HLFNBQVksSUFHM0RYLE1BQUVBLEVBQUtQLFdBQUVBLEdBQWVMLFFBRzlCLEdBQWlCLElBQWJVLEdBQWtCQSxFQUFXRSxHQUFTQSxFQUFRUCxFQUFXL0IsT0FDM0QsT0FJRixNQUFNdUMsRUFBUyxHQUFHaEQsaUJBQWlCd0MsRUFBV0ssRUFBVyxHQUFHSixXQUd0RGtCLEVBQWVKLEdBQVNBLEVBQU1LLE1BRzlCZCxFQUFRLENBQUNXLEdBQ1hFLEdBQ0ZiLEVBQU1lLEtBQUssS0FBTUYsR0FJZnhCLFFBQVFFLFFBQ1ZZLFdBQVdILEVBQU9FLEdBSWhCYixRQUFRQyxXQUNWYyxRQUFRUCxJQUFJUSxXQUNWQyxFQUNBLENBQUNKLEVBQU9qRCxXQUFXb0MsUUFBUUssV0FBV0ssRUFBVyxHQUFHSCxRQUFRVyxPQUFPLENBQ2pFUCxFQUFNaEUsUUFBUW9ELE9BQU9XLEVBQVcsT0FDN0JDLElBSVgsQ0FVTyxTQUFTZ0IsWUFBWUMsR0FFMUIsTUFBTWhCLE1BQUVBLEVBQUtpQixLQUFFQSxFQUFJQyxLQUFFQSxFQUFJN0IsVUFBRUEsRUFBU0MsT0FBRUEsR0FBVzBCLEVBR2pENUIsUUFBUUcsYUFBYyxFQUN0QkgsUUFBUUksVUFBWSxHQUdwQjJCLFlBQVluQixHQUdab0IscUJBQXFCL0IsR0FHckJnQyxrQkFBa0JKLEVBQU1DLEVBQU01QixFQUNoQyxDQVVPLFNBQVM2QixZQUFZbkIsR0FFeEI1QixPQUFPa0QsVUFBVXRCLElBQ2pCQSxHQUFTLEdBQ1RBLEdBQVNaLFFBQVFLLFdBQVcvQixTQUc1QjBCLFFBQVFZLE1BQVFBLEVBRXBCLENBU08sU0FBU29CLHFCQUFxQi9CLEdBRW5DRCxRQUFRQyxZQUFjQSxDQUN4QixDQWFPLFNBQVNnQyxrQkFBa0JKLEVBQU1DLEVBQU01QixHQUU1Q0YsUUFBUUUsU0FBV0EsRUFHZkYsUUFBUUUsU0FDVkYsUUFBUTZCLEtBQU9BLEdBQVEsR0FDdkI3QixRQUFROEIsS0FBT0EsR0FBUSxHQUUzQixDQVlBLFNBQVNoQixXQUFXSCxFQUFPRSxHQUNwQmIsUUFBUUcsZUFFVmdDLGNBQVcxRixnQkFBZ0J1RCxRQUFRNkIsUUFDbENPLEdBQUFBLFVBQVUzRixnQkFBZ0J1RCxRQUFRNkIsT0FHcEM3QixRQUFRSSxVQUFZM0QsZ0JBQWdCNEYsS0FBSUEsS0FBQ3JDLFFBQVE2QixLQUFNN0IsUUFBUThCLE9BSS9EOUIsUUFBUUcsYUFBYyxHQUl4Qm1DLEdBQVVBLFdBQ1J0QyxRQUFRSSxVQUNSLENBQUNTLEdBQVFLLE9BQU9QLEdBQU8wQixLQUFLLEtBQU8sTUFDbENqQixJQUNLQSxHQUFTcEIsUUFBUUUsUUFBVUYsUUFBUUcsY0FDckNILFFBQVFFLFFBQVMsRUFDakJGLFFBQVFHLGFBQWMsRUFDdEJnQixhQUFhLEVBQUdDLEVBQU8seUNBQ3hCLEdBR1AsQ0NqUE8sTUFBTW1CLGNBQWdCLENBQzNCQyxVQUFXLENBQ1QvQixLQUFNLENBQ0p2QixNQUFPLENBQ0wsbUNBQ0Esa0JBQ0EsMENBQ0EsMkJBQ0Esa0NBQ0Esa0NBQ0Esd0NBQ0EsMkNBQ0EscUJBQ0EsNEJBQ0EsMkNBQ0EsdURBQ0EsNkJBQ0EseUJBQ0EsMEJBQ0EsK0JBQ0EsdUJBQ0EsdUZBQ0EseUJBQ0Esb0NBQ0Esb0JBQ0EsMEJBQ0EsOENBQ0EsMkJBQ0EsMEJBQ0EsNkJBQ0EsbUNBQ0Esd0NBQ0EsbUNBQ0EsMkJBQ0Esa0NBQ0EsdUJBQ0EsaUJBQ0EseUJBQ0EsOEJBQ0Esb0JBQ0EsMkJBQ0EsZUFDQSw2QkFDQSxpQkFDQSxhQUNBLGVBQ0Esc0JBQ0EsY0FDQSx5QkFDQSxvQkFDQSx1QkFFRnVELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLGlCQUNUQyxRQUFTLGdCQUNUQyxZQUFhLCtCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLE9BQ051RyxVQUFXLE9BSWpCQyxXQUFZLENBQ1ZDLFFBQVMsQ0FDUDlELE1BQU8sU0FDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHFCQUNURSxZQUFhLHFCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1YwRyxPQUFRLENBQ04vRCxNQUFPLDhCQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMscUJBQ1RFLFlBQWEsaUNBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sU0FHVjJHLFdBQVksQ0FDVmhFLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLHlCQUNURSxZQUFhLGtEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Y0RyxVQUFXLENBQ1RqRSxNQUFPLFNBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyx3QkFDVEUsWUFBYSwrQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWNkcsWUFBYSxDQUNYbEUsTUFBTyxDQUFDLGFBQWMsa0JBQW1CLGlCQUN6Q3VELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLDBCQUNURSxZQUFhLG1DQUNiQyxjQUFlLENBQ2J0RyxLQUFNLGNBQ044RyxhQUFjLDBEQUdsQkMsY0FBZSxDQUNicEUsTUFBTyxDQUNMLFFBQ0EsTUFDQSxRQUNBLFlBQ0EsdUJBQ0EsZ0JBRUEsZUFDQSxRQUNBLE9BQ0EsYUFDQSxtQkFDQSxlQUNBLGNBQ0EsVUFDQSxVQUNBLGNBQ0EsV0FDQSxVQUNBLFlBQ0EsY0FDQSxZQUNBLHNCQUNBLFNBQ0EsU0FDQSxXQUNBLGFBQ0EsWUFDQSxlQUNBLHlCQUNBLFNBQ0EsZUFDQSxZQUNBLGtCQUNBLFNBQ0EsY0FDQSxtQkFDQSxlQUNBLGtCQUNBLGNBQ0EsZUFFQSxjQUNBLFdBQ0EsZUFDQSxXQUNBLFNBQ0EsT0FDQSxXQUNBLFlBQ0EsU0FDQSxxQkFDQSxhQUNBLFdBQ0EsV0FDQSxXQUNBLFdBQ0EsZUFDQSxVQUNBLGtCQUNBLG9CQUNBLGFBQ0EsVUFDQSxjQUNBLFlBQ0EsWUFFRnVELE1BQU8sQ0FBQyxZQUNSQyxRQUFTLDRCQUNURSxZQUFhLHFDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLGNBQ044RyxhQUFjLDBEQUdsQkUsaUJBQWtCLENBQ2hCckUsTUFBTyxDQUFDLGtCQUNSdUQsTUFBTyxDQUFDLFlBQ1JDLFFBQVMsK0JBQ1RFLFlBQWEsd0NBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sY0FDTjhHLGFBQWMsMERBR2xCRyxjQUFlLENBQ2J0RSxNQUFPLENBQ0wsd0VBQ0Esa0dBRUZ1RCxNQUFPLENBQUMsWUFDUkMsUUFBUyw0QkFDVEUsWUFBYSxxREFDYkMsY0FBZSxDQUNidEcsS0FBTSxPQUNOdUcsVUFBVyxPQUlqQlcsT0FBUSxDQUNOQyxPQUFRLENBQ054RSxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQkFDVEUsWUFDRSwrREFDRkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWb0gsTUFBTyxDQUNMekUsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsU0FBVSxRQUM1QkMsUUFBUyxlQUNURSxZQUNFLG1FQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZxSCxRQUFTLENBQ1AxRSxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxTQUFVLFFBQzVCQyxRQUFTLGlCQUNURSxZQUFhLCtCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZzSCxJQUFLLENBQ0gzRSxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxhQUNURSxZQUFhLG1EQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1Z1SCxNQUFPLENBQ0w1RSxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxlQUNURSxZQUNFLGdFQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZDLFFBQVMsQ0FDUDBDLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLGlCQUNURSxZQUNFLHFGQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZBLEtBQU0sQ0FDSjJDLE1BQU8sTUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGNBQ1RFLFlBQWEsb0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sU0FDTndILEtBQU0sZUFDTkMsUUFBUyxDQUFDLE1BQU8sT0FBUSxNQUFPLFNBR3BDL0gsT0FBUSxDQUNOaUQsTUFBTyxRQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsZ0JBQ1RFLFlBQ0UsdUVBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sU0FDTndILEtBQU0saUJBQ05DLFFBQVMsQ0FBQyxRQUFTLGFBQWMsV0FBWSxnQkFHakRDLElBQUssQ0FDSC9FLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGFBQ1RFLFlBQ0Usb0ZBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVjJILFdBQVksQ0FDVmhGLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLHFCQUNURSxZQUNFLDBFQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Y0SCxPQUFRLENBQ05qRixNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQkFDVEUsWUFBYSx5REFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWNkgsTUFBTyxDQUNMbEYsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsZUFDVEUsWUFBYSx3REFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWOEgsTUFBTyxDQUNMbkYsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsZUFDVEUsWUFDRSxnRkFDRkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWK0gsY0FBZSxDQUNicEYsTUFBTyxJQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsd0JBQ1RFLFlBQWEsa0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVmdJLGFBQWMsQ0FDWnJGLE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHVCQUNURSxZQUFhLGlEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZpSSxhQUFjLENBQ1p0RixNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyx1QkFDVEUsWUFDRSx5RUFDRkMsY0FBZSxDQUNidEcsS0FBTSxTQUNOa0ksSUFBSyxHQUNMQyxJQUFLLElBR1RDLGNBQWUsQ0FDYnpGLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFNBQVUsUUFDNUJDLFFBQVMsd0JBQ1RFLFlBQ0UsbUZBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sU0FHVnFJLGFBQWMsQ0FDWjFGLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFNBQVUsUUFDNUJDLFFBQVMsdUJBQ1RFLFlBQ0Usa0ZBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sU0FHVnNJLHFCQUFzQixDQUNwQjNGLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLCtCQUNURSxZQUFhLDZDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFlBSVp1SSxZQUFhLENBQ1hDLG1CQUFvQixDQUNsQjdGLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG9DQUNURSxZQUNFLG1FQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZtRCxtQkFBb0IsQ0FDbEJSLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG9DQUNURSxZQUNFLGtGQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZrRCxXQUFZLENBQ1ZQLE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLDJCQUNURSxZQUNFLHVIQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1Z5SSxTQUFVLENBQ1I5RixNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyx3QkFDVEUsWUFDRSxrRkFDRkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWMEksVUFBVyxDQUNUL0YsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsU0FBVSxRQUM1QkMsUUFBUyx5QkFDVEUsWUFDRSxzR0FDRkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWMkksV0FBWSxDQUNWaEcsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsMkJBQ1R5QyxXQUFZLFdBQ1p2QyxZQUFhLCtDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1Y2SSxhQUFjLENBQ1psRyxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyw2QkFDVEUsWUFDRSwrREFDRkMsY0FBZSxDQUNidEcsS0FBTSxVQUlaOEksT0FBUSxDQUNOQyxPQUFRLENBQ05wRyxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxnQkFDVEMsUUFBUyxlQUNUQyxZQUFhLDhCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZnSixLQUFNLENBQ0pyRyxNQUFPLFVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxjQUNURSxZQUFhLHlCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBR1ZpSixLQUFNLENBQ0p0RyxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxjQUNURSxZQUFhLDZCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZrSixZQUFhLENBQ1h2RyxNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxzQkFDVEUsWUFBYSxrQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWbUosYUFBYyxDQUNaeEcsT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsc0JBQ1RDLFFBQVMscUJBQ1RDLFlBQ0UsMEVBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVm9KLE1BQU8sQ0FDTEosS0FBTSxDQUNKckcsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsb0JBQ1RDLFFBQVMsWUFDVEMsWUFBYSwwQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWaUosS0FBTSxDQUNKdEcsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsb0JBQ1RDLFFBQVMsWUFDVEMsWUFBYSwwQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWcUosUUFBUyxDQUNQMUcsTUFBTyxJQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RDLFFBQVMsZUFDVEMsWUFDRSw4REFDRkMsY0FBZSxDQUNidEcsS0FBTSxZQUlac0osYUFBYyxDQUNaUCxPQUFRLENBQ05wRyxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyw4QkFDVEMsUUFBUyxxQkFDVEMsWUFBYSxrREFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWdUosWUFBYSxDQUNYNUcsTUFBTyxHQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsb0NBQ1R5QyxXQUFZLFlBQ1p2QyxZQUFhLGdEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Z3SixPQUFRLENBQ043RyxNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyw4QkFDVEUsWUFBYSwyQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWeUosTUFBTyxDQUNMOUcsTUFBTyxFQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsNkJBQ1RFLFlBQ0UsdUVBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVjBKLFdBQVksQ0FDVi9HLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLG1DQUNURSxZQUFhLHNEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1YySixRQUFTLENBQ1BoSCxNQUFPLEtBQ1B1RCxNQUFPLENBQUMsU0FBVSxRQUNsQkMsUUFBUyxnQ0FDVEUsWUFBYSx3REFDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWNEosVUFBVyxDQUNUakgsTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFNBQVUsUUFDbEJDLFFBQVMsa0NBQ1RFLFlBQWEsd0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sVUFJWjZKLElBQUssQ0FDSGQsT0FBUSxDQUNOcEcsT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsb0JBQ1RDLFFBQVMsWUFDVEMsWUFBYSxtQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWOEosTUFBTyxDQUNMbkgsT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsbUJBQ1RDLFFBQVMsV0FDVHdDLFdBQVksVUFDWnZDLFlBQWEsZ0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVmlKLEtBQU0sQ0FDSnRHLE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGtCQUNUQyxRQUFTLFVBQ1RDLFlBQWEsMEJBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVitKLFNBQVUsQ0FDUnBILE1BQU8sS0FDUHVELE1BQU8sQ0FBQyxTQUFVLFFBQ2xCQyxRQUFTLHVCQUNUQyxRQUFTLGNBQ1R3QyxXQUFZLFVBQ1p2QyxZQUFhLHVDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBS2RnSyxLQUFNLENBQ0pDLFdBQVksQ0FDVnRILE1BQU8sRUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLG1CQUNURSxZQUFhLHNEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZrSyxXQUFZLENBQ1Z2SCxNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxtQkFDVHlDLFdBQVksVUFDWnZDLFlBQWEsMENBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVm1LLFVBQVcsQ0FDVHhILE1BQU8sR0FDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGtCQUNURSxZQUFhLHdEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZvSyxlQUFnQixDQUNkekgsTUFBTyxJQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsbURBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVnFLLGNBQWUsQ0FDYjFILE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHNCQUNURSxZQUFhLGtEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZzSyxlQUFnQixDQUNkM0gsTUFBTyxJQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsb0RBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVnVLLFlBQWEsQ0FDWDVILE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLG9CQUNURSxZQUFhLHdEQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Z3SyxvQkFBcUIsQ0FDbkI3SCxNQUFPLElBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyw2QkFDVEUsWUFDRSx3RUFDRkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWeUssZUFBZ0IsQ0FDZDlILE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLHVCQUNURSxZQUNFLCtEQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZtSixhQUFjLENBQ1p4RyxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxvQkFDVEMsUUFBUyxtQkFDVEMsWUFBYSw2Q0FDYkMsY0FBZSxDQUNidEcsS0FBTSxZQUlaeUQsUUFBUyxDQUNQWSxNQUFPLENBQ0wxQixNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxnQkFDVEMsUUFBUyxXQUNUQyxZQUFhLDBCQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFNBQ05nRCxNQUFPLEVBQ1BrRixJQUFLLEVBQ0xDLElBQUssSUFHVDVDLEtBQU0sQ0FDSjVDLE1BQU8sK0JBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxlQUNUQyxRQUFTLFVBQ1RDLFlBQ0UsOERBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sU0FHVnNGLEtBQU0sQ0FDSjNDLE1BQU8sTUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLGVBQ1RDLFFBQVMsVUFDVEMsWUFBYSwwREFDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWMEQsVUFBVyxDQUNUZixPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxxQkFDVEMsUUFBUyxlQUNUQyxZQUFhLHNDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1YyRCxPQUFRLENBQ05oQixPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxrQkFDVEMsUUFBUyxZQUNUQyxZQUFhLHdDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFlBSVowSyxHQUFJLENBQ0YzQixPQUFRLENBQ05wRyxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxZQUNUQyxRQUFTLFdBQ1RDLFlBQWEsbURBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVjJLLE1BQU8sQ0FDTGhJLE1BQU8sSUFDUHVELE1BQU8sQ0FBQyxVQUNSQyxRQUFTLFdBQ1RDLFFBQVMsVUFDVEMsWUFBYSxnQ0FDYkMsY0FBZSxDQUNidEcsS0FBTSxVQUlaNEssTUFBTyxDQUNMQyxRQUFTLENBQ1BsSSxNQUFPLGFBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxpQkFDVEUsWUFBYSwrQkFDYkMsY0FBZSxDQUNidEcsS0FBTSxTQUdWOEsscUJBQXNCLENBQ3BCbkksT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsZ0NBQ1RFLFlBQWEsaURBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVitLLE9BQVEsQ0FDTnBJLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGdCQUNURSxZQUFhLCtDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZnTCxjQUFlLENBQ2JySSxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyx3QkFDVEUsWUFBYSxvREFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWaUwsaUJBQWtCLENBQ2hCdEksT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsMkJBQ1RFLFlBQWEseURBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sWUFJWmtMLE1BQU8sQ0FDTG5DLE9BQVEsQ0FDTnBHLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGVBQ1RDLFFBQVMsY0FDVEMsWUFBYSw0REFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWbUwsU0FBVSxDQUNSeEksT0FBTyxFQUNQdUQsTUFBTyxDQUFDLFdBQ1JDLFFBQVMsaUJBQ1RFLFlBQ0UsNkVBQ0ZDLGNBQWUsQ0FDYnRHLEtBQU0sV0FHVm9MLFNBQVUsQ0FDUnpJLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLGlCQUNURSxZQUFhLCtDQUNiQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZxTCxnQkFBaUIsQ0FDZjFJLE9BQU8sRUFDUHVELE1BQU8sQ0FBQyxXQUNSQyxRQUFTLDBCQUNURSxZQUNFLHFFQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1ZzTCxPQUFRLENBQ04zSSxPQUFPLEVBQ1B1RCxNQUFPLENBQUMsV0FDUkMsUUFBUyxlQUNURSxZQUNFLGtGQUNGQyxjQUFlLENBQ2J0RyxLQUFNLFdBR1Z1TCxPQUFRLENBQ041SSxNQUFPLEVBQ1B1RCxNQUFPLENBQUMsVUFDUkMsUUFBUyxnQkFDVEUsWUFBYSw0REFDYkMsY0FBZSxDQUNidEcsS0FBTSxXQUdWd0wsY0FBZSxDQUNiN0ksTUFBTyxLQUNQdUQsTUFBTyxDQUFDLFVBQ1JDLFFBQVMsdUJBQ1RFLFlBQWEsMEJBQ2JDLGNBQWUsQ0FDYnRHLEtBQU0sYUFPRHlMLFlBQWNDLG1CQUFtQjFGLGVBR2pDMkYsY0FBZ0JDLHFCQUFxQjVGLGVBb0JsRCxTQUFTMEYsbUJBQW1CRyxFQUFRSixFQUFjLENBQUEsRUFBSUssRUFBWSxJQXFCaEUsT0FwQkF6TSxPQUFPeUMsS0FBSytKLEdBQVFFLFNBQVMzTSxJQUUzQixNQUFNNE0sRUFBUUgsRUFBT3pNLFFBR00sSUFBaEI0TSxFQUFNckosTUFFZitJLG1CQUFtQk0sRUFBT1AsRUFBYSxHQUFHSyxLQUFhMU0sTUFHdkRxTSxFQUFZTyxFQUFNNUYsU0FBV2hILEdBQU8sR0FBRzBNLEtBQWExTSxJQUFNNk0sVUFBVSxRQUczQ3ZILElBQXJCc0gsRUFBTXBELGFBQ1I2QyxFQUFZTyxFQUFNcEQsWUFBYyxHQUFHa0QsS0FBYTFNLElBQU02TSxVQUFVLElBRW5FLElBSUlSLENBQ1QsQ0FpQkEsU0FBU0cscUJBQXFCQyxFQUFRRixFQUFnQixJQWtCcEQsT0FqQkF0TSxPQUFPeUMsS0FBSytKLEdBQVFFLFNBQVMzTSxJQUUzQixNQUFNNE0sRUFBUUgsRUFBT3pNLFFBR00sSUFBaEI0TSxFQUFNOUYsTUFFZjBGLHFCQUFxQkksRUFBT0wsR0FHeEJLLEVBQU05RixNQUFNcEcsU0FBUyxXQUN2QjZMLEVBQWN4RyxLQUFLL0YsRUFFdEIsSUFJSXVNLENBQ1QsQ0NyaENBTyxPQUFPTCxTQUlQLE1BQU1NLEVBQUksQ0FHUkMsTUFBUUMsR0FDTkMsSUFBQ0EsRUFDRUMsU0FDQUMsV0FBVzdKLEdBQ1ZBLEVBQ0d4QyxNQUFNLEtBQ05zTSxLQUFLOUosR0FBVUEsRUFBTW5CLFNBQ3JCa0wsUUFBUS9KLEdBQVUwSixFQUFZdk0sU0FBUzZDLE9BRTNDNkosV0FBVzdKLEdBQVdBLEVBQU1aLE9BQVNZLE9BQVErQixJQUlsRGlJLFFBQVMsSUFDUEwsSUFBQ0EsRUFDRU0sS0FBSyxDQUFDLE9BQVEsUUFBUyxLQUN2QkosV0FBVzdKLEdBQXFCLEtBQVZBLEVBQXlCLFNBQVZBLE9BQW1CK0IsSUFJN0RrSSxLQUFPcE0sR0FDTDhMLElBQUNBLEVBQ0VNLEtBQUssSUFBSXBNLEVBQVEsS0FDakJnTSxXQUFXN0osR0FBcUIsS0FBVkEsRUFBZUEsT0FBUStCLElBSWxENkgsT0FBUSxJQUNORCxJQUFDQSxFQUNFQyxTQUNBL0ssT0FDQXFMLFFBQ0VsSyxJQUNFLENBQUMsUUFBUyxZQUFhLE9BQVEsT0FBTzdDLFNBQVM2QyxJQUN0QyxLQUFWQSxJQUNEQSxJQUFXLENBQ1ZxQyxRQUFTLG1EQUFtRHJDLFNBRy9ENkosV0FBVzdKLEdBQXFCLEtBQVZBLEVBQWVBLE9BQVErQixJQUlsRG9JLFlBQWEsSUFDWFIsSUFBQ0EsRUFDRUMsU0FDQS9LLE9BQ0FxTCxRQUNFbEssR0FDVyxLQUFWQSxJQUFrQm9LLE1BQU1DLFdBQVdySyxLQUFXcUssV0FBV3JLLEdBQVMsSUFDbkVBLElBQVcsQ0FDVnFDLFFBQVMscURBQXFEckMsU0FHakU2SixXQUFXN0osR0FBcUIsS0FBVkEsRUFBZXFLLFdBQVdySyxRQUFTK0IsSUFJOUR1SSxlQUFnQixJQUNkWCxJQUFDQSxFQUNFQyxTQUNBL0ssT0FDQXFMLFFBQ0VsSyxHQUNXLEtBQVZBLElBQWtCb0ssTUFBTUMsV0FBV3JLLEtBQVdxSyxXQUFXckssSUFBVSxJQUNwRUEsSUFBVyxDQUNWcUMsUUFBUyx5REFBeURyQyxTQUdyRTZKLFdBQVc3SixHQUFxQixLQUFWQSxFQUFlcUssV0FBV3JLLFFBQVMrQixLQUduRHdJLE9BQVNaLElBQUNBLEVBQUNhLE9BQU8sQ0FFN0JDLGVBQWdCakIsRUFBRUksU0FHbEJjLG1CQUFvQmYsSUFBQ0EsRUFDbEJDLFNBQ0EvSyxPQUNBcUwsUUFDRWxLLEdBQVUsNkJBQTZCUixLQUFLUSxJQUFvQixLQUFWQSxJQUN0REEsSUFBVyxDQUNWcUMsUUFBUyw0RkFBNEZyQyxTQUd4RzZKLFdBQVc3SixHQUFxQixLQUFWQSxFQUFlQSxPQUFRK0IsSUFDaEQ0SSxtQkFBb0JoQixJQUFDQSxFQUNsQkMsU0FDQS9LLE9BQ0FxTCxRQUNFbEssR0FDQ0EsRUFBTVksV0FBVyxhQUNqQlosRUFBTVksV0FBVyxZQUNQLEtBQVZaLElBQ0RBLElBQVcsQ0FDVnFDLFFBQVMsNkZBQTZGckMsU0FHekc2SixXQUFXN0osR0FBcUIsS0FBVkEsRUFBZUEsT0FBUStCLElBQ2hENkksdUJBQXdCcEIsRUFBRVEsVUFDMUJhLHNCQUF1QnJCLEVBQUVJLFNBQ3pCa0IsdUJBQXdCdEIsRUFBRUksU0FDMUJtQix3QkFBeUJ2QixFQUFFQyxNQUFNcEcsY0FBY1EsV0FBV0ssWUFBWWxFLE9BQ3RFZ0wsMEJBQTJCeEIsRUFBRUMsTUFDM0JwRyxjQUFjUSxXQUFXTyxjQUFjcEUsT0FFekNpTCw2QkFBOEJ6QixFQUFFQyxNQUM5QnBHLGNBQWNRLFdBQVdRLGlCQUFpQnJFLE9BRTVDa0wsMEJBQTJCMUIsRUFBRUMsTUFDM0JwRyxjQUFjUSxXQUFXUyxjQUFjdEUsT0FJekNtTCxjQUFlM0IsRUFBRUksU0FDakJ3QixhQUFjNUIsRUFBRUksU0FDaEJ5QixlQUFnQjdCLEVBQUVJLFNBQ2xCMEIsV0FBWTlCLEVBQUVJLFNBQ2QyQixhQUFjL0IsRUFBRUksU0FDaEI0QixlQUFnQmhDLEVBQUVJLFNBQ2xCNkIsWUFBYWpDLEVBQUVTLEtBQUssQ0FBQyxPQUFRLE1BQU8sTUFBTyxRQUMzQ3lCLGNBQWVsQyxFQUFFUyxLQUFLLENBQUMsUUFBUyxhQUFjLFdBQVksZUFDMUQwQixXQUFZbkMsRUFBRVEsVUFDZDRCLG1CQUFvQnBDLEVBQUVRLFVBQ3RCNkIsY0FBZXJDLEVBQUVXLGNBQ2pCMkIsYUFBY3RDLEVBQUVXLGNBQ2hCNEIsYUFBY3ZDLEVBQUVXLGNBQ2hCNkIsc0JBQXVCeEMsRUFBRVcsY0FDekI4QixxQkFBc0J6QyxFQUFFVyxjQUN4QitCLHFCQUFzQjFDLEVBQUVXLGNBQ3hCZ0Msc0JBQXVCM0MsRUFBRUksU0FDekJ3QyxxQkFBc0I1QyxFQUFFSSxTQUN4QnlDLDZCQUE4QjdDLEVBQUVjLGlCQUdoQ2dDLGtDQUFtQzlDLEVBQUVRLFVBQ3JDdUMsa0NBQW1DL0MsRUFBRVEsVUFDckN3Qyx5QkFBMEJoRCxFQUFFSSxTQUM1QjZDLHNCQUF1QmpELEVBQUVJLFNBQ3pCOEMsdUJBQXdCbEQsRUFBRUksU0FDMUIrQyx5QkFBMEJuRCxFQUFFSSxTQUM1QmdELDJCQUE0QnBELEVBQUVJLFNBRzlCaUQsY0FBZXJELEVBQUVRLFVBQ2pCOEMsWUFBYXRELEVBQUVJLFNBQ2ZtRCxZQUFhdkQsRUFBRVcsY0FDZjZDLG9CQUFxQnhELEVBQUVXLGNBQ3ZCOEMsb0JBQXFCekQsRUFBRVEsVUFHdkJrRCxrQkFBbUIxRCxFQUFFSSxTQUNyQnVELGtCQUFtQjNELEVBQUVXLGNBQ3JCaUQscUJBQXNCNUQsRUFBRWMsaUJBR3hCK0MsNEJBQTZCN0QsRUFBRVEsVUFDL0JzRCxrQ0FBbUM5RCxFQUFFYyxpQkFDckNpRCw0QkFBNkIvRCxFQUFFYyxpQkFDL0JrRCwyQkFBNEJoRSxFQUFFYyxpQkFDOUJtRCxpQ0FBa0NqRSxFQUFFUSxVQUNwQzBELDhCQUErQmxFLEVBQUVJLFNBQ2pDK0QsZ0NBQWlDbkUsRUFBRUksU0FHbkNnRSxrQkFBbUJwRSxFQUFFUSxVQUNyQjZELGlCQUFrQnJFLEVBQUVRLFVBQ3BCOEQsZ0JBQWlCdEUsRUFBRVcsY0FDbkI0RCxxQkFBc0J2RSxFQUFFSSxTQUd4Qm9FLGlCQUFrQnhFLEVBQUVjLGlCQUNwQjJELGlCQUFrQnpFLEVBQUVjLGlCQUNwQjRELGdCQUFpQjFFLEVBQUVXLGNBQ25CZ0UscUJBQXNCM0UsRUFBRWMsaUJBQ3hCOEQsb0JBQXFCNUUsRUFBRWMsaUJBQ3ZCK0QscUJBQXNCN0UsRUFBRWMsaUJBQ3hCZ0Usa0JBQW1COUUsRUFBRWMsaUJBQ3JCaUUsMkJBQTRCL0UsRUFBRWMsaUJBQzlCa0UscUJBQXNCaEYsRUFBRWMsaUJBQ3hCbUUsa0JBQW1CakYsRUFBRVEsVUFHckIwRSxjQUFlL0UsSUFBQ0EsRUFDYkMsU0FDQS9LLE9BQ0FxTCxRQUNFbEssR0FDVyxLQUFWQSxJQUNFb0ssTUFBTUMsV0FBV3JLLEtBQ2pCcUssV0FBV3JLLElBQVUsR0FDckJxSyxXQUFXckssSUFBVSxJQUN4QkEsSUFBVyxDQUNWcUMsUUFBUyxtR0FBbUdyQyxTQUcvRzZKLFdBQVc3SixHQUFxQixLQUFWQSxFQUFlcUssV0FBV3JLLFFBQVMrQixJQUM1RDRNLGFBQWNuRixFQUFFSSxTQUNoQmdGLGFBQWNwRixFQUFFSSxTQUNoQmlGLG1CQUFvQnJGLEVBQUVRLFVBQ3RCOEUsZ0JBQWlCdEYsRUFBRVEsVUFHbkIrRSxVQUFXdkYsRUFBRVEsVUFDYmdGLFNBQVV4RixFQUFFSSxTQUdacUYsZUFBZ0J6RixFQUFFUyxLQUFLLENBQUMsY0FBZSxhQUFjLFNBQ3JEaUYsOEJBQStCMUYsRUFBRVEsVUFDakNtRixjQUFlM0YsRUFBRVEsVUFDakJvRixzQkFBdUI1RixFQUFFUSxVQUN6QnFGLHlCQUEwQjdGLEVBQUVRLFVBRzVCc0YsYUFBYzlGLEVBQUVRLFVBQ2hCdUYsZUFBZ0IvRixFQUFFUSxVQUNsQndGLGVBQWdCaEcsRUFBRVEsVUFDbEJ5Rix3QkFBeUJqRyxFQUFFUSxVQUMzQjBGLGFBQWNsRyxFQUFFUSxVQUNoQjJGLGNBQWVuRyxFQUFFYyxpQkFDakJzRixxQkFBc0JwRyxFQUFFVyxnQkFHYjBGLEtBQU90RixPQUFPdUYsVUFBVUMsTUFBTXBRLFFBQVFxUSxLQ3RPN0N2SyxjQUFnQndLLGFBQWE1TSxlQWU1QixTQUFTNk0sV0FBV0MsR0FBVSxHQUNuQyxPQUFPQSxFQUFVL1QsU0FBU3FKLGVBQWlCQSxhQUM3QyxDQWlCTyxTQUFTMkssY0FBY0MsRUFBWUYsR0FBVSxHQUVsRCxPQUFPRyxjQUFjSixXQUFXQyxHQUFVRSxFQUM1QyxDQXlETyxTQUFTRSxnQkFBZ0JDLEdBRTlCLE1BQU1ILEVBQWEsQ0FBQSxFQUduQixHQUFJclIsU0FBU3dSLEdBRVgsSUFBSyxNQUFPL1QsRUFBS3VELEtBQVV0RCxPQUFPK1QsUUFBUUQsR0FBYSxDQUVyRCxNQUFNRSxFQUFrQjVILFlBQVlyTSxHQUNoQ3FNLFlBQVlyTSxHQUFLZSxNQUFNLEtBQ3ZCLEdBSUprVCxFQUFnQkMsUUFDZCxDQUFDQyxFQUFLQyxFQUFNQyxJQUNURixFQUFJQyxHQUNISCxFQUFnQnRSLE9BQVMsSUFBTTBSLEVBQVE5USxFQUFRNFEsRUFBSUMsSUFBUyxJQUNoRVIsRUFFSCxNQUVEL08sSUFDRSxFQUNBLG1GQUtKLE9BQU8rTyxDQUNULENBb0JPLFNBQVNVLGdCQUNkN0gsT0FDQXhLLFVBQVcsRUFDWHNTLGdCQUFpQixHQUVqQixJQUVFLElBQUtoUyxTQUFTa0ssU0FBNkIsaUJBQVhBLE9BRTlCLE9BQU8sS0FJVCxNQUFNK0gsYUFDYyxpQkFBWC9ILE9BQ0g4SCxlQUNFRSxLQUFLLElBQUloSSxXQUNUaUksS0FBS3BCLE1BQU03RyxRQUNiQSxPQUdBa0ksbUJBQXFCQyxrQkFDekJKLGFBQ0FELGdCQUNBLEdBSUlNLGNBQWdCTixlQUNsQkcsS0FBS3BCLE1BQ0hzQixrQkFBa0JKLGFBQWNELGdCQUFnQixJQUNoRCxDQUFDTyxFQUFHdlIsUUFDZSxpQkFBVkEsT0FBc0JBLE1BQU1ZLFdBQVcsWUFDMUNzUSxLQUFLLElBQUlsUixVQUNUQSxRQUVSbVIsS0FBS3BCLE1BQU1xQixvQkFHZixPQUFPMVMsU0FBVzBTLG1CQUFxQkUsYUFDeEMsQ0FBQyxNQUFPcFAsR0FFUCxPQUFPLElBQ1IsQ0FDSCxDQThGQSxTQUFTK04sYUFBYS9HLEdBRXBCLE1BQU14RSxFQUFVLENBQUEsRUFHaEIsSUFBSyxNQUFPOE0sRUFBTXZTLEtBQVN2QyxPQUFPK1QsUUFBUXZILEdBQ3BDeE0sT0FBT0MsVUFBVUMsZUFBZUMsS0FBS29DLEVBQU0sY0FFbEI4QyxJQUF2QjhOLEtBQUs1USxFQUFLdUUsVUFBaUQsT0FBdkJxTSxLQUFLNVEsRUFBS3VFLFNBRWhEa0IsRUFBUThNLEdBQVEzQixLQUFLNVEsRUFBS3VFLFNBRzFCa0IsRUFBUThNLEdBQVF2UyxFQUFLZSxNQUl2QjBFLEVBQVE4TSxHQUFRdkIsYUFBYWhSLEdBS2pDLE9BQU95RixDQUNULENBWU8sU0FBUzRMLGNBQWNtQixFQUFpQnBCLEdBRTdDLEdBQUlyUixTQUFTeVMsSUFBb0J6UyxTQUFTcVIsR0FDeEMsSUFBSyxNQUFPNVQsRUFBS3VELEtBQVV0RCxPQUFPK1QsUUFBUUosR0FDeENvQixFQUFnQmhWLEdBQ2R1QyxTQUFTZ0IsS0FDUmdKLGNBQWM3TCxTQUFTVixTQUNDc0YsSUFBekIwUCxFQUFnQmhWLEdBQ1o2VCxjQUFjbUIsRUFBZ0JoVixHQUFNdUQsUUFDMUIrQixJQUFWL0IsRUFDRUEsRUFDQXlSLEVBQWdCaFYsSUFBUSxLQUtwQyxPQUFPZ1YsQ0FDVCxDQXNCTyxTQUFTSixrQkFBa0IzTSxFQUFTc00sRUFBZ0JVLEdBaUN6RCxPQUFPUCxLQUFLUSxVQUFVak4sR0FoQ0csQ0FBQzZNLEVBQUd2UixLQU8zQixHQUxxQixpQkFBVkEsSUFDVEEsRUFBUUEsRUFBTW5CLFFBS0csbUJBQVZtQixHQUNXLGlCQUFWQSxHQUNOQSxFQUFNWSxXQUFXLGFBQ2pCWixFQUFNVSxTQUFTLEtBQ2pCLENBRUEsR0FBSXNRLEVBRUYsT0FBT1UsRUFFSCxZQUFZMVIsRUFBUSxJQUFJNFIsV0FBVyxPQUFRLGVBRTNDLFdBQVc1UixFQUFRLElBQUk0UixXQUFXLE9BQVEsY0FHOUMsTUFBTSxJQUFJQyxLQUViLENBR0QsT0FBTzdSLENBQUssSUFJbUM0UixXQUMvQ0YsRUFBcUIseUJBQTJCLHFCQUNoRCxHQUVKLENDcllPSSxlQUFlQyxNQUFNQyxFQUFLQyxFQUFpQixJQUNoRCxPQUFPLElBQUlDLFNBQVEsQ0FBQzdULEVBQVM4VCxLQUMzQkMsbUJBQW1CSixHQUNoQkssSUFBSUwsRUFBS0MsR0FBaUJLLElBQ3pCLElBQUlDLEVBQWUsR0FHbkJELEVBQVNFLEdBQUcsUUFBU0MsSUFDbkJGLEdBQWdCRSxDQUFLLElBSXZCSCxFQUFTRSxHQUFHLE9BQU8sS0FDWkQsR0FDSEosRUFBTyxxQ0FFVEcsRUFBU0ksS0FBT0gsRUFDaEJsVSxFQUFRaVUsRUFBUyxHQUNqQixJQUVIRSxHQUFHLFNBQVV0USxJQUNaaVEsRUFBT2pRLEVBQU0sR0FDYixHQUVSLENBd0VBLFNBQVNrUSxtQkFBbUJKLEdBQzFCLE9BQU9BLEVBQUlwUixXQUFXLFNBQVcrUixNQUFRQyxJQUMzQyxDQ3BIQSxNQUFNQyxvQkFBb0JoQixNQVF4QixXQUFBaUIsQ0FBWXpRLEVBQVMwUSxHQUNuQkMsUUFFQUMsS0FBSzVRLFFBQVVBLEVBQ2Y0USxLQUFLM1EsYUFBZUQsRUFFaEIwUSxJQUNGRSxLQUFLRixXQUFhQSxFQUVyQixDQVNELFNBQUFHLENBQVVILEdBR1IsT0FGQUUsS0FBS0YsV0FBYUEsRUFFWEUsSUFDUixDQVVELFFBQUFFLENBQVNqUixHQWdCUCxPQWZBK1EsS0FBSy9RLE1BQVFBLEVBRVRBLEVBQU1zUCxPQUNSeUIsS0FBS3pCLEtBQU90UCxFQUFNc1AsTUFHaEJ0UCxFQUFNNlEsYUFDUkUsS0FBS0YsV0FBYTdRLEVBQU02USxZQUd0QjdRLEVBQU1LLFFBQ1IwUSxLQUFLM1EsYUFBZUosRUFBTUcsUUFDMUI0USxLQUFLMVEsTUFBUUwsRUFBTUssT0FHZDBRLElBQ1IsRUN4Q0gsTUFBTUcsTUFBUSxDQUNaclAsT0FBUSw4QkFDUnNQLGVBQWdCLENBQUUsRUFDbEJDLFFBQVMsR0FDVEMsVUFBVyxJQWVOekIsZUFBZTBCLG9CQUNwQkMsRUFDQUMsR0FFQSxJQUNFLElBQUlDLEVBR0osTUFBTTFQLEVBQVkyUCxlQUdaQyxFQUFlMVEsS0FBQUEsS0FBS2MsRUFBVyxpQkFDL0I2UCxFQUFhM1EsS0FBQUEsS0FBS2MsRUFBVyxjQU9uQyxJQUpDaEIsR0FBVUEsV0FBQ2dCLElBQWNmLEdBQVNBLFVBQUNlLEVBQVcsQ0FBRThQLFdBQVcsS0FJdkQ5USxHQUFBQSxXQUFXNFEsSUFBaUJKLEVBQWtCelAsV0FDakQxQyxJQUFJLEVBQUcseURBQ1BxUyxRQUF1QkssYUFDckJQLEVBQ0FDLEVBQ0FJLE9BRUcsQ0FDTCxJQUFJRyxHQUFnQixFQUdwQixNQUFNQyxFQUFXL0MsS0FBS3BCLE1BQU1wUCxHQUFZQSxhQUFDa1QsR0FBZSxRQUl4RCxHQUFJSyxFQUFTQyxTQUFXNVgsTUFBTUMsUUFBUTBYLEVBQVNDLFNBQVUsQ0FDdkQsTUFBTUMsRUFBWSxDQUFBLEVBQ2xCRixFQUFTQyxRQUFRL0ssU0FBU2lMLEdBQU9ELEVBQVVDLEdBQUssSUFDaERILEVBQVNDLFFBQVVDLENBQ3BCLENBR0QsTUFBTWxRLFlBQUVBLEVBQVdFLGNBQUVBLEVBQWFDLGlCQUFFQSxHQUNsQ29QLEVBQ0lhLEVBQ0pwUSxFQUFZOUUsT0FBU2dGLEVBQWNoRixPQUFTaUYsRUFBaUJqRixPQUszRDhVLEVBQVNwUSxVQUFZMlAsRUFBa0IzUCxTQUN6Q3hDLElBQ0UsRUFDQSx5RUFFRjJTLEdBQWdCLEdBRWhCdlgsT0FBT3lDLEtBQUsrVSxFQUFTQyxTQUFXLENBQUUsR0FBRS9VLFNBQVdrVixHQUUvQ2hULElBQ0UsRUFDQSwrRUFFRjJTLEdBQWdCLEdBR2hCQSxHQUFpQjdQLEdBQWlCLElBQUk5RSxNQUFNaVYsSUFDMUMsSUFBS0wsRUFBU0MsUUFBUUksR0FLcEIsT0FKQWpULElBQ0UsRUFDQSxlQUFlaVQsaURBRVYsQ0FDUixJQUtETixFQUNGTixRQUF1QkssYUFDckJQLEVBQ0FDLEVBQ0FJLElBR0Z4UyxJQUFJLEVBQUcsdURBR1A4UixNQUFNRSxRQUFVM1MsR0FBQUEsYUFBYW1ULEVBQVksUUFHekNILEVBQWlCTyxFQUFTQyxRQUcxQmYsTUFBTUcsVUFBWWlCLGVBQWVwQixNQUFNRSxTQUUxQyxPQUlLbUIsc0JBQXNCaEIsRUFBbUJFLEVBQ2hELENBQUMsTUFBT3pSLEdBQ1AsTUFBTSxJQUFJMlEsWUFDUiw4RUFDQSxLQUNBTSxTQUFTalIsRUFDWixDQUNILENBU08sU0FBU3dTLHVCQUNkLE9BQU90QixNQUFNRyxTQUNmLENBV096QixlQUFlNkMsd0JBQXdCQyxHQUU1QyxNQUFNbFEsRUFBVTBMLGNBQWMsQ0FDNUJ2TSxXQUFZLENBQ1ZDLFFBQVM4USxXQUtQcEIsb0JBQW9COU8sRUFBUWIsV0FBWWEsRUFBUXlCLE9BQU9NLE1BQy9ELENBV08sU0FBUytOLGVBQWVLLEdBQzdCLE9BQU9BLEVBQ0p2TCxVQUFVLEVBQUd1TCxFQUFhQyxRQUFRLE9BQ2xDNVgsUUFBUSxLQUFNLElBQ2RBLFFBQVEsS0FBTSxJQUNkQSxRQUFRLE1BQU8sSUFDZjJCLE1BQ0wsQ0FZTyxTQUFTa1csa0JBQWtCQyxHQUNoQyxPQUFPQSxFQUFXOVgsUUFDaEIscUVBQ0EsR0FFSixDQW9CTyxTQUFTMFcsZUFDZCxPQUFPclcsZ0JBQWdCMlMsYUFBYXJNLFdBQVdJLFVBQ2pELENBdUJBNk4sZUFBZW1ELHVCQUNiQyxFQUNBakQsRUFDQTBCLEVBQ0F3QixHQUFtQixHQUdmRCxFQUFPeFUsU0FBUyxTQUNsQndVLEVBQVNBLEVBQU81TCxVQUFVLEVBQUc0TCxFQUFPOVYsT0FBUyxJQUUvQ2tDLElBQUksRUFBRyw2QkFBNkI0VCxRQUdwQyxNQUFNNUMsUUFBaUJQLE1BQU0sR0FBR21ELE9BQWFqRCxHQUc3QyxHQUE0QixNQUF4QkssRUFBU1MsWUFBOEMsaUJBQWpCVCxFQUFTSSxLQUFrQixDQUNuRSxHQUFJaUIsRUFBZ0IsQ0FFbEJBLEVBRG1Cb0Isa0JBQWtCRyxJQUNSLENBQzlCLENBQ0QsT0FBTzVDLEVBQVNJLElBQ2pCLENBR0QsR0FBSXlDLEVBQ0YsTUFBTSxJQUFJdEMsWUFDUiwrQkFBK0JxQywyRUFBZ0Y1QyxFQUFTUyxlQUN4SCxLQUNBSSxTQUFTYixHQUVYaFIsSUFDRSxFQUNBLCtCQUErQjRULDZEQUdyQyxDQWlCQXBELGVBQWUyQyxzQkFBc0JoQixFQUFtQkUsRUFBaUIsSUFDdkUsTUFBTXlCLEVBQWMsQ0FDbEJ0UixRQUFTMlAsRUFBa0IzUCxRQUMzQnFRLFFBQVNSLEdBSVhQLE1BQU1DLGVBQWlCK0IsRUFFdkI5VCxJQUFJLEVBQUcsbUNBQ1AsSUFDRStULEdBQWFBLGNBQ1hsUyxVQUFLeVEsZUFBZ0IsaUJBQ3JCekMsS0FBS1EsVUFBVXlELEdBQ2YsT0FFSCxDQUFDLE1BQU9sVCxHQUNQLE1BQU0sSUFBSTJRLFlBQ1IsNENBQ0EsS0FDQU0sU0FBU2pSLEVBQ1osQ0FDSCxDQXVCQTRQLGVBQWV3RCxjQUNicFIsRUFDQUUsRUFDQUUsRUFDQW9QLEVBQ0FDLEdBR0EsSUFBSTRCLEVBQ0osTUFBTUMsRUFBWTlCLEVBQW1Cck4sS0FDL0JvUCxFQUFZL0IsRUFBbUJwTixLQUdyQyxHQUFJa1AsR0FBYUMsRUFDZixJQUNFRixFQUFhLElBQUlHLGdCQUFBQSxnQkFBZ0IsQ0FDL0JyUCxLQUFNbVAsRUFDTmxQLEtBQU1tUCxHQUVULENBQUMsTUFBT3ZULEdBQ1AsTUFBTSxJQUFJMlEsWUFDUiwwQ0FDQSxLQUNBTSxTQUFTalIsRUFDWixDQUlILE1BQU0rUCxFQUFpQnNELEVBQ25CLENBQ0VJLE1BQU9KLEVBQ1A3TyxRQUFTZ04sRUFBbUJoTixTQUU5QixHQUVFa1AsRUFBbUIsSUFDcEIxUixFQUFZNEYsS0FBS29MLEdBQ2xCRCx1QkFBdUIsR0FBR0MsSUFBVWpELEVBQWdCMEIsR0FBZ0IsUUFFbkV2UCxFQUFjMEYsS0FBS29MLEdBQ3BCRCx1QkFBdUIsR0FBR0MsSUFBVWpELEVBQWdCMEIsUUFFbkRyUCxFQUFjd0YsS0FBS29MLEdBQ3BCRCx1QkFBdUIsR0FBR0MsSUFBVWpELE1BS3hDLGFBRDZCQyxRQUFRMkQsSUFBSUQsSUFDbkJ6UyxLQUFLLE1BQzdCLENBb0JBMk8sZUFBZWtDLGFBQWFQLEVBQW1CQyxFQUFvQkksR0FFakUsTUFBTVAsRUFDMEIsV0FBOUJFLEVBQWtCM1AsUUFDZCxLQUNBLEdBQUcyUCxFQUFrQjNQLFVBR3JCQyxFQUFTMFAsRUFBa0IxUCxRQUFVcVAsTUFBTXJQLE9BRWpELElBQ0UsTUFBTTRQLEVBQWlCLENBQUEsRUF1Q3ZCLE9BckNBclMsSUFDRSxFQUNBLGlEQUFpRGlTLEdBQWEsYUFHaEVILE1BQU1FLGNBQWdCZ0MsY0FDcEIsSUFDSzdCLEVBQWtCdlAsWUFBWTRGLEtBQUtnTSxHQUNwQ3ZDLEVBQVksR0FBR3hQLEtBQVV3UCxLQUFhdUMsSUFBTSxHQUFHL1IsS0FBVStSLE9BRzdELElBQ0tyQyxFQUFrQnJQLGNBQWMwRixLQUFLdUssR0FDaEMsUUFBTkEsRUFDSWQsRUFDRSxHQUFHeFAsVUFBZXdQLGFBQXFCYyxJQUN2QyxHQUFHdFEsa0JBQXVCc1EsSUFDNUJkLEVBQ0UsR0FBR3hQLEtBQVV3UCxhQUFxQmMsSUFDbEMsR0FBR3RRLGFBQWtCc1EsU0FFMUJaLEVBQWtCcFAsaUJBQWlCeUYsS0FBS2lNLEdBQ3pDeEMsRUFDSSxHQUFHeFAsV0FBZ0J3UCxnQkFBd0J3QyxJQUMzQyxHQUFHaFMsc0JBQTJCZ1MsT0FHdEN0QyxFQUFrQm5QLGNBQ2xCb1AsRUFDQUMsR0FJRlAsTUFBTUcsVUFBWWlCLGVBQWVwQixNQUFNRSxTQUd2QytCLEdBQUFBLGNBQWN2QixFQUFZVixNQUFNRSxTQUN6QkssQ0FDUixDQUFDLE1BQU96UixHQUNQLE1BQU0sSUFBSTJRLFlBQ1IsdURBQ0EsS0FDQU0sU0FBU2pSLEVBQ1osQ0FDSCxDQ3BkTyxTQUFTOFQsa0JBQ2RDLFdBQVdDLFdBQWEsV0FDdEIsTUFBTyxDQUFFQyxTQUFVLEVBQ3ZCLENBQ0EsQ0FjT3JFLGVBQWVzRSxZQUFZQyxFQUFlQyxHQUUvQyxNQUFNcEcsV0FBRUEsRUFBVXFHLFdBQUVBLEVBQVVDLE1BQUVBLEVBQUtDLEtBQUVBLEdBQVNSLFdBSWhEQSxXQUFXUyxjQUFnQkYsR0FBTSxFQUFPLENBQUUsRUFBRXRHLEtBRzVDckosT0FBTzhQLGtCQUFtQixFQUMxQkYsRUFBS1IsV0FBV1csTUFBTWphLFVBQVcsUUFBUSxTQUFVa2EsRUFBU0MsRUFBYUMsS0FFdkVELEVBQWNOLEVBQU1NLEVBQWEsQ0FDL0JFLFVBQVcsQ0FDVEMsU0FBUyxHQUVYQyxZQUFhLENBQ1hDLE9BQVEsQ0FDTkMsTUFBTyxDQUNMSCxTQUFTLEtBT2ZJLFFBQVMsQ0FBRSxLQUdBRixRQUFVLElBQUkvTixTQUFRLFNBQVUrTixHQUMzQ0EsRUFBT0csV0FBWSxDQUN6QixJQUdTelEsT0FBTzBRLHFCQUNWMVEsT0FBTzBRLG1CQUFxQnRCLFdBQVd1QixTQUFTdkUsS0FBTSxVQUFVLEtBQzlEcE0sT0FBTzhQLGtCQUFtQixDQUFJLEtBSWxDRSxFQUFRL1UsTUFBTW1SLEtBQU0sQ0FBQzZELEVBQWFDLEdBQ3RDLElBRUVOLEVBQUtSLFdBQVd3QixPQUFPOWEsVUFBVyxRQUFRLFNBQVVrYSxFQUFTYSxFQUFPaFQsR0FDbEVtUyxFQUFRL1UsTUFBTW1SLEtBQU0sQ0FBQ3lFLEVBQU9oVCxHQUNoQyxJQUdFLE1BQU1pVCxFQUFvQixDQUN4QkQsTUFBTyxDQUVMSixXQUFXLEVBRVhyUyxPQUFRb1IsRUFBY3BSLE9BQ3RCQyxNQUFPbVIsRUFBY25SLE9BRXZCOFIsVUFBVyxDQUVUQyxTQUFTLElBS1BILEVBQWMsSUFBSWMsU0FBUyxVQUFVdkIsRUFBYzVSLFFBQXJDLEdBR2RpQixFQUFlLElBQUlrUyxTQUFTLFVBQVV2QixFQUFjM1EsZUFBckMsR0FHZkQsRUFBZ0IsSUFBSW1TLFNBQVMsVUFBVXZCLEVBQWM1USxnQkFBckMsR0FHaEJvUyxFQUFlckIsR0FDbkIsRUFDQTlRLEVBQ0FvUixFQUVBYSxHQUlJRyxFQUFnQnhCLEVBQW1CeFEsU0FDckMsSUFBSThSLFNBQVMsVUFBVXRCLEVBQW1CeFEsV0FBMUMsR0FDQSxLQUdBd1EsRUFBbUIvVixZQUNyQixJQUFJcVgsU0FBUyxVQUFXdEIsRUFBbUIvVixXQUEzQyxDQUF1RHVXLEdBSXJEclIsR0FDRjhRLEVBQVc5USxHQUlid1EsV0FBV0ksRUFBY3RaLFFBQVEsWUFBYThhLEVBQWNDLEdBRzVELE1BQU1DLEVBQWlCN0gsSUFHdkIsSUFBSyxNQUFNVyxLQUFRa0gsRUFDbUIsbUJBQXpCQSxFQUFlbEgsV0FDakJrSCxFQUFlbEgsR0FLMUIwRixFQUFXTixXQUFXUyxlQUd0QlQsV0FBV1MsY0FBZ0IsRUFDN0IsQ0M1SEEsTUFBTXNCLFNBQVdyWCxHQUFZQSxhQUMzQndDLFVBQUs1SCxZQUFXLFlBQWEsaUJBQzdCLFFBSUYsSUFBSTBjLFFBQVUsS0FtQ1BuRyxlQUFlb0csY0FBY0MsR0FFbEMsTUFBTTVQLE1BQUVBLEVBQUtOLE1BQUVBLEdBQVVpSSxjQUdqQjlKLE9BQVFnUyxLQUFpQkMsR0FBaUI5UCxFQUc1QytQLEVBQWdCLENBQ3BCOVAsVUFBVVAsRUFBTUssa0JBQW1CLFFBQ25DaVEsWUFBYSxNQUNiaFgsS0FBTTRXLEdBQWlCLEdBQ3ZCSyxjQUFjLEVBQ2RDLGVBQWUsRUFDZkMsY0FBYyxFQUNkQyxvQkFBb0IsRUFDcEJDLGdCQUFpQixRQUNiUixHQUFnQkMsR0FJdEIsSUFBS0osUUFBUyxDQUVaLElBQUlZLEVBQVcsRUFFZixNQUFNQyxFQUFPaEgsVUFDWCxJQUNFeFEsSUFDRSxFQUNBLHlEQUF5RHVYLE9BSTNEWixjQUFnQjNVLFVBQVV5VixPQUFPVCxFQUNsQyxDQUFDLE1BQU9wVyxHQVFQLEdBUEFELGFBQ0UsRUFDQUMsRUFDQSxvREFJRTJXLEVBQVcsSUFPYixNQUFNM1csRUFOTlosSUFBSSxFQUFHLHNDQUFzQ3VYLHVCQUd2QyxJQUFJM0csU0FBU0ksR0FBYTBHLFdBQVcxRyxFQUFVLGFBQy9Dd0csR0FJVCxHQUdILFVBQ1FBLElBR3lCLFVBQTNCUixFQUFjOVAsVUFDaEJsSCxJQUFJLEVBQUcsNkNBSUw4VyxHQUNGOVcsSUFBSSxFQUFHLDRDQUVWLENBQUMsTUFBT1ksR0FDUCxNQUFNLElBQUkyUSxZQUNSLGdFQUNBLEtBQ0FNLFNBQVNqUixFQUNaLENBRUQsSUFBSytWLFFBQ0gsTUFBTSxJQUFJcEYsWUFBWSwyQ0FBNEMsSUFFckUsQ0FHRCxPQUFPb0YsT0FDVCxDQVFPbkcsZUFBZW1ILGVBRWhCaEIsU0FBV0EsUUFBUWlCLGlCQUNmakIsUUFBUWtCLFFBRWhCbEIsUUFBVSxLQUNWM1csSUFBSSxFQUFHLGdDQUNULENBZ0JPd1EsZUFBZXNILFFBQVFDLEdBRTVCLElBQUtwQixVQUFZQSxRQUFRaUIsVUFDdkIsTUFBTSxJQUFJckcsWUFBWSwwQ0FBMkMsS0FnQm5FLEdBWkF3RyxFQUFhQyxXQUFhckIsUUFBUW1CLGdCQUc1QkMsRUFBYUMsS0FBS0MsaUJBQWdCLFNBR2xDQyxnQkFBZ0JILEVBQWFDLE1BR25DRyxlQUFlSixFQUFhQyxPQUd2QkQsRUFBYUMsTUFBUUQsRUFBYUMsS0FBS0ksV0FDMUMsTUFBTSxJQUFJN0csWUFBWSwyQ0FBNEMsSUFFdEUsQ0FrQk9mLGVBQWU2SCxVQUFVTixFQUFjTyxHQUFZLEdBQ3hELElBQ0UsR0FBSVAsRUFBYUMsT0FBU0QsRUFBYUMsS0FBS0ksV0FnQjFDLE9BZklFLFNBRUlQLEVBQWFDLEtBQUtPLEtBQUssY0FBZSxDQUMxQ0MsVUFBVywyQkFJUE4sZ0JBQWdCSCxFQUFhQyxhQUc3QkQsRUFBYUMsS0FBS1MsVUFBUyxLQUMvQnJlLFNBQVNzZSxLQUFLQyxVQUNaLDREQUE0RCxLQUczRCxDQUVWLENBQUMsTUFBTy9YLEdBQ1BELGFBQ0UsRUFDQUMsRUFDQSx5QkFBeUJtWCxFQUFhYSxtREFJeENiLEVBQWFjLFVBQVlqSyxhQUFhN0ksS0FBS0csVUFBWSxDQUN4RCxDQUNELE9BQU8sQ0FDVCxDQWlCT3NLLGVBQWVzSSxpQkFBaUJkLEVBQU1oRCxHQUUzQyxNQUFNK0QsRUFBb0IsR0FHcEJ0VSxFQUFZdVEsRUFBbUJ2USxVQUNyQyxHQUFJQSxFQUFXLENBQ2IsTUFBTXVVLEVBQWEsR0FVbkIsR0FQSXZVLEVBQVV3VSxJQUNaRCxFQUFXOVgsS0FBSyxDQUNkZ1ksUUFBU3pVLEVBQVV3VSxLQUtuQnhVLEVBQVUwVSxNQUNaLElBQUssTUFBTTdYLEtBQVFtRCxFQUFVMFUsTUFBTyxDQUNsQyxNQUFNQyxHQUFVOVgsRUFBS2hDLFdBQVcsUUFHaEMwWixFQUFXOVgsS0FDVGtZLEVBQ0ksQ0FDRUYsUUFBUzdaLEdBQUFBLGFBQWFwRCxnQkFBZ0JxRixHQUFPLFNBRS9DLENBQ0VvUCxJQUFLcFAsR0FHZCxDQUdILElBQUssTUFBTStYLEtBQWNMLEVBQ3ZCLElBQ0VELEVBQWtCN1gsV0FBVzhXLEVBQUtzQixhQUFhRCxHQUNoRCxDQUFDLE1BQU96WSxHQUNQRCxhQUFhLEVBQUdDLEVBQU8sOENBQ3hCLENBRUhvWSxFQUFXbGIsT0FBUyxFQUdwQixNQUFNeWIsRUFBYyxHQUNwQixHQUFJOVUsRUFBVStVLElBQUssQ0FDakIsSUFBSUMsRUFBYWhWLEVBQVUrVSxJQUFJRSxNQUFNLHVCQUNyQyxHQUFJRCxFQUVGLElBQUssSUFBSUUsS0FBaUJGLEVBQ3BCRSxJQUNGQSxFQUFnQkEsRUFDYi9kLFFBQVEsT0FBUSxJQUNoQkEsUUFBUSxVQUFXLElBQ25CQSxRQUFRLEtBQU0sSUFDZEEsUUFBUSxLQUFNLElBQ2RBLFFBQVEsSUFBSyxJQUNiQSxRQUFRLE1BQU8sSUFDZjJCLE9BR0NvYyxFQUFjcmEsV0FBVyxRQUMzQmlhLEVBQVlyWSxLQUFLLENBQ2Z3UCxJQUFLaUosSUFFRTNFLEVBQW1COVYsb0JBQzVCcWEsRUFBWXJZLEtBQUssQ0FDZnRFLEtBQU1YLGdCQUFnQjBkLE1BUWhDSixFQUFZclksS0FBSyxDQUNmZ1ksUUFBU3pVLEVBQVUrVSxJQUFJNWQsUUFBUSxzQkFBdUIsS0FBTyxNQUcvRCxJQUFLLE1BQU1nZSxLQUFlTCxFQUN4QixJQUNFUixFQUFrQjdYLFdBQVc4VyxFQUFLNkIsWUFBWUQsR0FDL0MsQ0FBQyxNQUFPaFosR0FDUEQsYUFDRSxFQUNBQyxFQUNBLCtDQUVILENBRUgyWSxFQUFZemIsT0FBUyxDQUN0QixDQUNGLENBQ0QsT0FBT2liLENBQ1QsQ0FlT3ZJLGVBQWVzSixtQkFBbUI5QixFQUFNZSxHQUM3QyxJQUNFLElBQUssTUFBTWdCLEtBQVloQixRQUNmZ0IsRUFBU0MsZ0JBSVhoQyxFQUFLUyxVQUFTLEtBRWxCLEdBQTBCLG9CQUFmOUQsV0FBNEIsQ0FFckMsTUFBTXNGLEVBQVl0RixXQUFXdUYsT0FHN0IsR0FBSWpmLE1BQU1DLFFBQVErZSxJQUFjQSxFQUFVbmMsT0FFeEMsSUFBSyxNQUFNcWMsS0FBWUYsRUFDckJFLEdBQVlBLEVBQVNDLFVBRXJCekYsV0FBV3VGLE9BQU8vZCxPQUd2QixDQUdELFNBQVVrZSxHQUFtQmpnQixTQUFTa2dCLHFCQUFxQixXQUVyRCxJQUFNQyxHQUFrQm5nQixTQUFTa2dCLHFCQUFxQixhQUVsREUsR0FBaUJwZ0IsU0FBU2tnQixxQkFBcUIsUUFHekQsSUFBSyxNQUFNRyxJQUFXLElBQ2pCSixLQUNBRSxLQUNBQyxHQUVIQyxFQUFRQyxRQUNULEdBRUosQ0FBQyxNQUFPOVosR0FDUEQsYUFBYSxFQUFHQyxFQUFPLDhDQUN4QixDQUNILENBWUE0UCxlQUFlMEgsZ0JBQWdCRixTQUV2QkEsRUFBSzJDLFdBQVdqRSxTQUFVLENBQUU4QixVQUFXLDJCQUd2Q1IsRUFBS3NCLGFBQWEsQ0FBRTFjLEtBQU1pRixLQUFJQSxLQUFDeVEsZUFBZ0Isc0JBRy9DMEYsRUFBS1MsU0FBUy9ELGdCQUN0QixDQVdBLFNBQVN5RCxlQUFlSCxHQUV0QixNQUFNL1EsTUFBRUEsR0FBVTJILGFBR2xCb0osRUFBSzlHLEdBQUcsYUFBYVYsVUFHZndILEVBQUtJLFVBRVIsSUFJQ25SLEVBQU1uQyxRQUFVbUMsRUFBTUcsaUJBQ3hCNFEsRUFBSzlHLEdBQUcsV0FBWW5RLElBQ2xCUixRQUFRUCxJQUFJLFdBQVdlLEVBQVFxUSxTQUFTLEdBRzlDLENDNWNBLElBQUF3SixZQUFlLElBQU0seVhDSU5DLFlBQUN4WCxHQUFRLDhMQVFsQnVYLDhFQUlFdlgsd0NDYURtTixlQUFlc0ssZ0JBQWdCOUMsRUFBTWpELEVBQWVDLEdBRXpELE1BQU0rRCxFQUFvQixHQUUxQixJQUNFLElBQUlnQyxHQUFRLEVBR1osR0FBSWhHLEVBQWMxUixJQUFLLENBSXJCLEdBSEFyRCxJQUFJLEVBQUcsbUNBR29CLFFBQXZCK1UsRUFBY2haLEtBQ2hCLE9BQU9nWixFQUFjMVIsSUFJdkIwWCxHQUFRLFFBR0YvQyxFQUFLMkMsV0FBV0UsWUFBWTlGLEVBQWMxUixLQUFNLENBQ3BEbVYsVUFBVyxvQkFFbkIsTUFDTXhZLElBQUksRUFBRywyQ0FHRGdZLEVBQUtTLFNBQVMzRCxZQUFhQyxFQUFlQyxHQU1sRCtELEVBQWtCN1gsY0FDTjRYLGlCQUFpQmQsRUFBTWhELElBSW5DLE1BQU1nRyxFQUFPRCxRQUNIL0MsRUFBS1MsVUFBVTVVLElBQ25CLE1BQU1vWCxFQUFhN2dCLFNBQVM4Z0IsY0FDMUIsc0NBSUlDLEVBQWNGLEVBQVd0WCxPQUFPeVgsUUFBUTFjLE1BQVFtRixFQUNoRHdYLEVBQWFKLEVBQVdyWCxNQUFNd1gsUUFBUTFjLE1BQVFtRixFQVVwRCxPQU5BekosU0FBU3NlLEtBQUs0QyxNQUFNQyxLQUFPMVgsRUFJM0J6SixTQUFTc2UsS0FBSzRDLE1BQU1FLE9BQVMsTUFFdEIsQ0FDTEwsY0FDQUUsYUFDRCxHQUNBdFMsV0FBV2dNLEVBQWNsUixjQUN0Qm1VLEVBQUtTLFVBQVMsS0FFbEIsTUFBTTBDLFlBQUVBLEVBQVdFLFdBQUVBLEdBQWU5VixPQUFPb1AsV0FBV3VGLE9BQU8sR0FPN0QsT0FGQTlmLFNBQVNzZSxLQUFLNEMsTUFBTUMsS0FBTyxFQUVwQixDQUNMSixjQUNBRSxhQUNELEtBSURJLEVBQUVBLEVBQUNDLEVBQUVBLFNBQVlDLGVBQWUzRCxHQUdoQzRELEVBQWlCL2MsS0FBS2dkLElBQzFCaGQsS0FBS2lkLEtBQUtkLEVBQUtHLGFBQWVwRyxFQUFjcFIsU0FJeENvWSxFQUFnQmxkLEtBQUtnZCxJQUN6QmhkLEtBQUtpZCxLQUFLZCxFQUFLSyxZQUFjdEcsRUFBY25SLFFBVTdDLElBQUlvWSxFQUVKLGFBUk1oRSxFQUFLaUUsWUFBWSxDQUNyQnRZLE9BQVFpWSxFQUNSaFksTUFBT21ZLEVBQ1BHLGtCQUFtQm5CLEVBQVEsRUFBSWhTLFdBQVdnTSxFQUFjbFIsU0FLbERrUixFQUFjaFosTUFDcEIsSUFBSyxNQUNIaWdCLFFBQWVHLFdBQVduRSxHQUMxQixNQUNGLElBQUssTUFDTCxJQUFLLE9BQ0hnRSxRQUFlSSxhQUNicEUsRUFDQWpELEVBQWNoWixLQUNkLENBQ0U2SCxNQUFPbVksRUFDUHBZLE9BQVFpWSxFQUNSSCxJQUNBQyxLQUVGM0csRUFBYzFRLHNCQUVoQixNQUNGLElBQUssTUFDSDJYLFFBQWVLLFdBQ2JyRSxFQUNBNEQsRUFDQUcsRUFDQWhILEVBQWMxUSxzQkFFaEIsTUFDRixRQUNFLE1BQU0sSUFBSWtOLFlBQ1IsdUNBQXVDd0QsRUFBY2haLFFBQ3JELEtBTU4sYUFETStkLG1CQUFtQjlCLEVBQU1lLEdBQ3hCaUQsQ0FDUixDQUFDLE1BQU9wYixHQUVQLGFBRE1rWixtQkFBbUI5QixFQUFNZSxHQUN4Qm5ZLENBQ1IsQ0FDSCxDQWNBNFAsZUFBZW1MLGVBQWUzRCxHQUM1QixPQUFPQSxFQUFLc0UsTUFBTSxvQkFBcUI3QixJQUNyQyxNQUFNZ0IsRUFBRUEsRUFBQ0MsRUFBRUEsRUFBQzlYLE1BQUVBLEVBQUtELE9BQUVBLEdBQVc4VyxFQUFROEIsd0JBQ3hDLE1BQU8sQ0FDTGQsSUFDQUMsSUFDQTlYLFFBQ0FELE9BQVE5RSxLQUFLMmQsTUFBTTdZLEVBQVMsRUFBSUEsRUFBUyxLQUMxQyxHQUVMLENBYUE2TSxlQUFlMkwsV0FBV25FLEdBQ3hCLE9BQU9BLEVBQUtzRSxNQUNWLGdDQUNDN0IsR0FBWUEsRUFBUWdDLFdBRXpCLENBa0JBak0sZUFBZTRMLGFBQWFwRSxFQUFNamMsRUFBTTJnQixFQUFNclksR0FDNUMsT0FBT3VNLFFBQVErTCxLQUFLLENBQ2xCM0UsRUFBSzRFLFdBQVcsQ0FDZDdnQixPQUNBMmdCLE9BQ0FHLFNBQVUsU0FDVkMsVUFBVSxFQUNWQyxrQkFBa0IsRUFDbEJDLHVCQUF1QixLQUNWLFFBQVRqaEIsRUFBaUIsQ0FBRWtoQixRQUFTLElBQU8sQ0FBQSxFQUV2Q0MsZUFBd0IsT0FBUm5oQixJQUVsQixJQUFJNlUsU0FBUSxDQUFDdU0sRUFBVXRNLElBQ3JCNkcsWUFDRSxJQUFNN0csRUFBTyxJQUFJVSxZQUFZLHdCQUF5QixPQUN0RGxOLEdBQXdCLFNBSWhDLENBaUJBbU0sZUFBZTZMLFdBQVdyRSxFQUFNclUsRUFBUUMsRUFBT1MsR0FFN0MsYUFETTJULEVBQUtvRixpQkFBaUIsVUFDckJwRixFQUFLcUYsSUFBSSxDQUVkMVosT0FBUUEsRUFBUyxFQUNqQkMsUUFDQWlaLFNBQVUsU0FDVnpYLFFBQVNmLEdBQXdCLE1BRXJDLENDblFBLElBQUkwQixLQUFPLEtBR1gsTUFBTXVYLFVBQVksQ0FDaEJDLGlCQUFrQixFQUNsQkMsaUJBQWtCLEVBQ2xCQyxlQUFnQixFQUNoQkMsZUFBZ0IsRUFDaEJDLG1CQUFvQixFQUNwQkMsdUJBQXdCLEVBQ3hCQywyQkFBNEIsRUFDNUJDLFVBQVcsRUFDWEMsaUJBQWtCLEdBcUJidk4sZUFBZXdOLFNBQVNDLEVBQWFwSCxTQUVwQ0QsY0FBY0MsR0FFcEIsSUFNRSxHQUxBN1csSUFDRSxFQUNBLDhDQUE4Q2llLEVBQVlqWSxtQkFBbUJpWSxFQUFZaFksZUFHdkZGLEtBS0YsWUFKQS9GLElBQ0UsRUFDQSx5RUFNQWllLEVBQVlqWSxXQUFhaVksRUFBWWhZLGFBQ3ZDZ1ksRUFBWWpZLFdBQWFpWSxFQUFZaFksWUFJdkNGLEtBQU8sSUFBSW1ZLEtBQUFBLEtBQUssSUFFWEMsU0FBU0YsR0FDWmhhLElBQUtnYSxFQUFZalksV0FDakI5QixJQUFLK1osRUFBWWhZLFdBQ2pCbVkscUJBQXNCSCxFQUFZOVgsZUFDbENrWSxvQkFBcUJKLEVBQVk3WCxjQUNqQ2tZLHFCQUFzQkwsRUFBWTVYLGVBQ2xDa1ksa0JBQW1CTixFQUFZM1gsWUFDL0JrWSwwQkFBMkJQLEVBQVkxWCxvQkFDdkNrWSxtQkFBb0JSLEVBQVl6WCxlQUNoQ2tZLHNCQUFzQixJQUl4QjNZLEtBQUttTCxHQUFHLFdBQVdWLE1BQU91SixJQUV4QixNQUFNNEUsUUFBb0J0RyxVQUFVMEIsR0FBVSxHQUM5Qy9aLElBQ0UsRUFDQSx5QkFBeUIrWixFQUFTbkIsZ0RBQWdEK0YsS0FDbkYsSUFHSDVZLEtBQUttTCxHQUFHLGtCQUFrQixDQUFDME4sRUFBVTdFLEtBQ25DL1osSUFDRSxFQUNBLHlCQUF5QitaLEVBQVNuQiwwQ0FFcENtQixFQUFTL0IsS0FBTyxJQUFJLElBR3RCLE1BQU02RyxFQUFtQixHQUV6QixJQUFLLElBQUlwSyxFQUFJLEVBQUdBLEVBQUl3SixFQUFZalksV0FBWXlPLElBQzFDLElBQ0UsTUFBTXNGLFFBQWlCaFUsS0FBSytZLFVBQVVDLFFBQ3RDRixFQUFpQjNkLEtBQUs2WSxFQUN2QixDQUFDLE1BQU9uWixHQUNQRCxhQUFhLEVBQUdDLEVBQU8sK0NBQ3hCLENBSUhpZSxFQUFpQi9XLFNBQVNpUyxJQUN4QmhVLEtBQUtpWixRQUFRakYsRUFBUyxJQUd4Qi9aLElBQ0UsRUFDQSw0QkFBMkI2ZSxFQUFpQi9nQixPQUFTLFNBQVMrZ0IsRUFBaUIvZ0Isb0NBQXNDLEtBRXhILENBQUMsTUFBTzhDLEdBQ1AsTUFBTSxJQUFJMlEsWUFDUiw2REFDQSxLQUNBTSxTQUFTalIsRUFDWixDQUNILENBWU80UCxlQUFleU8sV0FJcEIsR0FIQWpmLElBQUksRUFBRyw2REFHSCtGLEtBQU0sQ0FFUixJQUFLLE1BQU1tWixLQUFVblosS0FBS29aLEtBQ3hCcFosS0FBS2laLFFBQVFFLEVBQU9uRixVQUlqQmhVLEtBQUtxWixrQkFDRnJaLEtBQUtxVSxVQUNYcGEsSUFBSSxFQUFHLDRDQUVUK0YsS0FBTyxJQUNSLE9BR0s0UixjQUNSLENBbUJPbkgsZUFBZTZPLFNBQVNqYyxHQUM3QixJQUFJa2MsRUFFSixJQVlFLEdBWEF0ZixJQUFJLEVBQUcsZ0RBR0xzZCxVQUFVQyxpQkFHUm5hLEVBQVEyQyxLQUFLYixjQUNmcWEsZUFJR3haLEtBQ0gsTUFBTSxJQUFJd0wsWUFDUix1REFDQSxLQUtKLE1BQU1pTyxFQUFpQnJoQixjQUd2QixJQUNFNkIsSUFBSSxFQUFHLHFDQUdQc2YsUUFBcUJ2WixLQUFLK1ksVUFBVUMsUUFHaEMzYixFQUFReUIsT0FBT0ssY0FDakJsRixJQUNFLEVBQ0EsZ0JBQWVvRCxFQUFRcWMsVUFBWSxZQUFZcmMsRUFBUXFjLGdCQUFrQixJQUN6RSxrQ0FBa0NELFNBR3ZDLENBQUMsTUFBTzVlLEdBQ1AsTUFBTSxJQUFJMlEsWUFDUixVQUNFbk8sRUFBUXFjLFVBQVksWUFBWXJjLEVBQVFxYyxnQkFBa0IsMERBQ0pELFNBQ3hELEtBQ0EzTixTQUFTalIsRUFDWixDQUdELEdBRkFaLElBQUksRUFBRyxxQ0FFRnNmLEVBQWF0SCxLQUdoQixNQURBc0gsRUFBYXpHLFVBQVl6VixFQUFRMkMsS0FBS0csVUFBWSxFQUM1QyxJQUFJcUwsWUFDUixtRUFDQSxLQUtKLE1BQU1tTyxFQUFZbGlCLGlCQUVsQndDLElBQ0UsRUFDQSx5QkFBeUJzZixFQUFhMUcsMkNBSXhDLE1BQU0rRyxFQUFnQnhoQixjQUdoQjZkLFFBQWVsQixnQkFDbkJ3RSxFQUFhdEgsS0FDYjVVLEVBQVFILE9BQ1JHLEVBQVFrQixhQUlWLEdBQUkwWCxhQUFrQnpMLE1BbUJwQixLQU51QiwwQkFBbkJ5TCxFQUFPamIsVUFFVHVlLEVBQWF6RyxVQUFZelYsRUFBUTJDLEtBQUtHLFVBQVksRUFDbERvWixFQUFhdEgsS0FBTyxNQUlKLGlCQUFoQmdFLEVBQU85TCxNQUNZLDBCQUFuQjhMLEVBQU9qYixRQUVELElBQUl3USxZQUNSLFVBQ0VuTyxFQUFRcWMsVUFBWSxZQUFZcmMsRUFBUXFjLGdCQUFrQixtSEFFNUQ1TixTQUFTbUssR0FFTCxJQUFJekssWUFDUixVQUNFbk8sRUFBUXFjLFVBQVksWUFBWXJjLEVBQVFxYyxnQkFBa0Isc0NBQ3hCRSxVQUNwQzlOLFNBQVNtSyxHQUtYNVksRUFBUXlCLE9BQU9LLGNBQ2pCbEYsSUFDRSxFQUNBLGdCQUFlb0QsRUFBUXFjLFVBQVksWUFBWXJjLEVBQVFxYyxnQkFBa0IsSUFDekUsc0NBQXNDRSxVQUsxQzVaLEtBQUtpWixRQUFRTSxHQUliLE1BQ01NLEVBRFVwaUIsaUJBQ2FraUIsRUFTN0IsT0FQQXBDLFVBQVVRLFdBQWE4QixFQUN2QnRDLFVBQVVTLGlCQUNSVCxVQUFVUSxZQUFjUixVQUFVRSxpQkFFcEN4ZCxJQUFJLEVBQUcsNEJBQTRCNGYsUUFHNUIsQ0FDTDVELFNBQ0E1WSxVQUVILENBQUMsTUFBT3hDLEdBT1AsT0FORTBjLFVBQVVHLGVBRVI2QixHQUNGdlosS0FBS2laLFFBQVFNLEdBR1QxZSxDQUNQLENBQ0gsQ0FxQk8sU0FBU2lmLGVBQ2QsT0FBT3ZDLFNBQ1QsQ0FVTyxTQUFTd0Msa0JBQ2QsTUFBTyxDQUNMN2IsSUFBSzhCLEtBQUs5QixJQUNWQyxJQUFLNkIsS0FBSzdCLElBQ1ZpYixLQUFNcFosS0FBS2dhLFVBQ1hDLFVBQVdqYSxLQUFLa2EsVUFDaEJDLFdBQVluYSxLQUFLZ2EsVUFBWWhhLEtBQUtrYSxVQUNsQ0UsZ0JBQWlCcGEsS0FBS3FhLHFCQUN0QkMsZUFBZ0J0YSxLQUFLdWEsb0JBQ3JCQyxtQkFBb0J4YSxLQUFLeWEsd0JBQ3pCQyxnQkFBaUIxYSxLQUFLMGEsZ0JBQWdCM2lCLE9BQ3RDNGlCLFlBQ0UzYSxLQUFLZ2EsVUFDTGhhLEtBQUtrYSxVQUNMbGEsS0FBS3FhLHFCQUNMcmEsS0FBS3VhLG9CQUNMdmEsS0FBS3lhLHdCQUNMemEsS0FBSzBhLGdCQUFnQjNpQixPQUUzQixDQVNPLFNBQVN5aEIsY0FDZCxNQUFNdGIsSUFDSkEsRUFBR0MsSUFDSEEsRUFBR2liLEtBQ0hBLEVBQUlhLFVBQ0pBLEVBQVNFLFdBQ1RBLEVBQVVDLGdCQUNWQSxFQUFlRSxlQUNmQSxFQUFjRSxtQkFDZEEsRUFBa0JFLGdCQUNsQkEsRUFBZUMsWUFDZkEsR0FDRVosa0JBRUo5ZixJQUFJLEVBQUcsMkRBQTJEaUUsTUFDbEVqRSxJQUFJLEVBQUcsMkRBQTJEa0UsTUFDbEVsRSxJQUFJLEVBQUcsd0NBQXdDbWYsTUFDL0NuZixJQUFJLEVBQUcsd0NBQXdDZ2dCLE1BQy9DaGdCLElBQ0UsRUFDQSwrREFBK0RrZ0IsTUFFakVsZ0IsSUFDRSxFQUNBLDBEQUEwRG1nQixNQUU1RG5nQixJQUNFLEVBQ0EseURBQXlEcWdCLE1BRTNEcmdCLElBQ0UsRUFDQSwyREFBMkR1Z0IsTUFFN0R2Z0IsSUFDRSxFQUNBLDJEQUEyRHlnQixNQUU3RHpnQixJQUFJLEVBQUcsdUNBQXVDMGdCLEtBQ2hELENBV0EsU0FBU3ZDLFNBQVNGLEdBQ2hCLE1BQU8sQ0FjTDBDLE9BQVFuUSxVQUVOLE1BQU11SCxFQUFlLENBQ25CYSxHQUFJZ0ksS0FBQUEsS0FFSi9ILFVBQVdoYSxLQUFLRSxNQUFNRixLQUFLZ2lCLFVBQVk1QyxFQUFZL1gsVUFBWSxLQUdqRSxJQUVFLE1BQU00YSxFQUFZdGpCLGlCQWNsQixhQVhNc2EsUUFBUUMsR0FHZC9YLElBQ0UsRUFDQSx5QkFBeUIrWCxFQUFhYSw2Q0FDcENwYixpQkFBbUJzakIsUUFLaEIvSSxDQUNSLENBQUMsTUFBT25YLEdBS1AsTUFKQVosSUFDRSxFQUNBLHlCQUF5QitYLEVBQWFhLHFEQUVsQ2hZLENBQ1AsR0FnQkhtZ0IsU0FBVXZRLE1BQU91SCxHQWlCVkEsRUFBYUMsS0FTZEQsRUFBYUMsS0FBS0ksWUFDcEJwWSxJQUNFLEVBQ0EseUJBQXlCK1gsRUFBYWEseURBRWpDLEdBSUxiLEVBQWFDLEtBQUtnSixZQUFZQyxVQUNoQ2poQixJQUNFLEVBQ0EseUJBQXlCK1gsRUFBYWEsd0RBRWpDLEtBS1BxRixFQUFZL1gsYUFDVjZSLEVBQWFjLFVBQVlvRixFQUFZL1gsYUFFdkNsRyxJQUNFLEVBQ0EseUJBQXlCK1gsRUFBYWEseUNBQXlDcUYsRUFBWS9YLHlDQUV0RixJQWxDUGxHLElBQ0UsRUFDQSx5QkFBeUIrWCxFQUFhYSxzREFFakMsR0E4Q1h3QixRQUFTNUosTUFBT3VILElBTWQsR0FMQS9YLElBQ0UsRUFDQSx5QkFBeUIrWCxFQUFhYSw4QkFHcENiLEVBQWFDLE9BQVNELEVBQWFDLEtBQUtJLFdBQzFDLElBRUVMLEVBQWFDLEtBQUtrSixtQkFBbUIsYUFDckNuSixFQUFhQyxLQUFLa0osbUJBQW1CLFdBQ3JDbkosRUFBYUMsS0FBS2tKLG1CQUFtQix1QkFHL0JuSixFQUFhQyxLQUFLSCxPQUN6QixDQUFDLE1BQU9qWCxHQUtQLE1BSkFaLElBQ0UsRUFDQSx5QkFBeUIrWCxFQUFhYSxtREFFbENoWSxDQUNQLENBQ0YsRUFHUCxDQ3hrQk8sU0FBU3VnQixTQUFTbGtCLEdBRXZCLE1BQU1zSSxFQUFTLElBQUk2YixNQUFBQSxNQUFNLElBQUk3YixPQU03QixPQUhlOGIsVUFBVTliLEdBR1g0YixTQUFTbGtCLEVBQU8sQ0FBRXFrQixTQUFVLENBQUMsa0JBQzdDLENDREEsSUFBSS9jLG9CQUFxQixFQXFCbEJpTSxlQUFlK1EsYUFBYW5lLEdBRWpDLElBQUlBLElBQVdBLEVBQVFILE9Bd0NyQixNQUFNLElBQUlzTyxZQUNSLGtLQUNBLFdBeENJaVEsWUFDSixDQUFFdmUsT0FBUUcsRUFBUUgsT0FBUXFCLFlBQWFsQixFQUFRa0IsY0FDL0NrTSxNQUFPNVAsRUFBTzZnQixLQUVaLEdBQUk3Z0IsRUFDRixNQUFNQSxFQUlSLE1BQU02QyxJQUFFQSxFQUFHekgsUUFBRUEsRUFBT0QsS0FBRUEsR0FBUzBsQixFQUFLcmUsUUFBUUgsT0FHNUMsSUFDTVEsRUFFRnNRLEdBQWFBLGNBQ1gsR0FBRy9YLEVBQVFFLE1BQU0sS0FBS0MsU0FBVyxjQUNqQ2EsVUFBVXlrQixFQUFLekYsT0FBUWpnQixJQUl6QmdZLEdBQWFBLGNBQ1gvWCxHQUFXLFNBQVNELElBQ1gsUUFBVEEsRUFBaUJtQixPQUFPQyxLQUFLc2tCLEVBQUt6RixPQUFRLFVBQVl5RixFQUFLekYsT0FHaEUsQ0FBQyxNQUFPcGIsR0FDUCxNQUFNLElBQUkyUSxZQUNSLHNDQUNBLEtBQ0FNLFNBQVNqUixFQUNaLE9BR0txZSxVQUFVLEdBU3hCLENBc0JPek8sZUFBZWtSLFlBQVl0ZSxHQUVoQyxLQUFJQSxHQUFXQSxFQUFRSCxRQUFVRyxFQUFRSCxPQUFPSyxPQTRFOUMsTUFBTSxJQUFJaU8sWUFDUiwrR0FDQSxLQTlFbUQsQ0FFckQsTUFBTW9RLEVBQWlCLEdBR3ZCLElBQUssSUFBSUMsS0FBUXhlLEVBQVFILE9BQU9LLE1BQU1wSCxNQUFNLE1BQVEsR0FDbEQwbEIsRUFBT0EsRUFBSzFsQixNQUFNLEtBQ0UsSUFBaEIwbEIsRUFBSzlqQixPQUNQNmpCLEVBQWV6Z0IsS0FDYnNnQixZQUNFLENBQ0V2ZSxPQUFRLElBQ0hHLEVBQVFILE9BQ1hDLE9BQVEwZSxFQUFLLEdBQ2I1bEIsUUFBUzRsQixFQUFLLElBRWhCdGQsWUFBYWxCLEVBQVFrQixjQUV2QixDQUFDMUQsRUFBTzZnQixLQUVOLEdBQUk3Z0IsRUFDRixNQUFNQSxFQUlSLE1BQU02QyxJQUFFQSxFQUFHekgsUUFBRUEsRUFBT0QsS0FBRUEsR0FBUzBsQixFQUFLcmUsUUFBUUgsT0FHNUMsSUFDTVEsRUFFRnNRLEdBQWFBLGNBQ1gsR0FBRy9YLEVBQVFFLE1BQU0sS0FBS0MsU0FBVyxjQUNqQ2EsVUFBVXlrQixFQUFLekYsT0FBUWpnQixJQUl6QmdZLEdBQWFBLGNBQ1gvWCxFQUNTLFFBQVRELEVBQ0ltQixPQUFPQyxLQUFLc2tCLEVBQUt6RixPQUFRLFVBQ3pCeUYsRUFBS3pGLE9BR2QsQ0FBQyxNQUFPcGIsR0FDUCxNQUFNLElBQUkyUSxZQUNSLHNDQUNBLEtBQ0FNLFNBQVNqUixFQUNaLE1BS1BaLElBQUksRUFBRyx1REFLWCxNQUFNNmhCLFFBQXFCalIsUUFBUWtSLFdBQVdILFNBR3hDMUMsV0FHTjRDLEVBQWEvWixTQUFRLENBQUNrVSxFQUFReE0sS0FFeEJ3TSxFQUFPK0YsUUFDVHBoQixhQUNFLEVBQ0FxYixFQUFPK0YsT0FDUCwrQkFBK0J2UyxFQUFRLHNDQUUxQyxHQUVQLENBTUEsQ0FvQ09nQixlQUFlZ1IsWUFBWVEsRUFBY0MsR0FDOUMsSUFFRSxJQUFLdmtCLFNBQVNza0IsR0FDWixNQUFNLElBQUl6USxZQUNSLGlGQUNBLEtBS0osTUFBTW5PLEVBQVUwTCxjQUNkLENBQ0U3TCxPQUFRK2UsRUFBYS9lLE9BQ3JCcUIsWUFBYTBkLEVBQWExZCxjQUU1QixHQUlJeVEsRUFBZ0IzUixFQUFRSCxPQU05QixHQUhBakQsSUFBSSxFQUFHLDJDQUdzQixPQUF6QitVLEVBQWM3UixPQUFpQixDQUdqQyxJQUFJZ2YsRUFGSmxpQixJQUFJLEVBQUcsbURBR1AsSUFFRWtpQixFQUFjN2lCLEdBQVlBLGFBQ3hCcEQsZ0JBQWdCOFksRUFBYzdSLFFBQzlCLE9BRUgsQ0FBQyxNQUFPdEMsR0FDUCxNQUFNLElBQUkyUSxZQUNSLG1EQUNBLEtBQ0FNLFNBQVNqUixFQUNaLENBR0QsR0FBSW1VLEVBQWM3UixPQUFPOUQsU0FBUyxRQUVoQzJWLEVBQWMxUixJQUFNNmUsTUFDZixLQUFJbk4sRUFBYzdSLE9BQU85RCxTQUFTLFNBSXZDLE1BQU0sSUFBSW1TLFlBQ1Isa0RBQ0EsS0FKRndELEVBQWM1UixNQUFRK2UsQ0FNdkIsQ0FDRixDQUdELEdBQTBCLE9BQXRCbk4sRUFBYzFSLElBQWMsQ0FDOUJyRCxJQUFJLEVBQUcscURBR0w2ZixlQUFlakMsdUJBR2pCLE1BQU01QixRQUFlbUcsZUFDbkJoQixTQUFTcE0sRUFBYzFSLEtBQ3ZCRCxHQU9GLFFBSEV5YyxlQUFlbkMsZUFHVnVFLEVBQVksS0FBTWpHLEVBQzFCLENBR0QsR0FBNEIsT0FBeEJqSCxFQUFjNVIsT0FBNEMsT0FBMUI0UixFQUFjM1IsUUFBa0IsQ0FDbEVwRCxJQUFJLEVBQUcsc0RBR0w2ZixlQUFlaEMsMkJBR2pCLE1BQU03QixRQUFlb0csbUJBQ25Cck4sRUFBYzVSLE9BQVM0UixFQUFjM1IsUUFDckNBLEdBT0YsUUFIRXljLGVBQWVsQyxtQkFHVnNFLEVBQVksS0FBTWpHLEVBQzFCLENBR0QsT0FBT2lHLEVBQ0wsSUFBSTFRLFlBQ0YsZ0pBQ0EsS0FHTCxDQUFDLE1BQU8zUSxHQUNQLE9BQU9xaEIsRUFBWXJoQixFQUNwQixDQUNILENBU08sU0FBU3loQix3QkFDZCxPQUFPOWQsa0JBQ1QsQ0FVTyxTQUFTK2Qsc0JBQXNCNWpCLEdBQ3BDNkYsbUJBQXFCN0YsQ0FDdkIsQ0FrQkE4UixlQUFlMlIsZUFBZUksRUFBZW5mLEdBRTNDLEdBQzJCLGlCQUFsQm1mLElBQ05BLEVBQWMvTyxRQUFRLFNBQVcsR0FBSytPLEVBQWMvTyxRQUFRLFVBQVksR0FZekUsT0FWQXhULElBQUksRUFBRyxpQ0FHUG9ELEVBQVFILE9BQU9JLElBQU1rZixFQUdyQm5mLEVBQVFILE9BQU9FLE1BQVEsS0FDdkJDLEVBQVFILE9BQU9HLFFBQVUsS0FHbEJvZixlQUFlcGYsR0FFdEIsTUFBTSxJQUFJbU8sWUFBWSxtQ0FBb0MsSUFFOUQsQ0FrQkFmLGVBQWU0UixtQkFBbUJHLEVBQWVuZixHQUMvQ3BELElBQUksRUFBRyx1Q0FHUCxNQUFNOFAsRUFBcUJMLGdCQUN6QjhTLEdBQ0EsRUFDQW5mLEVBQVFrQixZQUFZQyxvQkFJdEIsR0FDeUIsT0FBdkJ1TCxHQUM4QixpQkFBdkJBLElBQ05BLEVBQW1CeFEsV0FBVyxPQUM5QndRLEVBQW1CMVEsU0FBUyxLQUU3QixNQUFNLElBQUltUyxZQUNSLG9QQUNBLEtBV0osT0FOQW5PLEVBQVFILE9BQU9FLE1BQVEyTSxFQUd2QjFNLEVBQVFILE9BQU9JLElBQU0sS0FHZG1mLGVBQWVwZixFQUN4QixDQWNBb04sZUFBZWdTLGVBQWVwZixHQUM1QixNQUFRSCxPQUFROFIsRUFBZXpRLFlBQWEwUSxHQUF1QjVSLEVBa0NuRSxPQS9CQTJSLEVBQWNoWixLQUFPSyxRQUFRMlksRUFBY2haLEtBQU1nWixFQUFjL1ksU0FHL0QrWSxFQUFjL1ksUUFBVUYsV0FBV2laLEVBQWNoWixLQUFNZ1osRUFBYy9ZLFNBR3JFK1ksRUFBY3RaLE9BQVNELFVBQVV1WixFQUFjdFosUUFHL0N1RSxJQUNFLEVBQ0EsK0JBQStCZ1YsRUFBbUJ6USxtQkFBcUIsVUFBWSxpQkFJckZrZSxtQkFBbUJ6TixFQUFvQkEsRUFBbUJ6USxvQkFHMURtZSxzQkFDRTNOLEVBQ0FDLEVBQW1COVYsbUJBQ25COFYsRUFBbUJ6USxvQkFJckJuQixFQUFRSCxPQUFTLElBQ1o4UixLQUNBNE4sZUFBZTVOLElBSWJzSyxTQUFTamMsRUFDbEIsQ0FxQkEsU0FBU3VmLGVBQWU1TixHQUV0QixNQUFRcUIsTUFBT3dNLEVBQWNsTixVQUFXbU4sR0FDdEM5TixFQUFjM1IsU0FBV3FNLGdCQUFnQnNGLEVBQWM1UixTQUFVLEdBRzNEaVQsTUFBTzBNLEVBQW9CcE4sVUFBV3FOLEdBQzVDdFQsZ0JBQWdCc0YsRUFBYzVRLGlCQUFrQixHQUcxQ2lTLE1BQU80TSxFQUFtQnROLFVBQVd1TixHQUMzQ3hULGdCQUFnQnNGLEVBQWMzUSxnQkFBaUIsRUFNM0NQLEVBQVFwRixZQUNaSSxLQUFLcUYsSUFDSCxHQUNBckYsS0FBS29GLElBQ0g4USxFQUFjbFIsT0FDWmdmLEdBQWtCaGYsT0FDbEJrZixHQUF3QmxmLE9BQ3hCb2YsR0FBdUJwZixPQUN2QmtSLEVBQWMvUSxjQUNkLEVBQ0YsSUFHSixHQTRCSWdYLEVBQU8sQ0FBRXJYLE9BdkJib1IsRUFBY3BSLFFBQ2RrZixHQUFrQkssY0FDbEJOLEdBQWNqZixRQUNkb2YsR0FBd0JHLGNBQ3hCSixHQUFvQm5mLFFBQ3BCc2YsR0FBdUJDLGNBQ3ZCRixHQUFtQnJmLFFBQ25Cb1IsRUFBY2pSLGVBQ2QsSUFlcUJGLE1BWHJCbVIsRUFBY25SLE9BQ2RpZixHQUFrQk0sYUFDbEJQLEdBQWNoZixPQUNkbWYsR0FBd0JJLGFBQ3hCTCxHQUFvQmxmLE9BQ3BCcWYsR0FBdUJFLGFBQ3ZCSCxHQUFtQnBmLE9BQ25CbVIsRUFBY2hSLGNBQ2QsSUFHNEJGLFNBRzlCLElBQUssSUFBS3VmLEVBQU8xa0IsS0FBVXRELE9BQU8rVCxRQUFRNkwsR0FDeENBLEVBQUtvSSxHQUNjLGlCQUFWMWtCLEdBQXNCQSxFQUFNOUMsUUFBUSxTQUFVLElBQU04QyxFQUkvRCxPQUFPc2MsQ0FDVCxDQWtCQSxTQUFTeUgsbUJBQW1Cek4sRUFBb0J6USxHQUU5QyxHQUFJQSxFQUFvQixDQUV0QixHQUE0QyxpQkFBakN5USxFQUFtQnZRLFVBRTVCdVEsRUFBbUJ2USxVQUFZNGUsaUJBQzdCck8sRUFBbUJ2USxVQUNuQnVRLEVBQW1COVYsb0JBQ25CLFFBRUcsSUFBSzhWLEVBQW1CdlEsVUFDN0IsSUFFRXVRLEVBQW1CdlEsVUFBWTRlLGlCQUM3QmhrQixHQUFBQSxhQUFhcEQsZ0JBQWdCLGtCQUFtQixRQUNoRCtZLEVBQW1COVYsb0JBQ25CLEVBRUgsQ0FBQyxNQUFPMEIsR0FDUFosSUFBSSxFQUFHLDREQUNSLENBSUgsSUFFRWdWLEVBQW1CL1YsV0FBYUQsV0FDOUJnVyxFQUFtQi9WLFdBQ25CK1YsRUFBbUI5VixtQkFFdEIsQ0FBQyxNQUFPMEIsR0FDUEQsYUFBYSxFQUFHQyxFQUFPLDhDQUd2Qm9VLEVBQW1CL1YsV0FBYSxJQUNqQyxDQUdELElBRUUrVixFQUFtQnhRLFNBQVd4RixXQUM1QmdXLEVBQW1CeFEsU0FDbkJ3USxFQUFtQjlWLG9CQUNuQixFQUVILENBQUMsTUFBTzBCLEdBQ1BELGFBQWEsRUFBR0MsRUFBTyw0Q0FHdkJvVSxFQUFtQnhRLFNBQVcsSUFDL0IsQ0FHRyxDQUFDLFVBQU0vRCxHQUFXNUUsU0FBU21aLEVBQW1CL1YsYUFDaERlLElBQUksRUFBRyx1REFJTCxDQUFDLFVBQU1TLEdBQVc1RSxTQUFTbVosRUFBbUJ4USxXQUNoRHhFLElBQUksRUFBRyxxREFJTCxDQUFDLFVBQU1TLEdBQVc1RSxTQUFTbVosRUFBbUJ2USxZQUNoRHpFLElBQUksRUFBRyxxREFFYixNQUlJLEdBQ0VnVixFQUFtQnhRLFVBQ25Cd1EsRUFBbUJ2USxXQUNuQnVRLEVBQW1CL1YsV0FRbkIsTUFMQStWLEVBQW1CeFEsU0FBVyxLQUM5QndRLEVBQW1CdlEsVUFBWSxLQUMvQnVRLEVBQW1CL1YsV0FBYSxLQUcxQixJQUFJc1MsWUFDUixvR0FDQSxJQUlSLENBa0JBLFNBQVM4UixpQkFDUDVlLEVBQVksS0FDWnZGLEVBQ0FxRixHQUdBLE1BQU0rZSxFQUFlLENBQUMsS0FBTSxNQUFPLFNBRW5DLElBQUlDLEVBQW1COWUsRUFDbkIrZSxHQUFtQixFQUd2QixHQUFJdGtCLEdBQXNCdUYsRUFBVXJGLFNBQVMsU0FDM0MsSUFDRW1rQixFQUFtQjlULGdCQUNqQnBRLEdBQUFBLGFBQWFwRCxnQkFBZ0J3SSxHQUFZLFNBQ3pDLEVBQ0FGLEVBRVIsQ0FBTSxNQUNBLE9BQU8sSUFDUixNQUdEZ2YsRUFBbUI5VCxnQkFBZ0JoTCxHQUFXLEVBQU9GLEdBR2pEZ2YsSUFBcUJya0IsVUFDaEJxa0IsRUFBaUJwSyxNQUs1QixJQUFLLE1BQU1zSyxLQUFZRixFQUNoQkQsRUFBYXpuQixTQUFTNG5CLEdBRWZELElBQ1ZBLEdBQW1CLFVBRlpELEVBQWlCRSxHQU81QixPQUFLRCxHQUtERCxFQUFpQnBLLFFBQ25Cb0ssRUFBaUJwSyxNQUFRb0ssRUFBaUJwSyxNQUFNM1EsS0FBSzdLLEdBQVNBLEVBQUtKLFdBQzlEZ21CLEVBQWlCcEssT0FBU29LLEVBQWlCcEssTUFBTXJiLFFBQVUsV0FDdkR5bEIsRUFBaUJwSyxPQUtyQm9LLEdBWkUsSUFhWCxDQW9CQSxTQUFTYixzQkFDUDNOLEVBQ0E3VixFQUNBcUYsR0FHQSxDQUFDLGdCQUFpQixnQkFBZ0J1RCxTQUFTNGIsSUFDekMsSUFFTTNPLEVBQWMyTyxLQUdkeGtCLEdBQ3NDLGlCQUEvQjZWLEVBQWMyTyxJQUNyQjNPLEVBQWMyTyxHQUFhdGtCLFNBQVMsU0FHcEMyVixFQUFjMk8sR0FBZWpVLGdCQUMzQnBRLEdBQUFBLGFBQWFwRCxnQkFBZ0I4WSxFQUFjMk8sSUFBZSxTQUMxRCxFQUNBbmYsR0FJRndRLEVBQWMyTyxHQUFlalUsZ0JBQzNCc0YsRUFBYzJPLElBQ2QsRUFDQW5mLEdBSVAsQ0FBQyxNQUFPM0QsR0FDUEQsYUFDRSxFQUNBQyxFQUNBLGlCQUFpQjhpQix5QkFJbkIzTyxFQUFjMk8sR0FBZSxJQUM5QixLQUlDLENBQUMsVUFBTWpqQixHQUFXNUUsU0FBU2taLEVBQWM1USxnQkFDM0NuRSxJQUFJLEVBQUcsMERBSUwsQ0FBQyxVQUFNUyxHQUFXNUUsU0FBU2taLEVBQWMzUSxlQUMzQ3BFLElBQUksRUFBRyx3REFFWCxDQ2wwQkEsTUFBTTJqQixTQUFXLEdBU1YsU0FBU0MsU0FBU2hMLEdBQ3ZCK0ssU0FBU3ppQixLQUFLMFgsRUFDaEIsQ0FRTyxTQUFTaUwsaUJBQ2Q3akIsSUFBSSxFQUFHLDJEQUNQLElBQUssTUFBTTRZLEtBQU0rSyxTQUNmRyxjQUFjbEwsR0FDZG1MLGFBQWFuTCxFQUVqQixDQ2ZBLFNBQVNvTCxtQkFBbUJwakIsRUFBT3FqQixFQUFTalQsRUFBVWtULEdBVXBELE9BUkF2akIsYUFBYSxFQUFHQyxHQUdtQixnQkFBL0JnTyxhQUFhakksTUFBTUMsZ0JBQ2RoRyxFQUFNSyxNQUlSaWpCLEVBQUt0akIsRUFDZCxDQVlBLFNBQVN1akIsc0JBQXNCdmpCLEVBQU9xakIsRUFBU2pULEVBQVVrVCxHQUV2RCxNQUFNbmpCLFFBQUVBLEVBQU9FLE1BQUVBLEdBQVVMLEVBR3JCNlEsRUFBYTdRLEVBQU02USxZQUFjLElBR3ZDVCxFQUFTb1QsT0FBTzNTLEdBQVk0UyxLQUFLLENBQUU1UyxhQUFZMVEsVUFBU0UsU0FDMUQsQ0FPZSxTQUFTcWpCLGdCQUFnQkMsR0FFdENBLEVBQUlDLElBQUlSLG9CQUdSTyxFQUFJQyxJQUFJTCxzQkFDVixDQzVDZSxTQUFTTSx1QkFBdUJGLEVBQUtHLEdBQ2xELElBRUUsR0FBSUgsR0FBT0csRUFBb0I1ZixPQUFRLENBQ3JDLE1BQU0vRCxFQUNKLHlFQUdJNGpCLEVBQWMsQ0FDbEJwZixPQUFRbWYsRUFBb0JuZixRQUFVLEVBQ3RDRCxZQUFhb2YsRUFBb0JwZixhQUFlLEdBQ2hERSxNQUFPa2YsRUFBb0JsZixPQUFTLEVBQ3BDQyxXQUFZaWYsRUFBb0JqZixhQUFjLEVBQzlDQyxRQUFTZ2YsRUFBb0JoZixTQUFXLEtBQ3hDQyxVQUFXK2UsRUFBb0IvZSxXQUFhLE1BSTFDZ2YsRUFBWWxmLFlBQ2Q4ZSxFQUFJemYsT0FBTyxlQUliLE1BQU04ZixFQUFVQyxVQUFVLENBRXhCQyxTQUErQixHQUFyQkgsRUFBWXBmLE9BQWMsSUFFcEN3ZixNQUFPSixFQUFZcmYsWUFFbkIwZixRQUFTTCxFQUFZbmYsTUFDckJ5ZixRQUFTLENBQUNoQixFQUFTalQsS0FDakJBLEVBQVNrVSxPQUFPLENBQ2RiLEtBQU0sS0FDSnJULEVBQVNvVCxPQUFPLEtBQUtlLEtBQUssQ0FBRXBrQixXQUFVLEVBRXhDcWtCLFFBQVMsS0FDUHBVLEVBQVNvVCxPQUFPLEtBQUtlLEtBQUtwa0IsRUFBUSxHQUVwQyxFQUVKc2tCLEtBQU9wQixHQUdxQixPQUF4QlUsRUFBWWpmLFNBQ2MsT0FBMUJpZixFQUFZaGYsV0FDWnNlLEVBQVFxQixNQUFNbnFCLE1BQVF3cEIsRUFBWWpmLFNBQ2xDdWUsRUFBUXFCLE1BQU1DLGVBQWlCWixFQUFZaGYsWUFFM0MzRixJQUFJLEVBQUcsMkNBQ0EsS0FPYnVrQixFQUFJQyxJQUFJSSxHQUVSNWtCLElBQ0UsRUFDQSw4Q0FBOEMya0IsRUFBWXJmLDRCQUE0QnFmLEVBQVlwZiw4Q0FBOENvZixFQUFZbGYsY0FFL0osQ0FDRixDQUFDLE1BQU83RSxHQUNQLE1BQU0sSUFBSTJRLFlBQ1IseUVBQ0EsS0FDQU0sU0FBU2pSLEVBQ1osQ0FDSCxDQ3pEQSxTQUFTNGtCLHNCQUFzQnZCLEVBQVNqVCxFQUFVa1QsR0FDaEQsSUFFRSxNQUFNdUIsRUFBY3hCLEVBQVF5QixRQUFRLGlCQUFtQixHQUd2RCxJQUNHRCxFQUFZNXBCLFNBQVMsc0JBQ3JCNHBCLEVBQVk1cEIsU0FBUyx1Q0FDckI0cEIsRUFBWTVwQixTQUFTLHVCQUV0QixNQUFNLElBQUkwVixZQUNSLGlIQUNBLEtBS0osT0FBTzJTLEdBQ1IsQ0FBQyxNQUFPdGpCLEdBQ1AsT0FBT3NqQixFQUFLdGpCLEVBQ2IsQ0FDSCxDQW1CQSxTQUFTK2tCLHNCQUFzQjFCLEVBQVNqVCxFQUFVa1QsR0FDaEQsSUFFRSxNQUFNeEwsRUFBT3VMLEVBQVF2TCxLQUdmK0csRUFBWW1CLEtBQUFBLEtBR2xCLElBQUtsSSxHQUFROWEsY0FBYzhhLEdBUXpCLE1BUEExWSxJQUNFLEVBQ0EseUJBQXlCeWYseUJBQ3ZCd0UsRUFBUXlCLFFBQVEsb0JBQXNCekIsRUFBUTJCLFdBQVdDLDJEQUl2RCxJQUFJdFUsWUFDUix5QkFBeUJrTyw4SkFDekIsS0FLSixNQUFNbGIsRUFBcUI4ZCx3QkFHckJsZixFQUFRc00sZ0JBRVppSixFQUFLdlYsT0FBU3VWLEVBQUt0VixTQUFXc1YsRUFBS3hWLFFBQVV3VixFQUFLK0ksTUFFbEQsRUFFQWxkLEdBSUYsR0FBYyxPQUFWcEIsSUFBbUJ1VixFQUFLclYsSUFRMUIsTUFQQXJELElBQ0UsRUFDQSx5QkFBeUJ5Zix5QkFDdkJ3RSxFQUFReUIsUUFBUSxvQkFBc0J6QixFQUFRMkIsV0FBV0MsMkZBQ21CaFcsS0FBS1EsVUFBVXFJLE9BR3pGLElBQUluSCxZQUNSLFlBQVlrTyxzUkFDWixLQUtKLEdBQUkvRyxFQUFLclYsS0FBT3RGLHVCQUF1QjJhLEVBQUtyVixLQUMxQyxNQUFNLElBQUlrTyxZQUNSLFlBQVlrTyxpTUFDWixLQTBDSixPQXJDQXdFLEVBQVE2QixpQkFBbUIsQ0FFekJyRyxZQUNBeGMsT0FBUSxDQUNORSxRQUNBRSxJQUFLcVYsRUFBS3JWLElBQ1ZySCxRQUNFMGMsRUFBSzFjLFNBQ0wsR0FBR2lvQixFQUFROEIsT0FBT0MsVUFBWSxXQUFXdE4sRUFBSzNjLE1BQVEsUUFDeERBLEtBQU0yYyxFQUFLM2MsS0FDWE4sT0FBUWlkLEVBQUtqZCxPQUNiZ0ksSUFBS2lWLEVBQUtqVixJQUNWQyxXQUFZZ1YsRUFBS2hWLFdBQ2pCQyxPQUFRK1UsRUFBSy9VLE9BQ2JDLE1BQU84VSxFQUFLOVUsTUFDWkMsTUFBTzZVLEVBQUs3VSxNQUNaTSxjQUFlc0wsZ0JBQ2JpSixFQUFLdlUsZUFDTCxFQUNBSSxHQUVGSCxhQUFjcUwsZ0JBQ1ppSixFQUFLdFUsY0FDTCxFQUNBRyxJQUdKRCxZQUFhLENBQ1hDLHFCQUNBckYsb0JBQW9CLEVBQ3BCRCxXQUFZeVosRUFBS3paLFdBQ2pCdUYsU0FBVWtVLEVBQUtsVSxTQUNmQyxVQUFXZ0wsZ0JBQWdCaUosRUFBS2pVLFdBQVcsRUFBTUYsS0FLOUMyZixHQUNSLENBQUMsTUFBT3RqQixHQUNQLE9BQU9zakIsRUFBS3RqQixFQUNiLENBQ0gsQ0FPZSxTQUFTcWxCLHFCQUFxQjFCLEdBRTNDQSxFQUFJMkIsS0FBSyxDQUFDLElBQUssY0FBZVYsdUJBRzlCakIsRUFBSTJCLEtBQUssQ0FBQyxJQUFLLGNBQWVQLHNCQUNoQyxDQzdLQSxNQUFNUSxhQUFlLENBQ25CQyxJQUFLLFlBQ0xDLEtBQU0sYUFDTkMsSUFBSyxZQUNMakosSUFBSyxrQkFDTGhhLElBQUssaUJBZ0JQbU4sZUFBZStWLGNBQWN0QyxFQUFTalQsRUFBVWtULEdBQzlDLElBRUUsTUFBTXNDLEVBQWlCcm9CLGNBR3ZCLElBQUlzb0IsR0FBb0IsRUFDeEJ4QyxFQUFReUMsT0FBT3hWLEdBQUcsU0FBVXlWLElBQ3RCQSxJQUNGRixHQUFvQixFQUNyQixJQUlILE1BQU05VixFQUFpQnNULEVBQVE2QixpQkFHekJyRyxFQUFZOU8sRUFBZThPLFVBR2pDemYsSUFBSSxFQUFHLHFCQUFxQnlmLDRDQUd0QitCLFlBQVk3USxHQUFnQixDQUFDL1AsRUFBTzZnQixLQUt4QyxHQUhBd0MsRUFBUXlDLE9BQU94RixtQkFBbUIsU0FHOUJ1RixFQUNGem1CLElBQ0UsRUFDQSxxQkFBcUJ5ZixtRkFIekIsQ0FTQSxHQUFJN2UsRUFDRixNQUFNQSxFQUlSLElBQUs2Z0IsSUFBU0EsRUFBS3pGLE9BU2pCLE1BUkFoYyxJQUNFLEVBQ0EscUJBQXFCeWYscUJBQ25Cd0UsRUFBUXlCLFFBQVEsb0JBQ2hCekIsRUFBUTJCLFdBQVdDLG1EQUNpQnBFLEVBQUt6RixXQUd2QyxJQUFJekssWUFDUixxQkFBcUJrTyx5R0FDckIsS0FLSixHQUFJZ0MsRUFBS3pGLE9BQVEsQ0FDZmhjLElBQ0UsRUFDQSxxQkFBcUJ5Zix5Q0FBaUQrRyxVQUl4RSxNQUFNenFCLEtBQUVBLEVBQUkwSCxJQUFFQSxFQUFHQyxXQUFFQSxFQUFVMUgsUUFBRUEsR0FBWXlsQixFQUFLcmUsUUFBUUgsT0FHeEQsT0FBSVEsRUFDS3VOLEVBQVNtVSxLQUFLbm9CLFVBQVV5a0IsRUFBS3pGLE9BQVFqZ0IsS0FJOUNpVixFQUFTNFYsT0FBTyxlQUFnQlQsYUFBYXBxQixJQUFTLGFBR2pEMkgsR0FDSHNOLEVBQVM2VixXQUFXN3FCLEdBSU4sUUFBVEQsRUFDSGlWLEVBQVNtVSxLQUFLMUQsRUFBS3pGLFFBQ25CaEwsRUFBU21VLEtBQUtqb0IsT0FBT0MsS0FBS3NrQixFQUFLekYsT0FBUSxXQUM1QyxDQWxEQSxDQWtEQSxHQUVKLENBQUMsTUFBT3BiLEdBQ1AsT0FBT3NqQixFQUFLdGpCLEVBQ2IsQ0FDSCxDQVNlLFNBQVNrbUIsYUFBYXZDLEdBS25DQSxFQUFJMkIsS0FBSyxJQUFLSyxlQU1kaEMsRUFBSTJCLEtBQUssYUFBY0ssY0FDekIsQ0NwSUEsTUFBTVEsZ0JBQWtCLElBQUl6cEIsS0FHdEIwcEIsWUFBY25YLEtBQUtwQixNQUN2QnBQLEdBQUFBLGFBQWF3QyxLQUFBQSxLQUFLNUgsWUFBVyxnQkFBaUIsU0FJMUNndEIsYUFBZSxHQUdmQyxlQUFpQixJQUdqQkMsV0FBYSxHQVVuQixTQUFTQywwQkFDUCxPQUFPSCxhQUFhNVgsUUFBTyxDQUFDZ1ksRUFBR0MsSUFBTUQsRUFBSUMsR0FBRyxHQUFLTCxhQUFhbnBCLE1BQ2hFLENBVUEsU0FBU3lwQixvQkFDUCxPQUFPQyxhQUFZLEtBQ2pCLE1BQU1DLEVBQVE1SCxlQUNSNkgsRUFDdUIsSUFBM0JELEVBQU1sSyxpQkFDRixFQUNDa0ssRUFBTWpLLGlCQUFtQmlLLEVBQU1sSyxpQkFBb0IsSUFFMUQwSixhQUFhL2xCLEtBQUt3bUIsR0FDZFQsYUFBYW5wQixPQUFTcXBCLFlBQ3hCRixhQUFhOXFCLE9BQ2QsR0FDQStxQixlQUNMLENBU2UsU0FBU1MsYUFBYXBELEdBR25DWCxTQUFTMkQscUJBS1RoRCxFQUFJeFQsSUFBSSxXQUFXLENBQUNrVCxFQUFTalQsRUFBVWtULEtBQ3JDLElBQ0Vsa0IsSUFBSSxFQUFHLHFDQUVQLE1BQU15bkIsRUFBUTVILGVBQ1IrSCxFQUFTWCxhQUFhbnBCLE9BQ3RCK3BCLEVBQWdCVCwwQkFHdEJwVyxFQUFTbVUsS0FBSyxDQUVaZixPQUFRLEtBQ1IwRCxTQUFVZixnQkFDVmdCLE9BQVEsR0FBR2xwQixLQUFLbXBCLE9BQU94cUIsaUJBQW1CdXBCLGdCQUFnQnRwQixXQUFhLElBQU8sY0FHOUV3cUIsY0FBZWpCLFlBQVl4a0IsUUFDM0IwbEIsa0JBQW1COVUsdUJBR25CK1Usa0JBQW1CVixFQUFNMUosaUJBQ3pCcUssaUJBQWtCWCxFQUFNbEssaUJBQ3hCOEssaUJBQWtCWixFQUFNakssaUJBQ3hCOEssY0FBZWIsRUFBTWhLLGVBQ3JCOEssWUFBY2QsRUFBTWpLLGlCQUFtQmlLLEVBQU1sSyxpQkFBb0IsSUFHakV4WCxLQUFNK1osa0JBR044SCxTQUNBQyxnQkFDQTltQixRQUNFK0gsTUFBTStlLEtBQW1CWixhQUFhbnBCLE9BQ2xDLG9FQUNBLFFBQVE4cEIsbUNBQXdDQyxFQUFjVyxRQUFRLE9BRzVFQyxXQUFZaEIsRUFBTS9KLGVBQ2xCZ0wsWUFBYWpCLEVBQU05SixtQkFDbkJnTCxtQkFBb0JsQixFQUFNN0osdUJBQzFCZ0wsb0JBQXFCbkIsRUFBTTVKLDRCQUU5QixDQUFDLE1BQU9qZCxHQUNQLE9BQU9zakIsRUFBS3RqQixFQUNiLElBRUwsQ0M5R2UsU0FBU2lvQixTQUFTdEUsR0FJL0JBLEVBQUl4VCxJQUFJbkMsYUFBYW5JLEdBQUdDLE9BQVMsS0FBSyxDQUFDdWQsRUFBU2pULEVBQVVrVCxLQUN4RCxJQUNFbGtCLElBQUksRUFBRyxxQ0FFUGdSLEVBQVM4WCxTQUFTam5CLEtBQUlBLEtBQUM1SCxZQUFXLFNBQVUsY0FBZSxDQUN6RDh1QixjQUFjLEdBRWpCLENBQUMsTUFBT25vQixHQUNQLE9BQU9zakIsRUFBS3RqQixFQUNiLElBRUwsQ0NmZSxTQUFTb29CLG9CQUFvQnpFLEdBSzFDQSxFQUFJMkIsS0FBSywrQkFBK0IxVixNQUFPeVQsRUFBU2pULEVBQVVrVCxLQUNoRSxJQUNFbGtCLElBQUksRUFBRywwQ0FHUCxNQUFNaXBCLEVBQWExYSxLQUFLL0UsdUJBR3hCLElBQUt5ZixJQUFlQSxFQUFXbnJCLE9BQzdCLE1BQU0sSUFBSXlULFlBQ1IsaUhBQ0EsS0FLSixNQUFNMlgsRUFBUWpGLEVBQVFsVCxJQUFJLFdBRzFCLElBQUttWSxHQUFTQSxJQUFVRCxFQUN0QixNQUFNLElBQUkxWCxZQUNSLDJFQUNBLEtBS0osSUFBSStCLEVBQWEyUSxFQUFROEIsT0FBT3pTLFdBQ2hDLElBQUlBLEVBbUJGLE1BQU0sSUFBSS9CLFlBQVkscUNBQXNDLEtBbEI1RCxVQUVROEIsd0JBQXdCQyxFQUMvQixDQUFDLE1BQU8xUyxHQUNQLE1BQU0sSUFBSTJRLFlBQ1IsNkJBQTZCM1EsRUFBTUcsVUFDbkMsS0FDQThRLFNBQVNqUixFQUNaLENBR0RvUSxFQUFTb1QsT0FBTyxLQUFLZSxLQUFLLENBQ3hCMVQsV0FBWSxJQUNaeVcsa0JBQW1COVUsdUJBQ25CclMsUUFBUywrQ0FBK0N1UyxNQU03RCxDQUFDLE1BQU8xUyxHQUNQLE9BQU9zakIsRUFBS3RqQixFQUNiLElBRUwsQ0MxQ0EsTUFBTXVvQixjQUFnQixJQUFJQyxJQUdwQjdFLElBQU04RSxVQXNCTDdZLGVBQWU4WSxZQUFZQyxHQUNoQyxJQUVFLE1BQU1ubUIsRUFBVTBMLGNBQWMsQ0FDNUJqSyxPQUFRMGtCLElBT1YsS0FIQUEsRUFBZ0JubUIsRUFBUXlCLFFBR0xDLFNBQVd5ZixJQUM1QixNQUFNLElBQUloVCxZQUNSLG1GQUNBLEtBTUosTUFBTWlZLEVBQStDLEtBQTVCRCxFQUFjdGtCLFlBQXFCLEtBR3REd2tCLEVBQVVDLE9BQU9DLGdCQUdqQkMsRUFBU0YsT0FBTyxDQUNwQkQsVUFDQUksT0FBUSxDQUNOQyxVQUFXTixLQTJDZixHQXRDQWpGLElBQUl3RixRQUFRLGdCQUdaeEYsSUFBSUMsSUFDRndGLEtBQUssQ0FDSEMsUUFBUyxDQUFDLE9BQVEsTUFBTyxjQU03QjFGLElBQUlDLEtBQUksQ0FBQ1AsRUFBU2pULEVBQVVrVCxLQUMxQmxULEVBQVNrWixJQUFJLGdCQUFpQixRQUM5QmhHLEdBQU0sSUFJUkssSUFBSUMsSUFDRjZFLFFBQVFoRixLQUFLLENBQ1hVLE1BQU95RSxLQUtYakYsSUFBSUMsSUFDRjZFLFFBQVFjLFdBQVcsQ0FDakJDLFVBQVUsRUFDVnJGLE1BQU95RSxLQUtYakYsSUFBSUMsSUFBSW9GLEVBQU9TLFFBR2Y5RixJQUFJQyxJQUFJNkUsUUFBUWlCLE9BQU96b0IsS0FBSUEsS0FBQzVILFlBQVcsYUFHbENzdkIsRUFBYzNqQixJQUFJQyxNQUFPLENBRTVCLE1BQU0wa0IsRUFBYWpaLEtBQUtrWixhQUFhakcsS0FHckNrRywyQkFBMkJGLEdBRzNCQSxFQUFXRyxPQUFPbkIsRUFBY3ZrQixLQUFNdWtCLEVBQWN4a0IsTUFBTSxLQUV4RG9rQixjQUFjZSxJQUFJWCxFQUFjdmtCLEtBQU11bEIsR0FFdEN2cUIsSUFDRSxFQUNBLG1DQUFtQ3VwQixFQUFjeGtCLFFBQVF3a0IsRUFBY3ZrQixRQUN4RSxHQUVKLENBR0QsR0FBSXVrQixFQUFjM2pCLElBQUlkLE9BQVEsQ0FFNUIsSUFBSTNKLEVBQUt3dkIsRUFFVCxJQUVFeHZCLEVBQU1rRSxHQUFZQSxhQUNoQndDLEtBQUlBLEtBQUM1RixnQkFBZ0JzdEIsRUFBYzNqQixJQUFJRSxVQUFXLGNBQ2xELFFBSUY2a0IsRUFBT3RyQixHQUFZQSxhQUNqQndDLEtBQUlBLEtBQUM1RixnQkFBZ0JzdEIsRUFBYzNqQixJQUFJRSxVQUFXLGNBQ2xELE9BRUgsQ0FBQyxNQUFPbEYsR0FDUFosSUFDRSxFQUNBLHFEQUFxRHVwQixFQUFjM2pCLElBQUlFLHNEQUUxRSxDQUVELEdBQUkzSyxHQUFPd3ZCLEVBQU0sQ0FFZixNQUFNQyxFQUFjdlosTUFBTW1aLGFBQWEsQ0FBRXJ2QixNQUFLd3ZCLFFBQVFwRyxLQUd0RGtHLDJCQUEyQkcsR0FHM0JBLEVBQVlGLE9BQU9uQixFQUFjM2pCLElBQUlaLEtBQU11a0IsRUFBY3hrQixNQUFNLEtBRTdEb2tCLGNBQWNlLElBQUlYLEVBQWMzakIsSUFBSVosS0FBTTRsQixHQUUxQzVxQixJQUNFLEVBQ0Esb0NBQW9DdXBCLEVBQWN4a0IsUUFBUXdrQixFQUFjM2pCLElBQUlaLFFBQzdFLEdBRUosQ0FDRixDQUdEeWYsdUJBQXVCRixJQUFLZ0YsRUFBY2xrQixjQUcxQzRnQixxQkFBcUIxQixLQUdyQnVDLGFBQWF2QyxLQUNib0QsYUFBYXBELEtBQ2JzRSxTQUFTdEUsS0FDVHlFLG9CQUFvQnpFLEtBR3BCRCxnQkFBZ0JDLElBQ2pCLENBQUMsTUFBTzNqQixHQUNQLE1BQU0sSUFBSTJRLFlBQ1IscURBQ0EsS0FDQU0sU0FBU2pSLEVBQ1osQ0FDSCxDQU9PLFNBQVNpcUIsZUFFZCxHQUFJMUIsY0FBY25PLEtBQU8sRUFBRyxDQUMxQmhiLElBQUksRUFBRyxpQ0FHUCxJQUFLLE1BQU9nRixFQUFNSCxLQUFXc2tCLGNBQzNCdGtCLEVBQU9nVCxPQUFNLEtBQ1hzUixjQUFjMkIsT0FBTzlsQixHQUNyQmhGLElBQUksRUFBRyxtQ0FBbUNnRixLQUFRLEdBR3ZELENBQ0gsQ0FTTyxTQUFTK2xCLGFBQ2QsT0FBTzVCLGFBQ1QsQ0FTTyxTQUFTNkIsYUFDZCxPQUFPM0IsT0FDVCxDQVNPLFNBQVM0QixTQUNkLE9BQU8xRyxHQUNULENBWU8sU0FBUzJHLG1CQUFtQnhHLEdBRWpDLE1BQU10aEIsRUFBVTBMLGNBQWMsQ0FDNUJqSyxPQUFRLENBQ05RLGFBQWNxZixLQUtsQkQsdUJBQXVCRixJQUFLbmhCLEVBQVF5QixPQUFPNmYsb0JBQzdDLENBVU8sU0FBU0YsSUFBSTVuQixLQUFTdXVCLEdBQzNCNUcsSUFBSUMsSUFBSTVuQixLQUFTdXVCLEVBQ25CLENBVU8sU0FBU3BhLElBQUluVSxLQUFTdXVCLEdBQzNCNUcsSUFBSXhULElBQUluVSxLQUFTdXVCLEVBQ25CLENBVU8sU0FBU2pGLEtBQUt0cEIsS0FBU3V1QixHQUM1QjVHLElBQUkyQixLQUFLdHBCLEtBQVN1dUIsRUFDcEIsQ0FTQSxTQUFTViwyQkFBMkI1bEIsR0FDbENBLEVBQU9xTSxHQUFHLGVBQWUsQ0FBQ3RRLEVBQU84bEIsS0FDL0IvbEIsYUFDRSxFQUNBQyxFQUNBLDBCQUEwQkEsRUFBTUcsK0JBRWxDMmxCLEVBQU90TSxTQUFTLElBR2xCdlYsRUFBT3FNLEdBQUcsU0FBVXRRLElBQ2xCRCxhQUFhLEVBQUdDLEVBQU8sMEJBQTBCQSxFQUFNRyxVQUFVLElBR25FOEQsRUFBT3FNLEdBQUcsY0FBZXdWLElBQ3ZCQSxFQUFPeFYsR0FBRyxTQUFVdFEsSUFDbEJELGFBQWEsRUFBR0MsRUFBTywwQkFBMEJBLEVBQU1HLFVBQVUsR0FDakUsR0FFTixDQUVBLElBQWU4RCxPQUFBLENBQ2J5a0Isd0JBQ0F1QiwwQkFDQUUsc0JBQ0FDLHNCQUNBQyxjQUNBQyxzQ0FDQTFHLFFBQ0F6VCxRQUNBbVYsV0N2VksxVixlQUFlNGEsZ0JBQWdCQyxFQUFXLFNBRXpDemEsUUFBUWtSLFdBQVcsQ0FFdkIrQixpQkFHQWdILGVBR0E1TCxhQUlGNWdCLFFBQVFpdEIsS0FBS0QsRUFDZixDQ1NPN2EsZUFBZSthLFdBQVdDLEdBRS9CLE1BQU1wb0IsRUFBVTBMLGNBQWMwYyxHQUc5QmxKLHNCQUFzQmxmLEVBQVFrQixZQUFZQyxvQkFHMUNwRCxZQUFZaUMsRUFBUTVELFNBR2hCNEQsRUFBUXVELE1BQU1FLHNCQUNoQjRrQixvQ0FJSXZaLG9CQUFvQjlPLEVBQVFiLFdBQVlhLEVBQVF5QixPQUFPTSxhQUd2RDZZLFNBQVM1YSxFQUFRMkMsS0FBTTNDLEVBQVFwQixVQUFVL0IsS0FDakQsQ0FTQSxTQUFTd3JCLDhCQUNQenJCLElBQUksRUFBRyxzREFHUDNCLFFBQVE2UyxHQUFHLFFBQVN3YSxJQUNsQjFyQixJQUFJLEVBQUcsc0NBQXNDMHJCLEtBQVEsSUFJdkRydEIsUUFBUTZTLEdBQUcsVUFBVVYsTUFBT04sRUFBTXdiLEtBQ2hDMXJCLElBQUksRUFBRyxpQkFBaUJrUSxzQkFBeUJ3YixZQUMzQ04saUJBQWlCLElBSXpCL3NCLFFBQVE2UyxHQUFHLFdBQVdWLE1BQU9OLEVBQU13YixLQUNqQzFyQixJQUFJLEVBQUcsaUJBQWlCa1Esc0JBQXlCd2IsWUFDM0NOLGlCQUFpQixJQUl6Qi9zQixRQUFRNlMsR0FBRyxVQUFVVixNQUFPTixFQUFNd2IsS0FDaEMxckIsSUFBSSxFQUFHLGlCQUFpQmtRLHNCQUF5QndiLFlBQzNDTixpQkFBaUIsSUFJekIvc0IsUUFBUTZTLEdBQUcscUJBQXFCVixNQUFPNVAsRUFBT3NQLEtBQzVDdlAsYUFBYSxFQUFHQyxFQUFPLGlCQUFpQnNQLGtCQUNsQ2tiLGdCQUFnQixFQUFFLEdBRTVCLENBRUEsSUFBZTViLE1BQUEsSUFFVjNLLE9BR0grSixzQkFDQUUsNEJBQ0FHLGdDQUdBc2Msc0JBQ0FoSywwQkFDQUcsd0JBQ0FGLHdCQUdBdkMsa0JBQ0FtTSxnQ0FHQXByQixRQUNBVywwQkFDQVksWUFBYSxTQUFVbkIsR0FTckJtQixZQVBnQnVOLGNBQWMsQ0FDNUJ0UCxRQUFTLENBQ1BZLFdBS2dCWixRQUFRWSxNQUM3QixFQUNEb0IscUJBQXNCLFNBQVUvQixHQVM5QitCLHFCQVBnQnNOLGNBQWMsQ0FDNUJ0UCxRQUFTLENBQ1BDLGVBS3lCRCxRQUFRQyxVQUN0QyxFQUNEZ0Msa0JBQW1CLFNBQVVKLEVBQU1DLEVBQU01QixHQUV2QyxNQUFNMEQsRUFBVTBMLGNBQWMsQ0FDNUJ0UCxRQUFTLENBQ1A2QixPQUNBQyxPQUNBNUIsWUFLSitCLGtCQUNFMkIsRUFBUTVELFFBQVE2QixLQUNoQitCLEVBQVE1RCxRQUFROEIsS0FDaEI4QixFQUFRNUQsUUFBUUUsT0FFbkIifQ== diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index 1c954d2f..53e3cbbe 100644 --- a/dist/index.esm.js.map +++ b/dist/index.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, join, normalize, resolve } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? normalize(path) : resolve(path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getCopy=true] - Specifies whether to return a copied\r\n * object of the global options (`true`) or a reference to the global options\r\n * object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} A copy of the global options object, or a reference\r\n * to the global options object.\r\n */\r\nexport function getOptions(getCopy = true) {\r\n return getCopy ? deepCopy(globalOptions) : globalOptions;\r\n}\r\n\r\n/**\r\n * Updates a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(getOptions(getCopy), newOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return reference to the global options\r\n return getOptions(false);\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Get the allow flags for the custom logic check\r\n const { allowCodeExecution, allowFileResources } = getOptions().customLogic;\r\n\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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') ? false : true;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n path: getAbsolutePath(cssImportPath)\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `imageOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} imageOptions - The `imageOptions` object, which should\r\n * include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(imageOptions, endCallback) {\r\n try {\r\n // Check if provided options are in an object\r\n if (!isObject(imageOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: imageOptions.export,\r\n customLogic: imageOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 = readFileSync(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = readFileSync(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","normalize","resolve","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","join","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","getCopy","updateOptions","newOptions","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","imageOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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KA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMmB,cAAgB,CAC3BC,UAAW,CACT/B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFuD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbtG,KAAM,OACNuG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP9D,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbtG,KAAM,SAGV0G,OAAQ,CACN/D,MAAO,8BACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbtG,KAAM,SAGV2G,WAAY,CACVhE,OAAO,EACPuD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbtG,KAAM,WAGV4G,UAAW,CACTjE,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbtG,KAAM,SAGV6G,YAAa,CACXlE,MAAO,CAAC,aAAc,kBAAmB,iBACzCuD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbtG,KAAM,cACN8G,aAAc,0DAGlBC,cAAe,CACbpE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,k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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, normalize, resolve } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? normalize(path) : resolve(path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getCopy=true] - Specifies whether to return a copied\r\n * object of the global options (`true`) or a reference to the global options\r\n * object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} A copy of the global options object, or a reference\r\n * to the global options object.\r\n */\r\nexport function getOptions(getCopy = true) {\r\n return getCopy ? deepCopy(globalOptions) : globalOptions;\r\n}\r\n\r\n/**\r\n * Updates a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(getOptions(getCopy), newOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return reference to the global options\r\n return getOptions(false);\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Get the allow flags for the custom logic check\r\n const { allowCodeExecution, allowFileResources } = getOptions().customLogic;\r\n\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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') ? false : true;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n path: getAbsolutePath(cssImportPath)\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `imageOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} imageOptions - The `imageOptions` object, which should\r\n * include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(imageOptions, endCallback) {\r\n try {\r\n // Check if provided options are in an object\r\n if (!isObject(imageOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: imageOptions.export,\r\n customLogic: imageOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 = readFileSync(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = readFileSync(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","normalize","resolve","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","join","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","getCopy","updateOptions","newOptions","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","imageOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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KA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMmB,cAAgB,CAC3BC,UAAW,CACT/B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFuD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbtG,KAAM,OACNuG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP9D,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbtG,KAAM,SAGV0G,OAAQ,CACN/D,MAAO,8BACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbtG,KAAM,SAGV2G,WAAY,CACVhE,OAAO,EACPuD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbtG,KAAM,WAGV4G,UAAW,CACTjE,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbtG,KAAM,SAGV6G,YAAa,CACXlE,MAAO,CAAC,aAAc,kBAAmB,iBACzCuD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbtG,KAAM,cACN8G,aAAc,0DAGlBC,cAAe,CACbpE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,k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"} \ No newline at end of file diff --git a/lib/utils.js b/lib/utils.js index ca07f9e9..6309835b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -19,7 +19,7 @@ See LICENSE file in root for details. */ import { readFileSync } from 'fs'; -import { isAbsolute, join, normalize, resolve } from 'path'; +import { isAbsolute, normalize, resolve } from 'path'; import { fileURLToPath } from 'url'; const MAX_BACKOFF_ATTEMPTS = 6; From 82eb3e7a40124701aee40f88333a9bd9629a4341 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Fri, 24 Jan 2025 17:54:35 +0100 Subject: [PATCH 085/102] Minor visual corrections. --- lib/server/middlewares/validation.js | 4 ++-- lib/server/routes/export.js | 6 +++--- lib/server/routes/versionChange.js | 9 +++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/server/middlewares/validation.js b/lib/server/middlewares/validation.js index 45b105df..96d6b51d 100644 --- a/lib/server/middlewares/validation.js +++ b/lib/server/middlewares/validation.js @@ -133,7 +133,7 @@ function requestBodyMiddleware(request, response, next) { ); throw new ExportError( - `Request [${requestId}] - [validation] 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.`, + `[validation] Request [${requestId}] - 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 ); } @@ -141,7 +141,7 @@ function requestBodyMiddleware(request, response, next) { // Throw an error if test of xlink:href elements from payload's SVG fails if (body.svg && isPrivateRangeUrlFound(body.svg)) { throw new ExportError( - `Request [${requestId}] - [validation] 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.`, + `[validation] Request [${requestId}] - 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 ); } diff --git a/lib/server/routes/export.js b/lib/server/routes/export.js index 1def9744..c664a573 100644 --- a/lib/server/routes/export.js +++ b/lib/server/routes/export.js @@ -63,16 +63,16 @@ async function requestExport(request, response, next) { }); // Get the options previously validated in the validation middleware - const requestOptions = request.validatedOptions; + const options = request.validatedOptions; // Get the request id - const requestId = requestOptions.requestId; + const requestId = options.requestId; // Info about an incoming request with correct data log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`); // Start the export process - await startExport(requestOptions, (error, data) => { + await startExport(options, (error, data) => { // Remove the close event from the socket request.socket.removeAllListeners('close'); diff --git a/lib/server/routes/versionChange.js b/lib/server/routes/versionChange.js index 32b615e2..4e20e4c9 100644 --- a/lib/server/routes/versionChange.js +++ b/lib/server/routes/versionChange.js @@ -45,7 +45,7 @@ export default function versionChangeRoutes(app) { // Check the existence of the token if (!adminToken || !adminToken.length) { throw new ExportError( - '[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.', + '[version] The server is not configured to perform run-time version changes: `HIGHCHARTS_ADMIN_TOKEN` is not set.', 401 ); } @@ -61,11 +61,12 @@ export default function versionChangeRoutes(app) { ); } - // Compare versions - let newVersion = request.params.newVersion; + // Get the new version from the params + const newVersion = request.params.newVersion; + + // Update version if (newVersion) { try { - // Update version await updateHighchartsVersion(newVersion); } catch (error) { throw new ExportError( From 78aa15a9b0bde592ab56b95e88e364aabe1327a2 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Fri, 24 Jan 2025 17:56:03 +0100 Subject: [PATCH 086/102] Added a check for the data size before sending it to a page for export process. --- dist/index.cjs | 4 ++-- dist/index.esm.js | 2 +- dist/index.esm.js.map | 2 +- lib/chart.js | 44 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/dist/index.cjs b/dist/index.cjs index 5ea96214..02abf87a 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?path.normalize(e):path.resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIG5vcm1hbGl6ZSwgcmVzb2x2ZSB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBkaXJlY3RvcnkgcGF0aFxyXG5leHBvcnQgY29uc3QgX19kaXJuYW1lID0gZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuLi8uJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFuZCBzdGFuZGFyZGl6ZXMgdGV4dCBieSByZXBsYWNpbmcgbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZVxyXG4gKiBjaGFyYWN0ZXJzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIHRyaW1taW5nIGFueSBsZWFkaW5nIG9yIHRyYWlsaW5nXHJcbiAqIHdoaXRlc3BhY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhclRleHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyB0aGUgJy9cXHNcXHMrL2cnIFJlZ0V4cC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtyZXBsYWNlcj0nICddIC0gVGhlIHN0cmluZyB1c2VkIHRvIHJlcGxhY2UgbXVsdGlwbGVcclxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgJyAnIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyVGV4dCh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpIHtcclxuICByZXR1cm4gdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBkZWVwQ29weVxyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8QXJyYXkpfSBvYmpBcnIgLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fEFycmF5KX0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDb3B5KG9iakFycikge1xyXG4gIC8vIElmIHRoZSBgb2JqQXJyYCBpcyBudWxsIG9yIG5vdCBvZiB0aGUgYG9iamVjdGAgdHlwZSwgcmV0dXJuIGl0XHJcbiAgaWYgKG9iakFyciA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqQXJyICE9PSAnb2JqZWN0Jykge1xyXG4gICAgcmV0dXJuIG9iakFycjtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZWl0aGVyIGEgbmV3IGFycmF5IG9yIGEgbmV3IG9iamVjdFxyXG4gIGNvbnN0IG9iakFyckNvcHkgPSBBcnJheS5pc0FycmF5KG9iakFycikgPyBbXSA6IHt9O1xyXG5cclxuICAvLyBSZWN1cnNpdmVseSBjb3B5IGVhY2ggcHJvcGVydHlcclxuICBmb3IgKGNvbnN0IGtleSBpbiBvYmpBcnIpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqQXJyLCBrZXkpKSB7XHJcbiAgICAgIG9iakFyckNvcHlba2V5XSA9IGRlZXBDb3B5KG9iakFycltrZXldKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY29waWVkIG9iamVjdFxyXG4gIHJldHVybiBvYmpBcnJDb3B5O1xyXG59XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZXhwQmFja29mZlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBmdW5jdGlvbiB0byBiZSByZXRyaWVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYDBgLlxyXG4gKiBAcGFyYW0gey4uLnVua25vd259IGFyZ3MgLSBBcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSByZXN1bHRcclxuICogb2YgdGhlIGZ1bmN0aW9uIGlmIHN1Y2Nlc3NmdWwuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCBpZiB0aGUgbWF4aW11bSBudW1iZXIgb2YgYXR0ZW1wdHNcclxuICogaXMgcmVhY2hlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleHBCYWNrb2ZmKGZuLCBhdHRlbXB0ID0gMCwgLi4uYXJncykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBUcnkgdG8gY2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIHJldHVybiBhd2FpdCBmbiguLi5hcmdzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gQ2FsY3VsYXRlIGRlbGF5IGluIG1zXHJcbiAgICBjb25zdCBkZWxheUluTXMgPSAyICoqIGF0dGVtcHQgKiAxMDAwO1xyXG5cclxuICAgIC8vIElmIHRoZSBhdHRlbXB0IGV4Y2VlZHMgdGhlIG1heGltdW0gYXR0ZW1wdHMgb2YgcmVwZWF0LCB0aHJvdyBhbiBlcnJvclxyXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXYWl0IGdpdmVuIGFtb3VudCBvZiB0aW1lXHJcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIGRlbGF5SW5NcykpO1xyXG5cclxuICAgIC8vLyBUTyBETzogQ29ycmVjdFxyXG4gICAgLy8gLy8gSW5mb3JtYXRpb24gYWJvdXQgdGhlIHJlc291cmNlIHRpbWVvdXRcclxuICAgIC8vIGxvZyhcclxuICAgIC8vICAgMyxcclxuICAgIC8vICAgYFt1dGlsc10gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIG9mIElEOiAke2FyZ3NbMF19LmBcclxuICAgIC8vICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRqdXN0cyB0aGUgY29uc3RydWN0b3IgbmFtZSBieSB0cmFuc2Zvcm1pbmcgYW5kIG5vcm1hbGl6aW5nIGl0IGJhc2VkXHJcbiAqIG9uIGNvbW1vbiBjaGFydCB0eXBlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeENvbnN0clxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY29uc3RyIC0gVGhlIG9yaWdpbmFsIGNvbnN0cnVjdG9yIG5hbWUgdG8gYmUgZml4ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IgbmFtZSwgb3IgJ2NoYXJ0JyBpZiB0aGUgaW5wdXRcclxuICogaXMgbm90IHJlY29nbml6ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4Q29uc3RyKGNvbnN0cikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBGaXggdGhlIGNvbnN0cnVjdG9yIGJ5IGxvd2VyaW5nIGNhc2luZ1xyXG4gICAgY29uc3QgZml4ZWRDb25zdHIgPSBgJHtjb25zdHIudG9Mb3dlckNhc2UoKS5yZXBsYWNlKCdjaGFydCcsICcnKX1DaGFydGA7XHJcblxyXG4gICAgLy8gSGFuZGxlIHRoZSBjYXNlIHdoZXJlIHRoZSByZXN1bHQgaXMganVzdCAnQ2hhcnQnXHJcbiAgICBpZiAoZml4ZWRDb25zdHIgPT09ICdDaGFydCcpIHtcclxuICAgICAgZml4ZWRDb25zdHIudG9Mb3dlckNhc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZXR1cm4gdGhlIGNvcnJlY3RlZCBjb25zdHJ1Y3Rvciwgb3RoZXJ3aXNlIGRlZmF1bHQgdG8gJ2NoYXJ0J1xyXG4gICAgcmV0dXJuIFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10uaW5jbHVkZXMoXHJcbiAgICAgIGZpeGVkQ29uc3RyXHJcbiAgICApXHJcbiAgICAgID8gZml4ZWRDb25zdHJcclxuICAgICAgOiAnY2hhcnQnO1xyXG4gIH0gY2F0Y2gge1xyXG4gICAgLy8gRGVmYXVsdCB0byAnY2hhcnQnIGluIGNhc2Ugb2YgYW55IGVycm9yXHJcbiAgICByZXR1cm4gJ2NoYXJ0JztcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGaXhlcyB0aGUgb3V0ZmlsZSBiYXNlZCBvbiBwcm92aWRlZCB0eXBlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZml4T3V0ZmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSBvcmlnaW5hbCBleHBvcnQgdHlwZS5cclxuICogQHBhcmFtIHtzdHJpbmd9IG91dGZpbGUgLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgb3V0ZmlsZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhPdXRmaWxlKHR5cGUsIG91dGZpbGUpIHtcclxuICAvLyBHZXQgdGhlIGZpbGUgbmFtZSBmcm9tIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgY29uc3QgZmlsZU5hbWUgPSBnZXRBYnNvbHV0ZVBhdGgob3V0ZmlsZSB8fCAnY2hhcnQnKVxyXG4gICAgLnNwbGl0KCcuJylcclxuICAgIC5zaGlmdCgpO1xyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IG91dGZpbGVcclxuICByZXR1cm4gYCR7ZmlsZU5hbWV9LiR7dHlwZX1gO1xyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIGV4cG9ydCB0eXBlIGJhc2VkIG9uIE1JTUUgdHlwZXMgYW5kIGZpbGUgZXh0ZW5zaW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeFR5cGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbb3V0ZmlsZT1udWxsXSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYG51bGxgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeFR5cGUodHlwZSwgb3V0ZmlsZSA9IG51bGwpIHtcclxuICAvLyBNSU1FIHR5cGVzXHJcbiAgY29uc3QgbWltZVR5cGVzID0ge1xyXG4gICAgJ2ltYWdlL3BuZyc6ICdwbmcnLFxyXG4gICAgJ2ltYWdlL2pwZWcnOiAnanBlZycsXHJcbiAgICAnYXBwbGljYXRpb24vcGRmJzogJ3BkZicsXHJcbiAgICAnaW1hZ2Uvc3ZnK3htbCc6ICdzdmcnXHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IGZvcm1hdHNcclxuICBjb25zdCBmb3JtYXRzID0gT2JqZWN0LnZhbHVlcyhtaW1lVHlwZXMpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICAvLyBTdXBwb3J0IHRoZSBKUEcgdHlwZVxyXG4gICAgaWYgKG91dFR5cGUgPT09ICdqcGcnKSB7XHJcbiAgICAgIHR5cGUgPSAnanBlZyc7XHJcbiAgICB9IGVsc2UgaWYgKGZvcm1hdHMuaW5jbHVkZXMob3V0VHlwZSkgJiYgdHlwZSAhPT0gb3V0VHlwZSkge1xyXG4gICAgICB0eXBlID0gb3V0VHlwZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGNvcnJlY3QgdHlwZVxyXG4gIHJldHVybiBtaW1lVHlwZXNbdHlwZV0gfHwgZm9ybWF0cy5maW5kKCh0KSA9PiB0ID09PSB0eXBlKSB8fCAncG5nJztcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gcGF0aCBpcyByZWxhdGl2ZSBvciBhYnNvbHV0ZSBhbmQgcmV0dXJucyB0aGUgY29ycmVjdGVkLFxyXG4gKiBhYnNvbHV0ZSBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBYnNvbHV0ZVBhdGhcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byBiZSBjaGVja2VkIG9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgYWJzb2x1dGUgcGF0aC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBYnNvbHV0ZVBhdGgocGF0aCkge1xyXG4gIHJldHVybiBpc0Fic29sdXRlKHBhdGgpID8gbm9ybWFsaXplKHBhdGgpIDogcmVzb2x2ZShwYXRoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGlucHV0IGRhdGEgdG8gYSBCYXNlNjQgc3RyaW5nIGJhc2VkIG9uIHRoZSBleHBvcnQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJhc2U2NFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgaW5wdXQgdG8gYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZvcm1hdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBCYXNlNjQgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRCYXNlNjQoaW5wdXQsIHR5cGUpIHtcclxuICAvLyBGb3IgcGRmIGFuZCBzdmcgdHlwZXMgdGhlIGlucHV0IG11c3QgYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZyb20gYSBidWZmZXJcclxuICBpZiAodHlwZSA9PT0gJ3BkZicgfHwgdHlwZSA9PSAnc3ZnJykge1xyXG4gICAgcmV0dXJuIEJ1ZmZlci5mcm9tKGlucHV0LCAndXRmOCcpLnRvU3RyaW5nKCdiYXNlNjQnKTtcclxuICB9XHJcblxyXG4gIC8vIEZvciBwbmcgYW5kIGpwZWcgaW5wdXQgaXMgYWxyZWFkeSBhIEJhc2U2NCBzdHJpbmdcclxuICByZXR1cm4gaW5wdXQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHN0cmluZ2lmaWVkIGRhdGUgd2l0aG91dCB0aGUgR01UIHRleHQgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3RGF0ZSgpIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIHJldHVybiBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBzdG9yZWQgdGltZSB2YWx1ZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlVGltZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGVUaW1lKCkge1xyXG4gIHJldHVybiBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gaXRlbSBpcyBhbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgaXRlbSBpcyBhbiBvYmplY3QsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdChpdGVtKSB7XHJcbiAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChpdGVtKSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIG9iamVjdCBpcyBlbXB0eS5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW0gLSBUaGUgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdEVtcHR5KGl0ZW0pIHtcclxuICByZXR1cm4gKFxyXG4gICAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgICAhQXJyYXkuaXNBcnJheShpdGVtKSAmJlxyXG4gICAgaXRlbSAhPT0gbnVsbCAmJlxyXG4gICAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kLCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChpdGVtKSB7XHJcbiAgY29uc3QgcmVnZXhQYXR0ZXJucyA9IFtcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT9sb2NhbGhvc3RcXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEwXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMjdcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE3MlxcLigxWzYtOV18MlswLTldfDNbMC0xXSlcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xOTJcXC4xNjhcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiL1xyXG4gIF07XHJcblxyXG4gIHJldHVybiByZWdleFBhdHRlcm5zLnNvbWUoKHBhdHRlcm4pID0+IHBhdHRlcm4udGVzdChpdGVtKSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVdGlsaXR5IHRvIG1lYXN1cmUgZWxhcHNlZCB0aW1lIHVzaW5nIHRoZSBOb2RlLmpzIGBwcm9jZXNzLmhydGltZSgpYCBtZXRob2QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtZWFzdXJlVGltZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IEEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBlbGFwc2VkIHRpbWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1lYXN1cmVUaW1lKCkge1xyXG4gIGNvbnN0IHN0YXJ0ID0gcHJvY2Vzcy5ocnRpbWUuYmlnaW50KCk7XHJcbiAgcmV0dXJuICgpID0+IE51bWJlcihwcm9jZXNzLmhydGltZS5iaWdpbnQoKSAtIHN0YXJ0KSAvIDEwMDAwMDA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSb3VuZHMgYSBudW1iZXIgdG8gdGhlIHNwZWNpZmllZCBwcmVjaXNpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByb3VuZE51bWJlclxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBwcmVjaXNpb24gLSBUaGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIHRvIHJvdW5kIHRvLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgcm91bmRlZCBudW1iZXIuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcm91bmROdW1iZXIodmFsdWUsIHByZWNpc2lvbiA9IDEpIHtcclxuICBjb25zdCBtdWx0aXBsaWVyID0gTWF0aC5wb3coMTAsIHByZWNpc2lvbiB8fCAwKTtcclxuICByZXR1cm4gTWF0aC5yb3VuZCgrdmFsdWUgKiBtdWx0aXBsaWVyKSAvIG11bHRpcGxpZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBhIHZhbHVlIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHRvQm9vbGVhblxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSBib29sZWFuIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dCB2YWx1ZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB0b0Jvb2xlYW4oaXRlbSkge1xyXG4gIHJldHVybiBbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTicsICcwJywgJyddLmluY2x1ZGVzKGl0ZW0pXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6ICEhaXRlbTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFdyYXBzIGN1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgaXQgc2FmZWx5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gd3JhcEFyb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tQ29kZSAtIFRoZSBjdXN0b20gY29kZSB0byBiZSB3cmFwcGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEZsYWcgdG8gYWxsb3cgbG9hZGluZyBjb2RlIGZyb20gYSBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtpc0NhbGxiYWNrPWZhbHNlXSAtIEZsYWcgdGhhdCBpbmRpY2F0ZXMgdGhlIHJldHVybmVkIGNvZGVcclxuICogbXVzdCBiZSBpbiBhIGNhbGxiYWNrIGZvcm1hdC5cclxuICpcclxuICogQHJldHVybnMgeyhzdHJpbmd8bnVsbCl9IFRoZSB3cmFwcGVkIGN1c3RvbSBjb2RlIG9yIG51bGwgaWYgd3JhcHBpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gd3JhcEFyb3VuZChjdXN0b21Db2RlLCBhbGxvd0ZpbGVSZXNvdXJjZXMsIGlzQ2FsbGJhY2sgPSBmYWxzZSkge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICAvLyBMb2FkIGEgZmlsZSBpZiB0aGUgZmlsZSByZXNvdXJjZXMgYXJlIGFsbG93ZWRcclxuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICAgID8gd3JhcEFyb3VuZChcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChjdXN0b21Db2RlKSwgJ3V0ZjgnKSxcclxuICAgICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgICBpc0NhbGxiYWNrXHJcbiAgICAgICAgICApXHJcbiAgICAgICAgOiBudWxsO1xyXG4gICAgfSBlbHNlIGlmIChcclxuICAgICAgIWlzQ2FsbGJhY2sgJiZcclxuICAgICAgKGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24oKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbiAoKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpID0+JykpXHJcbiAgICApIHtcclxuICAgICAgLy8gVHJlYXQgYSBmdW5jdGlvbiBhcyBhIHNlbGYtaW52b2tpbmcgZXhwcmVzc2lvblxyXG4gICAgICByZXR1cm4gYCgke2N1c3RvbUNvZGV9KSgpYDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBPciByZXR1cm4gYXMgYSBzdHJpbmdpZmllZCBjb2RlXHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGRlZXBDb3B5LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4Q29uc3RyLFxyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIGdldE5ld0RhdGUsXHJcbiAgZ2V0TmV3RGF0ZVRpbWUsXHJcbiAgaXNPYmplY3QsXHJcbiAgaXNPYmplY3RFbXB0eSxcclxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxyXG4gIG1lYXN1cmVUaW1lLFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHRvQm9vbGVhbixcclxuICB3cmFwQXJvdW5kXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSBmb3IgbWFuYWdpbmcgbG9nZ2luZyBmdW5jdGlvbmFsaXR5IHdpdGggY3VzdG9taXphYmxlXHJcbiAqIGxvZyBsZXZlbHMsIGNvbnNvbGUgYW5kIGZpbGUgbG9nZ2luZyBvcHRpb25zLCBhbmQgZXJyb3IgaGFuZGxpbmcgc3VwcG9ydC5cclxuICogVGhlIG1vZHVsZSBhbHNvIGVuc3VyZXMgdGhhdCBmaWxlLWJhc2VkIGxvZ3MgYXJlIHN0b3JlZCBpbiBhIHN0cnVjdHVyZWRcclxuICogZGlyZWN0b3J5LCBjcmVhdGluZyB0aGUgbmVjZXNzYXJ5IHBhdGhzIGF1dG9tYXRpY2FsbHkgaWYgdGhleSBkbyBub3QgZXhpc3QuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYXBwZW5kRmlsZSwgZXhpc3RzU3luYywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGgsIGdldE5ld0RhdGUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmNvbnN0IGxvZ2dpbmcgPSB7XHJcbiAgLy8gRmxhZ3MgZm9yIGxvZ2dpbmcgc3RhdHVzXHJcbiAgdG9Db25zb2xlOiB0cnVlLFxyXG4gIHRvRmlsZTogZmFsc2UsXHJcbiAgcGF0aENyZWF0ZWQ6IGZhbHNlLFxyXG4gIC8vIEZ1bGwgcGF0aCB0byB0aGUgbG9nIGZpbGVcclxuICBwYXRoVG9Mb2c6ICcnLFxyXG4gIC8vIExvZyBsZXZlbHNcclxuICBsZXZlbHNEZXNjOiBbXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnZXJyb3InLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzBdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3dhcm5pbmcnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzFdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ25vdGljZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMl1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAndmVyYm9zZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbM11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnYmVuY2htYXJrJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1s0XVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGEgbWVzc2FnZSB3aXRoIGEgc3BlY2lmaWVkIGxvZyBsZXZlbC4gQWNjZXB0cyBhIHZhcmlhYmxlIG51bWJlclxyXG4gKiBvZiBhcmd1bWVudHMuIFRoZSBhcmd1bWVudHMgYWZ0ZXIgdGhlIGBsZXZlbGAgYXJlIHBhc3NlZCB0byBgY29uc29sZS5sb2dgXHJcbiAqIGFuZC9vciB1c2VkIHRvIGNvbnN0cnVjdCBhbmQgYXBwZW5kIG1lc3NhZ2VzIHRvIGEgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dcclxuICpcclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQW4gYXJyYXkgb2YgYXJndW1lbnRzIHdoZXJlIHRoZSBmaXJzdCBpcyB0aGUgbG9nXHJcbiAqIGxldmVsIGFuZCB0aGUgcmVtYWluaW5nIGFyZSBzdHJpbmdzIHVzZWQgdG8gYnVpbGQgdGhlIGxvZyBtZXNzYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nKC4uLmFyZ3MpIHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbHNEZXNjLCBsZXZlbCB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlIG9yIGlzIGl0IGEgYmVuY2htYXJrIGxvZ1xyXG4gIGlmIChcclxuICAgIG5ld0xldmVsICE9PSA1ICYmXHJcbiAgICAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKVxyXG4gICkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdCh0ZXh0cylcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIGFsb25nIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ1dpdGhTdGFja1xyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbG9nIGxldmVsLlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QgY29udGFpbmluZyB0aGUgc3RhY2sgdHJhY2UuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21NZXNzYWdlIC0gQW4gb3B0aW9uYWwgY3VzdG9tIG1lc3NhZ2UgdG8gYmUgaW5jbHVkZWRcclxuICogaW4gdGhlIGxvZyBhbG9uZ3NpZGUgdGhlIGVycm9yLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nV2l0aFN0YWNrKG5ld0xldmVsLCBlcnJvciwgY3VzdG9tTWVzc2FnZSkge1xyXG4gIC8vIEdldCB0aGUgbWFpbiBtZXNzYWdlXHJcbiAgY29uc3QgbWFpbk1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlIHx8IChlcnJvciAmJiBlcnJvci5tZXNzYWdlKSB8fCAnJztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQWRkIHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXHJcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID0gZXJyb3IgJiYgZXJyb3Iuc3RhY2s7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2UsIGlmIGV4aXN0c1xyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlXTtcclxuICBpZiAoc3RhY2tNZXNzYWdlKSB7XHJcbiAgICB0ZXh0cy5wdXNoKCdcXG4nLCBzdGFja01lc3NhZ2UpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcclxuICAgICAgICB0ZXh0cy5zaGlmdCgpW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAuLi50ZXh0c1xyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpbml0TG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgbG9nZ2luZ2Agb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpbml0TG9nZ2luZyhsb2dnaW5nT3B0aW9ucykge1xyXG4gIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGBsb2dnaW5nT3B0aW9uc2Agb2JqZWN0XHJcbiAgY29uc3QgeyBsZXZlbCwgZGVzdCwgZmlsZSwgdG9Db25zb2xlLCB0b0ZpbGUgfSA9IGxvZ2dpbmdPcHRpb25zO1xyXG5cclxuICAvLyBSZXNldCBmbGFncyB0byB0aGUgZGVmYXVsdCB2YWx1ZXNcclxuICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgbG9nZ2luZy5wYXRoVG9Mb2cgPSAnJztcclxuXHJcbiAgLy8gU2V0IHRoZSBsb2dnaW5nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobGV2ZWwpO1xyXG5cclxuICAvLyBTZXQgdGhlIGNvbnNvbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nKHRvQ29uc29sZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgZmlsZSBsb2dnaW5nXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGxvZyBsZXZlbCB0byB0aGUgc3BlY2lmaWVkIHZhbHVlLiBMb2cgbGV2ZWxzIGFyZSAoYDBgID0gbm8gbG9nZ2luZyxcclxuICogYDFgID0gZXJyb3IsIGAyYCA9IHdhcm5pbmcsIGAzYCA9IG5vdGljZSwgYDRgID0gdmVyYm9zZSwgb3IgYDVgID0gYmVuY2htYXJrKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldExvZ0xldmVsXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsb2cgbGV2ZWwgdG8gYmUgc2V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldExvZ0xldmVsKGxldmVsKSB7XHJcbiAgaWYgKFxyXG4gICAgTnVtYmVyLmlzSW50ZWdlcihsZXZlbCkgJiZcclxuICAgIGxldmVsID49IDAgJiZcclxuICAgIGxldmVsIDw9IGxvZ2dpbmcubGV2ZWxzRGVzYy5sZW5ndGhcclxuICApIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgbGV2ZWxgIG9wdGlvblxyXG4gICAgbG9nZ2luZy5sZXZlbCA9IGxldmVsO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgY29uc29sZSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB0b0NvbnNvbGUgLSBUaGUgZmxhZyBmb3Igc2V0dGluZyB0aGUgbG9nZ2luZyB0byB0aGUgY29uc29sZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpIHtcclxuICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYHRvQ29uc29sZWAgb3B0aW9uXHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSAhIXRvQ29uc29sZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUgbmFtZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBkZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggd2hlcmUgdGhlIGxvZyBmaWxlIHNob3VsZFxyXG4gKiBiZSBzYXZlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGZpbGUgLSBUaGUgbmFtZSBvZiB0aGUgbG9nIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9GaWxlIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2dnaW5nIHNob3VsZFxyXG4gKiBiZSBkaXJlY3RlZCB0byBhIGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0ZpbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9GaWxlID0gISF0b0ZpbGU7XHJcblxyXG4gIC8vIFNldCB0aGUgYGRlc3RgIGFuZCBgZmlsZWAgb3B0aW9ucyBvbmx5IGlmIHRoZSBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZFxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ID0gZGVzdCB8fCAnJztcclxuICAgIGxvZ2dpbmcuZmlsZSA9IGZpbGUgfHwgJyc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyB0aGUgcHJvdmlkZWQgdGV4dHMgdG8gYSBmaWxlLCBpZiBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZC4gSXQgY3JlYXRlc1xyXG4gKiB0aGUgbmVjZXNzYXJ5IGRpcmVjdG9yeSBzdHJ1Y3R1cmUgaWYgbm90IGFscmVhZHkgY3JlYXRlZCBhbmQgYXBwZW5kc1xyXG4gKiB0aGUgY29udGVudCwgaW5jbHVkaW5nIGFuIG9wdGlvbmFsIHByZWZpeCwgdG8gdGhlIHNwZWNpZmllZCBsb2cgZmlsZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2dUb0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBBbiBvcHRpb25hbCBwcmVmaXggdG8gYmUgYWRkZWQgdG8gZWFjaCBsb2cgZW50cnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpIHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpICYmXHJcbiAgICAgIG1rZGlyU3luYyhnZXRBYnNvbHV0ZVBhdGgobG9nZ2luZy5kZXN0KSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBmdWxsIHBhdGhcclxuICAgIGxvZ2dpbmcucGF0aFRvTG9nID0gZ2V0QWJzb2x1dGVQYXRoKGpvaW4obG9nZ2luZy5kZXN0LCBsb2dnaW5nLmZpbGUpKTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yICYmIGxvZ2dpbmcudG9GaWxlICYmIGxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29uZmlndXJhdGlvbiBtYW5hZ2VtZW50IG1vZHVsZSBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci5cclxuICogUHJvdmlkZXMgZGVmYXVsdCBjb25maWd1cmF0aW9ucyB0aGF0IHN1cHBvcnQgZW52aXJvbm1lbnQgdmFyaWFibGVzLCBDTElcclxuICogYXJndW1lbnRzLCBhbmQgaW50ZXJhY3RpdmUgcHJvbXB0cyBmb3IgY3VzdG9taXphdGlvbiBvZiBvcHRpb25zIGFuZCBmZWF0dXJlcy5cclxuICogQWRkaXRpb25hbGx5LCBpdCBtYXBzIGxlZ2FjeSBvcHRpb25zIHRvIG1vZGVybiBzdHJ1Y3R1cmVzLCBnZW5lcmF0ZXMgbmVzdGVkXHJcbiAqIGFyZ3VtZW50IG1hcHBpbmdzLCBhbmQgZGlzcGxheXMgQ0xJIHVzYWdlIGluZm9ybWF0aW9uLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBhbGwgYXZhaWxhYmxlIG9wdGlvbnMsIG9yZ2FuaXplZFxyXG4gKiBieSBzZWN0aW9ucy5cclxuICpcclxuICogVGhpcyBvYmplY3QgaW5jbHVkZXM6XHJcbiAqIC0gRGVmYXVsdCB2YWx1ZXMgZm9yIGVhY2ggb3B0aW9uXHJcbiAqIC0gRGF0YSB0eXBlcyBmb3IgdmFsaWRhdGlvblxyXG4gKiAtIE5hbWVzIG9mIGNvcnJlc3BvbmRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIC0gRGVzY3JpcHRpb25zIG9mIGVhY2ggcHJvcGVydHlcclxuICogLSBJbmZvcm1hdGlvbiB1c2VkIGZvciBwcm9tcHRzIGluIGludGVyYWN0aXZlIGNvbmZpZ3VyYXRpb25cclxuICogLSBbT3B0aW9uYWxdIENvcnJlc3BvbmRpbmcgQ0xJIGFyZ3VtZW50IG5hbWVzIGZvciBDTEkgdXNhZ2VcclxuICogLSBbT3B0aW9uYWxdIExlZ2FjeSBuYW1lcyBmcm9tIHRoZSBwcmV2aW91cyBQaGFudG9tSlMtYmFzZWQgc2VydmVyXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IHtcclxuICAgIGFyZ3M6IHtcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnLS1hbGxvdy1ydW5uaW5nLWluc2VjdXJlLWNvbnRlbnQnLFxyXG4gICAgICAgICctLWFzaC1uby1udWRnZXMnLFxyXG4gICAgICAgICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICAgICAgICctLWJsb2NrLW5ldy13ZWItY29udGVudHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYWNjZWxlcmF0ZWQtMmQtY2FudmFzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtbmV0d29ya2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLXRpbWVyLXRocm90dGxpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZGluZy1vY2NsdWRlZC13aW5kb3dzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJyZWFrcGFkJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNoZWNrZXItaW1hZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jbGllbnQtc2lkZS1waGlzaGluZy1kZXRlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LWV4dGVuc2lvbnMtd2l0aC1iYWNrZ3JvdW5kLXBhZ2VzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICAgICAgICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAgICAgICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZmVhdHVyZXM9Q2FsY3VsYXRlTmF0aXZlV2luT2NjbHVzaW9uLEludGVyZXN0RmVlZENvbnRlbnRTdWdnZXN0aW9ucyxXZWJPVFAnLFxyXG4gICAgICAgICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWlwYy1mbG9vZGluZy1wcm90ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWxvZ2dpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtbm90aWZpY2F0aW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1vZmZlci1zdG9yZS11bm1hc2tlZC13YWxsZXQtY2FyZHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtcG9wdXAtYmxvY2tpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJpbnQtcHJldmlldycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcm9tcHQtb24tcmVwb3N0JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXJlbmRlcmVyLWJhY2tncm91bmRpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2VhcmNoLWVuZ2luZS1jaG9pY2Utc2NyZWVuJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlc3Npb24tY3Jhc2hlZC1idWJibGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2l0ZS1pc29sYXRpb24tdHJpYWxzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICAgICAgICctLWRpc2FibGUtc3luYycsXHJcbiAgICAgICAgJy0tZW5hYmxlLXVuc2FmZS13ZWJncHUnLFxyXG4gICAgICAgICctLWhpZGUtY3Jhc2gtcmVzdG9yZS1idWJibGUnLFxyXG4gICAgICAgICctLWhpZGUtc2Nyb2xsYmFycycsXHJcbiAgICAgICAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgICAgICAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgICAgICAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAgICAgICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICAgICAgICctLW5vLXBpbmdzJyxcclxuICAgICAgICAnLS1uby1zYW5kYm94JyxcclxuICAgICAgICAnLS1uby1zdGFydHVwLXdpbmRvdycsXHJcbiAgICAgICAgJy0tbm8tenlnb3RlJyxcclxuICAgICAgICAnLS1wYXNzd29yZC1zdG9yZT1iYXNpYycsXHJcbiAgICAgICAgJy0tcHJvY2Vzcy1wZXItdGFiJyxcclxuICAgICAgICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuICAgICAgXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ1BVUFBFVEVFUl9BUkdTJyxcclxuICAgICAgY2xpTmFtZTogJ3B1cHBldGVlckFyZ3MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FycmF5IG9mIFB1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfVkVSU0lPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2RuVXJsOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NETl9VUkwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0NETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGbGFnIHRvIHJlZmV0Y2ggc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdG9yeSBwYXRoIGZvciBjYWNoZWQgSGlnaGNoYXJ0cyBzY3JpcHRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DT1JFX1NDUklQVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtb2R1bGVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ3N0b2NrJyxcclxuICAgICAgICAnbWFwJyxcclxuICAgICAgICAnZ2FudHQnLFxyXG4gICAgICAgICdleHBvcnRpbmcnLFxyXG4gICAgICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAgICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgICAgIC8vICdhbm5vdGF0aW9ucy1hZHZhbmNlZCcsXHJcbiAgICAgICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAgICAgJ2Jvb3N0JyxcclxuICAgICAgICAnZGF0YScsXHJcbiAgICAgICAgJ2RhdGEtdG9vbHMnLFxyXG4gICAgICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICAgICAnc3RhdGljLXNjYWxlJyxcclxuICAgICAgICAnYnJva2VuLWF4aXMnLFxyXG4gICAgICAgICdoZWF0bWFwJyxcclxuICAgICAgICAndGlsZW1hcCcsXHJcbiAgICAgICAgJ3RpbGVkd2VibWFwJyxcclxuICAgICAgICAndGltZWxpbmUnLFxyXG4gICAgICAgICd0cmVlbWFwJyxcclxuICAgICAgICAndHJlZWdyYXBoJyxcclxuICAgICAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgICAgICdkcmlsbGRvd24nLFxyXG4gICAgICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICAgICAnYnVsbGV0JyxcclxuICAgICAgICAnZnVubmVsJyxcclxuICAgICAgICAnZnVubmVsM2QnLFxyXG4gICAgICAgICdnZW9oZWF0bWFwJyxcclxuICAgICAgICAncHlyYW1pZDNkJyxcclxuICAgICAgICAnbmV0d29ya2dyYXBoJyxcclxuICAgICAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAgICAgJ3BhcmV0bycsXHJcbiAgICAgICAgJ3BhdHRlcm4tZmlsbCcsXHJcbiAgICAgICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAgICAgJ3ByaWNlLWluZGljYXRvcicsXHJcbiAgICAgICAgJ3NhbmtleScsXHJcbiAgICAgICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICAgICAnZGVwZW5kZW5jeS13aGVlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1sYWJlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1vbi1wb2ludCcsXHJcbiAgICAgICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICAgICAnc29uaWZpY2F0aW9uJyxcclxuICAgICAgICAvLyAnc3RvY2stdG9vbHMnLFxyXG4gICAgICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAgICAgJ3N1bmJ1cnN0JyxcclxuICAgICAgICAndmFyaWFibGUtcGllJyxcclxuICAgICAgICAndmFyaXdpZGUnLFxyXG4gICAgICAgICd2ZWN0b3InLFxyXG4gICAgICAgICd2ZW5uJyxcclxuICAgICAgICAnd2luZGJhcmInLFxyXG4gICAgICAgICd3b3JkY2xvdWQnLFxyXG4gICAgICAgICd4cmFuZ2UnLFxyXG4gICAgICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgICAgICdkcmFnLXBhbmVzJyxcclxuICAgICAgICAnZGVidWdnZXInLFxyXG4gICAgICAgICdkdW1iYmVsbCcsXHJcbiAgICAgICAgJ2xvbGxpcG9wJyxcclxuICAgICAgICAnY3lsaW5kZXInLFxyXG4gICAgICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgICAgICdkb3RwbG90JyxcclxuICAgICAgICAnbWFya2VyLWNsdXN0ZXJzJyxcclxuICAgICAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgICAgICdoZWlraW5hc2hpJyxcclxuICAgICAgICAnZmxvd21hcCcsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAnbmF2aWdhdG9yJyxcclxuICAgICAgICAndGV4dHBhdGgnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIG1vZHVsZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbmRpY2F0b3JTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2luZGljYXRvcnMtYWxsJ10sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGluZGljYXRvciBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjMwLjEvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuNDUvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBZGRpdGlvbmFsIGN1c3RvbSBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgICAgc2VwYXJhdG9yOiAnOydcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5GSUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0IGZpbGVuYW1lIHdpdGggdHlwZSwgZm9ybWF0dGVkIGNvcnJlY3RseSBhcyBKU09OIG9yIFNWRycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9JTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdmVycmlkZXMgdGhlIGBpbmZpbGVgIHdpdGggSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgU1ZHIGlucHV0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgb3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWxpYXMgZm9yIHRoZSBgaW5zdHJgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHN2Zzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TVkcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1NWRyBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IHRvIHJlbmRlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJhdGNoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0JBVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0JhdGNoIGpvYiBzdHJpbmcgd2l0aCBpbnB1dC9vdXRwdXQgcGFpcnM6IFwiaW49b3V0O2luPW91dDsuLi5cIicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG91dGZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1VURklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdXRwdXQgZmlsZW5hbWUgd2l0aCB0eXBlLiBDYW4gYmUganBlZywgcG5nLCBwZGYsIG9yIHN2ZyBhbmQgaWdub3JlcyBgdHlwZWAgb3B0aW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdHlwZToge1xyXG4gICAgICB2YWx1ZTogJ3BuZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1RZUEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0ZpbGUgZXhwb3J0IGZvcm1hdC4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3NlbGVjdCcsXHJcbiAgICAgICAgaGludDogJ0RlZmF1bHQ6IHBuZycsXHJcbiAgICAgICAgY2hvaWNlczogWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvbnN0cjoge1xyXG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfQ09OU1RSJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NoYXJ0IGNvbnN0cnVjdG9yLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBjaGFydCcsXHJcbiAgICAgICAgY2hvaWNlczogWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYjY0OiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0I2NCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byB0aGUgY2hhcnQgc2hvdWxkIGJlIHJlY2VpdmVkIGluIEJhc2U2NCBmb3JtYXQgaW5zdGVhZCBvZiBiaW5hcnknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vRG93bmxvYWQ6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfTk9fRE9XTkxPQUQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gaW5jbHVkZSBvciBleGNsdWRlIGF0dGFjaG1lbnQgaGVhZGVycyBpbiB0aGUgcmVzcG9uc2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgd2lkdGg6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfV0lEVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1dpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGVzIGNoYXJ0IHNldHRpbmdzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzY2FsZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TQ0FMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdTY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncy4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdEhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX0hFSUdIVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRXaWR0aDoge1xyXG4gICAgICB2YWx1ZTogNjAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWZhdWx0IHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0U2NhbGU6IHtcclxuICAgICAgdmFsdWU6IDEsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGVmYXVsdCBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldC4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBtaW46IDAuMSxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGdsb2JhbE9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0dMT0JBTF9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCBnbG9iYWwgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdGhlbWVPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9USEVNRV9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCB0aGVtZSBvcHRpb25zIGZvciBIaWdoY2hhcnRzLnNldE9wdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXN0ZXJpemF0aW9uVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMTUwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaWxsaXNlY29uZHMgdG8gd2FpdCBmb3Igd2VicGFnZSByZW5kZXJpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgY3VzdG9tTG9naWM6IHtcclxuICAgIGFsbG93Q29kZUV4ZWN1dGlvbjoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbGxvd3Mgb3IgZGlzYWxsb3dzIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBkdXJpbmcgZXhwb3J0aW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBpbmplY3Rpb24gb2YgZmlsZXN5c3RlbSByZXNvdXJjZXMgKGRpc2FibGVkIGluIHNlcnZlciBtb2RlKScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tQ29kZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DVVNUT01fQ09ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDdXN0b20gY29kZSB0byBleGVjdXRlIGJlZm9yZSBjaGFydCBpbml0aWFsaXphdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24sIGNvZGUgd3JhcHBlZCBpbiBhIGZ1bmN0aW9uLCBvciBhIC5qcyBmaWxlbmFtZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhbGxiYWNrOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0NBTExCQUNLJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0phdmFTY3JpcHQgY29kZSB0byBydW4gZHVyaW5nIGNvbnN0cnVjdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24gb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX1JFU09VUkNFUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBZGRpdGlvbmFsIHJlc291cmNlcyBhcyBKU09OLCBzdHJpbmdpZmllZCBKU09OLCBvciBmaWxlbmFtZSwgY29udGFpbmluZyBmaWxlcywganMsIGFuZCBjc3Mgc2VjdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsb2FkQ29uZmlnOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHJyxcclxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIHdpdGggYSBwcmUtZGVmaW5lZCBjb25maWd1cmF0aW9uIHRvIHVzZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZUNvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1Byb21wdC1iYXNlZCBvcHRpb24gc2V0dGluZywgc2F2ZWQgdG8gYSBwcm92aWRlZCBjb25maWcgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU3RhcnRzIHRoZSBzZXJ2ZXIgd2hlbiB0cnVlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIHZhbHVlOiAnMC4wLjAuMCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hvc3RuYW1lIG9mIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA3ODAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG51bWJlciBmb3IgdGhlIHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdXBsb2FkTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDMsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX1VQTE9BRF9MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSByZXF1ZXN0IGJvZHkgc2l6ZSBpbiBNQicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGlzcGxheXMgb3Igbm90IGFjdGlvbiBkdXJhdGlvbnMgaW4gbWlsbGlzZWNvbmRzIGR1cmluZyBzZXJ2ZXIgcmVxdWVzdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHByb3h5OiB7XHJcbiAgICAgIGhvc3Q6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfSE9TVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5SG9zdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdIb3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcG9ydDoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgb2YgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRpbWVvdXQ6IHtcclxuICAgICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX1RJTUVPVVQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVRpbWVvdXQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJhdGVMaW1pdGluZzoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdlbmFibGVSYXRlTGltaXRpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzZXJ2ZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBtYXhSZXF1ZXN0czoge1xyXG4gICAgICAgIHZhbHVlOiAxMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAncmF0ZUxpbWl0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgcGVyIG1pbnV0ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIHZhbHVlOiAxLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1cnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGltZSB3aW5kb3cgaW4gbWludXRlcyBmb3IgcmF0ZSBsaW1pdGluZycsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGRlbGF5OiB7XHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdEZWxheSBkdXJhdGlvbiBiZXR3ZWVuIHN1Y2Nlc3NpdmUgcmVxdWVzdHMgYmVmb3JlIHJlYWNoaW5nIHRoZSBsaW1pdCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRydXN0UHJveHk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTZXQgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBza2lwS2V5OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnS2V5IHRvIGJ5cGFzcyB0aGUgcmF0ZSBsaW1pdGVyLCB1c2VkIHdpdGggYHNraXBUb2tlbmAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTicsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUb2tlbiB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwS2V5YCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgU1NMIHByb3RvY29sJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgZm9yY2U6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0ZPUkNFJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsRm9yY2UnLFxyXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdzc2xPbmx5JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0ZvcmNlcyB0aGUgc2VydmVyIHRvIHVzZSBIVFRQUyBvbmx5IHdoZW4gdHJ1ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogNDQzLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX1BPUlQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgZm9yIHRoZSBTU0wgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgY2VydFBhdGg6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0NFUlRfUEFUSCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbENlcnRQYXRoJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsUGF0aCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHRoZSBTU0wgY2VydGlmaWNhdGUva2V5IGZpbGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX01JTl9XT1JLRVJTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaW5pbXVtIGFuZCBpbml0aWFsIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG1heFdvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDgsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NQVhfV09SS0VSUycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICd3b3JrZXJzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdvcmtMaW1pdDoge1xyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9XT1JLX0xJTUlUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdOdW1iZXIgb2YgdGFza3MgYSB3b3JrZXIgY2FuIGhhbmRsZSBiZWZvcmUgcmVzdGFydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWNxdWlyZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBhY3F1aXJpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3JlYXRlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGlkbGVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiAzMDAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0lETEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGRlc3Ryb3lpbmcgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWw6IHtcclxuICAgICAgdmFsdWU6IDIwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYmVmb3JlIHJldHJ5aW5nIHJlc291cmNlIGNyZWF0aW9uIG9uIGZhaWx1cmUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0ludGVydmFsIGluIG1pbGxpc2Vjb25kcyB0byBjaGVjayBhbmQgZGVzdHJveSBpZGxlIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAncG9vbEJlbmNobWFya2luZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU2hvd3Mgc3RhdGlzdGljcyBmb3IgdGhlIHBvb2wgb2YgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfTEVWRUwnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0xvZ2dpbmcgdmVyYm9zaXR5IGxldmVsJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIHJvdW5kOiAwLFxyXG4gICAgICAgIG1pbjogMCxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgdmFsdWU6ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdMb2cgZmlsZSBuYW1lLiBSZXF1aXJlcyBgbG9nVG9GaWxlYCBhbmQgYGxvZ0Rlc3RgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Q6IHtcclxuICAgICAgdmFsdWU6ICdsb2cnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfREVTVCcsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgdG8gYmUgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9Db25zb2xlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0NPTlNPTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9Db25zb2xlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGNvbnNvbGUgbG9nZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9GaWxlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGxvZ2dpbmcgdG8gYSBmaWxlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcm91dGU6IHtcclxuICAgICAgdmFsdWU6ICcvJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9ST1VURScsXHJcbiAgICAgIGNsaU5hbWU6ICd1aVJvdXRlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgZW5kcG9pbnQgcm91dGUgZm9yIHRoZSBVSScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgb3RoZXI6IHtcclxuICAgIG5vZGVFbnY6IHtcclxuICAgICAgdmFsdWU6ICdwcm9kdWN0aW9uJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT0RFX0VOVicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIE5vZGUuanMgZW52aXJvbm1lbnQgdHlwZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvUHJvY2Vzc0V4aXRzOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gYXR0YWNoIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbm9Mb2dvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9fTE9HTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGlzcGxheSBvciBza2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIHN0YXJ0dXAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhhcmRSZXNldFBhZ2U6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9IQVJEX1JFU0VUX1BBR0UnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHJlc2V0IHRoZSBwYWdlIGNvbnRlbnQgZW50aXJlbHknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJyb3dzZXJTaGVsbE1vZGU6IHtcclxuICAgICAgdmFsdWU6IHRydWUsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX0JST1dTRVJfU0hFTExfTU9ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBzaGVsbCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGRlYnVnOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlRGVidWcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIHVuZGVybHlpbmcgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaGVhZGxlc3M6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19IRUFETEVTUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byBzZXQgdGhlIGJyb3dzZXIgdG8gcnVuIGluIGhlYWRsZXNzIG1vZGUgZHVyaW5nIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGV2dG9vbHM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19ERVZUT09MUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBEZXZUb29scyBpbiBoZWFkZnVsIG1vZGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0xJU1RFTl9UT19DT05TT0xFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VuYWJsZXMgb3IgZGlzYWJsZXMgbGlzdGVuaW5nIHRvIGNvbnNvbGUgbWVzc2FnZXMgZnJvbSB0aGUgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZHVtcGlvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRFVNUElPJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1JlZGlyZWN0cyBvciBub3QgYnJvd3NlciBzdGRvdXQgYW5kIHN0ZGVyciB0byBwcm9jZXNzLnN0ZG91dCBhbmQgcHJvY2Vzcy5zdGRlcnInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNsb3dNbzoge1xyXG4gICAgICB2YWx1ZTogMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19TTE9XX01PJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWxheXMgUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBtaWxsaXNlY29uZHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlYnVnZ2luZ1BvcnQ6IHtcclxuICAgICAgdmFsdWU6IDkyMjIsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVCVUdHSU5HX1BPUlQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgdXNlZCBmb3IgZGVidWdnaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vLyBQcm9wZXJ0aWVzIG5lc3RpbmcgbGV2ZWwgb2YgYWxsIG9wdGlvbnNcclxuZXhwb3J0IGNvbnN0IG5lc3RlZFByb3BzID0gX2NyZWF0ZU5lc3RlZFByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLy8gUHJvcGVydGllcyBuYW1lcyB0aGF0IHNob3VsZCBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkXHJcbmV4cG9ydCBjb25zdCBhYnNvbHV0ZVByb3BzID0gX2NyZWF0ZUFic29sdXRlUHJvcHMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgZ2VuZXJhdGVzIGEgbWFwcGluZyBvZiBuZXN0ZWQgYXJndW1lbnQgY2hhaW5zIGZyb20gYSBuZXN0ZWRcclxuICogY29uZmlnIG9iamVjdC4gVGhpcyBmdW5jdGlvbiB0cmF2ZXJzZXMgYSBuZXN0ZWQgb2JqZWN0IGFuZCBjcmVhdGVzIGEgbWFwcGluZ1xyXG4gKiB3aGVyZSBlYWNoIGtleSBpcyBhbiBhcmd1bWVudCBuYW1lIChlaXRoZXIgZnJvbSBgY2xpTmFtZWAsIGBsZWdhY3lOYW1lYCxcclxuICogb3IgdGhlIG9yaWdpbmFsIGtleSkgYW5kIGVhY2ggdmFsdWUgaXMgYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBjaGFpblxyXG4gKiBvZiBuZXN0ZWQgcHJvcGVydGllcyBsZWFkaW5nIHRvIHRoYXQgYXJndW1lbnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtuZXN0ZWRQcm9wcz17fV0gLSBUaGUgYWNjdW11bGF0b3Igb2JqZWN0IGZvciBzdG9yaW5nXHJcbiAqIHRoZSByZXN1bHRpbmcgYXJndW1lbnRzIGNoYWlucy4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtwcm9wQ2hhaW49JyddIC0gVGhlIGN1cnJlbnQgY2hhaW4gb2YgbmVzdGVkIHByb3BlcnRpZXMsXHJcbiAqIHVzZWQgaW50ZXJuYWxseSBkdXJpbmcgcmVjdXJzaW9uLiAgVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgc3RyaW5nLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgbWFwcGluZyBhcmd1bWVudCBuYW1lcyB0byB0aGVpciBjb3JyZXNwb25kaW5nXHJcbiAqIG5lc3RlZCBwcm9wZXJ0eSBjaGFpbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHMoY29uZmlnLCBuZXN0ZWRQcm9wcyA9IHt9LCBwcm9wQ2hhaW4gPSAnJykge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHMgb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBfY3JlYXRlTmVzdGVkUHJvcHMoZW50cnksIG5lc3RlZFByb3BzLCBgJHtwcm9wQ2hhaW59LiR7a2V5fWApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIHRoZSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzXHJcbiAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmNsaU5hbWUgfHwga2V5XSA9IGAke3Byb3BDaGFpbn0uJHtrZXl9YC5zdWJzdHJpbmcoMSk7XHJcblxyXG4gICAgICAvLyBTdXBwb3J0IGZvciB0aGUgbGVnYWN5LCBQaGFudG9tSlMgcHJvcGVydGllcyBuYW1lc1xyXG4gICAgICBpZiAoZW50cnkubGVnYWN5TmFtZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgbmVzdGVkUHJvcHNbZW50cnkubGVnYWN5TmFtZV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb2JqZWN0IHdpdGggbmVzdGVkIGFyZ3VtZW50IGNoYWluc1xyXG4gIHJldHVybiBuZXN0ZWRQcm9wcztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdhdGhlcnMgdGhlIG5hbWVzIG9mIHByb3BlcnRpZXMgZnJvbSBhIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRoYXRcclxuICogY2FuIGJlIHRyZWF0ZWQgYXMgYWJzb2x1dGUgcHJvcGVydGllcy4gVGhlc2UgcHJvcGVydGllcyBoYXZlIHZhbHVlcyB0aGF0XHJcbiAqIGFyZSBvYmplY3RzIGFuZCBkbyBub3QgY29udGFpbiBmdXJ0aGVyIG5lc3RlZCBkZXB0aCB3aGVuIG1lcmdpbmcgYW4gb2JqZWN0XHJcbiAqIGNvbnRhaW5pbmcgdGhlc2Ugb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVBYnNvbHV0ZVByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IFthYnNvbHV0ZVByb3BzPVtdXSAtIEFuIGFycmF5IHRvIGNvbGxlY3QgdGhlIG5hbWVzXHJcbiAqIG9mIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPHN0cmluZz59IEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG5hbWVzIG9mIGFic29sdXRlXHJcbiAqIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wcyhjb25maWcsIGFic29sdXRlUHJvcHMgPSBbXSkge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS50eXBlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHNcclxuICAgICAgX2NyZWF0ZUFic29sdXRlUHJvcHMoZW50cnksIGFic29sdXRlUHJvcHMpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSWYgdGhlIG9wdGlvbiBjYW4gYmUgYW4gb2JqZWN0LCBzYXZlIGl0cyB0eXBlIGluIHRoZSBhcnJheVxyXG4gICAgICBpZiAoZW50cnkudHlwZXMuaW5jbHVkZXMoJ09iamVjdCcpKSB7XHJcbiAgICAgICAgYWJzb2x1dGVQcm9wcy5wdXNoKGtleSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBhcnJheSB3aXRoIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzXHJcbiAgcmV0dXJuIGFic29sdXRlUHJvcHM7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBkZWZhdWx0Q29uZmlnLFxyXG4gIG5lc3RlZFByb3BzLFxyXG4gIGFic29sdXRlUHJvcHNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgZmlsZSBpcyByZXNwb25zaWJsZSBmb3IgcGFyc2luZyB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIHdpdGggdGhlICd6b2QnIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkXHJcbiAqIHRvIGJlIHVzZWQgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIGBlbnZzYC4gV2Ugc2hvdWxkIG5vdCB1c2UgdGhlIGBwcm9jZXNzLmVudmBcclxuICogZGlyZWN0bHkgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuXHJcbiAqIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lclxyXG4gKiBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICovXHJcblxyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5cclxuaW1wb3J0IHsgZGVmYXVsdENvbmZpZyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuLy8gTG9hZCAuZW52IGludG8gZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbi8vIE9iamVjdCB3aXRoIGN1c3RvbSB2YWxpZGF0b3JzIGFuZCB0cmFuc2Zvcm1lcnMsIHRvIGF2b2lkIHJlcGV0aXRpb25cclxuLy8gaW4gdGhlIENvbmZpZyBvYmplY3RcclxuY29uc3QgdiA9IHtcclxuICAvLyBTcGxpdHMgc3RyaW5nIHZhbHVlIGludG8gZWxlbWVudHMgaW4gYW4gYXJyYXksIHRyaW1zIGV2ZXJ5IGVsZW1lbnQsIGNoZWNrc1xyXG4gIC8vIGlmIGFuIGFycmF5IGlzIGNvcnJlY3QsIGlmIGl0IGlzIGVtcHR5LCBhbmQgaWYgaXQgaXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgYXJyYXk6IChmaWx0ZXJBcnJheSkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlXHJcbiAgICAgICAgICAuc3BsaXQoJywnKVxyXG4gICAgICAgICAgLm1hcCgodmFsdWUpID0+IHZhbHVlLnRyaW0oKSlcclxuICAgICAgICAgIC5maWx0ZXIoKHZhbHVlKSA9PiBmaWx0ZXJBcnJheS5pbmNsdWRlcyh2YWx1ZSkpXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZS5sZW5ndGggPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3Mgb25seSB0cnVlLCBmYWxzZSBhbmQgY29ycmVjdGx5IHBhcnNlIHRoZSB2YWx1ZSB0byBib29sZWFuXHJcbiAgLy8gb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSB1bmRlZmluZWRcclxuICBib29sZWFuOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbJ3RydWUnLCAnZmFsc2UnLCAnJ10pXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgPT09ICd0cnVlJyA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3MgcGFzc2VkIHZhbHVlcyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsXHJcbiAgLy8gYmUgdW5kZWZpbmVkXHJcbiAgZW51bTogKHZhbHVlcykgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWy4uLnZhbHVlcywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIFRyaW1zIHRoZSBzdHJpbmcgdmFsdWUgYW5kIGNoZWNrcyBpZiBpdCBpcyBlbXB0eSBvciBjb250YWlucyBzdHJpbmdpZmllZFxyXG4gIC8vIHZhbHVlcyBzdWNoIGFzIGZhbHNlLCB1bmRlZmluZWQsIG51bGwsIE5hTiwgaWYgaXQgZG9lcywgcmV0dXJucyB1bmRlZmluZWRcclxuICBzdHJpbmc6ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgIVsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJ10uaW5jbHVkZXModmFsdWUpIHx8XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSBzdHJpbmcgY29udGFpbnMgZm9yYmlkZGVuIHZhbHVlcywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBvc2l0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIHBvc2l0aXZlTnVtOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBwb3NpdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG5vbi1uZWdhdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlXHJcbiAgLy8gd2lsbCBiZSB1bmRlZmluZWRcclxuICBub25OZWdhdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCksXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSB2YWx1ZSBtdXN0IGJlIG51bWVyaWMgYW5kIG5vbi1uZWdhdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKVxyXG59O1xyXG5cclxuZXhwb3J0IGNvbnN0IENvbmZpZyA9IHoub2JqZWN0KHtcclxuICAvLyBwdXBwZXRlZXJcclxuICBQVVBQRVRFRVJfQVJHUzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEhJR0hDSEFSVFNfVkVSU0lPTiBtdXN0IGJlICdsYXRlc3QnLCBhIG1ham9yIHZlcnNpb24sIG9yIGluIHRoZSBmb3JtIFhYLllZLlpaLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfQ0ROX1VSTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHA6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcbiAgSElHSENIQVJUU19GT1JDRV9GRVRDSDogdi5ib29sZWFuKCksXHJcbiAgSElHSENIQVJUU19DQUNIRV9QQVRIOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQURNSU5fVE9LRU46IHYuc3RyaW5nKCksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNvcmVTY3JpcHRzLnZhbHVlKSxcclxuICBISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLm1vZHVsZVNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuaW5kaWNhdG9yU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcbiAgSElHSENIQVJUU19DVVNUT01fU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jdXN0b21TY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuXHJcbiAgLy8gZXhwb3J0XHJcbiAgRVhQT1JUX0lORklMRTogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfSU5TVFI6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1NWRzogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfQkFUQ0g6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09VVEZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RZUEU6IHYuZW51bShbJ2pwZWcnLCAncG5nJywgJ3BkZicsICdzdmcnXSksXHJcbiAgRVhQT1JUX0NPTlNUUjogdi5lbnVtKFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10pLFxyXG4gIEVYUE9SVF9CNjQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9OT19ET1dOTE9BRDogdi5ib29sZWFuKCksXHJcbiAgRVhQT1JUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1dJRFRIOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfR0xPQkFMX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RIRU1FX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG5cclxuICAvLyBjdXN0b21cclxuICBDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT046IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUzogdi5ib29sZWFuKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NVU1RPTV9DT0RFOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19DQUxMQkFDSzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfUkVTT1VSQ0VTOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gc2VydmVyXHJcbiAgU0VSVkVSX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX0hPU1Q6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfVVBMT0FEX0xJTUlUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX0JFTkNITUFSS0lORzogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIHNlcnZlciBwcm94eVxyXG4gIFNFUlZFUl9QUk9YWV9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QUk9YWV9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1BST1hZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gc2VydmVyIHJhdGUgbGltaXRpbmdcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFk6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU46IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlciBzc2xcclxuICBTRVJWRVJfU1NMX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9GT1JDRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1NTTF9DRVJUX1BBVEg6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHBvb2xcclxuICBQT09MX01JTl9XT1JLRVJTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9NQVhfV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfV09SS19MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfREVTVFJPWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfUkVBUEVSX0lOVEVSVkFMOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBsb2dnZXJcclxuICBMT0dHSU5HX0xFVkVMOiB6XHJcbiAgICAuc3RyaW5nKClcclxuICAgIC50cmltKClcclxuICAgIC5yZWZpbmUoXHJcbiAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZSA9PT0gJycgfHxcclxuICAgICAgICAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPD0gNSksXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgTE9HR0lOR19MRVZFTC4gV2Ugb25seSBhY2NlcHQgdmFsdWVzIGZyb20gMCB0byA1IGFzIGxvZ2dpbmcgbGV2ZWxzLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpLFxyXG4gIExPR0dJTkdfRklMRTogdi5zdHJpbmcoKSxcclxuICBMT0dHSU5HX0RFU1Q6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19UT19DT05TT0xFOiB2LmJvb2xlYW4oKSxcclxuICBMT0dHSU5HX1RPX0ZJTEU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyB1aVxyXG4gIFVJX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgVUlfUk9VVEU6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIG90aGVyXHJcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcclxuICBPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfTk9fTE9HTzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcclxuICBPVEhFUl9CUk9XU0VSX1NIRUxMX01PREU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBkZWJ1Z2dlclxyXG4gIERFQlVHX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfSEVBRExFU1M6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RFVlRPT0xTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19MSVNURU5fVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfRFVNUElPOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19TTE9XX01POiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgREVCVUdfREVCVUdHSU5HX1BPUlQ6IHYucG9zaXRpdmVOdW0oKVxyXG59KTtcclxuXHJcbmV4cG9ydCBjb25zdCBlbnZzID0gQ29uZmlnLnBhcnRpYWwoKS5wYXJzZShwcm9jZXNzLmVudik7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBNYW5hZ2VzIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgYnkgbG9hZGluZ1xyXG4gKiBhbmQgbWVyZ2luZyBvcHRpb25zIGZyb20gbXVsdGlwbGUgc291cmNlcywgc3VjaCBhcyBkZWZhdWx0IHNldHRpbmdzLFxyXG4gKiBlbnZpcm9ubWVudCB2YXJpYWJsZXMsIHVzZXItcHJvdmlkZWQgb3B0aW9ucywgYW5kIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqIEVuc3VyZXMgdGhlIGdsb2JhbCBvcHRpb25zIGFyZSB1cC10by1kYXRlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHkgdmFsdWVzLlxyXG4gKiBQcm92aWRlcyBmdW5jdGlvbnMgZm9yIGFjY2Vzc2luZyBhbmQgdXBkYXRpbmcgY29uZmlndXJhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBkZWVwQ29weSwgZ2V0QWJzb2x1dGVQYXRoLCBpc09iamVjdCB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IHsgYWJzb2x1dGVQcm9wcywgbmVzdGVkUHJvcHMsIGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0T3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PXRydWVdIC0gU3BlY2lmaWVzIHdoZXRoZXIgdG8gcmV0dXJuIGEgY29waWVkXHJcbiAqIG9iamVjdCBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgKGB0cnVlYCkgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCBvciBhIHJlZmVyZW5jZVxyXG4gKiB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoZ2V0Q29weSA9IHRydWUpIHtcclxuICByZXR1cm4gZ2V0Q29weSA/IGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpIDogZ2xvYmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXBkYXRlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBuZXcgb3B0aW9ucyB0byBiZVxyXG4gKiBtZXJnZWQgaW50byB0aGUgZ2xvYmFsIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldENvcHk9ZmFsc2VdIC0gRGV0ZXJtaW5lcyB3aGV0aGVyIHRvIG1lcmdlIHRoZSBuZXdcclxuICogb3B0aW9ucyBpbnRvIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IChgdHJ1ZWApIG9yIGRpcmVjdGx5IGludG9cclxuICogdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0LCBlaXRoZXIgdGhlIG1vZGlmaWVkIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9yIGEgbW9kaWZpZWQgY29weSwgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGBnZXRDb3B5YC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVPcHRpb25zKG5ld09wdGlvbnMsIGdldENvcHkgPSBmYWxzZSkge1xyXG4gIC8vIE1lcmdlIG5ldyBvcHRpb25zIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyBvciBpdHMgY29weSBhbmQgcmV0dXJuIHRoZSByZXN1bHRcclxuICByZXR1cm4gX21lcmdlT3B0aW9ucyhnZXRPcHRpb25zKGdldENvcHkpLCBuZXdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggdmFsdWVzIHByb3ZpZGVkIHRocm91Z2ggdGhlIENMSSwga2VlcGluZ1xyXG4gKiB0aGUgcHJpbmNpcGxlIG9mIG9wdGlvbnMgbG9hZCBwcmlvcml0eS4gVGhpcyBmdW5jdGlvbiBhY2NlcHRzIGEgYGNsaUFyZ3NgXHJcbiAqIGFycmF5IGNvbnRhaW5pbmcgYXJndW1lbnRzIGZyb20gdGhlIENMSSwgd2hpY2ggd2lsbCBiZSB2YWxpZGF0ZWQgYW5kIGFwcGxpZWRcclxuICogaWYgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIFRoZSBwcmlvcml0eSBvcmRlciBmb3Igc2V0dGluZyB2YWx1ZXMgaXM6XHJcbiAqXHJcbiAqIDEuIFZhbHVlcyBmcm9tIGEgY3VzdG9tIEpTT04gZmlsZSAobG9hZGVkIGJ5IHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24pLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSB0aGUgY29tbWFuZCBsaW5lIGludGVyZmFjZSAoQ0xJKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldENsaU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQgbGluZSBhcmd1bWVudHMgdXNlZFxyXG4gKiBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHJlZmxlY3RpbmcgdGhlIG1lcmdlZFxyXG4gKiBjb25maWd1cmF0aW9uIGZyb20gc291cmNlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEkuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0Q2xpT3B0aW9ucyhjbGlBcmdzKSB7XHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzICYmIEFycmF5LmlzQXJyYXkoY2xpQXJncykgJiYgY2xpQXJncy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGxvYWRlZCB2aWEgdGhlIGAtLWxvYWRDb25maWdgXHJcbiAgICBjb25zdCBjb25maWdPcHRpb25zID0gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNvbmZpZ09wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNvbmZpZ09wdGlvbnMpO1xyXG5cclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIENMSVxyXG4gICAgY29uc3QgY2xpT3B0aW9ucyA9IF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncyk7XHJcblxyXG4gICAgLy8gVXBkYXRlIGdsb2JhbCBvcHRpb25zIHdpdGggdGhlIHZhbHVlcyBmcm9tIHRoZSBgY2xpT3B0aW9uc2BcclxuICAgIHVwZGF0ZU9wdGlvbnMoY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnZXRPcHRpb25zKGZhbHNlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG9sZE9wdGlvbnMpKSB7XHJcbiAgICAvLyBJdGVyYXRlIG92ZXIgZWFjaCBrZXktdmFsdWUgcGFpciBpbiB0aGUgb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgdGhlcmUgaXMgYSBuZXN0ZWQgbWFwcGluZywgc3BsaXQgaXQgaW50byBhIHByb3BlcnRpZXMgY2hhaW5cclxuICAgICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNba2V5XVxyXG4gICAgICAgID8gbmVzdGVkUHJvcHNba2V5XS5zcGxpdCgnLicpXHJcbiAgICAgICAgOiBbXTtcclxuXHJcbiAgICAgIC8vIElmIGl0IGlzIHRoZSBsYXN0IHByb3BlcnR5IGluIHRoZSBjaGFpbiwgYXNzaWduIHRoZSB2YWx1ZSwgb3RoZXJ3aXNlLFxyXG4gICAgICAvLyBjcmVhdGUgb3IgcmV1c2UgdGhlIG5lc3RlZCBvYmplY3RcclxuICAgICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cclxuICAgICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgICAgbmV3T3B0aW9uc1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgICdbY29uZmlnXSBObyBjb3JyZWN0IG9iamVjdCB3aXRoIG9wdGlvbnMgd2FzIHByb3ZpZGVkLiBSZXR1cm5pbmcgYW4gZW1wdHkgYXJyYXkuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgbmV3LCBzdHJ1Y3R1cmVkIG9wdGlvbnMgb2JqZWN0XHJcbiAgcmV0dXJuIG5ld09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBWYWxpZGF0ZXMsIHBhcnNlcywgYW5kIGNoZWNrcyBpZiB0aGUgcHJvdmlkZWQgY29uZmlnIGlzIGFsbG93ZWQgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0FsbG93ZWRDb25maWdcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBjb25maWcgLSBUaGUgY29uZmlnIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkIGFzIGEgc2V0XHJcbiAqIG9mIG9wdGlvbnMuIE11c3QgYmUgZWl0aGVyIGFuIG9iamVjdCBvciBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBbdG9TdHJpbmc9ZmFsc2VdIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCB2ZXJzaW9uXHJcbiAqIG9mIHRoZSBwYXJzZWQgY29uZmlnLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFthbGxvd0Z1bmN0aW9ucz1mYWxzZV0gLSBXaGV0aGVyIHRvIGFsbG93IGZ1bmN0aW9uc1xyXG4gKiBpbiB0aGUgcGFyc2VkIGNvbmZpZy4gSWYgdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWQuIE90aGVyd2lzZSwgd2hlblxyXG4gKiBhIGZ1bmN0aW9uIGlzIGZvdW5kLCBudWxsIGlzIHJldHVybmVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFZlcnNpb24obm9Mb2dvID0gZmFsc2UpIHtcclxuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGAuZW52YCBvciBmcm9tIGBwYWNrYWdlLmpzb25gXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuICApLnZlcnNpb247XHJcblxyXG4gIC8vIFByaW50IHRleHQgb25seVxyXG4gIGlmIChub0xvZ28pIHtcclxuICAgIGNvbnNvbGUubG9nKGBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259YCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFByaW50IHRoZSBsb2dvXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAnbXNnJywgJ3N0YXJ0dXAubXNnJyksICd1dGY4JykudG9TdHJpbmcoKVxyXG4gICAgICAgIC5ib2xkLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IGJhc2VkIG9uIHRoZSBwcm92aWRlZFxyXG4gKiBjb25maWd1cmF0aW9uLCBzZXR0aW5nIHZhbHVlcyBmcm9tIG5lc3RlZCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5LlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIChkZWZhdWx0cykuXHJcbiAqIDIuIFZhbHVlcyBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcyAoc3BlY2lmaWVkIGluIHRoZSBgLmVudmAgZmlsZSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaW5pdE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogdGhlIGdsb2JhbCBvcHRpb25zLiBJdCBzaG91bGQgaW5jbHVkZSBuZXN0ZWQgcHJvcGVydGllcyB3aXRoIGEgYHZhbHVlYFxyXG4gKiBhbmQgYW4gYGVudkxpbmtgIGZvciBsaW5raW5nIHRvIGVudmlyb25tZW50IHZhcmlhYmxlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGluaXRpYWxpemVkIGdsb2JhbCBvcHRpb25zIG9iamVjdCwgcG9wdWxhdGVkIHdpdGhcclxuICogdmFsdWVzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHlcclxuICogb3JkZXIuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaW5pdE9wdGlvbnMoY29uZmlnKSB7XHJcbiAgLy8gSW5pdCB0aGUgb2JqZWN0IGZvciBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBTdGFydCBpbml0aWFsaXppbmcgdGhlIGBvcHRpb25zYCBvYmplY3QgcmVjdXJzaXZlbHlcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBpdGVtXSBvZiBPYmplY3QuZW50cmllcyhjb25maWcpKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY29ycmVjdCB2YWx1ZSBiYXNlZCBvbiB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHkgb3JkZXJcclxuICAgICAgaWYgKGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gdW5kZWZpbmVkICYmIGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gbnVsbCkge1xyXG4gICAgICAgIC8vIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdmFsdWVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gZW52c1tpdGVtLmVudkxpbmtdO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRoZSB2YWx1ZSBmcm9tIHRoZSBjb25maWcgZmlsZVxyXG4gICAgICAgIG9wdGlvbnNbbmFtZV0gPSBpdGVtLnZhbHVlO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDcmVhdGUgYSBzZWN0aW9uIGluIHRoZSBvcHRpb25zXHJcbiAgICAgIG9wdGlvbnNbbmFtZV0gPSBfaW5pdE9wdGlvbnMoaXRlbSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNyZWF0ZWQgYG9wdGlvbnNgIG9iamVjdFxyXG4gIHJldHVybiBvcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogTWVyZ2VzIHR3byBzZXRzIG9mIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgY29uc2lkZXJpbmcgYWJzb2x1dGUgcHJvcGVydGllcy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9tZXJnZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9yaWdpbmFsT3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTWVyZ2VkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9ucywgbmV3T3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgb3JpZ2luYWxPcHRpb25zYCBhbmQgYG5ld09wdGlvbnNgIGFyZSBjb3JyZWN0IG9iamVjdHNcclxuICBpZiAoaXNPYmplY3Qob3JpZ2luYWxPcHRpb25zKSAmJiBpc09iamVjdChuZXdPcHRpb25zKSkge1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobmV3T3B0aW9ucykpIHtcclxuICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gPVxyXG4gICAgICAgIGlzT2JqZWN0KHZhbHVlKSAmJlxyXG4gICAgICAgICFhYnNvbHV0ZVByb3BzLmluY2x1ZGVzKGtleSkgJiZcclxuICAgICAgICBvcmlnaW5hbE9wdGlvbnNba2V5XSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICA/IF9tZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zW2tleV0sIHZhbHVlKVxyXG4gICAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgICAgOiBvcmlnaW5hbE9wdGlvbnNba2V5XSB8fCBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBvcmlnaW5hbCAobW9kaWZpZWQgb3Igbm90KSBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIHRoZSBwcm92aWRlZCBvcHRpb25zIG9iamVjdCB0byBhIEpTT04tZm9ybWF0dGVkIHN0cmluZ1xyXG4gKiB3aXRoIHRoZSBvcHRpb24gdG8gcHJlc2VydmUgZnVuY3Rpb25zLiBJbiBvcmRlciBmb3IgYSBmdW5jdGlvblxyXG4gKiB0byBiZSBwcmVzZXJ2ZWQsIGl0IG5lZWRzIHRvIGZvbGxvdyB0aGUgZm9ybWF0IGBmdW5jdGlvbiAoLi4uKSB7Li4ufWAuXHJcbiAqIFN1Y2ggYSBmdW5jdGlvbiBjYW4gYWxzbyBiZSBzdHJpbmdpZmllZC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9vcHRpb25zU3RyaW5naWZ5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IHRvIGJlIGNvbnZlcnRlZCB0byBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0Z1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHByZXNlcnZlZFxyXG4gKiBpbiB0aGUgb3V0cHV0LiBPdGhlcndpc2UgYW4gZXJyb3IgaXMgdGhyb3duLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHN0cmluZ2lmeUZ1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHNhdmVkXHJcbiAqIGFzIHN0cmluZ3MuIFRoZSBgYWxsb3dGdW5jdGlvbnNgIG11c3QgYmUgc2V0IHRvIHRydWUgYXMgd2VsbCBmb3IgdGhpcyB0byB0YWtlXHJcbiAqIGFuIGVmZmVjdC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCB3aGVuIGZ1bmN0aW9ucyBhcmUgbm90IGFsbG93ZWQgYnV0IGFyZVxyXG4gKiBmb3VuZCBpbiBwcm92aWRlZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeShvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucywgc3RyaW5naWZ5RnVuY3Rpb25zKSB7XHJcbiAgY29uc3QgcmVwbGFjZXJDYWxsYmFjayA9IChfLCB2YWx1ZSkgPT4ge1xyXG4gICAgLy8gVHJpbSBzdHJpbmcgdmFsdWVzXHJcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xyXG4gICAgICB2YWx1ZSA9IHZhbHVlLnRyaW0oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBJZiB2YWx1ZSBpcyBhIGZ1bmN0aW9uIG9yIHN0cmluZ2lmaWVkIGZ1bmN0aW9uXHJcbiAgICBpZiAoXHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJyB8fFxyXG4gICAgICAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJykgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIElmIGFsbG93RnVuY3Rpb25zIGlzIHNldCB0byB0cnVlLCBwcmVzZXJ2ZSBmdW5jdGlvbnNcclxuICAgICAgaWYgKGFsbG93RnVuY3Rpb25zKSB7XHJcbiAgICAgICAgLy8gQmFzZWQgb24gdGhlIGBzdHJpbmdpZnlGdW5jdGlvbnNgIG9wdGlvbnMsIHNldCBmdW5jdGlvbiB2YWx1ZXNcclxuICAgICAgICByZXR1cm4gc3RyaW5naWZ5RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IC8vIEFzIHN0cmluZ2lmaWVkIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgXCJFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5cImBcclxuICAgICAgICAgIDogLy8gQXMgZnVuY3Rpb25zXHJcbiAgICAgICAgICAgIGBFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5gO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRocm93IGFuIGVycm9yIG90aGVyd2lzZVxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcigpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSW4gYWxsIG90aGVyIGNhc2VzLCBzaW1wbHkgcmV0dXJuIHRoZSB2YWx1ZVxyXG4gICAgcmV0dXJuIHZhbHVlO1xyXG4gIH07XHJcblxyXG4gIC8vIFN0cmluZ2lmeSBvcHRpb25zIGFuZCBpZiByZXF1aXJlZCwgcmVwbGFjZSBzcGVjaWFsIGZ1bmN0aW9ucyBtYXJrc1xyXG4gIHJldHVybiBKU09OLnN0cmluZ2lmeShvcHRpb25zLCByZXBsYWNlckNhbGxiYWNrKS5yZXBsYWNlQWxsKFxyXG4gICAgc3RyaW5naWZ5RnVuY3Rpb25zID8gL1xcXFxcIkVYUF9GVU58RVhQX0ZVTlxcXFxcIi9nIDogL1wiRVhQX0ZVTnxFWFBfRlVOXCIvZyxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgcHJvdmlkZWQgdmlhXHJcbiAqIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gaW4gdGhlIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgdG8gc2VhcmNoXHJcbiAqIGZvciB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncykge1xyXG4gIC8vIEdldCB0aGUgYWxsb3cgZmxhZ3MgZm9yIHRoZSBjdXN0b20gbG9naWMgY2hlY2tcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiwgYWxsb3dGaWxlUmVzb3VyY2VzIH0gPSBnZXRPcHRpb25zKCkuY3VzdG9tTG9naWM7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiB2YWx1ZVxyXG4gIGNvbnN0IGNvbmZpZ0ZpbGVOYW1lID0gY29uZmlnSW5kZXggPiAtMSAmJiBjbGlBcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lICYmIGFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICByZXR1cm4gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7Y29uZmlnRmlsZU5hbWV9IGZpbGUuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gYWRkaXRpb25hbCBvcHRpb25zIHRvIHJldHVyblxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhcnNlcyBjb21tYW5kLWxpbmUgYXJndW1lbnRzIGFuZCBwYWlycyBlYWNoIGFyZ3VtZW50IHdpdGggaXRzIGNvcnJlc3BvbmRpbmdcclxuICogb3B0aW9uIGluIHRoZSBjb25maWd1cmF0aW9uLiBUaGUgdmFsdWVzIGFyZSBzdHJ1Y3R1cmVkIGludG8gYSBuZXN0ZWQgb3B0aW9uc1xyXG4gKiBvYmplY3QsIGJhc2VkIG9uIHByZWRlZmluZWQgbWFwcGluZ3MuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gbmVzdGVkUHJvcHMgLSBBbiBhcnJheSBvZiBuZXN0aW5nIGxldmVsIGZvciBhbGxcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQtbGluZSBhcmd1bWVudHNcclxuICogY29udGFpbmluZyBvcHRpb25zIGFuZCB0aGVpciBhc3NvY2lhdGVkIHZhbHVlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gdXBkYXRlZCBvcHRpb25zIG9iamVjdCB3aGVyZSBlYWNoIG9wdGlvbiBmcm9tXHJcbiAqIHRoZSBjb21tYW5kLWxpbmUgaXMgcGFpcmVkIHdpdGggaXRzIHZhbHVlLCBzdHJ1Y3R1cmVkIGludG8gbmVzdGVkIG9iamVjdHNcclxuICogYXMgZGVmaW5lZC5cclxuICovXHJcbmZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncykge1xyXG4gIC8vIEFuIGVtcHR5IG9iamVjdCB0byBjb2xsZWN0IGFuZCBzdHJ1Y3R1cml6ZSBkYXRhIGZyb20gdGhlIGFyZ3NcclxuICBjb25zdCBjbGlPcHRpb25zID0ge307XHJcblxyXG4gIC8vIEN5Y2xlIHRocm91Z2ggYWxsIENMSSBhcmdzIGFuZCBmaWx0ZXIgdGhlbVxyXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2xpQXJncy5sZW5ndGg7IGkrKykge1xyXG4gICAgY29uc3Qgb3B0aW9uID0gY2xpQXJnc1tpXS5yZXBsYWNlKC8tL2csICcnKTtcclxuXHJcbiAgICAvLyBGaW5kIHRoZSByaWdodCBwbGFjZSBmb3IgcHJvcGVydHkncyB2YWx1ZVxyXG4gICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZFByb3BzW29wdGlvbl0uc3BsaXQoJy4nKVxyXG4gICAgICA6IFtdO1xyXG5cclxuICAgIC8vIENyZWF0ZSBvcHRpb25zIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIENMSSBmb3IgbGF0ZXIgcGFyc2luZyBhbmQgbWVyZ2luZ1xyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZSgob2JqLCBwcm9wLCBpbmRleCkgPT4ge1xyXG4gICAgICBpZiAocHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4KSB7XHJcbiAgICAgICAgY29uc3QgdmFsdWUgPSBjbGlBcmdzWysraV07XHJcbiAgICAgICAgaWYgKCF2YWx1ZSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NvbmZpZ10gTWlzc2luZyB2YWx1ZSBmb3IgdGhlIENMSSAnLS0ke29wdGlvbn0nIGFyZ3VtZW50LiBVc2luZyB0aGUgZGVmYXVsdCB2YWx1ZS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgICBvYmpbcHJvcF0gPSB2YWx1ZSB8fCBudWxsO1xyXG4gICAgICB9IGVsc2UgaWYgKG9ialtwcm9wXSA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgb2JqW3Byb3BdID0ge307XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcclxuICAgIH0sIGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHBhcnNlZCBDTEkgb3B0aW9uc1xyXG4gIHJldHVybiBjbGlPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgdHJhdmVyc2VzIHRoZSBvcHRpb25zIG9iamVjdCB0byBwcmludCB0aGUgdXNhZ2UgaW5mb3JtYXRpb25cclxuICogZm9yIGVhY2ggb3B0aW9uIGNhdGVnb3J5IGFuZCBpbmRpdmlkdWFsIG9wdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBDTEkgb3B0aW9ucy4gSXQgbWF5XHJcbiAqIGluY2x1ZGUgbmVzdGVkIGNhdGVnb3JpZXMgYW5kIGluZGl2aWR1YWwgb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9ucykge1xyXG4gIGZvciAoY29uc3QgW25hbWUsIG9wdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcclxuICAgIC8vIElmIHRoZSBjdXJyZW50IGVudHJ5IGlzIGEgY2F0ZWdvcnkgYW5kIG5vdCBhIGxlYWYgb3B0aW9uLCByZWN1cnNlIGludG8gaXRcclxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9wdGlvbiwgJ3ZhbHVlJykpIHtcclxuICAgICAgX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb24pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gUHJlcGFyZSBkZXNjcmlwdGlvblxyXG4gICAgICBjb25zdCBkZXNjTmFtZSA9IGAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9YDtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdmFsdWVcclxuICAgICAgbGV0IG9wdGlvblZhbHVlID0gb3B0aW9uLnZhbHVlO1xyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGFycmF5IG9mIHN0cmluZ3NcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nW10nKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID1cclxuICAgICAgICAgICdbJyArIG9wdGlvblZhbHVlLm1hcCgoaXRlbSkgPT4gYCcke2l0ZW19J2ApLmpvaW4oJywgJykgKyAnXSc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhIHN0cmluZ1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmcnKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID0gYCcke29wdGlvblZhbHVlfSdgO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgIGRlc2NOYW1lLmdyZWVuLFxyXG4gICAgICAgIGAkeygnPCcgKyBvcHRpb24udHlwZXMuam9pbignfCcpICsgJz4nKS55ZWxsb3d9YCxcclxuICAgICAgICBgJHtTdHJpbmcob3B0aW9uVmFsdWUpLmJvbGR9YC5ibHVlLFxyXG4gICAgICAgIGAtICR7b3B0aW9uLmRlc2NyaXB0aW9ufS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIHNldENsaU9wdGlvbnMsXHJcbiAgbWFwVG9OZXdPcHRpb25zLFxyXG4gIGlzQWxsb3dlZENvbmZpZyxcclxuICBwcmludExpY2Vuc2UsXHJcbiAgcHJpbnRVc2FnZSxcclxuICBwcmludFZlcnNpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhUVFAgdXRpbGl0eSBtb2R1bGUgZm9yIGZldGNoaW5nIGFuZCBwb3N0aW5nIGRhdGEuIFN1cHBvcnRzIGJvdGhcclxuICogSFRUUCBhbmQgSFRUUFMgcHJvdG9jb2xzLCBwcm92aWRpbmcgbWV0aG9kcyB0byBtYWtlIEdFVCBhbmQgUE9TVCByZXF1ZXN0c1xyXG4gKiB3aXRoIGN1c3RvbWl6YWJsZSBvcHRpb25zLiBJbmNsdWRlcyBwcm90b2NvbCBkZXRlcm1pbmF0aW9uIGJhc2VkIG9uIFVSTFxyXG4gKiBhbmQgYXVnbWVudHMgcmVzcG9uc2Ugb2JqZWN0cyB3aXRoIGEgJ3RleHQnIHByb3BlcnR5IGZvciBlYXNpZXIgZGF0YSBhY2Nlc3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBkYXRhIGZyb20gdGhlIHNwZWNpZmllZCBVUkwgdXNpbmcgZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZmV0Y2hcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZmV0Y2ggZGF0YSBmcm9tLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZldGNoKHVybCwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAuZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIGlmICghcmVzcG9uc2VEYXRhKSB7XHJcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgUE9TVCByZXF1ZXN0IHRvIHRoZSBzcGVjaWZpZWQgVVJMIHdpdGggdGhlIHByb3ZpZGVkIEpTT04gYm9keSB1c2luZ1xyXG4gKiBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIHNlbmQgdGhlIFBPU1QgcmVxdWVzdCB0by5cclxuICogQHBhcmFtIHtPYmplY3R9IFtib2R5PXt9XSAtIFRoZSBKU09OIGJvZHkgdG8gaW5jbHVkZSBpbiB0aGUgUE9TVCByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcG9zdCh1cmwsIGJvZHkgPSB7fSwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoYm9keSk7XHJcblxyXG4gICAgLy8gU2V0IGRlZmF1bHQgaGVhZGVycyBhbmQgbWVyZ2Ugd2l0aCByZXF1ZXN0T3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oXHJcbiAgICAgIHtcclxuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcclxuICAgICAgICBoZWFkZXJzOiB7XHJcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxyXG4gICAgICAgICAgJ0NvbnRlbnQtTGVuZ3RoJzogZGF0YS5sZW5ndGhcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zXHJcbiAgICApO1xyXG5cclxuICAgIGNvbnN0IHJlcXVlc3QgPSBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAucmVxdWVzdCh1cmwsIG9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgIC8vIFdyaXRlIHRoZSByZXF1ZXN0IGJvZHkgYW5kIGVuZCB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC53cml0ZShkYXRhKTtcclxuICAgIHJlcXVlc3QuZW5kKCk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgVVJMLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGRldGVybWluZSB0aGUgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSAoaHR0cCBvciBodHRwcykuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKSB7XHJcbiAgcmV0dXJuIHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZmV0Y2gsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBlcnJvciBjbGFzcyBmb3IgaGFuZGxpbmcgZXhwb3J0LXJlbGF0ZWQgZXJyb3JzLiBFeHRlbmRzIHRoZSBuYXRpdmVcclxuICogYEVycm9yYCBjbGFzcyB0byBpbmNsdWRlIGFkZGl0aW9uYWwgcHJvcGVydGllcyBsaWtlIHN0YXR1cyBjb2RlIGFuZCBzdGFja1xyXG4gKiB0cmFjZSBkZXRhaWxzLlxyXG4gKi9cclxuY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gVGhlIGVycm9yIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkLlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gT3B0aW9uYWwgSFRUUCBzdGF0dXMgY29kZSBhc3NvY2lhdGVkXHJcbiAgICogd2l0aCB0aGUgZXJyb3IgKGUuZy4sIDQwMCwgNTAwKS5cclxuICAgKi9cclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXNDb2RlKSB7XHJcbiAgICBzdXBlcigpO1xyXG5cclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcblxyXG4gICAgaWYgKHN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgb3IgdXBkYXRlcyB0aGUgSFRUUCBzdGF0dXMgY29kZSBmb3IgdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBUaGUgSFRUUCBzdGF0dXMgY29kZSB0byBhc3NpZ24gdG8gdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgYWRkaXRpb25hbCBlcnJvciBkZXRhaWxzIGJhc2VkIG9uIGFuIGV4aXN0aW5nIGVycm9yIG9iamVjdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gQW4gZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgZGV0YWlscyB0byBwb3B1bGF0ZVxyXG4gICAqIHRoZSBgRXhwb3J0RXJyb3JgIGluc3RhbmNlLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG5cclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoZSBjYWNoZSBtYW5hZ2VyIGlzIHJlc3BvbnNpYmxlIGZvciBoYW5kbGluZyBhbmQgbWFuYWdpbmdcclxuICogdGhlIEhpZ2hjaGFydHMgbGlicmFyeSBhbG9uZyB3aXRoIGl0cyBkZXBlbmRlbmNpZXMuIEl0IGVuc3VyZXMgdGhhdCB0aGVzZVxyXG4gKiByZXNvdXJjZXMgYXJlIHN0b3JlZCBhbmQgcmV0cmlldmVkIGVmZmljaWVudGx5IHRvIG9wdGltaXplIHBlcmZvcm1hbmNlXHJcbiAqIGFuZCByZWR1Y2UgcmVkdW5kYW50IG5ldHdvcmsgcmVxdWVzdHMuIFRoZSBjYWNoZSBpcyBzdG9yZWQgaW4gdGhlIGAuY2FjaGVgXHJcbiAqIGRpcmVjdG9yeSBieSBkZWZhdWx0LCB3aGljaCBzZXJ2ZXMgYXMgYSBkZWRpY2F0ZWQgZm9sZGVyIGZvciBrZWVwaW5nIGNhY2hlZFxyXG4gKiBmaWxlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnNcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNhY2hlIHBhdGhcclxuICAgIGNvbnN0IGNhY2hlUGF0aCA9IGdldENhY2hlUGF0aCgpO1xyXG5cclxuICAgIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgICBjb25zdCBtYW5pZmVzdFBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKTtcclxuICAgIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ3NvdXJjZXMuanMnKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcclxuXHJcbiAgICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIHRoZSBgbWFuaWZlc3QuanNvbmAgZG9lcyBub3QgZXhpc3RcclxuICAgIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICAgIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGhpZ2hjaGFydHNPcHRpb25zLmZvcmNlRmV0Y2gpIHtcclxuICAgICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAgIC8vIFJlYWQgdGhlIG1hbmlmZXN0IEpTT05cclxuICAgICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpLCAndXRmOCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG1vZHVsZXMgaXMgYW4gYXJyYXksIGlmIHNvLCB3ZSByZXdyaXRlIGl0IHRvIGEgbWFwIHRvIG1ha2VcclxuICAgICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIGFjdHVhbCBudW1iZXIgb2Ygc2NyaXB0cyB0byBiZSBmZXRjaGVkXHJcbiAgICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9XHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnM7XHJcbiAgICAgIGNvbnN0IG51bWJlck9mTW9kdWxlcyA9XHJcbiAgICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcclxuICAgICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24pIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAgIE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzXHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgX3VwZGF0ZUNhY2hlKFxyXG4gICAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgICBzb3VyY2VQYXRoXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xyXG5cclxuICAgICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEZpbmFsbHksIHNhdmUgdGhlIG5ldyBtYW5pZmVzdCwgd2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjdXJyZW50IGNvbmZpZ1xyXG4gICAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgICBhd2FpdCBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY29uZmlndXJlIGNhY2hlIGFuZCBjcmVhdGUgb3IgdXBkYXRlIHRoZSBjb25maWcgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIFVwZGF0ZSB0byB0aGUgbmV3IHZlcnNpb25cclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICBoaWdoY2hhcnRzOiB7XHJcbiAgICAgIHZlcnNpb246IG5ld1ZlcnNpb25cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgsICd1dGY4Jyk7IC8vICM1NjJcclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBgZmV0Y2hlZE1vZHVsZXNgIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxyXG4gKiB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxyXG4gKiBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuIFRoZSBkZWZhdWx0XHJcbiAqIHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbVxyXG4gKiB3aXRoIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxyXG4gIHNjcmlwdCxcclxuICByZXF1ZXN0T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlcyxcclxuICBzaG91bGRUaHJvd0Vycm9yID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgLmpzIGZyb20gdGhlIGN1c3RvbSBzdHJpbmdzXHJcbiAgaWYgKHNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgIHNjcmlwdCA9IHNjcmlwdC5zdWJzdHJpbmcoMCwgc2NyaXB0Lmxlbmd0aCAtIDMpO1xyXG4gIH1cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gIH1cclxuXHJcbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYCxcclxuICAgICAgNDA0XHJcbiAgICApLnNldEVycm9yKHJlc3BvbnNlKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uLmBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3RcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbZmV0Y2hlZE1vZHVsZXM9e31dIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzT3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMgPSB7fSkge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzXHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihnZXRDYWNoZVBhdGgoKSwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgYHNjcmlwdHNgIGFuZCBgY3VzdG9tU2NyaXB0c2AgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNvcmVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBjb3JlIHNjcmlwdHMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG1vZHVsZVNjcmlwdHMgLSBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGN1c3RvbVNjcmlwdHMgLSBDdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsXHJcbiAqIFVSTHMpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmZXRjaGVkIHNjcmlwdHNcclxuICogY29udGVudCBqb2luZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogc2V0dGluZyBhbiBIVFRQIEFnZW50IGZvciBwcm94eS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHMoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pIHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gc2VydmVyUHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gc2VydmVyUHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiBleGlzdHMsIGFkZCBwcm94eSBhZ2VudCB0byByZXF1ZXN0IG9wdGlvbnNcclxuICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHByb3h5QWdlbnRcclxuICAgID8ge1xyXG4gICAgICAgIGFnZW50OiBwcm94eUFnZW50LFxyXG4gICAgICAgIHRpbWVvdXQ6IHNlcnZlclByb3h5T3B0aW9ucy50aW1lb3V0XHJcbiAgICAgIH1cclxuICAgIDoge307XHJcblxyXG4gIGNvbnN0IGFsbEZldGNoUHJvbWlzZXMgPSBbXHJcbiAgICAuLi5jb3JlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcylcclxuICAgICksXHJcbiAgICAuLi5jdXN0b21TY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucylcclxuICAgIClcclxuICBdO1xyXG5cclxuICBjb25zdCBmZXRjaGVkU2NyaXB0cyA9IGF3YWl0IFByb21pc2UuYWxsKGFsbEZldGNoUHJvbWlzZXMpO1xyXG4gIHJldHVybiBmZXRjaGVkU2NyaXB0cy5qb2luKCc7XFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgcmVwcmVzZW50aW5nXHJcbiAqIHRoZSBmZXRjaGVkIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF91cGRhdGVDYWNoZShoaWdoY2hhcnRzT3B0aW9ucywgc2VydmVyUHJveHlPcHRpb25zLCBzb3VyY2VQYXRoKSB7XHJcbiAgLy8gR2V0IEhpZ2hjaGFydHMgdmVyc2lvbiBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGhjVmVyc2lvbiA9XHJcbiAgICBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uID09PSAnbGF0ZXN0J1xyXG4gICAgICA/IG51bGxcclxuICAgICAgOiBgJHtoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9ufWA7XHJcblxyXG4gIC8vIEdldCB0aGUgQ0ROIHVybCBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGNkblVybCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVybCB8fCBjYWNoZS5jZG5Vcmw7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5zb3VyY2VzID0gYXdhaXQgX2ZldGNoU2NyaXB0cyhcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoYykgPT5cclxuICAgICAgICAgIGhjVmVyc2lvbiA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259LyR7Y31gIDogYCR7Y2RuVXJsfS8ke2N9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vbWFwcy8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tYXBzL21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgOiBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICksXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGkpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb25cclxuICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L3N0b2NrLyR7aGNWZXJzaW9ufS9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9zdG9jay9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldEhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIGV4dHJhY3RWZXJzaW9uLFxyXG4gIGV4dHJhY3RNb2R1bGVOYW1lLFxyXG4gIGdldENhY2hlLFxyXG4gIGdldENhY2hlUGF0aFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWV0aG9kcyBmb3IgaW5pdGlhbGl6aW5nIEhpZ2hjaGFydHMgd2l0aCBjdXN0b21pemVkXHJcbiAqIGFuaW1hdGlvbiBzZXR0aW5ncyBhbmQgdHJpZ2dlcmluZyB0aGUgY3JlYXRpb24gb2YgSGlnaGNoYXJ0cyBjaGFydHMgd2l0aFxyXG4gKiBleHBvcnQtc3BlY2lmaWMgY29uZmlndXJhdGlvbnMgaW4gdGhlIHBhZ2UgY29udGV4dC4gU3VwcG9ydHMgZHluYW1pYyBvcHRpb25cclxuICogbWVyZ2luZywgY3VzdG9tIGxvZ2ljIGluamVjdGlvbiwgYW5kIGNvbnRyb2wgb3ZlciByZW5kZXJpbmcgYmVoYXZpb3JzLiBVc2VkXHJcbiAqIGJ5IHRoZSBQdXBwZXRlZXIgcGFnZS5cclxuICovXHJcblxyXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xyXG5cclxuLyoqXHJcbiAqIFNldHRpbmcgdGhlIGBIaWdoY2hhcnRzLmFuaW1PYmplY3RgIGZ1bmN0aW9uLiBDYWxsZWQgd2hlbiBpbml0aW5nIHRoZSBwYWdlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xyXG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcclxuICAgIHJldHVybiB7IGR1cmF0aW9uOiAwIH07XHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBIaWdoY2hhcnRzIGNoYXJ0IG9uIGEgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVDaGFydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQ2hhcnQoZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgc2V0T3B0aW9ucywgbWVyZ2UsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHRoZSBgdXNlck9wdGlvbnNgIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB0aGUgYHVzZXJPcHRpb25zYCB3aGVuIGBmb3JFeHBvcnRgIGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBTb21lIG1hbmRhdG9yeSBhZGRpdGlvbmFsIGBjaGFydGAgYW5kIGBleHBvcnRpbmdgIG9wdGlvbnNcclxuICBjb25zdCBhZGRpdGlvbmFsT3B0aW9ucyA9IHtcclxuICAgIGNoYXJ0OiB7XHJcbiAgICAgIC8vIEJ5IGRlZmF1bHQgYW5pbWF0aW9uIGlzIGRpc2FibGVkXHJcbiAgICAgIGFuaW1hdGlvbjogZmFsc2UsXHJcbiAgICAgIC8vIEdldCB0aGUgcmlnaHQgc2l6ZSB2YWx1ZXNcclxuICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcclxuICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmluc3RyfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYHRoZW1lT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgdGhlbWVPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9uc31gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb25cclxuICBjb25zdCBnbG9iYWxPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gTWVyZ2UgdGhlIGZvbGxvd2luZyBvcHRpb25zIG9iamVjdHMgdG8gY3JlYXRlIGZpbmFsIG9wdGlvbnNcclxuICBjb25zdCBmaW5hbE9wdGlvbnMgPSBtZXJnZShcclxuICAgIGZhbHNlLFxyXG4gICAgdGhlbWVPcHRpb25zLFxyXG4gICAgdXNlck9wdGlvbnMsXHJcbiAgICAvLyBQbGFjZWQgaXQgaGVyZSBpbnN0ZWFkIGluIHRoZSBpbml0IGJlY2F1c2Ugb2YgdGhlIHNpemUgaXNzdWVzXHJcbiAgICBhZGRpdGlvbmFsT3B0aW9uc1xyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjYWxsYmFja2Agb3B0aW9uXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke2N1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja31gKSgpXHJcbiAgICA6IG51bGw7XHJcblxyXG4gIC8vIFRyaWdnZXIgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbignb3B0aW9ucycsIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbZXhwb3J0T3B0aW9ucy5jb25zdHJdKCdjb250YWluZXInLCBmaW5hbE9wdGlvbnMsIGZpbmFsQ2FsbGJhY2spO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBgc2V0T3B0aW9uc2Agd2FzIHVzZWQgaW4gdGhlIGBjdXN0b21Db2RlYClcclxuICBmb3IgKGNvbnN0IHByb3AgaW4gZGVmYXVsdE9wdGlvbnMpIHtcclxuICAgIGlmICh0eXBlb2YgZGVmYXVsdE9wdGlvbnNbcHJvcF0gIT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgZGVsZXRlIGRlZmF1bHRPcHRpb25zW3Byb3BdO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBkZWZhdWx0IG9wdGlvbnMgYmFja1xyXG4gIHNldE9wdGlvbnMoSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqKTtcclxuXHJcbiAgLy8gRW1wdHkgdGhlIGN1c3RvbSBnbG9iYWwgb3B0aW9ucyBvYmplY3RcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSB7fTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNldHVwSGlnaGNoYXJ0cyxcclxuICBjcmVhdGVDaGFydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZSwgY3JlYXRpbmcgYW5kIGNsZWFyaW5nIHBhZ2VzLCBpbmplY3RpbmcgY3VzdG9tIHJlc291cmNlcyxcclxuICogYW5kIHNldHRpbmcgdXAgSGlnaGNoYXJ0cyBmb3Igc2VydmVyLXNpZGUgcmVuZGVyaW5nLiBUaGUgbW9kdWxlIGVuc3VyZXNcclxuICogdGhhdCByZXNvdXJjZXMgYXJlIGNvcnJlY3RseSBtYW5hZ2VkIGFuZCBjYW4gaGFuZGxlIGZhaWx1cmVzIGR1cmluZ1xyXG4gKiBvcGVyYXRpb25zIGxpa2UgbGF1bmNoaW5nIHRoZSBicm93c2VyIG9yIGNyZWF0aW5nIG5ldyBwYWdlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciBwYWdlc1xyXG5jb25zdCB0ZW1wbGF0ZSA9IHJlYWRGaWxlU3luYyhcclxuICBqb2luKF9fZGlybmFtZSwgJ3RlbXBsYXRlcycsICd0ZW1wbGF0ZS5odG1sJyksXHJcbiAgJ3V0ZjgnXHJcbik7XHJcblxyXG4vLyBUbyBzYXZlIHRoZSBicm93c2VyXHJcbmxldCBicm93c2VyID0gbnVsbDtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QnJvd3NlclxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBubyB2YWxpZCBicm93c2VyXHJcbiAqIGhhcyBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QnJvd3NlcigpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nLCA1MDApO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXJcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgY3JlYXRlZCBQdXBwZXRlZXJcclxuICogYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG1heCByZXRyaWVzIHRvIG9wZW5cclxuICogYSBicm93c2VyIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyXHJcbiAqIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2AgYW5kIGBvdGhlcmAgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWdPcHRpb25zIH0gPSBkZWJ1ZztcclxuXHJcbiAgLy8gTGF1bmNoIG9wdGlvbnMgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgY29uc3QgbGF1bmNoT3B0aW9ucyA9IHtcclxuICAgIGhlYWRsZXNzOiBvdGhlci5icm93c2VyU2hlbGxNb2RlID8gJ3NoZWxsJyA6IHRydWUsXHJcbiAgICB1c2VyRGF0YURpcjogJ3RtcCcsXHJcbiAgICBhcmdzOiBwdXBwZXRlZXJBcmdzIHx8IFtdLFxyXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHSFVQOiBmYWxzZSxcclxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXHJcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXHJcbiAgICAuLi4oZW5hYmxlZERlYnVnICYmIGRlYnVnT3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICAvLyBBIGNvdW50ZXIgZm9yIHRoZSBicm93c2VyJ3MgbGF1bmNoIHJldHJpZXNcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFticm93c2VyXSBBdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeSAkeysrdHJ5Q291bnR9KS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gTGF1bmNoIHRoZSBicm93c2VyXHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgJ1ticm93c2VyXSBGYWlsZWQgdG8gbGF1bmNoIGEgYnJvd3NlciBpbnN0YW5jZS4nXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0cnkgdG8gbGF1bmNoIGJyb3dzZXIgdW50aWwgcmVhY2hpbmcgbWF4IGF0dGVtcHRzXHJcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcclxuICAgICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIFJldHJ5IHRvIG9wZW4gYSBicm93c2VyICgke3RyeUNvdW50fSBvdXQgb2YgMjUpLmApO1xyXG5cclxuICAgICAgICAgIC8vIFdhaXQgZm9yIGEgNCBzZWNvbmRzIGJlZm9yZSB0cnlpbmcgYWdhaW5cclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG5cclxuICAgICAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGxhdW5jaE9wdGlvbnMuaGVhZGxlc3MgPT09ICdzaGVsbCcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIHNoZWxsIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERlYnVnIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChlbmFibGVkRGVidWcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIGRlYnVnIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2Jyb3dzZXJdIE1heGltdW0gcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlciBpbnN0YW5jZSByZWFjaGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nLCA1MDApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xvc2VCcm93c2VyXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2VCcm93c2VyKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIgJiYgYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgYnJvd3NlciA9IG51bGw7XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgcGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHRoZSBgX3NldFBhZ2VDb250ZW50KClgLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBuZXdQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIGBpZGAsXHJcbiAqIGB3b3JrQ291bnRgLCBhbmQgYHBhZ2VgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjb25uZWN0ZWQgb3IgaWYgYSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG5ld1BhZ2UocG9vbFJlc291cmNlKSB7XHJcbiAgLy8gRXJyb3IgaW4gY2FzZSBvZiBubyBjb25uZWN0ZWQgYnJvd3NlclxyXG4gIGlmICghYnJvd3NlciB8fCAhYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW2Jyb3dzZXJdIEJyb3dzZXIgaXMgbm90IHlldCBjb25uZWN0ZWQuYCwgNTAwKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBwb29sUmVzb3VyY2UucGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBEaXNhYmxlIGNhY2hlXHJcbiAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXHJcbiAgX3NldFBhZ2VFdmVudHMocG9vbFJlc291cmNlLnBhZ2UpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgcGFnZSBpcyBjb3JyZWN0bHkgY3JlYXRlZFxyXG4gIGlmICghcG9vbFJlc291cmNlLnBhZ2UgfHwgcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgdGhlIGNvbnRlbnQgb2YgYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG1vZGUuIExvZ3NcclxuICogdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIG9mIGEgcGFnZSdzIGNvbnRlbnQgZmFpbHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIHBhZ2UgYW5kIGlkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtoYXJkUmVzZXQ9ZmFsc2VdIC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gYGFib3V0OmJsYW5rYCBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgd2hlbiBwYWdlXHJcbiAqIGlzIGNvcnJlY3RseSBjbGVhcmVkIGFuZCBmYWxzZSB3aGVuIGl0IGlzIG5vdC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2UocG9vbFJlc291cmNlLCBoYXJkUmVzZXQgPSBmYWxzZSkge1xyXG4gIHRyeSB7XHJcbiAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGBhYm91dDpibGFua2BcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHtcclxuICAgICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBfc2V0UGFnZUNvbnRlbnQocG9vbFJlc291cmNlLnBhZ2UpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gQ29udGVudCBvZiB0aGUgcGFnZSBjb3VsZCBub3QgYmUgY2xlYXJlZC5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICBwb29sUmVzb3VyY2Uud29ya0NvdW50ID0gZ2V0T3B0aW9ucygpLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICB9XHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyBjdXN0b20gSlMgYW5kIENTUyByZXNvdXJjZXMgdG8gYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYWRkUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9IGZpbGUuc3RhcnRzV2l0aCgnaHR0cCcpID8gZmFsc2UgOiB0cnVlO1xyXG5cclxuICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gc2NyaXB0IGZyb20gcmVzb3VyY2VzJyBmaWxlc1xyXG4gICAgICAgIGluamVjdGVkSnMucHVzaChcclxuICAgICAgICAgIGlzTG9jYWxcclxuICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICBjb250ZW50OiByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGZpbGUpLCAndXRmOCcpXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA6IHtcclxuICAgICAgICAgICAgICAgIHVybDogZmlsZVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgZm9yIChjb25zdCBqc1Jlc291cmNlIG9mIGluamVjdGVkSnMpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU2NyaXB0VGFnKGpzUmVzb3VyY2UpKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gVGhlIEpTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGluamVjdGVkSnMubGVuZ3RoID0gMDtcclxuXHJcbiAgICAvLyBMb2FkIENTU1xyXG4gICAgY29uc3QgaW5qZWN0ZWRDc3MgPSBbXTtcclxuICAgIGlmIChyZXNvdXJjZXMuY3NzKSB7XHJcbiAgICAgIGxldCBjc3NJbXBvcnRzID0gcmVzb3VyY2VzLmNzcy5tYXRjaCgvQGltcG9ydFxccyooW147XSopOy9nKTtcclxuICAgICAgaWYgKGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAvLyBIYW5kbGUgY3NzIHNlY3Rpb25cclxuICAgICAgICBmb3IgKGxldCBjc3NJbXBvcnRQYXRoIG9mIGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoKSB7XHJcbiAgICAgICAgICAgIGNzc0ltcG9ydFBhdGggPSBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ3VybCgnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgnQGltcG9ydCcsICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvJy9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvOy8sICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cXCkvZywgJycpXHJcbiAgICAgICAgICAgICAgLnRyaW0oKTtcclxuXHJcbiAgICAgICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBjc3MgZnJvbSByZXNvdXJjZXNcclxuICAgICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGguc3RhcnRzV2l0aCgnaHR0cCcpKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICBwYXRoOiBnZXRBYnNvbHV0ZVBhdGgoY3NzSW1wb3J0UGF0aClcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVGhlIHJlc3Qgb2YgdGhlIENTUyBzZWN0aW9uIHdpbGwgYmUgY29udGVudCBieSBub3dcclxuICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmNzcy5yZXBsYWNlKC9AaW1wb3J0XFxzKihbXjtdKik7L2csICcnKSB8fCAnICdcclxuICAgICAgfSk7XHJcblxyXG4gICAgICBmb3IgKGNvbnN0IGNzc1Jlc291cmNlIG9mIGluamVjdGVkQ3NzKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyhjc3NSZXNvdXJjZSkpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICBgW2Jyb3dzZXJdIFRoZSBDU1MgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBpbmplY3RlZENzcy5sZW5ndGggPSAwO1xyXG4gICAgfVxyXG4gIH1cclxuICByZXR1cm4gaW5qZWN0ZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgb3V0IGFsbCBzdGF0ZSBzZXQgb24gdGhlIHBhZ2Ugd2l0aCBgYWRkU2NyaXB0VGFnYCBhbmQgYGFkZFN0eWxlVGFnYC5cclxuICogUmVtb3ZlcyBpbmplY3RlZCByZXNvdXJjZXMgYW5kIHJlc2V0cyBDU1MgYW5kIHNjcmlwdCB0YWdzIG9uIHRoZSBwYWdlLlxyXG4gKiBBZGRpdGlvbmFsbHksIGl0IGRlc3Ryb3lzIHByZXZpb3VzbHkgZXhpc3RpbmcgY2hhcnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgZnJvbSB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxPYmplY3Q+fSBpbmplY3RlZFJlc291cmNlcyAtIEFycmF5IG9mIGluamVjdGVkIHJlc291cmNlc1xyXG4gKiB0byBiZSBjbGVhcmVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcykge1xyXG4gIHRyeSB7XHJcbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlc291cmNlLmRpc3Bvc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IG9sZCBjaGFydHMgYWZ0ZXIgZXhwb3J0IGlzIGRvbmUgYW5kIHJlc2V0IGFsbCBDU1MgYW5kIHNjcmlwdCB0YWdzXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgLy8gV2UgYXJlIG5vdCBndWFyYW50ZWVkIHRoYXQgSGlnaGNoYXJ0cyBpcyBsb2FkZWQsIHdoZW4gZG9pbmcgU1ZHIGV4cG9ydHNcclxuICAgICAgaWYgKHR5cGVvZiBIaWdoY2hhcnRzICE9PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgIGNvbnN0IG9sZENoYXJ0cyA9IEhpZ2hjaGFydHMuY2hhcnRzO1xyXG5cclxuICAgICAgICAvLyBDaGVjayBpbiBhbnkgYWxyZWFkeSBleGlzdGluZyBjaGFydHNcclxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShvbGRDaGFydHMpICYmIG9sZENoYXJ0cy5sZW5ndGgpIHtcclxuICAgICAgICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0c1xyXG4gICAgICAgICAgZm9yIChjb25zdCBvbGRDaGFydCBvZiBvbGRDaGFydHMpIHtcclxuICAgICAgICAgICAgb2xkQ2hhcnQgJiYgb2xkQ2hhcnQuZGVzdHJveSgpO1xyXG4gICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgICAgSGlnaGNoYXJ0cy5jaGFydHMuc2hpZnQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gQ291bGQgbm90IGNsZWFyIHBhZ2UncyByZXNvdXJjZXMuYCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgY29udGVudCBmb3IgYSBQdXBwZXRlZXIgUGFnZSB1c2luZyBhIHByZWRlZmluZWQgdGVtcGxhdGVcclxuICogYW5kIGFkZGl0aW9uYWwgc2NyaXB0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBjb250ZW50XHJcbiAqIGlzIGJlaW5nIHNldC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zZXRQYWdlQ29udGVudChwYWdlKSB7XHJcbiAgLy8gU2V0IHRoZSBpbml0aWFsIHBhZ2UgY29udGVudFxyXG4gIGF3YWl0IHBhZ2Uuc2V0Q29udGVudCh0ZW1wbGF0ZSwgeyB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJyB9KTtcclxuXHJcbiAgLy8gQWRkIGFsbCByZWdpc3RlcmVkIEhpZ2NoYXJ0cyBzY3JpcHRzLCBxdWl0ZSBkZW1hbmRpbmdcclxuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdzb3VyY2VzLmpzJykgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBgYW5pbU9iamVjdGAgZm9yIEhpZ2hjaGFydHNcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKHNldHVwSGlnaGNoYXJ0cyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgZXZlbnRzIChsaWtlIGBwYWdlZXJyb3JgIGFuZCBgY29uc29sZWApIGZvciBhIFB1cHBldGVlciBQYWdlIGluIG9yZGVyXHJcbiAqIHRvIGNhdGNoIGFuZCBkaXNwbGF5IGVycm9ycyBhbmQgY29uc29sZSBsb2dzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VFdmVudHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBsaXN0ZW5lcnNcclxuICogYXJlIGJlaW5nIHNldC5cclxuICovXHJcbmZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzKHBhZ2UpIHtcclxuICAvLyBHZXQgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBkZWJ1ZyB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBTZXQgdGhlIGBwYWdlZXJyb3JgIGxpc3RlbmVyXHJcbiAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKCkgPT4ge1xyXG4gICAgLy8gSXQgd291bGQgc2VlbSBsaWtlIHRoaXMgbWF5IGZpcmUgYXQgdGhlIHNhbWUgdGltZSBvciBzaG9ydGx5IGJlZm9yZVxyXG4gICAgLy8gYSBwYWdlIGlzIGNsb3NlZC5cclxuICAgIGlmIChwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGBjb25zb2xlYCBsaXN0ZW5lciwgaWYgbmVlZGVkXHJcbiAgaWYgKGRlYnVnLmVuYWJsZSAmJiBkZWJ1Zy5saXN0ZW5Ub0NvbnNvbGUpIHtcclxuICAgIHBhZ2Uub24oJ2NvbnNvbGUnLCAobWVzc2FnZSkgPT4ge1xyXG4gICAgICBjb25zb2xlLmxvZyhgW2RlYnVnXSAke21lc3NhZ2UudGV4dCgpfWApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0QnJvd3NlcixcclxuICBjcmVhdGVCcm93c2VyLFxyXG4gIGNsb3NlQnJvd3NlcixcclxuICBuZXdQYWdlLFxyXG4gIGNsZWFyUGFnZSxcclxuICBhZGRQYWdlUmVzb3VyY2VzLFxyXG4gIGNsZWFyUGFnZVJlc291cmNlc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgQ1NTIHRvIGJlIHVzZWQgb24gdGhlIGV4cG9ydGVkIHBhZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBDU1MgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0ICgpID0+IGBcclxuXHJcbmh0bWwsIGJvZHkge1xyXG4gIG1hcmdpbjogMDtcclxuICBwYWRkaW5nOiAwO1xyXG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XHJcbn1cclxuXHJcbiN0YWJsZS1kaXYsICNzbGlkZXJzLCAjZGF0YXRhYmxlLCAjY29udHJvbHMsIC5sZC1yb3cge1xyXG4gIGRpc3BsYXk6IG5vbmU7XHJcbiAgaGVpZ2h0OiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyIHtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG4gIG1hcmdpbjogMDtcclxuICBvdmVyZmxvdzogYXV0bztcclxuICBmb250LXNpemU6IDA7XHJcbn1cclxuXHJcbiNjaGFydC1jb250YWluZXIgPiBmaWd1cmUsIGRpdiB7XHJcbiAgbWFyZ2luLXRvcDogMCAhaW1wb3J0YW50O1xyXG4gIG1hcmdpbi1ib3R0b206IDAgIWltcG9ydGFudDtcclxufVxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY3NzVGVtcGxhdGUgZnJvbSAnLi9jc3MuanMnO1xyXG5cclxuLyoqXHJcbiAqIFRoZSBTVkcgdGVtcGxhdGUgdG8gdXNlIHdoZW4gbG9hZGluZyBTVkcgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHN2ZyAtIFRoZSBTVkcgaW5wdXQgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIFNWRyB0ZW1wbGF0ZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChzdmcpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNoYXJ0cyBFeHBvcnQ8L3RpdGxlPlxyXG4gIDwvaGVhZD5cclxuICA8c3R5bGU+XHJcbiAgICAke2Nzc1RlbXBsYXRlKCl9XHJcbiAgPC9zdHlsZT5cclxuICA8Ym9keT5cclxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cclxuICAgICAgJHtzdmd9XHJcbiAgICA8L2Rpdj5cclxuICA8L2JvZHk+XHJcbjwvaHRtbD5cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBoYW5kbGVzIGNoYXJ0IGV4cG9ydCBmdW5jdGlvbmFsaXR5IHVzaW5nIFB1cHBldGVlci5cclxuICogSXQgc3VwcG9ydHMgZXhwb3J0aW5nIGNoYXJ0cyBhcyBTVkcsIFBORywgSlBFRywgYW5kIFBERiBmb3JtYXRzLiBUaGUgbW9kdWxlXHJcbiAqIG1hbmFnZXMgcGFnZSByZXNvdXJjZXMsIHNldHMgdXAgdGhlIGV4cG9ydCBlbnZpcm9ubWVudCwgYW5kIHByb2Nlc3NlcyBjaGFydFxyXG4gKiBjb25maWd1cmF0aW9ucyBvciBTVkcgaW5wdXRzIGZvciByZW5kZXJpbmcuIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZVxyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYWRkUGFnZVJlc291cmNlcywgY2xlYXJQYWdlUmVzb3VyY2VzIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgY3JlYXRlQ2hhcnQgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgc3ZnVGVtcGxhdGUgZnJvbSAnLi4vdGVtcGxhdGVzL3N2Z0V4cG9ydC9zdmdFeHBvcnQuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2UgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTwoc3RyaW5nfEJ1ZmZlcnxFeHBvcnRFcnJvcik+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlc1xyXG4gKiB0byB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgZXhwb3J0IHRvIGFuIHVuc3VwcG9ydGVkXHJcbiAqIG91dHB1dCBmb3JtYXQgb2NjdXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydChwYWdlLCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXkgKGFkZGl0aW9uYWwgSlMgYW5kIENTUylcclxuICBjb25zdCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICB0cnkge1xyXG4gICAgbGV0IGlzU1ZHID0gZmFsc2U7XHJcblxyXG4gICAgLy8gRGVjaWRlIG9uIHRoZSBleHBvcnQgbWV0aG9kXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy5zdmcpIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgYHR5cGVgIGlzIGFsc28gU1ZHLCByZXR1cm4gdGhlIGlucHV0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGV4cG9ydE9wdGlvbnMuc3ZnO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBNYXJrIGFzIFNWRyBleHBvcnQgZm9yIHRoZSBsYXRlciBzaXplIGNvcnJlY3Rpb25zXHJcbiAgICAgIGlzU1ZHID0gdHJ1ZTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRcclxuICAgICAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKGV4cG9ydE9wdGlvbnMuc3ZnKSwge1xyXG4gICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgIH0pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBKU09OIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoY3JlYXRlQ2hhcnQsIGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXHJcbiAgICAvLyBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAuLi4oYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBjdXN0b21Mb2dpY09wdGlvbnMpKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZSBhbmQgc2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XHJcbiAgICBjb25zdCBzaXplID0gaXNTVkdcclxuICAgICAgPyBhd2FpdCBwYWdlLmV2YWx1YXRlKChzY2FsZSkgPT4ge1xyXG4gICAgICAgICAgY29uc3Qgc3ZnRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXHJcbiAgICAgICAgICAgICcjY2hhcnQtY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJ1xyXG4gICAgICAgICAgKTtcclxuXHJcbiAgICAgICAgICAvLyBHZXQgdGhlIHZhbHVlcyBjb3JyZWN0bHkgc2NhbGVkXHJcbiAgICAgICAgICBjb25zdCBjaGFydEhlaWdodCA9IHN2Z0VsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0V2lkdGggPSBzdmdFbGVtZW50LndpZHRoLmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBJbiBjYXNlIG9mIFNWRyB0aGUgem9vbSBtdXN0IGJlIHNldCBkaXJlY3RseSBmb3IgYm9keSBhcyBzY2FsZVxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBTZXQgdGhlIG1hcmdpbiB0byAwcHhcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcclxuXHJcbiAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICBjaGFydEhlaWdodCxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aFxyXG4gICAgICAgICAgfTtcclxuICAgICAgICB9LCBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpKVxyXG4gICAgICA6IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBjb25zdCB7IGNoYXJ0SGVpZ2h0LCBjaGFydFdpZHRoIH0gPSB3aW5kb3cuSGlnaGNoYXJ0cy5jaGFydHNbMF07XHJcblxyXG4gICAgICAgICAgLy8gTm8gbmVlZCBmb3Igc3VjaCBzY2FsZSBtYW5pcHVsYXRpb24gaW4gY2FzZSBvZiBvdGhlciB0eXBlc1xyXG4gICAgICAgICAgLy8gb2YgZXhwb3J0cy4gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjbGlwIHJlZ2lvbiBmb3IgdGhlIHBhZ2VcclxuICAgIGNvbnN0IHsgeCwgeSB9ID0gYXdhaXQgX2dldENsaXBSZWdpb24ocGFnZSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGBoZWlnaHRgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRIZWlnaHQgfHwgZXhwb3J0T3B0aW9ucy5oZWlnaHQpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgd2lkdGhgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRXaWR0aCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcclxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgIH0pO1xyXG5cclxuICAgIGxldCByZXN1bHQ7XHJcbiAgICAvLyBSYXN0ZXJpemF0aW9uIHByb2Nlc3NcclxuICAgIHN3aXRjaCAoZXhwb3J0T3B0aW9ucy50eXBlKSB7XHJcbiAgICAgIGNhc2UgJ3N2Zyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVNWRyhwYWdlKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncG5nJzpcclxuICAgICAgY2FzZSAnanBlZyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZUltYWdlKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMudHlwZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIHgsXHJcbiAgICAgICAgICAgIHlcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncGRmJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlUERGKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBkZWZhdWx0OlxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBVbnN1cHBvcnRlZCBvdXRwdXQgZm9ybWF0OiAke2V4cG9ydE9wdGlvbnMudHlwZX0uYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2xlYXIgcHJldmlvdXNseSBpbmplY3RlZCBKUyBhbmQgQ1NTIHJlc291cmNlc1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMgb2YgdGhlIHNwZWNpZmllZCBwYWdlIGVsZW1lbnRcclxuICogd2l0aCB0aGUgJ2NoYXJ0LWNvbnRhaW5lcicgaWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2dldENsaXBSZWdpb25cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGB4YCwgYHlgLCBgd2lkdGhgLCBhbmQgYGhlaWdodGAgcHJvcGVydGllcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XHJcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB4LFxyXG4gICAgICB5LFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXHJcbiAgICB9O1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBTVkcgYnkgZXZhbHVhdGluZyB0aGUgYG91dGVySFRNTGAgb2YgdGhlIGZpcnN0ICdzdmcnIGVsZW1lbnRcclxuICogaW5zaWRlIGFuIGVsZW1lbnQgd2l0aCB0aGUgaWQgJ2NvbnRhaW5lcicuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVNWR1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFNWRyBzdHJpbmcuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlU1ZHKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbChcclxuICAgICcjY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJyxcclxuICAgIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIGltYWdlIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgYHNjcmVlbnNob3RgIGZ1bmN0aW9uYWxpdHkgd2l0aFxyXG4gKiBzcGVjaWZpZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlSW1hZ2VcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gSW1hZ2UgdHlwZS5cclxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgaW1hZ2UgYnVmZmVyXHJcbiAqIG9yIHJlamVjdGluZyB3aXRoIGFuIGBFeHBvcnRFcnJvcmAgZm9yIHRpbWVvdXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlSW1hZ2UocGFnZSwgdHlwZSwgY2xpcCwgcmFzdGVyaXphdGlvblRpbWVvdXQpIHtcclxuICByZXR1cm4gUHJvbWlzZS5yYWNlKFtcclxuICAgIHBhZ2Uuc2NyZWVuc2hvdCh7XHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGNsaXAsXHJcbiAgICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgICAgZnVsbFBhZ2U6IGZhbHNlLFxyXG4gICAgICBvcHRpbWl6ZUZvclNwZWVkOiB0cnVlLFxyXG4gICAgICBjYXB0dXJlQmV5b25kVmlld3BvcnQ6IHRydWUsXHJcbiAgICAgIC4uLih0eXBlICE9PSAncG5nJyA/IHsgcXVhbGl0eTogODAgfSA6IHt9KSxcclxuICAgICAgLy8gQWx3YXlzIHJlbmRlciBvbiBhIHRyYW5zcGFyZW50IHBhZ2UgaWYgdGhlIGV4cGVjdGVkIHR5cGUgZm9ybWF0IGlzIFBOR1xyXG4gICAgICBvbWl0QmFja2dyb3VuZDogdHlwZSA9PSAncG5nJyAvLyAjNDQ3LCAjNDYzXHJcbiAgICB9KSxcclxuICAgIG5ldyBQcm9taXNlKChfcmVzb2x2ZSwgcmVqZWN0KSA9PlxyXG4gICAgICBzZXRUaW1lb3V0KFxyXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcsIDQwOCkpLFxyXG4gICAgICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICAgICAgKVxyXG4gICAgKVxyXG4gIF0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIFBERiB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBwZGZgIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlUERGXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0IC0gUERGIGhlaWdodC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoIC0gUERGIHdpZHRoLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFBERiBidWZmZXIuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlUERGKHBhZ2UsIGhlaWdodCwgd2lkdGgsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgYXdhaXQgcGFnZS5lbXVsYXRlTWVkaWFUeXBlKCdzY3JlZW4nKTtcclxuICByZXR1cm4gcGFnZS5wZGYoe1xyXG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXHJcbiAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICB3aWR0aCxcclxuICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgIHRpbWVvdXQ6IHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHB1cHBldGVlckV4cG9ydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgYSB3b3JrZXIgcG9vbCBpbXBsZW1lbnRhdGlvbiBmb3IgbWFuYWdpbmdcclxuICogdGhlIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHBhZ2VzLCBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIHVzZSB3aXRoXHJcbiAqIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuIEl0IG9wdGltaXplcyByZXNvdXJjZXMgdXNhZ2UgYW5kIHBlcmZvcm1hbmNlXHJcbiAqIGJ5IG1haW50YWluaW5nIGEgcG9vbCBvZiB3b3JrZXJzIHRoYXQgY2FuIGhhbmRsZSBjb25jdXJyZW50IGV4cG9ydCB0YXNrc1xyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgUG9vbCB9IGZyb20gJ3Rhcm4nO1xyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBjcmVhdGVCcm93c2VyLCBjbG9zZUJyb3dzZXIsIG5ld1BhZ2UsIGNsZWFyUGFnZSB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IHB1cHBldGVlckV4cG9ydCB9IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldE5ld0RhdGVUaW1lLCBtZWFzdXJlVGltZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBwb29sIGluc3RhbmNlXHJcbmxldCBwb29sID0gbnVsbDtcclxuXHJcbi8vIFBvb2wgc3RhdGlzdGljc1xyXG5jb25zdCBwb29sU3RhdHMgPSB7XHJcbiAgZXhwb3J0c0F0dGVtcHRlZDogMCxcclxuICBleHBvcnRzUGVyZm9ybWVkOiAwLFxyXG4gIGV4cG9ydHNEcm9wcGVkOiAwLFxyXG4gIGV4cG9ydHNGcm9tU3ZnOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uczogMCxcclxuICBleHBvcnRzRnJvbVN2Z0F0dGVtcHRzOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzOiAwLFxyXG4gIHRpbWVTcGVudDogMCxcclxuICB0aW1lU3BlbnRBdmVyYWdlOiAwXHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwb29sIHdpdGggdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24sIGNyZWF0aW5nXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhbmQgc2V0dGluZyB1cCB3b3JrZXIgcmVzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGluaXRQb29sXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlclxyXG4gKiBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBlbmRpbmcgdGhlIGZ1bmN0aW9uXHJcbiAqIGV4ZWN1dGlvbiB3aGVuIGFuIGFscmVhZHkgaW5pdGlhbGl6ZWQgcG9vbCBvZiByZXNvdXJjZXMgaXMgZm91bmQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY3JlYXRlIHRoZSBwb29sXHJcbiAqIG9mIHdvcmtlcnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdFBvb2wocG9vbE9wdGlvbnMsIHB1cHBldGVlckFyZ3MpIHtcclxuICAvLyBDcmVhdGUgYSBicm93c2VyIGluc3RhbmNlIHdpdGggdGhlIHB1cHBldGVlciBhcmd1bWVudHNcclxuICBhd2FpdCBjcmVhdGVCcm93c2VyKHB1cHBldGVlckFyZ3MpO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIEluaXRpYWxpemluZyBwb29sIHdpdGggd29ya2VyczogbWluICR7cG9vbE9wdGlvbnMubWluV29ya2Vyc30sIG1heCAke3Bvb2xPcHRpb25zLm1heFdvcmtlcnN9LmBcclxuICAgICk7XHJcblxyXG4gICAgaWYgKHBvb2wpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXAgYW4gZXllIG9uIGEgY29ycmVjdCBtaW4gYW5kIG1heCB3b3JrZXJzIG51bWJlclxyXG4gICAgaWYgKHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzKSB7XHJcbiAgICAgIHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPSBwb29sT3B0aW9ucy5tYXhXb3JrZXJzO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENyZWF0ZSBhIHBvb2wgYWxvbmcgd2l0aCBhIG1pbmltYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgcG9vbCA9IG5ldyBQb29sKHtcclxuICAgICAgLy8gR2V0IHRoZSBgY3JlYXRlYCwgYHZhbGlkYXRlYCwgYW5kIGBkZXN0cm95YCBmdW5jdGlvbnNcclxuICAgICAgLi4uX2ZhY3RvcnkocG9vbE9wdGlvbnMpLFxyXG4gICAgICBtaW46IHBvb2xPcHRpb25zLm1pbldvcmtlcnMsXHJcbiAgICAgIG1heDogcG9vbE9wdGlvbnMubWF4V29ya2VycyxcclxuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmFjcXVpcmVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVUaW1lb3V0LFxyXG4gICAgICBkZXN0cm95VGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuZGVzdHJveVRpbWVvdXQsXHJcbiAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5pZGxlVGltZW91dCxcclxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlUmV0cnlJbnRlcnZhbCxcclxuICAgICAgcmVhcEludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5yZWFwZXJJbnRlcnZhbCxcclxuICAgICAgcHJvcGFnYXRlQ3JlYXRlRXJyb3I6IGZhbHNlXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBTZXQgZXZlbnRzXHJcbiAgICBwb29sLm9uKCdyZWxlYXNlJywgYXN5bmMgKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIENsZWFyIHBhZ2VcclxuICAgICAgY29uc3QgY2xlYXJTdGF0dXMgPSBhd2FpdCBjbGVhclBhZ2UocmVzb3VyY2UsIGZhbHNlKTtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBSZWxlYXNpbmcgYSB3b3JrZXIuIENsZWFyIHBhZ2Ugc3RhdHVzOiAke2NsZWFyU3RhdHVzfS5gXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChfZXZlbnRJZCwgcmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBEZXN0cm95ZWQgYSB3b3JrZXIgc3VjY2Vzc2Z1bGx5LmBcclxuICAgICAgKTtcclxuICAgICAgcmVzb3VyY2UucGFnZSA9IG51bGw7XHJcbiAgICB9KTtcclxuXHJcbiAgICBjb25zdCBpbml0aWFsUmVzb3VyY2VzID0gW107XHJcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBvb2xPcHRpb25zLm1pbldvcmtlcnM7IGkrKykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuICAgICAgICBpbml0aWFsUmVzb3VyY2VzLnB1c2gocmVzb3VyY2UpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIGFuIGluaXRpYWwgcmVzb3VyY2UuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXMgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgaW5pdGlhbFJlc291cmNlcy5mb3JFYWNoKChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICBwb29sLnJlbGVhc2UocmVzb3VyY2UpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIFRoZSBwb29sIGlzIHJlYWR5JHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aCA/IGAgd2l0aCAke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RofSBpbml0aWFsIHJlc291cmNlcyB3YWl0aW5nLmAgOiAnLid9YFxyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3Bvb2xdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogVGVybWluYXRlcyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24ga2lsbFBvb2xcclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgYWxsIHdvcmtlcnMgYXJlXHJcbiAqIHRlcm1pbmF0ZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIHN1Y2Nlc3NmdWxseSBjbG9zZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XHJcbiAgbG9nKDMsICdbcG9vbF0gS2lsbGluZyBwb29sIHdpdGggYWxsIHdvcmtlcnMgYW5kIGNsb3NpbmcgYnJvd3Nlci4nKTtcclxuXHJcbiAgLy8gSWYgc3RpbGwgYWxpdmUsIGRlc3Ryb3kgdGhlIHBvb2wgb2YgcGFnZXMgYmVmb3JlIGNsb3NpbmcgYSBicm93c2VyXHJcbiAgaWYgKHBvb2wpIHtcclxuICAgIC8vIEZyZWUgdXAgbm90IHJlbGVhc2VkIHdvcmtlcnNcclxuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHBvb2wudXNlZCkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VyLnJlc291cmNlKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IHRoZSBwb29sIGlmIGl0IGlzIHN0aWxsIGF2YWlsYWJsZVxyXG4gICAgaWYgKCFwb29sLmRlc3Ryb3llZCkge1xyXG4gICAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gRGVzdHJveWVkIHRoZSBwb29sIG9mIHJlc291cmNlcy4nKTtcclxuICAgIH1cclxuICAgIHBvb2wgPSBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICBhd2FpdCBjbG9zZUJyb3dzZXIoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByb2Nlc3NlcyB0aGUgZXhwb3J0IHdvcmsgdXNpbmcgYSB3b3JrZXIgZnJvbSB0aGUgcG9vbC4gQWNxdWlyZXMgYSB3b3JrZXJcclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcclxuICogdGhlIHdvcmtlciBoYW5kbGUgYmFjayB0byB0aGUgcG9vbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0V29ya1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZXhwb3J0IHJlc3VsdFxyXG4gKiBhbmQgb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3RXb3JrKG9wdGlvbnMpIHtcclxuICBsZXQgd29ya2VySGFuZGxlO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgICAvLyBBbiBleHBvcnQgYXR0ZW1wdCBjb3VudGVkXHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzQXR0ZW1wdGVkO1xyXG5cclxuICAgIC8vIERpc3BsYXkgdGhlIHBvb2wgaW5mb3JtYXRpb24gaWYgbmVlZGVkXHJcbiAgICBpZiAob3B0aW9ucy5wb29sLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGluIGNhc2Ugb2YgbGFja2luZyB0aGUgcG9vbCBpbnN0YW5jZVxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhlIGFjcXVpcmUgY291bnRlclxyXG4gICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFRyeSB0byBhY3F1aXJlIHRoZSB3b3JrZXIgYWxvbmcgd2l0aCB0aGUgaWQsIHdvcmtzIGNvdW50IGFuZCBwYWdlXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgICAgLy8gQWNxdWlyZSBhIHBvb2wgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxyXG4gICAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgICAgYEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUgdG9vayAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFJlc29sdmVkIHdvcmtlciBwYWdlIGlzIGludmFsaWQ6IHRoZSBwb29sIHNldHVwIGlzIHdvbmt5LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgY29uc3Qgd29ya1N0YXJ0ID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHt3b3JrZXJIYW5kbGUuaWR9XSAtIFN0YXJ0aW5nIHdvcmsgb24gdGhpcyBwb29sIGVudHJ5LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU3RhcnQgbWVhc3VyaW5nIGV4cG9ydCB0aW1lXHJcbiAgICBjb25zdCBleHBvcnRDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KFxyXG4gICAgICB3b3JrZXJIYW5kbGUucGFnZSxcclxuICAgICAgb3B0aW9ucy5leHBvcnQsXHJcbiAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKHJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIElmIHRoZXJlJ3MgYSByYXN0ZXJpemF0aW9uIHRpbWVvdXQsIHdlIHdhbnQgbmVlZCB0byBmbHVzaCB0aGUgcGFnZS5cclxuICAgICAgLy8gVGhpcyBpcyBiZWNhdXNlIHRoZSBwYWdlIG1heSBiZSBpbiBhIHN0YXRlIHdoZXJlIGl0J3Mgd2FpdGluZyBmb3JcclxuICAgICAgLy8gdGhlIHNjcmVlbnNob3QgdG8gZmluaXNoIGV2ZW4gdGhvdWdoIHRoZSB0aW1lb3V0IGhhcyBvY2N1cmVkLlxyXG4gICAgICAvLyBXaGljaCBvZiBjb3Vyc2UgY2F1c2VzIGEgbG90IG9mIGlzc3VlcyB3aXRoIHRoZSBldmVudCBzeXN0ZW0sXHJcbiAgICAgIC8vIGFuZCBwYWdlIGNvbnNpc3RlbmN5LlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBPbmx5IHBhZ2Uuc2NyZWVuc2hvdCB3aWxsIHRocm93IHRoaXMsIHRpbWVvdXRzIGZvciBQREYncyBhcmVcclxuICAgICAgLy8gaGFuZGxlZCBieSB0aGUgcGFnZS5wZGYgZnVuY3Rpb24gaXRzZWxmLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyAuLi55ZXMsIHRoaXMgaXMgdWdseS5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlID0gbnVsbDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJlc3VsdC5uYW1lID09PSAnVGltZW91dEVycm9yJyB8fFxyXG4gICAgICAgIHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0J1xyXG4gICAgICApIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfVJhc3Rlcml6YXRpb24gdGltZW91dDogeW91ciBjaGFydCBtYXkgYmUgdG9vIGNvbXBsZXggb3IgbGFyZ2UsIGFuZCBmYWlsZWQgdG8gcmVuZGVyIHdpdGhpbiB0aGUgYWxsb3R0ZWQgdGltZS5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGV4cG9ydDogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIHRoZSBQdXBwZXRlZXIgZXhwb3J0IHRpbWVcclxuICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDUsXHJcbiAgICAgICAgYFtiZW5jaG1hcmtdICR7b3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnfWAsXHJcbiAgICAgICAgYEV4cG9ydGluZyBhIGNoYXJ0IHN1Y2Vzc2Z1bGx5IHRvb2sgJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIHJlc291cmNlIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG5cclxuICAgIC8vIFVzZWQgZm9yIHN0YXRpc3RpY3MgaW4gYXZlcmFnZVRpbWUgYW5kIHByb2Nlc3NlZFdvcmtDb3VudCwgd2hpY2hcclxuICAgIC8vIGluIHR1cm4gaXMgdXNlZCBieSB0aGUgL2hlYWx0aCByb3V0ZS5cclxuICAgIGNvbnN0IHdvcmtFbmQgPSBnZXROZXdEYXRlVGltZSgpO1xyXG4gICAgY29uc3QgZXhwb3J0VGltZSA9IHdvcmtFbmQgLSB3b3JrU3RhcnQ7XHJcblxyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UgPVxyXG4gICAgICBwb29sU3RhdHMudGltZVNwZW50IC8gKytwb29sU3RhdHMuZXhwb3J0c1BlcmZvcm1lZDtcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBXb3JrIGNvbXBsZXRlZCBpbiAke2V4cG9ydFRpbWV9bXMuYCk7XHJcblxyXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICByZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICsrcG9vbFN0YXRzLmV4cG9ydHNEcm9wcGVkO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhyb3cgZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fG51bGwpfSBUaGUgY3VycmVudCBwb29sIGluc3RhbmNlIGlmIGluaXRpYWxpemVkLCBvciBudWxsXHJcbiAqIGlmIHRoZSBwb29sIGhhcyBub3QgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2woKSB7XHJcbiAgcmV0dXJuIHBvb2w7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBzdGF0aXN0aWMgb2YgYSBwb29sIGluc3RhY2UgYWJvdXQgZXhwb3J0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xTdGF0c1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY3VycmVudCBwb29sIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbFN0YXRzKCkge1xyXG4gIHJldHVybiBwb29sU3RhdHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgcG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdCwgaW5jbHVkaW5nIG1pbmltdW0gYW5kIG1heGltdW1cclxuICogd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OKCkge1xyXG4gIHJldHVybiB7XHJcbiAgICBtaW46IHBvb2wubWluLFxyXG4gICAgbWF4OiBwb29sLm1heCxcclxuICAgIHVzZWQ6IHBvb2wubnVtVXNlZCgpLFxyXG4gICAgYXZhaWxhYmxlOiBwb29sLm51bUZyZWUoKSxcclxuICAgIGFsbENyZWF0ZWQ6IHBvb2wubnVtVXNlZCgpICsgcG9vbC5udW1GcmVlKCksXHJcbiAgICBwZW5kaW5nQWNxdWlyZXM6IHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCksXHJcbiAgICBwZW5kaW5nQ3JlYXRlczogcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zOiBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzOiBwb29sLnBlbmRpbmdEZXN0cm95cy5sZW5ndGgsXHJcbiAgICBhYnNvbHV0ZUFsbDpcclxuICAgICAgcG9vbC5udW1Vc2VkKCkgK1xyXG4gICAgICBwb29sLm51bUZyZWUoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdDcmVhdGVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpICtcclxuICAgICAgcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoXHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHBvb2wsIGluY2x1ZGluZyB0aGUgbWluaW11bVxyXG4gKiBhbmQgbWF4aW11bSB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmVcclxuICogcmVxdWVzdHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sSW5mb1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvKCkge1xyXG4gIGNvbnN0IHtcclxuICAgIG1pbixcclxuICAgIG1heCxcclxuICAgIHVzZWQsXHJcbiAgICBhdmFpbGFibGUsXHJcbiAgICBhbGxDcmVhdGVkLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXMsXHJcbiAgICBwZW5kaW5nVmFsaWRhdGlvbnMsXHJcbiAgICBwZW5kaW5nRGVzdHJveXMsXHJcbiAgICBhYnNvbHV0ZUFsbFxyXG4gIH0gPSBnZXRQb29sSW5mb0pTT04oKTtcclxuXHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1pbmltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWlufS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttYXh9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgdXNlZCByZXNvdXJjZXM6ICR7dXNlZH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBmcmVlIHJlc291cmNlczogJHthdmFpbGFibGV9LmApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIGNyZWF0ZWQgKHVzZWQgYW5kIGZyZWUpIHJlc291cmNlczogJHthbGxDcmVhdGVkfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGFjcXVpcmVkOiAke3BlbmRpbmdBY3F1aXJlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBjcmVhdGVkOiAke3BlbmRpbmdDcmVhdGVzfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIHZhbGlkYXRlZDogJHtwZW5kaW5nVmFsaWRhdGlvbnN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgZGVzdHJveWVkOiAke3BlbmRpbmdEZXN0cm95c30uYFxyXG4gICk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgcmVzb3VyY2VzOiAke2Fic29sdXRlQWxsfS5gKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZhY3RvcnkgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGFuIG9iamVjdCB3aXRoIGBjcmVhdGVgLCBgdmFsaWRhdGVgLFxyXG4gKiBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9ucyBmb3IgdGhlIHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmFjdG9yeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9mYWN0b3J5KHBvb2xPcHRpb25zKSB7XHJcbiAgcmV0dXJuIHtcclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyB3b3JrZXIgcGFnZSBmb3IgdGhlIGV4cG9ydCBwb29sLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGNyZWF0ZVxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdFxyXG4gICAgICogY29udGFpbmluZyB0aGUgd29ya2VyIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgaW5pdGlhbFxyXG4gICAgICogd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gZXJyb3IgZHVyaW5nXHJcbiAgICAgKiB0aGUgY3JlYXRpb24gb2YgdGhlIG5ldyBwYWdlLlxyXG4gICAgICovXHJcbiAgICBjcmVhdGU6IGFzeW5jICgpID0+IHtcclxuICAgICAgLy8gSW5pdCB0aGUgcmVzb3VyY2Ugd2l0aCB1bmlxdWUgaWQgYW5kIHdvcmsgY291bnRcclxuICAgICAgY29uc3QgcG9vbFJlc291cmNlID0ge1xyXG4gICAgICAgIGlkOiB1dWlkKCksXHJcbiAgICAgICAgLy8gVHJ5IHRvIGRpc3RyaWJ1dGUgdGhlIGluaXRpYWwgd29yayBjb3VudFxyXG4gICAgICAgIHdvcmtDb3VudDogTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogKHBvb2xPcHRpb25zLndvcmtMaW1pdCAvIDIpKVxyXG4gICAgICB9O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBTdGFydCBtZWFzdXJpbmcgYSBwYWdlIGNyZWF0aW9uIHRpbWVcclxuICAgICAgICBjb25zdCBzdGFydERhdGUgPSBnZXROZXdEYXRlVGltZSgpO1xyXG5cclxuICAgICAgICAvLyBDcmVhdGUgYSBuZXcgcGFnZVxyXG4gICAgICAgIGF3YWl0IG5ld1BhZ2UocG9vbFJlc291cmNlKTtcclxuXHJcbiAgICAgICAgLy8gTWVhc3VyZSB0aGUgdGltZSBvZiBmdWxsIGNyZWF0aW9uIGFuZCBjb25maWd1cmF0aW9uIG9mIGEgcGFnZVxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBhIHdvcmtlciwgdG9vayAke1xyXG4gICAgICAgICAgICBnZXROZXdEYXRlVGltZSgpIC0gc3RhcnREYXRlXHJcbiAgICAgICAgICB9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHVybiByZWFkeSBwb29sIHJlc291cmNlXHJcbiAgICAgICAgcmV0dXJuIHBvb2xSZXNvdXJjZTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBjcmVhdGluZyBhIG5ldyBwYWdlLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcclxuICAgICAqIHRoZSB3b3JrIGxpbWl0LlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIHZhbGlkYXRlXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHBvb2xSZXNvdXJjZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAgICogdGhlIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBpZiB0aGUgd29ya2VyXHJcbiAgICAgKiBpcyB2YWxpZCBhbmQgd2l0aGluIHRoZSB3b3JrIGxpbWl0OyBvdGhlcndpc2UsIHRvIGZhbHNlLlxyXG4gICAgICovXHJcbiAgICB2YWxpZGF0ZTogYXN5bmMgKHBvb2xSZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJbiBjZXJ0YWluIGNhc2VzIGFjcXVpcmluZyB0aHJvd3MgYSBUYXJnZXRDbG9zZUVycm9yLCB3aGljaCBtYXlcclxuICAgICAgLy8gYmUgY2F1c2VkIGJ5IHR3byB0aGluZ3M6XHJcbiAgICAgIC8vIC0gVGhlIHBhZ2UgaXMgY2xvc2VkIGFuZCBhdHRlbXB0ZWQgdG8gYmUgcmV1c2VkLlxyXG4gICAgICAvLyAtIExvc3QgY29udGFjdCB3aXRoIHRoZSBicm93c2VyLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBXaGF0IHdlJ3JlIHNlZWluZyBpbiBsb2dzIGlzIHRoYXQgc3VjY2Vzc2l2ZSBleHBvcnRzIHR5cGljYWxseVxyXG4gICAgICAvLyBzdWNjZWVkcywgYW5kIHRoZSBzZXJ2ZXIgcmVjb3ZlcnMsIGluZGljYXRpbmcgdGhhdCBpdCdzIGxpa2VseVxyXG4gICAgICAvLyB0aGUgZmlyc3QgY2FzZS4gVGhpcyBpcyBhbiBhdHRlbXB0IGF0IGFsbGlldmF0aW5nIHRoZSBpc3N1ZSBieVxyXG4gICAgICAvLyBzaW1wbHkgbm90IHZhbGlkYXRpbmcgdGhlIHdvcmtlciBpZiB0aGUgcGFnZSBpcyBudWxsIG9yIGNsb3NlZC5cclxuICAgICAgLy9cclxuICAgICAgLy8gVGhlIGFjdHVhbCByZXN1bHQgZnJvbSB3aGVuIHRoaXMgaGFwcGVuZWQsIHdhcyB0aGF0IGEgd29ya2VyIHdvdWxkXHJcbiAgICAgIC8vIGJlIGNvbXBsZXRlbHkgbG9ja2VkLCBzdG9wcGluZyBpdCBmcm9tIGJlaW5nIGFjcXVpcmVkIHVudGlsXHJcbiAgICAgIC8vIGl0cyB3b3JrIGNvdW50IHJlYWNoZWQgdGhlIGxpbWl0LlxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBwYWdlYCBpcyB2YWxpZFxyXG4gICAgICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChubyB2YWxpZCBwYWdlIGlzIGZvdW5kKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgY2xvc2VkXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlIGlzIGNsb3NlZCBvciBpbnZhbGlkKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgbWFpbkZyYW1lYCBpcyBkZXRhY2hlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UubWFpbkZyYW1lKCkuZGV0YWNoZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKHBhZ2UncyBmcmFtZSBpcyBkZXRhY2hlZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHdvcmtMaW1pdGAgaXMgZXhjZWVkZWRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHBvb2xPcHRpb25zLndvcmtMaW1pdCAmJlxyXG4gICAgICAgICsrcG9vbFJlc291cmNlLndvcmtDb3VudCA+IHBvb2xPcHRpb25zLndvcmtMaW1pdFxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKGV4Y2VlZGVkIHRoZSAke3Bvb2xPcHRpb25zLndvcmtMaW1pdH0gd29ya3MgcGVyIHJlc291cmNlIGxpbWl0KS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSBgcG9vbFJlc291cmNlYCBpcyB2YWxpZGF0ZWRcclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gZGVzdHJveVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKi9cclxuICAgIGRlc3Ryb3k6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRGVzdHJveWluZyBhIHdvcmtlci5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgLy8gUmVtb3ZlIGFsbCBhdHRhY2hlZCBldmVudCBsaXN0ZW5lcnMgZnJvbSB0aGUgcmVzb3VyY2VcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygncGFnZWVycm9yJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2NvbnNvbGUnKTtcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygnZnJhbWVkZXRhY2hlZCcpO1xyXG5cclxuICAgICAgICAgIC8vIFdlIG5lZWQgdG8gd2FpdCBhcm91bmQgZm9yIHRoaXNcclxuICAgICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gUGFnZSBjb3VsZCBub3QgYmUgY2xvc2VkIHVwb24gZGVzdHJveWluZy5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGluaXRQb29sLFxyXG4gIGtpbGxQb29sLFxyXG4gIHBvc3RXb3JrLFxyXG4gIGdldFBvb2wsXHJcbiAgZ2V0UG9vbFN0YXRzLFxyXG4gIGdldFBvb2xJbmZvLFxyXG4gIGdldFBvb2xJbmZvSlNPTlxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVXNlZCB0byBzYW5pdGl6ZSB0aGUgc3RyaW5ncyBjb21pbmcgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZVxyXG4gKiB0byBwcmV2ZW50IFhTUyBhdHRhY2tzICh3aXRoIHRoZSBET01QdXJpZnkgbGlicmFyeSkuXHJcbiAqL1xyXG5cclxuaW1wb3J0IERPTVB1cmlmeSBmcm9tICdkb21wdXJpZnknO1xyXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcclxuXHJcbi8qKlxyXG4gKiBTYW5pdGl6ZXMgYSBnaXZlbiBIVE1MIHN0cmluZyBieSByZW1vdmluZyA8c2NyaXB0PiB0YWdzLiBUaGlzIGZ1bmN0aW9uIHVzZXNcclxuICogYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gZmluZCBhbmQgcmVtb3ZlIGFsbCBvY2N1cnJlbmNlcyBvZiA8c2NyaXB0Pjwvc2NyaXB0PlxyXG4gKiB0YWdzIGFuZCBhbnkgY29udGVudCB3aXRoaW4gdGhlbS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNhbml0aXplXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCAtIFRoZSBIVE1MIHN0cmluZyB0byBiZSBzYW5pdGl6ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTCBzdHJpbmcuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXQpIHtcclxuICAvLyBHZXQgdGhlIHZpcnR1YWwgRE9NXHJcbiAgY29uc3Qgd2luZG93ID0gbmV3IEpTRE9NKCcnKS53aW5kb3c7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHB1cmlmeWluZyBpbnN0YW5jZVxyXG4gIGNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cpO1xyXG5cclxuICAvLyBSZXR1cm4gc2FuaXRpemVkIGlucHV0XHJcbiAgcmV0dXJuIHB1cmlmeS5zYW5pdGl6ZShpbnB1dCwgeyBBRERfVEFHUzogWydmb3JlaWduT2JqZWN0J10gfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzYW5pdGl6ZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIHRvIHByZXBhcmUgZm9yIHRoZSBleHBvcnRpbmcgY2hhcnRzXHJcbiAqIGludG8gdmFyaW91cyBpbWFnZSBvdXRwdXQgZm9ybWF0cyBzdWNoIGFzIEpQRUcsIFBORywgUERGLCBhbmQgU1ZHcy5cclxuICogSXQgc3VwcG9ydHMgc2luZ2xlIGFuZCBiYXRjaCBleHBvcnQgb3BlcmF0aW9ucyBhbmQgYWxsb3dzIGN1c3RvbWl6YXRpb25cclxuICogdGhyb3VnaCBvcHRpb25zIHBhc3NlZCBmcm9tIGNvbmZpZ3VyYXRpb25zIG9yIEFQSXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnLCB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBpc09iamVjdCxcclxuICByb3VuZE51bWJlcixcclxuICB3cmFwQXJvdW5kXHJcbn0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGdsb2JhbCBmbGFnIGZvciB0aGUgY29kZSBleGVjdXRpb24gcGVybWlzc2lvblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucyBhbmQgc2F2ZXNcclxuICogdGhlIHJlc3VsdGluZyBpbWFnZSB0byB0aGUgcHJvdmlkZWQgb3V0cHV0IGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIFRoZSBvYmplY3QgbXVzdCBjb250YWluIGF0IGxlYXN0IG9uZVxyXG4gKiBvZiB0aGUgZm9sbG93aW5nIGBleHBvcnRgIHByb3BlcnRpZXM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgXHJcbiAqIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpbmdsZUV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0KSB7XHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQoXHJcbiAgICAgIHsgZXhwb3J0OiBvcHRpb25zLmV4cG9ydCwgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWMgfSxcclxuICAgICAgYXN5bmMgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSBvbmUgb2YgdGhlIGZvbGxvd2luZyBvcHRpb25zOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIGluZm9ybWF0aW9uXHJcbiAqIHByb3ZpZGVkIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZ1xyXG4gKiBmb3JtYXQ6IFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzXHJcbiAqIGFyZSBzYXZlZCB0byB0aGUgc3BlY2lmaWVkIG91dHB1dCBmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIGZyb21cclxuICogdGhlIGBleHBvcnRgIHNlY3Rpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3NlcyBhcmUgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJhdGNoRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQgJiYgb3B0aW9ucy5leHBvcnQuYmF0Y2gpIHtcclxuICAgIC8vIEFuIGFycmF5IGZvciBjb2xsZWN0aW5nIGJhdGNoIGV4cG9ydHNcclxuICAgIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIGBiYXRjaGAgYXJndW1lbnRzXHJcbiAgICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykgfHwgW10pIHtcclxuICAgICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgaW1hZ2VPcHRpb25zYCBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXRcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgcGFydGlhbFxyXG4gKiBvcHRpb25zIGFyZSBwcm92aWRlZCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICpcclxuICogVGhlIGBlbmRDYWxsYmFja2AgZnVuY3Rpb24gaXMgaW52b2tlZCB1cG9uIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBleHBvcnQsXHJcbiAqIGVpdGhlciBzdWNjZXNzZnVsbHkgb3Igd2l0aCBhbiBlcnJvci4gVGhlIGBlcnJvcmAgb2JqZWN0IGlzIHByb3ZpZGVkXHJcbiAqIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIGNvbnRhaW5pbmdcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5XHJcbiAqIGFuZCB0aGUgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW1hZ2VPcHRpb25zIC0gVGhlIGBpbWFnZU9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkXHJcbiAqIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgdGhlIHByb3ZpZGVkXHJcbiAqIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHRoZSBleHBvcnQgcHJvY2VzcyBvciB1cG9uIGVuY291bnRlcmluZyBhbiBlcnJvci4gVGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGlzIHRoZSBgZXJyb3JgIG9iamVjdCwgYW5kIHRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIGBkYXRhYCBvYmplY3QsXHJcbiAqIHdoaWNoIGluY2x1ZGVzIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YFxyXG4gKiBwcm9wZXJ0eSBhbmQgdGhlIGZ1bGwgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGltYWdlT3B0aW9ucywgZW5kQ2FsbGJhY2spIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgb3B0aW9ucyBhcmUgaW4gYW4gb2JqZWN0XHJcbiAgICBpZiAoIWlzT2JqZWN0KGltYWdlT3B0aW9ucykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgcHJvdmlkZWQgYGltYWdlT3B0aW9uc2AuIE5lZWRzIHRvIGJlIGFuIG9iamVjdC4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE1lcmdlIGFkZGl0aW9uYWwgb3B0aW9ucyB0byB0aGUgY29weSBvZiB0aGUgaW5zdGFuY2Ugb3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoXHJcbiAgICAgIHtcclxuICAgICAgICBleHBvcnQ6IGltYWdlT3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgY3VzdG9tTG9naWM6IGltYWdlT3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBudWxsO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICByZXR1cm4gcG9zdFdvcmsob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBmb3IgY2hhcnQgZXhwb3J0cyBiYXNlZFxyXG4gKiBvbiB0aGUgcHJvdmlkZWQgZXhwb3J0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICogMS4gVGhlIGBoZWlnaHRgLCBgd2lkdGhgLCBgc2NhbGVgIGZyb20gdGhlIGBleHBvcnRPcHRpb25zYC5cclxuICogMi4gT3B0aW9ucyBmcm9tIHRoZSBjaGFydCBjb25maWd1cmF0aW9uIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogMy4gT3B0aW9ucyBmcm9tIHRoZSBnbG9iYWwgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCkuXHJcbiAqIDQuIE9wdGlvbnMgZnJvbSB0aGUgdGhlbWUgb3B0aW9ucyAoZnJvbSBgZXhwb3J0aW5nYCBhbmQgYGNoYXJ0YCBzZWN0aW9ucykuXHJcbiAqIDUuIEZhbGxiYWNrIGRlZmF1bHQgdmFsdWVzIChgaGVpZ2h0ID0gNDAwYCwgYHdpZHRoID0gNjAwYCwgYHNjYWxlID0gMWApLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZpbmRDaGFydFNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgY2FsY3VsYXRlZCBgaGVpZ2h0YCwgYHdpZHRoYFxyXG4gKiBhbmQgYHNjYWxlYCB2YWx1ZXMgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5vcHRpb25zIHx8IGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmluc3RyKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IGdsb2JhbE9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBnbG9iYWxPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgdGhlbWVPcHRpb25zYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IHRoZW1lT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IHRoZW1lT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykgfHwgZmFsc2U7XHJcblxyXG4gIC8vIEZpbmQgdGhlIGBzY2FsZWAgdmFsdWU6XHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgbG93ZXIgdGhhbiAwLjFcclxuICAvLyAtIEl0IGNhbm5vdCBiZSBoaWdoZXIgdGhhbiA1LjBcclxuICAvLyAtIEl0IG11c3QgYmUgcm91bmRlZCB0byAyIGRlY2ltYWwgcGxhY2VzIChlLmcuIDAuMjMyMzQgLT4gMC4yMylcclxuICBjb25zdCBzY2FsZSA9IHJvdW5kTnVtYmVyKFxyXG4gICAgTWF0aC5tYXgoXHJcbiAgICAgIDAuMSxcclxuICAgICAgTWF0aC5taW4oXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5zY2FsZSB8fFxyXG4gICAgICAgICAgb3B0aW9uc0V4cG9ydGluZz8uc2NhbGUgfHxcclxuICAgICAgICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICA1LjBcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIDJcclxuICApO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgaGVpZ2h0YCB2YWx1ZVxyXG4gIGNvbnN0IGhlaWdodCA9XHJcbiAgICBleHBvcnRPcHRpb25zLmhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdEhlaWdodCB8fFxyXG4gICAgNDAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgd2lkdGhgIHZhbHVlXHJcbiAgY29uc3Qgd2lkdGggPVxyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCB8fFxyXG4gICAgb3B0aW9uc0V4cG9ydGluZz8uc291cmNlV2lkdGggfHxcclxuICAgIG9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8ud2lkdGggfHxcclxuICAgIGV4cG9ydE9wdGlvbnMuZGVmYXVsdFdpZHRoIHx8XHJcbiAgICA2MDA7XHJcblxyXG4gIC8vIEdhdGhlciBgaGVpZ2h0YCwgYHdpZHRoYCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiBvbmUgb2JqZWN0XHJcbiAgY29uc3Qgc2l6ZSA9IHsgaGVpZ2h0LCB3aWR0aCwgc2NhbGUgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgYHB4YCBhbmQgYCVgXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgc2l6ZSBvYmplY3RcclxuICByZXR1cm4gc2l6ZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4ZWN1dGlvbiBvZiBjdXN0b20gbG9naWMgb3B0aW9ucywgaW5jbHVkaW5nIGxvYWRpbmcgYHJlc291cmNlc2AsXHJcbiAqIGBjdXN0b21Db2RlYCwgYW5kIGBjYWxsYmFja2AuIElmIGNvZGUgZXhlY3V0aW9uIGlzIGFsbG93ZWQsIGl0IHByb2Nlc3Nlc1xyXG4gKiB0aGUgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYWNjb3JkaW5nbHkuIElmIGNvZGUgZXhlY3V0aW9uIGlzIG5vdCBhbGxvd2VkLFxyXG4gKiBpdCBkaXNhYmxlcyB0aGUgdXNhZ2Ugb2YgcmVzb3VyY2VzLCBjdXN0b20gY29kZSBhbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlQ3VzdG9tTG9naWNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb2RlIGV4ZWN1dGlvblxyXG4gKiBpcyBub3QgYWxsb3dlZCBidXQgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYXJlIHN0aWxsIHByb3ZpZGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucywgYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgLy8gSW4gY2FzZSBvZiBhbGxvd2luZyBjb2RlIGV4ZWN1dGlvblxyXG4gIGlmIChhbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIC8vIFByb2Nlc3MgdGhlIGByZXNvdXJjZXNgIG9wdGlvblxyXG4gICAgaWYgKHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID09PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBDdXN0b20gc3RyaW5naWZpZWQgcmVzb3VyY2VzXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gTG9hZCB0aGUgZGVmYXVsdCBvbmVcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoJ3Jlc291cmNlcy5qc29uJyksICd1dGY4JyksXHJcbiAgICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IGByZXNvdXJjZXMuanNvbmAgZmlsZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGN1c3RvbSBjb2RlIGFuZCB3cmFwIGFyb3VuZCBpdCBpbiBhIHNlbGYgaW52b2tpbmcgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1tjaGFydF0gVGhlIGBjdXN0b21Db2RlYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY2FsbGJhY2tgIG9wdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gVHJ5IHRvIGxvYWQgY2FsbGJhY2sgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2ssXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICB0cnVlXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY2FsbGJhY2tgIGNhbm5vdCBiZSBsb2FkZWQuJyk7XHJcblxyXG4gICAgICAvLyBJbiBjYXNlIG9mIGFuIGVycm9yLCBzZXQgdGhlIG9wdGlvbiB3aXRoIG51bGxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGN1c3RvbUNvZGVgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSkpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGN1c3RvbUNvZGVgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYGNhbGxiYWNrYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgY2FsbGJhY2tgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHJlc291cmNlc2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgLy8gSWYgdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIGZsYWcgaXMgc2V0IHRvIGZhbHNlLCB3ZSBzaG91bGQgcmVmdXNlXHJcbiAgICAvLyB0aGUgdXNhZ2Ugb2YgdGhlIGBjYWxsYmFja2AsIGByZXNvdXJjZXNgLCBhbmQgYGN1c3RvbUNvZGVgIG9wdGlvbnMuXHJcbiAgICAvLyBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbCByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LlxyXG4gICAgaWYgKFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFJlc2V0IGFsbCBjdXN0b20gY29kZSBvcHRpb25zXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcblxyXG4gICAgICAvLyBTZW5kIGEgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydCB0aGVzZSBzZXR0aW5nc1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnLCBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gLFxyXG4gICAgICAgIDQwM1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZnJvbSB0aGUgYHJlc291cmNlc2Agb3B0aW9uIGZvciBleHBvcnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFtyZXNvdXJjZXM9bnVsbF0gLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuXHJcbiAqIENhbiBiZSBlaXRoZXIgYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiwgYSBwYXRoIHRvIGEgSlNPTiBmaWxlLFxyXG4gKiBvciBudWxsLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgbnVsbGAuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2FkaW5nXHJcbiAqIHJlc291cmNlcyBmcm9tIGZpbGVzIGlzIGFsbG93ZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxudWxsKX0gVGhlIGhhbmRsZWQgcmVzb3VyY2VzIG9yIG51bGwgaWYgbm8gdmFsaWQgcmVzb3VyY2VzXHJcbiAqIGFyZSBmb3VuZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgcmVzb3VyY2VzID0gbnVsbCxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIExpc3Qgb2YgYWxsb3dlZCBzZWN0aW9ucyBpbiB0aGUgcmVzb3VyY2VzIEpTT05cclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKHJlc291cmNlcyksICd1dGY4JyksXHJcbiAgICAgICAgZmFsc2UsXHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxyXG4gICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQWxsb3dlZENvbmZpZyhyZXNvdXJjZXMsIGZhbHNlLCBhbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIGZpbGVzIHNlY3Rpb25cclxuICAgIGlmIChoYW5kbGVkUmVzb3VyY2VzICYmICFhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaWx0ZXIgZnJvbSB1bm5lY2Vzc2FyeSBwcm9wZXJ0aWVzXHJcbiAgZm9yIChjb25zdCBwcm9wTmFtZSBpbiBoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXNbcHJvcE5hbWVdO1xyXG4gICAgfSBlbHNlIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiBhbGxvd2VkIHByb3BlcnRpZXMgaXMgcHJlc2VudFxyXG4gIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgcmV0dXJuIG51bGw7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgbG9hZGluZyBhbmQgdmFsaWRhdGlvbiBvZiB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYFxyXG4gKiBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIElmIHRoZSBvcHRpb24gaXMgYSBzdHJpbmcgYW5kIHJlZmVyZW5jZXMgYSBKU09OIGZpbGVcclxuICogKHdoZW4gdGhlIGBhbGxvd0ZpbGVSZXNvdXJjZXNgIGlzIHRydWUpLCBpdCByZWFkcyBhbmQgcGFyc2VzIHRoZSBmaWxlLlxyXG4gKiBPdGhlcndpc2UsIGl0IGF0dGVtcHRzIHRvIHBhcnNlIHRoZSBzdHJpbmcgb3Igb2JqZWN0IGFzIEpTT04uIElmIGFueSBlcnJvcnNcclxuICogb2NjdXIgZHVyaW5nIHRoaXMgcHJvY2VzcywgdGhlIG9wdGlvbiBpcyBzZXQgdG8gbnVsbC4gSWYgdGhlcmUgaXMgYW4gZXJyb3JcclxuICogbG9hZGluZyBvciBwYXJzaW5nIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3IgYHRoZW1lT3B0aW9uc2AsIHRoZSBlcnJvciBpcyBsb2dnZWRcclxuICogYW5kIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShcclxuICBleHBvcnRPcHRpb25zLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIGludGVydmFsc1xyXG4gKiBhbmQgdGltZW91dHMgaW4gYSBjZW50cmFsaXplZCBtYW5uZXIuIEl0IG1haW50YWlucyBhIHJlZ2lzdHJ5IG9mIGFsbCBhY3RpdmVcclxuICogdGltZXJzIGFuZCBhbGxvd3MgZm9yIHRoZWlyIGVmZmljaWVudCBjbGVhbnVwIHdoZW4gbmVlZGVkLiBUaGlzIGNhbiBiZSB1c2VmdWxcclxuICogaW4gYXBwbGljYXRpb25zIHdoZXJlIHByb3BlciByZXNvdXJjZSBtYW5hZ2VtZW50IGFuZCBjbGVhbiBzaHV0ZG93biBvZiB0aW1lcnNcclxuICogYXJlIGNyaXRpY2FsIHRvIGF2b2lkIG1lbW9yeSBsZWFrcyBvciB1bmludGVuZGVkIGJlaGF2aW9yLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbi8vIEFycmF5IHRoYXQgY29udGFpbnMgaWRzIG9mIGFsbCBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHNcclxuY29uc3QgdGltZXJJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIHRoZSBgc2V0SW50ZXJ2YWxgIG9yIGBzZXRUaW1lb3V0YCBhbmQgdG8gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBhZGRUaW1lclxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsIG9yIGEgdGltZW91dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhZGRUaW1lcihpZCkge1xyXG4gIHRpbWVySWRzLnB1c2goaWQpO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBhbmQgdGltZW91dHMgYnkgaWRzIGdhdGhlcmVkXHJcbiAqIGluIHRoZSBgdGltZXJJZHNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xlYXJBbGxUaW1lcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbGVhckFsbFRpbWVycygpIHtcclxuICBsb2coNCwgYFt0aW1lcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIHRpbWVySWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICAgIGNsZWFyVGltZW91dChpZCk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkVGltZXIsXHJcbiAgY2xlYXJBbGxUaW1lcnNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlc1xyXG4gKiBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2VzIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbiB3aXRoXHJcbiAqIHRoZSBwYXNzZWQgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiBsb2dFcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGdldE9wdGlvbnMoKS5vdGhlci5ub2RlRW52ICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBgcmV0dXJuRXJyb3JNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmUoZXJyb3IsIHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuXHJcbiAgLy8gVXNlIHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIG9yIHRoZSBkZWZhdWx0IDQwMFxyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlIHx8IDQwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXNwb25zZS5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgZXJyb3IgbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBlcnJvck1pZGRsZXdhcmUoYXBwKSB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciBjb25maWd1cmluZyBhbmQgZW5hYmxpbmcgcmF0ZVxyXG4gKiBsaW1pdGluZyBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBlbmFibGluZyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzcGVjaWZpZWQgRXhwcmVzcyBhcHAuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXRcclxuICogdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCByYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIENoZWNrIGlmIHRoZSByYXRlIGxpbWl0aW5nIGlzIGVuYWJsZWQgYW5kIHRoZSBhcHAgZXhpc3RzXHJcbiAgICBpZiAoYXBwICYmIHJhdGVMaW1pdGluZ09wdGlvbnMuZW5hYmxlKSB7XHJcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPVxyXG4gICAgICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgICAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICAgICAgd2luZG93OiByYXRlTGltaXRpbmdPcHRpb25zLndpbmRvdyB8fCAxLFxyXG4gICAgICAgIG1heFJlcXVlc3RzOiByYXRlTGltaXRpbmdPcHRpb25zLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgICAgIGRlbGF5OiByYXRlTGltaXRpbmdPcHRpb25zLmRlbGF5IHx8IDAsXHJcbiAgICAgICAgdHJ1c3RQcm94eTogcmF0ZUxpbWl0aW5nT3B0aW9ucy50cnVzdFByb3h5IHx8IGZhbHNlLFxyXG4gICAgICAgIHNraXBLZXk6IHJhdGVMaW1pdGluZ09wdGlvbnMuc2tpcEtleSB8fCBudWxsLFxyXG4gICAgICAgIHNraXBUb2tlbjogcmF0ZUxpbWl0aW5nT3B0aW9ucy5za2lwVG9rZW4gfHwgbnVsbFxyXG4gICAgICB9O1xyXG5cclxuICAgICAgLy8gU2V0IGlmIGJlaGluZCBhIHByb3h5XHJcbiAgICAgIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICAgICAgYXBwLmVuYWJsZSgndHJ1c3QgcHJveHknKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gICAgICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcclxuICAgICAgICAvLyBUaW1lIGZyYW1lIGZvciB3aGljaCByZXF1ZXN0cyBhcmUgY2hlY2tlZCBhbmQgcmVtZW1iZXJlZFxyXG4gICAgICAgIHdpbmRvd01zOiByYXRlT3B0aW9ucy53aW5kb3cgKiA2MCAqIDEwMDAsXHJcbiAgICAgICAgLy8gTGltaXQgZWFjaCBJUCB0byAxMDAgcmVxdWVzdHMgcGVyIGB3aW5kb3dNc2BcclxuICAgICAgICBsaW1pdDogcmF0ZU9wdGlvbnMubWF4UmVxdWVzdHMsXHJcbiAgICAgICAgLy8gRGlzYWJsZSBkZWxheWluZywgZnVsbCBzcGVlZCB1bnRpbCB0aGUgbWF4IGxpbWl0IGlzIHJlYWNoZWRcclxuICAgICAgICBkZWxheU1zOiByYXRlT3B0aW9ucy5kZWxheSxcclxuICAgICAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlLmZvcm1hdCh7XHJcbiAgICAgICAgICAgIGpzb246ICgpID0+IHtcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZSB9KTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobWVzc2FnZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgICAgIC8vIEFsbG93IGJ5cGFzc2luZyB0aGUgbGltaXRlciBpZiBhIHZhbGlkIGtleS90b2tlbiBoYXMgYmVlbiBzZW50XHJcbiAgICAgICAgICBpZiAoXHJcbiAgICAgICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IG51bGwgJiZcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBudWxsICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgICAgIHJlcXVlc3QucXVlcnkuYWNjZXNzX3Rva2VuID09PSByYXRlT3B0aW9ucy5za2lwVG9rZW5cclxuICAgICAgICAgICkge1xyXG4gICAgICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcblxyXG4gICAgICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gICAgICBhcHAudXNlKGxpbWl0ZXIpO1xyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtyYXRlIGxpbWl0aW5nXSBFbmFibGVkIHJhdGUgbGltaXRpbmcgd2l0aCAke3JhdGVPcHRpb25zLm1heFJlcXVlc3RzfSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcmF0ZSBsaW1pdGluZ10gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnMuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1pZGRsZXdhcmUgZnVuY3Rpb25zIGZvciB2YWxpZGF0aW5nIGluY29taW5nIEhUVFAgcmVxdWVzdHNcclxuICogaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi4gVGhpcyBtb2R1bGUgZW5zdXJlcyB0aGF0IHJlcXVlc3RzIGNvbnRhaW5cclxuICogYXBwcm9wcmlhdGUgY29udGVudCB0eXBlcyBhbmQgdmFsaWQgcmVxdWVzdCBib2RpZXMsIGluY2x1ZGluZyBwcm9wZXIgSlNPTlxyXG4gKiBzdHJ1Y3R1cmVzIGFuZCBjaGFydCBkYXRhIGZvciBleHBvcnRzLiBJdCBjaGVja3MgZm9yIHBvdGVudGlhbCBpc3N1ZXMgc3VjaFxyXG4gKiBhcyBtaXNzaW5nIG9yIG1hbGZvcm1lZCBkYXRhLCBwcml2YXRlIHJhbmdlIFVSTHMgaW4gU1ZHIHBheWxvYWRzLCBhbmQgYWxsb3dzXHJcbiAqIGZvciBmbGV4aWJsZSBvcHRpb25zIHZhbGlkYXRpb24uIFRoZSBtaWRkbGV3YXJlIGxvZ3MgZGV0YWlsZWQgaW5mb3JtYXRpb25cclxuICogYW5kIGhhbmRsZXMgZXJyb3JzIHJlbGF0ZWQgdG8gaW5jb3JyZWN0IHBheWxvYWRzLCBjaGFydCBkYXRhLCBhbmQgcHJpdmF0ZSBVUkxcclxuICogdXNhZ2UuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uIH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBpc0FsbG93ZWRDb25maWcgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpc09iamVjdEVtcHR5LCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgY29udGVudC10eXBlIGhlYWRlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge3VuZGVmaW5lZH0gVGhlIGNhbGwgdG8gdGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBjb250ZW50LXR5cGVcclxuICogaXMgbm90IGNvcnJlY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBjb250ZW50VHlwZU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSBjb250ZW50IHR5cGUgaGVhZGVyXHJcbiAgICBjb25zdCBjb250ZW50VHlwZSA9IHJlcXVlc3QuaGVhZGVyc1snY29udGVudC10eXBlJ10gfHwgJyc7XHJcblxyXG4gICAgLy8gQWxsb3cgb25seSBKU09OLCBVUkwtZW5jb2RlZCBhbmQgZm9ybSBkYXRhIHdpdGhvdXQgZmlsZXMgdHlwZXMgb2YgZGF0YVxyXG4gICAgaWYgKFxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL2pzb24nKSAmJlxyXG4gICAgICAhY29udGVudFR5cGUuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnbXVsdGlwYXJ0L2Zvcm0tZGF0YScpXHJcbiAgICApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbdmFsaWRhdGlvbl0gQ29udGVudC1UeXBlIG11c3QgYmUgYXBwbGljYXRpb24vanNvbiwgYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkLCBvciBtdWx0aXBhcnQvZm9ybS1kYXRhLicsXHJcbiAgICAgICAgNDE1XHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYHJlcXVlc3RCb2R5TWlkZGxld2FyZWAgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHZhbGlkYXRpbmcgdGhlIHJlcXVlc3QncyBib2R5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEJvZHlNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGJvZHkgaXMgbm90IGNvcnJlY3QuXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgY2hhcnQgZGF0YSBmcm9tIHRoZSBib2R5XHJcbiAqIGlzIG5vdCBjb3JyZWN0LlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaW4gY2FzZSBvZiB0aGUgcHJpdmF0ZSByYW5nZVxyXG4gKiB1cmwgZXJyb3IuXHJcbiAqL1xyXG5mdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmUocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGJvZHlcclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHV1aWQoKTtcclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0aGVyZSBpcyBubyBjb3JyZWN0IGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgZW1wdHkuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGJvZHkgaXMgcmVxdWlyZWQuIFBsZWFzZSBlbnN1cmUgdGhhdCB5b3VyIENvbnRlbnQtVHlwZSBoZWFkZXIgaXMgY29ycmVjdC4gQWNjZXB0ZWQgdHlwZXMgYXJlICdhcHBsaWNhdGlvbi9qc29uJyBhbmQgJ211bHRpcGFydC9mb3JtLWRhdGEnLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gR2V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uIGZvciB0aGUgc2VydmVyXHJcbiAgICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb24gPSBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKTtcclxuXHJcbiAgICAvLyBGaW5kIGEgY29ycmVjdCBjaGFydCBvcHRpb25zXHJcbiAgICBjb25zdCBpbnN0ciA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgLy8gVXNlIG9uZSBvZiB0aGUgYmVsb3dcclxuICAgICAgYm9keS5pbnN0ciB8fCBib2R5Lm9wdGlvbnMgfHwgYm9keS5pbmZpbGUgfHwgYm9keS5kYXRhLFxyXG4gICAgICAvLyBTdHJpbmdpZnkgb3B0aW9uc1xyXG4gICAgICB0cnVlLFxyXG4gICAgICAvLyBBbGxvdyBvciBkaXNhbGxvdyBmdW5jdGlvbnNcclxuICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRoZXJlIGlzIG5vIGNvcnJlY3QgY2hhcnQgZGF0YVxyXG4gICAgaWYgKGluc3RyID09PSBudWxsICYmICFib2R5LnN2Zykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgW3ZhbGlkYXRpb25dIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgcmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBSZWNlaXZlZCBwYXlsb2FkIGlzIG1pc3NpbmcgY29ycmVjdCBjaGFydCBkYXRhIGZvciBleHBvcnQ6ICR7SlNPTi5zdHJpbmdpZnkoYm9keSl9LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLmAsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGVzdCBvZiB4bGluazpocmVmIGVsZW1lbnRzIGZyb20gcGF5bG9hZCdzIFNWRyBmYWlsc1xyXG4gICAgaWYgKGJvZHkuc3ZnICYmIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQoYm9keS5zdmcpKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFt2YWxpZGF0aW9uXSBTVkcgcG90ZW50aWFsbHkgY29udGFpbiBhdCBsZWFzdCBvbmUgZm9yYmlkZGVuIFVSTCBpbiAneGxpbms6aHJlZicgZWxlbWVudC4gUGxlYXNlIHJldmlldyB0aGUgU1ZHIGNvbnRlbnQgYW5kIGVuc3VyZSB0aGF0IGFsbCByZWZlcmVuY2VkIFVSTHMgY29tcGx5IHdpdGggc2VjdXJpdHkgcG9saWNpZXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3Qgb3B0aW9ucyBhbmQgc3RvcmUgcGFyc2VkIHN0cnVjdHVyZSBpbiB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC52YWxpZGF0ZWRPcHRpb25zID0ge1xyXG4gICAgICAvLyBTZXQgdGhlIGNyZWF0ZWQgSUQgYXMgYSBgcmVxdWVzdElkYCBwcm9wZXJ0eSBpbiB0aGUgb3B0aW9uc1xyXG4gICAgICByZXF1ZXN0SWQsXHJcbiAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIHN2ZzogYm9keS5zdmcsXHJcbiAgICAgICAgb3V0ZmlsZTpcclxuICAgICAgICAgIGJvZHkub3V0ZmlsZSB8fFxyXG4gICAgICAgICAgYCR7cmVxdWVzdC5wYXJhbXMuZmlsZW5hbWUgfHwgJ2NoYXJ0J30uJHtib2R5LnR5cGUgfHwgJ3BuZyd9YCxcclxuICAgICAgICB0eXBlOiBib2R5LnR5cGUsXHJcbiAgICAgICAgY29uc3RyOiBib2R5LmNvbnN0cixcclxuICAgICAgICBiNjQ6IGJvZHkuYjY0LFxyXG4gICAgICAgIG5vRG93bmxvYWQ6IGJvZHkubm9Eb3dubG9hZCxcclxuICAgICAgICBoZWlnaHQ6IGJvZHkuaGVpZ2h0LFxyXG4gICAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxyXG4gICAgICAgIHNjYWxlOiBib2R5LnNjYWxlLFxyXG4gICAgICAgIGdsb2JhbE9wdGlvbnM6IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgIGJvZHkuZ2xvYmFsT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApLFxyXG4gICAgICAgIHRoZW1lT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS50aGVtZU9wdGlvbnMsXHJcbiAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgKVxyXG4gICAgICB9LFxyXG4gICAgICBjdXN0b21Mb2dpYzoge1xyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvbixcclxuICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IGZhbHNlLFxyXG4gICAgICAgIGN1c3RvbUNvZGU6IGJvZHkuY3VzdG9tQ29kZSxcclxuICAgICAgICBjYWxsYmFjazogYm9keS5jYWxsYmFjayxcclxuICAgICAgICByZXNvdXJjZXM6IGlzQWxsb3dlZENvbmZpZyhib2R5LnJlc291cmNlcywgdHJ1ZSwgYWxsb3dDb2RlRXhlY3V0aW9uKVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIC8vIENhbGwgdGhlIG5leHQgbWlkZGxld2FyZVxyXG4gICAgcmV0dXJuIG5leHQoKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIHZhbGlkYXRpb24gbWlkZGxld2FyZXMgdG8gdGhlIHBhc3NlZCBleHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApIHtcclxuICAvLyBBZGQgY29udGVudCB0eXBlIHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIGNvbnRlbnRUeXBlTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCByZXF1ZXN0IGJvZHkgcmVxdWVzdCB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICBhcHAucG9zdChbJy8nLCAnLzpmaWxlbmFtZSddLCByZXF1ZXN0Qm9keU1pZGRsZXdhcmUpO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIHRoZSBleHBvcnQgcm91dGVzIGFuZCBsb2dpYyBmb3IgaGFuZGxpbmcgY2hhcnQgZXhwb3J0XHJcbiAqIHJlcXVlc3RzIGluIGFuIEV4cHJlc3Mgc2VydmVyLiBUaGlzIG1vZHVsZSBwcm9jZXNzZXMgaW5jb21pbmcgcmVxdWVzdHNcclxuICogdG8gZXhwb3J0IGNoYXJ0cyBpbiB2YXJpb3VzIGZvcm1hdHMgKGUuZy4gSlBFRywgUE5HLCBQREYsIFNWRykuIEl0IGludGVncmF0ZXNcclxuICogd2l0aCBIaWdoY2hhcnRzJyBjb3JlIGZ1bmN0aW9uYWxpdGllcyBhbmQgc3VwcG9ydHMgYm90aCBpbW1lZGlhdGUgZG93bmxvYWRcclxuICogcmVzcG9uc2VzIGFuZCBCYXNlNjQtZW5jb2RlZCBjb250ZW50IHJldHVybnMuIFRoZSBjb2RlIGFsc28gZmVhdHVyZXNcclxuICogYmVuY2htYXJraW5nIGZvciBwZXJmb3JtYW5jZSBtb25pdG9yaW5nLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHN0YXJ0RXhwb3J0IH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRCYXNlNjQsIG1lYXN1cmVUaW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBSZXZlcnNlZCBNSU1FIHR5cGVzXHJcbmNvbnN0IHJldmVyc2VkTWltZSA9IHtcclxuICBwbmc6ICdpbWFnZS9wbmcnLFxyXG4gIGpwZWc6ICdpbWFnZS9qcGVnJyxcclxuICBnaWY6ICdpbWFnZS9naWYnLFxyXG4gIHBkZjogJ2FwcGxpY2F0aW9uL3BkZicsXHJcbiAgc3ZnOiAnaW1hZ2Uvc3ZnK3htbCdcclxufTtcclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleHBvcnQgcmVxdWVzdHMgZnJvbSB0aGUgY2xpZW50LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnRcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHJlcXVlc3RFeHBvcnQocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICB0cnkge1xyXG4gICAgLy8gU3RhcnQgY291bnRpbmcgdGltZSBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCByZXF1ZXN0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gSW4gY2FzZSB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQsIGZvcmNlIHRvIGFib3J0IGZ1cnRoZXIgYWN0aW9uc1xyXG4gICAgbGV0IGNvbm5lY3Rpb25BYm9ydGVkID0gZmFsc2U7XHJcbiAgICByZXF1ZXN0LnNvY2tldC5vbignY2xvc2UnLCAoaGFkRXJyb3JzKSA9PiB7XHJcbiAgICAgIGlmIChoYWRFcnJvcnMpIHtcclxuICAgICAgICBjb25uZWN0aW9uQWJvcnRlZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEdldCB0aGUgb3B0aW9ucyBwcmV2aW91c2x5IHZhbGlkYXRlZCBpbiB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHJlcXVlc3QudmFsaWRhdGVkT3B0aW9ucztcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3QgaWRcclxuICAgIGNvbnN0IHJlcXVlc3RJZCA9IHJlcXVlc3RPcHRpb25zLnJlcXVlc3RJZDtcclxuXHJcbiAgICAvLyBJbmZvIGFib3V0IGFuIGluY29taW5nIHJlcXVlc3Qgd2l0aCBjb3JyZWN0IGRhdGFcclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3QuYCk7XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChyZXF1ZXN0T3B0aW9ucywgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgIC8vIFJlbW92ZSB0aGUgY2xvc2UgZXZlbnQgZnJvbSB0aGUgc29ja2V0XHJcbiAgICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcclxuXHJcbiAgICAgIC8vIElmIHRoZSBjb25uZWN0aW9uIHdhcyBjbG9zZWQsIGRvIG5vdGhpbmdcclxuICAgICAgaWYgKGNvbm5lY3Rpb25BYm9ydGVkKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gYmVmb3JlIHRoZSBjaGFydCBmaW5pc2hlZCBwcm9jZXNzaW5nLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZXJyb3IsIGxvZyBpdCBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCBsb2cgdGhlIG1lc3NhZ2UgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKCFkYXRhIHx8ICFkYXRhLnJlc3VsdCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8XHJcbiAgICAgICAgICAgIHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHJlc3VsdCBpcyAke2RhdGEucmVzdWx0fS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBVbmV4cGVjdGVkIHJldHVybiBvZiB0aGUgZXhwb3J0IHJlc3VsdCBmcm9tIHRoZSBjaGFydCBnZW5lcmF0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciByZXF1ZXN0IGRhdGEuYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFJldHVybiB0aGUgcmVzdWx0IGluIGFuIGFwcHJvcHJpYXRlIGZvcm1hdFxyXG4gICAgICBpZiAoZGF0YS5yZXN1bHQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3MgdG9vayAke3JlcXVlc3RDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYHR5cGVgLCBgYjY0YCwgYG5vRG93bmxvYWRgLCBhbmQgYG91dGZpbGVgIGZyb20gb3B0aW9uc1xyXG4gICAgICAgIGNvbnN0IHsgdHlwZSwgYjY0LCBub0Rvd25sb2FkLCBvdXRmaWxlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBJZiBvbmx5IEJhc2U2NCBpcyByZXF1aXJlZCwgcmV0dXJuIGl0XHJcbiAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTZXQgY29ycmVjdCBjb250ZW50IHR5cGVcclxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XHJcblxyXG4gICAgICAgIC8vIERlY2lkZSB3aGV0aGVyIHRvIGRvd25sb2FkIG9yIG5vdCBjaGFydCBmaWxlXHJcbiAgICAgICAgaWYgKCFub0Rvd25sb2FkKSB7XHJcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KG91dGZpbGUpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgU1ZHLCByZXR1cm4gcGxhaW4gY29udGVudCwgb3RoZXJ3aXNlIGEgYjY0IHN0cmluZyBmcm9tIGEgYnVmZmVyXHJcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXHJcbiAgICAgICAgICA/IHJlc3BvbnNlLnNlbmQoZGF0YS5yZXN1bHQpXHJcbiAgICAgICAgICA6IHJlc3BvbnNlLnNlbmQoQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYGV4cG9ydGAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZXhwb3J0Um91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gZXhwb3J0Um91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy8nIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyBhdCB0aGUgcm9vdFxyXG4gICAqIGVuZHBvaW50LlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvJywgcmVxdWVzdEV4cG9ydCk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy86ZmlsZW5hbWUnIC0gQSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyB3aXRoXHJcbiAgICogYSBzcGVjaWZpZWQgZmlsZW5hbWUgcGFyYW1ldGVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgcmVxdWVzdEV4cG9ydCk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmVyIGhlYWx0aCBtb25pdG9yaW5nLCBpbmNsdWRpbmdcclxuICogdXB0aW1lLCBzdWNjZXNzIHJhdGVzLCBhbmQgb3RoZXIgc2VydmVyIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBnZXRQb29sSW5mb0pTT04gfSBmcm9tICcuLi8uLi9wb29sLmpzJztcclxuaW1wb3J0IHsgYWRkVGltZXIgfSBmcm9tICcuLi8uLi90aW1lci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0TmV3RGF0ZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vLyBTZXQgdGhlIHN0YXJ0IGRhdGUgb2YgdGhlIHNlcnZlclxyXG5jb25zdCBzZXJ2ZXJTdGFydFRpbWUgPSBuZXcgRGF0ZSgpO1xyXG5cclxuLy8gR2V0IHRoZSBgcGFja2FnZS5qc29uYCBjb250ZW50XHJcbmNvbnN0IHBhY2thZ2VGaWxlID0gSlNPTi5wYXJzZShcclxuICByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICdwYWNrYWdlLmpzb24nKSwgJ3V0ZjgnKVxyXG4pO1xyXG5cclxuLy8gQW4gYXJyYXkgZm9yIHN1Y2Nlc3MgcmF0ZSByYXRpb3NcclxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XHJcblxyXG4vLyBSZWNvcmQgZXZlcnkgbWludXRlXHJcbmNvbnN0IHJlY29yZEludGVydmFsID0gNjAgKiAxMDAwO1xyXG5cclxuLy8gMzAgbWludXRlc1xyXG5jb25zdCB3aW5kb3dTaXplID0gMzA7XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgYHN1Y2Nlc3NSYXRlc2BcclxuICogYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBBIG1vdmluZyBhdmVyYWdlIGZvciBzdWNjZXNzIHJhdGlvIG9mIHRoZSBzZXJ2ZXIgZXhwb3J0cy5cclxuICovXHJcbmZ1bmN0aW9uIF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCkge1xyXG4gIHJldHVybiBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIHRoZSBpbnRlcnZhbCByZXNwb25zaWJsZSBmb3IgY2FsY3VsYXRpbmcgY3VycmVudCBzdWNjZXNzIHJhdGUgcmF0aW9cclxuICogYW5kIGNvbGxlY3RzIHJlY29yZHMgdG8gdGhlIGBzdWNjZXNzUmF0ZXNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3N0YXJ0U3VjY2Vzc1JhdGVcclxuICpcclxuICogQHJldHVybnMge05vZGVKUy5UaW1lb3V0fSBJZCBvZiBhbiBpbnRlcnZhbC5cclxuICovXHJcbmZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlKCkge1xyXG4gIHJldHVybiBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICBjb25zdCBzdGF0cyA9IGdldFBvb2xTdGF0cygpO1xyXG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID1cclxuICAgICAgc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCA9PT0gMFxyXG4gICAgICAgID8gMVxyXG4gICAgICAgIDogKHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQgLyBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkKSAqIDEwMDtcclxuXHJcbiAgICBzdWNjZXNzUmF0ZXMucHVzaChzdWNjZXNzUmF0aW8pO1xyXG4gICAgaWYgKHN1Y2Nlc3NSYXRlcy5sZW5ndGggPiB3aW5kb3dTaXplKSB7XHJcbiAgICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xyXG4gICAgfVxyXG4gIH0sIHJlY29yZEludGVydmFsKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBoZWFsdGhgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGhlYWx0aFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGhlYWx0aFJvdXRlcyhhcHApIHtcclxuICAvLyBTdGFydCBwcm9jZXNzaW5nIHN1Y2Nlc3MgcmF0ZSByYXRpbyBpbnRlcnZhbCBhbmQgc2F2ZSBpdHMgaWQgdG8gdGhlIGFycmF5XHJcbiAgLy8gZm9yIHRoZSBncmFjZWZ1bCBjbGVhcmluZyBvbiBzaHV0ZG93biB3aXRoIGluamVjdGVkIGBhZGRUaW1lcmAgZnVudGlvblxyXG4gIGFkZFRpbWVyKF9zdGFydFN1Y2Nlc3NSYXRlKCkpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy9oZWFsdGgnIC0gQSByb3V0ZSBmb3IgZ2V0dGluZyB0aGUgYmFzaWMgc3RhdHMgb2YgdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KCcvaGVhbHRoJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1toZWFsdGhdIFJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgICAgY29uc3QgbW92aW5nQXZlcmFnZSA9IF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgICAvLyBTZW5kIHRoZSBzZXJ2ZXIncyBzdGF0aXN0aWNzXHJcbiAgICAgIHJlc3BvbnNlLnNlbmQoe1xyXG4gICAgICAgIC8vIFN0YXR1cyBhbmQgdGltZXNcclxuICAgICAgICBzdGF0dXM6ICdPSycsXHJcbiAgICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcclxuICAgICAgICB1cHRpbWU6IGAke01hdGguZmxvb3IoKGdldE5ld0RhdGVUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MCl9IG1pbnV0ZXNgLFxyXG5cclxuICAgICAgICAvLyBWZXJzaW9uc1xyXG4gICAgICAgIHNlcnZlclZlcnNpb246IHBhY2thZ2VGaWxlLnZlcnNpb24sXHJcbiAgICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IGdldEhpZ2hjaGFydHNWZXJzaW9uKCksXHJcblxyXG4gICAgICAgIC8vIEV4cG9ydHNcclxuICAgICAgICBhdmVyYWdlRXhwb3J0VGltZTogc3RhdHMudGltZVNwZW50QXZlcmFnZSxcclxuICAgICAgICBhdHRlbXB0ZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkLFxyXG4gICAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQsXHJcbiAgICAgICAgZmFpbGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0Ryb3BwZWQsXHJcbiAgICAgICAgc3VjZXNzUmF0aW86IChzdGF0cy5leHBvcnRzUGVyZm9ybWVkIC8gc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCkgKiAxMDAsXHJcblxyXG4gICAgICAgIC8vIFBvb2xcclxuICAgICAgICBwb29sOiBnZXRQb29sSW5mb0pTT04oKSxcclxuXHJcbiAgICAgICAgLy8gTW92aW5nIGF2ZXJhZ2VcclxuICAgICAgICBwZXJpb2QsXHJcbiAgICAgICAgbW92aW5nQXZlcmFnZSxcclxuICAgICAgICBtZXNzYWdlOlxyXG4gICAgICAgICAgaXNOYU4obW92aW5nQXZlcmFnZSkgfHwgIXN1Y2Nlc3NSYXRlcy5sZW5ndGhcclxuICAgICAgICAgICAgPyAnVG9vIGVhcmx5IHRvIHJlcG9ydC4gTm8gZXhwb3J0cyBtYWRlIHlldC4gUGxlYXNlIGNoZWNrIGJhY2sgc29vbi4nXHJcbiAgICAgICAgICAgIDogYExhc3QgJHtwZXJpb2R9IG1pbnV0ZXMgaGFkIGEgc3VjY2VzcyByYXRlIG9mICR7bW92aW5nQXZlcmFnZS50b0ZpeGVkKDIpfSUuYCxcclxuXHJcbiAgICAgICAgLy8gU1ZHIGFuZCBKU09OIGV4cG9ydHNcclxuICAgICAgICBzdmdFeHBvcnRzOiBzdGF0cy5leHBvcnRzRnJvbVN2ZyxcclxuICAgICAgICBqc29uRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zLFxyXG4gICAgICAgIHN2Z0V4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyxcclxuICAgICAgICBqc29uRXhwb3J0c0F0dGVtcHRzOiBzdGF0cy5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0c1xyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciBzZXJ2aW5nIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXJcclxuICogd2hlbiBlbmFibGVkLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHVpYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB1aVJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHVpUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIEdFVCAnLycgLSBBIHJvdXRlIGZvciBhIFVJIHdoZW4gZW5hYmxlZCBvbiB0aGUgZXhwb3J0IHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KGdldE9wdGlvbnMoKS51aS5yb3V0ZSB8fCAnLycsIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdWldIFJldHVybmluZyBVSSBmb3IgdGhlIGV4cG9ydC4nKTtcclxuXHJcbiAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwge1xyXG4gICAgICAgIGFjY2VwdFJhbmdlczogZmFsc2VcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3IgdXBkYXRpbmcgdGhlIEhpZ2hjaGFydHMgdmVyc2lvblxyXG4gKiBvbiB0aGUgc2VydmVyLCB3aXRoIGF1dGhlbnRpY2F0aW9uIGFuZCB2YWxpZGF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGdldEhpZ2hjaGFydHNWZXJzaW9uLCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYHZlcnNpb25fY2hhbmdlYCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApIHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUICcvdmVyc2lvbl9jaGFuZ2UvOm5ld1ZlcnNpb24nIC0gQSByb3V0ZSBmb3IgY2hhbmdpbmdcclxuICAgKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicsIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbdmVyc2lvbl0gQ2hhbmdpbmcgSGlnaGNoYXJ0cyB2ZXJzaW9uLicpO1xyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBkaXJlY3RseSBmcm9tIGVudnNcclxuICAgICAgY29uc3QgYWRtaW5Ub2tlbiA9IGVudnMuSElHSENIQVJUU19BRE1JTl9UT0tFTjtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBleGlzdGVuY2Ugb2YgdGhlIHRva2VuXHJcbiAgICAgIGlmICghYWRtaW5Ub2tlbiB8fCAhYWRtaW5Ub2tlbi5sZW5ndGgpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIFRoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0b2tlbiBmcm9tIHRoZSBoYy1hdXRoIGhlYWRlclxyXG4gICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbdmVyc2lvbl0gSW52YWxpZCBvciBtaXNzaW5nIHRva2VuOiBTZXQgdGhlIHRva2VuIGluIHRoZSBoYy1hdXRoIGhlYWRlci4nLFxyXG4gICAgICAgICAgNDAxXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xyXG4gICAgICBsZXQgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XHJcbiAgICAgIGlmIChuZXdWZXJzaW9uKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIC8vIFVwZGF0ZSB2ZXJzaW9uXHJcbiAgICAgICAgICBhd2FpdCB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICBgW3ZlcnNpb25dIFZlcnNpb24gY2hhbmdlOiAke2Vycm9yLm1lc3NhZ2V9YCxcclxuICAgICAgICAgICAgNDAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFN1Y2Nlc3NcclxuICAgICAgICByZXNwb25zZS5zdGF0dXMoMjAwKS5zZW5kKHtcclxuICAgICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcclxuICAgICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpLFxyXG4gICAgICAgICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIEhpZ2hjaGFydHMgdG8gdmVyc2lvbjogJHtuZXdWZXJzaW9ufS5gXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gTm8gdmVyc2lvbiBzcGVjaWZpZWRcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1t2ZXJzaW9uXSBObyBuZXcgdmVyc2lvbiBzdXBwbGllZC4nLCA0MDApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEEgbW9kdWxlIHRoYXQgc2V0cyB1cCBhbmQgbWFuYWdlcyBIVFRQIGFuZCBIVFRQUyBzZXJ2ZXJzXHJcbiAqIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBoYW5kbGVzIHNlcnZlciBpbml0aWFsaXphdGlvbixcclxuICogY29uZmlndXJhdGlvbiwgZXJyb3IgaGFuZGxpbmcsIG1pZGRsZXdhcmUgc2V0dXAsIHJvdXRlIGRlZmluaXRpb24sIGFuZCByYXRlXHJcbiAqIGxpbWl0aW5nLiBUaGUgbW9kdWxlIGV4cG9ydHMgZnVuY3Rpb25zIHRvIHN0YXJ0LCBzdG9wLCBhbmQgbWFuYWdlIHNlcnZlclxyXG4gKiBpbnN0YW5jZXMsIGFzIHdlbGwgYXMgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIGRlZmluaW5nIHJvdXRlcyBhbmQgYXR0YWNoaW5nXHJcbiAqIG1pZGRsZXdhcmVzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xyXG5cclxuaW1wb3J0IHsgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgZXJyb3JNaWRkbGV3YXJlIGZyb20gJy4vbWlkZGxld2FyZXMvZXJyb3IuanMnO1xyXG5pbXBvcnQgcmF0ZUxpbWl0aW5nTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3JhdGVMaW1pdGluZy5qcyc7XHJcbmltcG9ydCB2YWxpZGF0aW9uTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL3ZhbGlkYXRpb24uanMnO1xyXG5cclxuaW1wb3J0IGV4cG9ydFJvdXRlcyBmcm9tICcuL3JvdXRlcy9leHBvcnQuanMnO1xyXG5pbXBvcnQgaGVhbHRoUm91dGVzIGZyb20gJy4vcm91dGVzL2hlYWx0aC5qcyc7XHJcbmltcG9ydCB1aVJvdXRlcyBmcm9tICcuL3JvdXRlcy91aS5qcyc7XHJcbmltcG9ydCB2ZXJzaW9uQ2hhbmdlUm91dGVzIGZyb20gJy4vcm91dGVzL3ZlcnNpb25DaGFuZ2UuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4uL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBBcnJheSBvZiBhbiBhY3RpdmUgc2VydmVyc1xyXG5jb25zdCBhY3RpdmVTZXJ2ZXJzID0gbmV3IE1hcCgpO1xyXG5cclxuLy8gQ3JlYXRlIGV4cHJlc3MgYXBwXHJcbmNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gSFRUUCBhbmQvb3IgSFRUUFMgc2VydmVyIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLlxyXG4gKiBUaGUgYHNlcnZlck9wdGlvbnNgIG9iamVjdCBjb250YWlucyBzZXJ2ZXItcmVsYXRlZCBwcm9wZXJ0aWVzIChyZWZlclxyXG4gKiB0byB0aGUgYHNlcnZlcmAgc2VjdGlvbiBpbiB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIGZvciBkZXRhaWxzKS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydFNlcnZlclxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBzZXJ2ZXJgXHJcbiAqIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2YgdGhlIGBzZXJ2ZXJgXHJcbiAqIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlcyB3aWxsIGRlZmF1bHRcclxuICogdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgZWl0aGVyXHJcbiAqIG5vdCBlbmFibGVkIG9yIG5vIHZhbGlkIEV4cHJlc3MgYXBwIGlzIGZvdW5kLCBzaWduYWxpbmcgdGhlIGVuZCBvZiB0aGVcclxuICogZnVuY3Rpb24ncyBleGVjdXRpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgc2VydmVyIGNhbm5vdFxyXG4gKiBiZSBjb25maWd1cmVkIGFuZCBzdGFydGVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0U2VydmVyKHNlcnZlck9wdGlvbnMpIHtcclxuICB0cnkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBzZXJ2ZXI6IHNlcnZlck9wdGlvbnNcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFVzZSB2YWxpZGF0ZWQgb3B0aW9uc1xyXG4gICAgc2VydmVyT3B0aW9ucyA9IG9wdGlvbnMuc2VydmVyO1xyXG5cclxuICAgIC8vIFN0b3AgaWYgbm90IGVuYWJsZWRcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5lbmFibGUgfHwgIWFwcCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1tzZXJ2ZXJdIFNlcnZlciBjYW5ub3QgYmUgc3RhcnRlZCAobm90IGVuYWJsZWQgb3Igbm8gY29ycmVjdCBFeHByZXNzIGFwcCBmb3VuZCkuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUb28gYmlnIGxpbWl0cyBsZWFkIHRvIHRpbWVvdXRzIGluIHRoZSBleHBvcnQgcHJvY2VzcyB3aGVuXHJcbiAgICAvLyB0aGUgcmFzdGVyaXphdGlvbiB0aW1lb3V0IGlzIHNldCB0b28gbG93XHJcbiAgICBjb25zdCB1cGxvYWRMaW1pdEJ5dGVzID0gc2VydmVyT3B0aW9ucy51cGxvYWRMaW1pdCAqIDEwMjQgKiAxMDI0O1xyXG5cclxuICAgIC8vIE1lbW9yeSBzdG9yYWdlIGZvciBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XHJcblxyXG4gICAgLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBtdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3QgdXBsb2FkID0gbXVsdGVyKHtcclxuICAgICAgc3RvcmFnZSxcclxuICAgICAgbGltaXRzOiB7XHJcbiAgICAgICAgZmllbGRTaXplOiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIERpc2FibGUgdGhlIFgtUG93ZXJlZC1CeSBoZWFkZXJcclxuICAgIGFwcC5kaXNhYmxlKCd4LXBvd2VyZWQtYnknKTtcclxuXHJcbiAgICAvLyBFbmFibGUgQ09SUyBzdXBwb3J0XHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBjb3JzKHtcclxuICAgICAgICBtZXRob2RzOiBbJ1BPU1QnLCAnR0VUJywgJ09QVElPTlMnXVxyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXR0aW5nIGEgbG90IG9mIGBSYW5nZU5vdFNhdGlzZmlhYmxlRXJyb3JgIGV4Y2VwdGlvbnMgKGV2ZW4gdGhvdWdoIHRoaXNcclxuICAgIC8vIGlzIGEgZGVwcmVjYXRlZCBvcHRpb25zLCBsZXQncyB0cnkgdG8gc2V0IGl0IHRvIGZhbHNlKVxyXG4gICAgYXBwLnVzZSgocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgICAgcmVzcG9uc2Uuc2V0KCdBY2NlcHQtUmFuZ2VzJywgJ25vbmUnKTtcclxuICAgICAgbmV4dCgpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBKU09OIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MuanNvbih7XHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyIGZvciBVUkwtZW5jb2RlZCBmb3JtIGRhdGFcclxuICAgIGFwcC51c2UoXHJcbiAgICAgIGV4cHJlc3MudXJsZW5jb2RlZCh7XHJcbiAgICAgICAgZXh0ZW5kZWQ6IHRydWUsXHJcbiAgICAgICAgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gVXNlIG9ubHkgbm9uLWZpbGUgbXVsdGlwYXJ0IGZvcm0gZmllbGRzXHJcbiAgICBhcHAudXNlKHVwbG9hZC5ub25lKCkpO1xyXG5cclxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcclxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQIHNlcnZlclxyXG4gICAgaWYgKCFzZXJ2ZXJPcHRpb25zLnNzbC5mb3JjZSkge1xyXG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcCk7XHJcblxyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKGh0dHBTZXJ2ZXIpO1xyXG5cclxuICAgICAgLy8gTGlzdGVuXHJcbiAgICAgIGh0dHBTZXJ2ZXIubGlzdGVuKHNlcnZlck9wdGlvbnMucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFAgc2VydmVyXHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5zZXQoc2VydmVyT3B0aW9ucy5wb3J0LCBodHRwU2VydmVyKTtcclxuXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyT3B0aW9ucy5ob3N0fToke3NlcnZlck9wdGlvbnMucG9ydH0uYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQUyBzZXJ2ZXJcclxuICAgIGlmIChzZXJ2ZXJPcHRpb25zLnNzbC5lbmFibGUpIHtcclxuICAgICAgLy8gU2V0IHVwIGFuIFNTTCBzZXJ2ZXIgYWxzb1xyXG4gICAgICBsZXQga2V5LCBjZXJ0O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBrZXlcclxuICAgICAgICBrZXkgPSByZWFkRmlsZVN5bmMoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIua2V5JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBjZXJ0aWZpY2F0ZVxyXG4gICAgICAgIGNlcnQgPSByZWFkRmlsZVN5bmMoXHJcbiAgICAgICAgICBqb2luKGdldEFic29sdXRlUGF0aChzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aCksICdzZXJ2ZXIuY3J0JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gVW5hYmxlIHRvIGxvYWQga2V5L2NlcnRpZmljYXRlIGZyb20gdGhlICcke3NlcnZlck9wdGlvbnMuc3NsLmNlcnRQYXRofScgcGF0aC4gQ291bGQgbm90IHJ1biBzZWN1cmVkIGxheWVyIHNlcnZlci5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKGtleSAmJiBjZXJ0KSB7XHJcbiAgICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFBTKVxyXG4gICAgICAgIGNvbnN0IGh0dHBzU2VydmVyID0gaHR0cHMuY3JlYXRlU2VydmVyKHsga2V5LCBjZXJ0IH0sIGFwcCk7XHJcblxyXG4gICAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgIC8vIExpc3RlblxyXG4gICAgICAgIGh0dHBzU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBzZXJ2ZXJPcHRpb25zLmhvc3QsICgpID0+IHtcclxuICAgICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQUyBzZXJ2ZXJcclxuICAgICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMuc3NsLnBvcnQsIGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0fS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSByYXRlIGxpbWl0ZXJcclxuICAgIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBzZXJ2ZXJPcHRpb25zLnJhdGVMaW1pdGluZyk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSB2YWxpZGF0aW9uIGhhbmRsZXJcclxuICAgIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHJvdXRlc1xyXG4gICAgZXhwb3J0Um91dGVzKGFwcCk7XHJcbiAgICBoZWFsdGhSb3V0ZXMoYXBwKTtcclxuICAgIHVpUm91dGVzKGFwcCk7XHJcbiAgICB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHRoZSBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXHJcbiAgICBlcnJvck1pZGRsZXdhcmUoYXBwKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbG9zZVNlcnZlcnNcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjbG9zZVNlcnZlcnMoKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgYXJlIHNlcnZlcnMgd29ya2luZ1xyXG4gIGlmIChhY3RpdmVTZXJ2ZXJzLnNpemUgPiAwKSB7XHJcbiAgICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NpbmcgYWxsIHNlcnZlcnMuYCk7XHJcblxyXG4gICAgLy8gQ2xvc2UgZWFjaCBvbmUgb2Ygc2VydmVyc1xyXG4gICAgZm9yIChjb25zdCBbcG9ydCwgc2VydmVyXSBvZiBhY3RpdmVTZXJ2ZXJzKSB7XHJcbiAgICAgIHNlcnZlci5jbG9zZSgoKSA9PiB7XHJcbiAgICAgICAgYWN0aXZlU2VydmVycy5kZWxldGUocG9ydCk7XHJcbiAgICAgICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zZWQgc2VydmVyIG9uIHBvcnQ6ICR7cG9ydH0uYCk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRTZXJ2ZXJzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtBcnJheS48T2JqZWN0Pn0gU2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VydmVycygpIHtcclxuICByZXR1cm4gYWN0aXZlU2VydmVycztcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEV4cHJlc3NcclxuICpcclxuICogQHJldHVybnMge0V4cHJlc3N9IFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEV4cHJlc3MoKSB7XHJcbiAgcmV0dXJuIGV4cHJlc3M7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QXBwXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXBwKCkge1xyXG4gIHJldHVybiBhcHA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXRcclxuICogb2YgdGhlIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlc1xyXG4gKiB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlUmF0ZUxpbWl0aW5nKHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgc2VydmVyOiB7XHJcbiAgICAgIHJhdGVMaW1pdGluZzogcmF0ZUxpbWl0aW5nT3B0aW9uc1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9uc1xyXG4gIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBvcHRpb25zLnNlcnZlci5yYXRlTGltaXRpbmdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFwcGx5IG1pZGRsZXdhcmUocykgdG8gYSBzcGVjaWZpYyBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXNlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggR0VUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIFBPU1QgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5wb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEF0dGFjaCBlcnJvciBoYW5kbGVycyB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnNcclxuICpcclxuICogQHBhcmFtIHsoaHR0cC5TZXJ2ZXJ8aHR0cHMuU2VydmVyKX0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoc2VydmVyKSB7XHJcbiAgc2VydmVyLm9uKCdjbGllbnRFcnJvcicsIChlcnJvciwgc29ja2V0KSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDEsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9LCBkZXN0cm95aW5nIHNvY2tldC5gXHJcbiAgICApO1xyXG4gICAgc29ja2V0LmRlc3Ryb3koKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignY29ubmVjdGlvbicsIChzb2NrZXQpID0+IHtcclxuICAgIHNvY2tldC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGNsb3NlU2VydmVycyxcclxuICBnZXRTZXJ2ZXJzLFxyXG4gIGdldEV4cHJlc3MsXHJcbiAgZ2V0QXBwLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhhbmRsZXMgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciwgZW5zdXJpbmdcclxuICogcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzIHN1Y2ggYXMgYnJvd3NlciwgcGFnZXMsIHNlcnZlcnMsIGFuZCB0aW1lcnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBjbGVhckFsbFRpbWVycyB9IGZyb20gJy4vdGltZXIuanMnO1xyXG5cclxuaW1wb3J0IHsgY2xvc2VTZXJ2ZXJzIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBjbGVhbnVwIG9wZXJhdGlvbnMgdG8gZW5zdXJlIGEgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIHByb2Nlc3MuXHJcbiAqIFRoaXMgaW5jbHVkZXMgY2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgdGltZW91dHMvaW50ZXJ2YWxzLCBjbG9zaW5nIGFjdGl2ZVxyXG4gKiBzZXJ2ZXJzLCB0ZXJtaW5hdGluZyByZXNvdXJjZXMgKHBhZ2VzKSBvZiB0aGUgcG9vbCwgcG9vbCBpdHNlbGYsIGFuZCBjbG9zaW5nXHJcbiAqIHRoZSBicm93c2VyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbZXhpdENvZGU9MF0gLSBUaGUgZXhpdCBjb2RlIHRvIHVzZSB3aXRoIGBwcm9jZXNzLmV4aXQoKWAuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGAwYC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXAoZXhpdENvZGUgPSAwKSB7XHJcbiAgLy8gQXdhaXQgZnJlZWluZyBhbGwgcmVzb3VyY2VzXHJcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcclxuICAgIC8vIENsZWFyIGFsbCBvbmdvaW5nIGludGVydmFsc1xyXG4gICAgY2xlYXJBbGxUaW1lcnMoKSxcclxuXHJcbiAgICAvLyBHZXQgYXZhaWxhYmxlIHNlcnZlciBpbnN0YW5jZXMgKEhUVFAvSFRUUFMpIGFuZCBjbG9zZSB0aGVtXHJcbiAgICBjbG9zZVNlcnZlcnMoKSxcclxuXHJcbiAgICAvLyBDbG9zZSBhbiBhY3RpdmUgcG9vbCBhbG9uZyB3aXRoIGl0cyB3b3JrZXJzIGFuZCB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gICAga2lsbFBvb2woKVxyXG4gIF0pO1xyXG5cclxuICAvLyBFeGl0IHByb2Nlc3Mgd2l0aCBhIGNvcnJlY3QgY29kZVxyXG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaHV0ZG93bkNsZWFuVXBcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IENvcmUgbW9kdWxlIGZvciBpbml0aWFsaXppbmcgYW5kIG1hbmFnaW5nIHRoZSBIaWdoY2hhcnRzIEV4cG9ydFxyXG4gKiBTZXJ2ZXIuIFByb3ZpZGVzIGZ1bmN0aW9uYWxpdGllcyBmb3IgY29uZmlndXJpbmcgZXhwb3J0cywgc2V0dGluZyB1cCBzZXJ2ZXJcclxuICogb3BlcmF0aW9ucywgbG9nZ2luZywgc2NyaXB0cyBjYWNoaW5nLCByZXNvdXJjZSBwb29saW5nLCBhbmQgZ3JhY2VmdWwgcHJvY2Vzc1xyXG4gKiBjbGVhbnVwLlxyXG4gKi9cclxuXHJcbmltcG9ydCAnY29sb3JzJztcclxuXHJcbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHtcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMsIG1hcFRvTmV3T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHtcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIGluaXRMb2dnaW5nLFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG4gIHNldExvZ0xldmVsXHJcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpbml0UG9vbCwga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzaHV0ZG93bkNsZWFuVXAgfSBmcm9tICcuL3Jlc291cmNlUmVsZWFzZS5qcyc7XHJcblxyXG5pbXBvcnQgc2VydmVyIGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwcm9jZXNzLiBUYXNrcyBzdWNoIGFzIGNvbmZpZ3VyaW5nIGxvZ2dpbmcsIGNoZWNraW5nXHJcbiAqIHRoZSBjYWNoZSBhbmQgc291cmNlcywgYW5kIGluaXRpYWxpemluZyB0aGUgcmVzb3VyY2UgcG9vbCBvY2N1ciBkdXJpbmcgdGhpc1xyXG4gKiBzdGFnZS5cclxuICpcclxuICogVGhpcyBmdW5jdGlvbiBtdXN0IGJlIGNhbGxlZCBiZWZvcmUgYXR0ZW1wdGluZyB0byBleHBvcnQgY2hhcnRzIG9yIHNldFxyXG4gKiB1cCBhIHNlcnZlci5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBpbml0RXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpbml0T3B0aW9ucyAtIFRoZSBgaW5pdE9wdGlvbnNgIG9iamVjdCwgd2hpY2ggbWF5XHJcbiAqIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXQgb2Ygb3B0aW9ucy4gSWYgdGhlIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmdcclxuICogdmFsdWVzIHdpbGwgZGVmYXVsdCB0byB0aGUgY3VycmVudCBnbG9iYWwgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbml0RXhwb3J0KGluaXRPcHRpb25zKSB7XHJcbiAgLy8gSW5pdCBhbmQgdXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKGluaXRPcHRpb25zKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBwZXIgZXhwb3J0IG1vZHVsZSBzY29wZVxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gIC8vIEluaXQgdGhlIGxvZ2dpbmdcclxuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAob3B0aW9ucy5vdGhlci5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXHJcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMsIG9wdGlvbnMuc2VydmVyLnByb3h5KTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgcG9vbFxyXG4gIGF3YWl0IGluaXRQb29sKG9wdGlvbnMucG9vbCwgb3B0aW9ucy5wdXBwZXRlZXIuYXJncyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXHJcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nXHJcbiAqIGFuZCAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVyc1xyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCkge1xyXG4gIGxvZygzLCAnW3Byb2Nlc3NdIEF0dGFjaGluZyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdleGl0J1xyXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gUHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0uYCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSU5UJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdURVJNJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICd1bmNhdWdodEV4Y2VwdGlvbidcclxuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGVycm9yLmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDEpO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgLy8gU2VydmVyXHJcbiAgLi4uc2VydmVyLFxyXG5cclxuICAvLyBPcHRpb25zXHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIG1hcFRvTmV3T3B0aW9ucyxcclxuXHJcbiAgLy8gRXhwb3J0aW5nXHJcbiAgaW5pdEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcblxyXG4gIC8vIFJlbGVhc2VcclxuICBraWxsUG9vbCxcclxuICBzaHV0ZG93bkNsZWFuVXAsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsOiBmdW5jdGlvbiAobGV2ZWwpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGxldmVsXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICBzZXRMb2dMZXZlbChvcHRpb25zLmxvZ2dpbmcubGV2ZWwpO1xyXG4gIH0sXHJcbiAgZW5hYmxlQ29uc29sZUxvZ2dpbmc6IGZ1bmN0aW9uICh0b0NvbnNvbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIHRvQ29uc29sZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlQ29uc29sZUxvZ2dpbmcob3B0aW9ucy5sb2dnaW5nLnRvQ29uc29sZSk7XHJcbiAgfSxcclxuICBlbmFibGVGaWxlTG9nZ2luZzogZnVuY3Rpb24gKGRlc3QsIGZpbGUsIHRvRmlsZSkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBsb2dnaW5nOiB7XHJcbiAgICAgICAgZGVzdCxcclxuICAgICAgICBmaWxlLFxyXG4gICAgICAgIHRvRmlsZVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy5kZXN0LFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZmlsZSxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLnRvRmlsZVxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcbiJdLCJuYW1lcyI6WyJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInRhZ05hbWUiLCJ0b1VwcGVyQ2FzZSIsInNyYyIsImJhc2VVUkkiLCJkZWVwQ29weSIsIm9iakFyciIsIm9iakFyckNvcHkiLCJBcnJheSIsImlzQXJyYXkiLCJrZXkiLCJPYmplY3QiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJmaXhDb25zdHIiLCJjb25zdHIiLCJmaXhlZENvbnN0ciIsInRvTG93ZXJDYXNlIiwicmVwbGFjZSIsImluY2x1ZGVzIiwiZml4T3V0ZmlsZSIsInR5cGUiLCJvdXRmaWxlIiwiZ2V0QWJzb2x1dGVQYXRoIiwic3BsaXQiLCJzaGlmdCIsImZpeFR5cGUiLCJtaW1lVHlwZXMiLCJmb3JtYXRzIiwidmFsdWVzIiwib3V0VHlwZSIsInBvcCIsImZpbmQiLCJ0IiwicGF0aCIsImlzQWJzb2x1dGUiLCJub3JtYWxpemUiLCJyZXNvbHZlIiwiZ2V0QmFzZTY0IiwiaW5wdXQiLCJCdWZmZXIiLCJmcm9tIiwidG9TdHJpbmciLCJnZXROZXdEYXRlIiwiRGF0ZSIsInRyaW0iLCJnZXROZXdEYXRlVGltZSIsImdldFRpbWUiLCJpc09iamVjdCIsIml0ZW0iLCJpc09iamVjdEVtcHR5Iiwia2V5cyIsImxlbmd0aCIsImlzUHJpdmF0ZVJhbmdlVXJsRm91bmQiLCJzb21lIiwicGF0dGVybiIsInRlc3QiLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwicHJvY2VzcyIsImhydGltZSIsImJpZ2ludCIsIk51bWJlciIsInJvdW5kTnVtYmVyIiwidmFsdWUiLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwiTWF0aCIsInBvdyIsInJvdW5kIiwid3JhcEFyb3VuZCIsImN1c3RvbUNvZGUiLCJhbGxvd0ZpbGVSZXNvdXJjZXMiLCJpc0NhbGxiYWNrIiwiZW5kc1dpdGgiLCJyZWFkRmlsZVN5bmMiLCJzdGFydHNXaXRoIiwiY29sb3JzIiwibG9nZ2luZyIsInRvQ29uc29sZSIsInRvRmlsZSIsInBhdGhDcmVhdGVkIiwicGF0aFRvTG9nIiwibGV2ZWxzRGVzYyIsInRpdGxlIiwiY29sb3IiLCJsb2ciLCJhcmdzIiwibmV3TGV2ZWwiLCJ0ZXh0cyIsImxldmVsIiwicHJlZml4IiwiX2xvZ1RvRmlsZSIsImNvbnNvbGUiLCJhcHBseSIsInVuZGVmaW5lZCIsImNvbmNhdCIsImxvZ1dpdGhTdGFjayIsImVycm9yIiwiY3VzdG9tTWVzc2FnZSIsIm1haW5NZXNzYWdlIiwibWVzc2FnZSIsInN0YWNrTWVzc2FnZSIsInN0YWNrIiwicHVzaCIsImluaXRMb2dnaW5nIiwibG9nZ2luZ09wdGlvbnMiLCJkZXN0IiwiZmlsZSIsInNldExvZ0xldmVsIiwiZW5hYmxlQ29uc29sZUxvZ2dpbmciLCJlbmFibGVGaWxlTG9nZ2luZyIsImlzSW50ZWdlciIsImV4aXN0c1N5bmMiLCJta2RpclN5bmMiLCJqb2luIiwiYXBwZW5kRmlsZSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwic2VwYXJhdG9yIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VcmwiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiY29yZVNjcmlwdHMiLCJpbnN0cnVjdGlvbnMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJzdmciLCJiYXRjaCIsImhpbnQiLCJjaG9pY2VzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwibWluIiwibWF4IiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsImJyb3dzZXJTaGVsbE1vZGUiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJjb25maWciLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJkb3RlbnYiLCJ2IiwiYXJyYXkiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJtYXAiLCJmaWx0ZXIiLCJib29sZWFuIiwiZW51bSIsInJlZmluZSIsInBvc2l0aXZlTnVtIiwiaXNOYU4iLCJwYXJzZUZsb2F0Iiwibm9uTmVnYXRpdmVOdW0iLCJDb25maWciLCJvYmplY3QiLCJQVVBQRVRFRVJfQVJHUyIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfQ1VTVE9NX1NDUklQVFMiLCJFWFBPUlRfSU5GSUxFIiwiRVhQT1JUX0lOU1RSIiwiRVhQT1JUX09QVElPTlMiLCJFWFBPUlRfU1ZHIiwiRVhQT1JUX0JBVENIIiwiRVhQT1JUX09VVEZJTEUiLCJFWFBPUlRfVFlQRSIsIkVYUE9SVF9DT05TVFIiLCJFWFBPUlRfQjY0IiwiRVhQT1JUX05PX0RPV05MT0FEIiwiRVhQT1JUX0hFSUdIVCIsIkVYUE9SVF9XSURUSCIsIkVYUE9SVF9TQ0FMRSIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfR0xPQkFMX09QVElPTlMiLCJFWFBPUlRfVEhFTUVfT1BUSU9OUyIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUiLCJDVVNUT01fTE9HSUNfQ0FMTEJBQ0siLCJDVVNUT01fTE9HSUNfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHIiwiQ1VTVE9NX0xPR0lDX0NSRUFURV9DT05GSUciLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9VUExPQURfTElNSVQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIkxPR0dJTkdfVE9fQ09OU09MRSIsIkxPR0dJTkdfVE9fRklMRSIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJlbnZzIiwicGFydGlhbCIsInBhcnNlIiwiZW52IiwiX2luaXRPcHRpb25zIiwiZ2V0T3B0aW9ucyIsImdldENvcHkiLCJ1cGRhdGVPcHRpb25zIiwibmV3T3B0aW9ucyIsIl9tZXJnZU9wdGlvbnMiLCJtYXBUb05ld09wdGlvbnMiLCJvbGRPcHRpb25zIiwiZW50cmllcyIsInByb3BlcnRpZXNDaGFpbiIsInJlZHVjZSIsIm9iaiIsInByb3AiLCJpbmRleCIsImlzQWxsb3dlZENvbmZpZyIsImFsbG93RnVuY3Rpb25zIiwib2JqZWN0Q29uZmlnIiwiZXZhbCIsIkpTT04iLCJzdHJpbmdpZmllZE9wdGlvbnMiLCJfb3B0aW9uc1N0cmluZ2lmeSIsInBhcnNlZE9wdGlvbnMiLCJfIiwibmFtZSIsIm9yaWdpbmFsT3B0aW9ucyIsInN0cmluZ2lmeUZ1bmN0aW9ucyIsInN0cmluZ2lmeSIsInJlcGxhY2VBbGwiLCJFcnJvciIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZWplY3QiLCJfZ2V0UHJvdG9jb2xNb2R1bGUiLCJnZXQiLCJyZXNwb25zZSIsInJlc3BvbnNlRGF0YSIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiaHR0cHMiLCJodHRwIiwiRXhwb3J0RXJyb3IiLCJjb25zdHJ1Y3RvciIsInN0YXR1c0NvZGUiLCJzdXBlciIsInRoaXMiLCJzZXRTdGF0dXMiLCJzZXRFcnJvciIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsImhpZ2hjaGFydHNPcHRpb25zIiwic2VydmVyUHJveHlPcHRpb25zIiwiZmV0Y2hlZE1vZHVsZXMiLCJnZXRDYWNoZVBhdGgiLCJtYW5pZmVzdFBhdGgiLCJzb3VyY2VQYXRoIiwicmVjdXJzaXZlIiwiX3VwZGF0ZUNhY2hlIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlcyIsIm1vZHVsZU1hcCIsIm0iLCJudW1iZXJPZk1vZHVsZXMiLCJtb2R1bGVOYW1lIiwiZXh0cmFjdFZlcnNpb24iLCJfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRIaWdoY2hhcnRzVmVyc2lvbiIsInVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIiwibmV3VmVyc2lvbiIsImNhY2hlU291cmNlcyIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0Iiwic2NyaXB0Iiwic2hvdWxkVGhyb3dFcnJvciIsIm5ld01hbmlmZXN0Iiwid3JpdGVGaWxlU3luYyIsIl9mZXRjaFNjcmlwdHMiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiYyIsImkiLCJzZXR1cEhpZ2hjaGFydHMiLCJIaWdoY2hhcnRzIiwiYW5pbU9iamVjdCIsImR1cmF0aW9uIiwiY3JlYXRlQ2hhcnQiLCJleHBvcnRPcHRpb25zIiwiY3VzdG9tTG9naWNPcHRpb25zIiwic2V0T3B0aW9ucyIsIm1lcmdlIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJpc1JlbmRlckNvbXBsZXRlIiwiQ2hhcnQiLCJwcm9jZWVkIiwidXNlck9wdGlvbnMiLCJjYiIsImV4cG9ydGluZyIsImVuYWJsZWQiLCJwbG90T3B0aW9ucyIsInNlcmllcyIsImxhYmVsIiwidG9vbHRpcCIsImFuaW1hdGlvbiIsIm9uSGlnaGNoYXJ0c1JlbmRlciIsImFkZEV2ZW50IiwiU2VyaWVzIiwiY2hhcnQiLCJhZGRpdGlvbmFsT3B0aW9ucyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImRlZmF1bHRPcHRpb25zIiwidGVtcGxhdGUiLCJicm93c2VyIiwiY3JlYXRlQnJvd3NlciIsInB1cHBldGVlckFyZ3MiLCJlbmFibGVkRGVidWciLCJkZWJ1Z09wdGlvbnMiLCJsYXVuY2hPcHRpb25zIiwidXNlckRhdGFEaXIiLCJoYW5kbGVTSUdJTlQiLCJoYW5kbGVTSUdURVJNIiwiaGFuZGxlU0lHSFVQIiwid2FpdEZvckluaXRpYWxQYWdlIiwiZGVmYXVsdFZpZXdwb3J0IiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwic2V0VGltZW91dCIsImNsb3NlQnJvd3NlciIsImNvbm5lY3RlZCIsImNsb3NlIiwibmV3UGFnZSIsInBvb2xSZXNvdXJjZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJfc2V0UGFnZUNvbnRlbnQiLCJfc2V0UGFnZUV2ZW50cyIsImlzQ2xvc2VkIiwiY2xlYXJQYWdlIiwiaGFyZFJlc2V0IiwiZ290byIsIndhaXRVbnRpbCIsImV2YWx1YXRlIiwiYm9keSIsImlubmVySFRNTCIsImlkIiwid29ya0NvdW50IiwiYWRkUGFnZVJlc291cmNlcyIsImluamVjdGVkUmVzb3VyY2VzIiwiaW5qZWN0ZWRKcyIsImpzIiwiY29udGVudCIsImZpbGVzIiwiaXNMb2NhbCIsImpzUmVzb3VyY2UiLCJhZGRTY3JpcHRUYWciLCJpbmplY3RlZENzcyIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJjc3NSZXNvdXJjZSIsImFkZFN0eWxlVGFnIiwiY2xlYXJQYWdlUmVzb3VyY2VzIiwicmVzb3VyY2UiLCJkaXNwb3NlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJlbGVtZW50IiwicmVtb3ZlIiwic2V0Q29udGVudCIsImNzc1RlbXBsYXRlIiwic3ZnVGVtcGxhdGUiLCJwdXBwZXRlZXJFeHBvcnQiLCJpc1NWRyIsInNpemUiLCJzdmdFbGVtZW50IiwicXVlcnlTZWxlY3RvciIsImNoYXJ0SGVpZ2h0IiwiYmFzZVZhbCIsImNoYXJ0V2lkdGgiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ4IiwieSIsIl9nZXRDbGlwUmVnaW9uIiwidmlld3BvcnRIZWlnaHQiLCJhYnMiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsInJlc3VsdCIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJfY3JlYXRlU1ZHIiwiX2NyZWF0ZUltYWdlIiwiX2NyZWF0ZVBERiIsIiRldmFsIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidHJ1bmMiLCJvdXRlckhUTUwiLCJjbGlwIiwicmFjZSIsInNjcmVlbnNob3QiLCJlbmNvZGluZyIsImZ1bGxQYWdlIiwib3B0aW1pemVGb3JTcGVlZCIsImNhcHR1cmVCZXlvbmRWaWV3cG9ydCIsInF1YWxpdHkiLCJvbWl0QmFja2dyb3VuZCIsIl9yZXNvbHZlIiwiZW11bGF0ZU1lZGlhVHlwZSIsInBkZiIsInBvb2xTdGF0cyIsImV4cG9ydHNBdHRlbXB0ZWQiLCJleHBvcnRzUGVyZm9ybWVkIiwiZXhwb3J0c0Ryb3BwZWQiLCJleHBvcnRzRnJvbVN2ZyIsImV4cG9ydHNGcm9tT3B0aW9ucyIsImV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMiLCJleHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cyIsInRpbWVTcGVudCIsInRpbWVTcGVudEF2ZXJhZ2UiLCJpbml0UG9vbCIsInBvb2xPcHRpb25zIiwiUG9vbCIsIl9mYWN0b3J5IiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsImNsZWFyU3RhdHVzIiwiX2V2ZW50SWQiLCJpbml0aWFsUmVzb3VyY2VzIiwiYWNxdWlyZSIsInByb21pc2UiLCJyZWxlYXNlIiwia2lsbFBvb2wiLCJ3b3JrZXIiLCJ1c2VkIiwiZGVzdHJveWVkIiwicG9zdFdvcmsiLCJ3b3JrZXJIYW5kbGUiLCJnZXRQb29sSW5mbyIsImFjcXVpcmVDb3VudGVyIiwicmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsImV4cG9ydFRpbWUiLCJnZXRQb29sU3RhdHMiLCJnZXRQb29sSW5mb0pTT04iLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwibnVtRnJlZSIsImFsbENyZWF0ZWQiLCJwZW5kaW5nQWNxdWlyZXMiLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwZW5kaW5nQ3JlYXRlcyIsIm51bVBlbmRpbmdDcmVhdGVzIiwicGVuZGluZ1ZhbGlkYXRpb25zIiwibnVtUGVuZGluZ1ZhbGlkYXRpb25zIiwicGVuZGluZ0Rlc3Ryb3lzIiwiYWJzb2x1dGVBbGwiLCJjcmVhdGUiLCJ1dWlkIiwicmFuZG9tIiwic3RhcnREYXRlIiwidmFsaWRhdGUiLCJtYWluRnJhbWUiLCJkZXRhY2hlZCIsInJlbW92ZUFsbExpc3RlbmVycyIsInNhbml0aXplIiwiSlNET00iLCJET01QdXJpZnkiLCJBRERfVEFHUyIsInNpbmdsZUV4cG9ydCIsInN0YXJ0RXhwb3J0IiwiZGF0YSIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiYmF0Y2hSZXN1bHRzIiwiYWxsU2V0dGxlZCIsInJlYXNvbiIsImltYWdlT3B0aW9ucyIsImVuZENhbGxiYWNrIiwiZmlsZUNvbnRlbnQiLCJfZXhwb3J0RnJvbVN2ZyIsIl9leHBvcnRGcm9tT3B0aW9ucyIsImdldEFsbG93Q29kZUV4ZWN1dGlvbiIsInNldEFsbG93Q29kZUV4ZWN1dGlvbiIsImlucHV0VG9FeHBvcnQiLCJfcHJlcGFyZUV4cG9ydCIsIl9oYW5kbGVDdXN0b21Mb2dpYyIsIl9oYW5kbGVHbG9iYWxBbmRUaGVtZSIsIl9maW5kQ2hhcnRTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidGltZXJJZHMiLCJhZGRUaW1lciIsImNsZWFyQWxsVGltZXJzIiwiY2xlYXJJbnRlcnZhbCIsImNsZWFyVGltZW91dCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcXVlc3QiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RhdHVzIiwianNvbiIsImVycm9yTWlkZGxld2FyZSIsImFwcCIsInVzZSIsInJhdGVMaW1pdGluZ01pZGRsZXdhcmUiLCJyYXRlTGltaXRpbmdPcHRpb25zIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwicmF0ZUxpbWl0Iiwid2luZG93TXMiLCJsaW1pdCIsImRlbGF5TXMiLCJoYW5kbGVyIiwiZm9ybWF0Iiwic2VuZCIsImRlZmF1bHQiLCJza2lwIiwicXVlcnkiLCJhY2Nlc3NfdG9rZW4iLCJjb250ZW50VHlwZU1pZGRsZXdhcmUiLCJjb250ZW50VHlwZSIsImhlYWRlcnMiLCJyZXF1ZXN0Qm9keU1pZGRsZXdhcmUiLCJjb25uZWN0aW9uIiwicmVtb3RlQWRkcmVzcyIsInZhbGlkYXRlZE9wdGlvbnMiLCJwYXJhbXMiLCJmaWxlbmFtZSIsInZhbGlkYXRpb25NaWRkbGV3YXJlIiwicG9zdCIsInJldmVyc2VkTWltZSIsInBuZyIsImpwZWciLCJnaWYiLCJyZXF1ZXN0RXhwb3J0IiwicmVxdWVzdENvdW50ZXIiLCJjb25uZWN0aW9uQWJvcnRlZCIsInNvY2tldCIsImhhZEVycm9ycyIsImhlYWRlciIsImF0dGFjaG1lbnQiLCJleHBvcnRSb3V0ZXMiLCJzZXJ2ZXJTdGFydFRpbWUiLCJwYWNrYWdlRmlsZSIsInN1Y2Nlc3NSYXRlcyIsInJlY29yZEludGVydmFsIiwid2luZG93U2l6ZSIsIl9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlIiwiYSIsImIiLCJfc3RhcnRTdWNjZXNzUmF0ZSIsInNldEludGVydmFsIiwic3RhdHMiLCJzdWNjZXNzUmF0aW8iLCJoZWFsdGhSb3V0ZXMiLCJwZXJpb2QiLCJtb3ZpbmdBdmVyYWdlIiwiYm9vdFRpbWUiLCJ1cHRpbWUiLCJmbG9vciIsInNlcnZlclZlcnNpb24iLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VFeHBvcnRUaW1lIiwiYXR0ZW1wdGVkRXhwb3J0cyIsInBlcmZvcm1lZEV4cG9ydHMiLCJmYWlsZWRFeHBvcnRzIiwic3VjZXNzUmF0aW8iLCJ0b0ZpeGVkIiwic3ZnRXhwb3J0cyIsImpzb25FeHBvcnRzIiwic3ZnRXhwb3J0c0F0dGVtcHRzIiwianNvbkV4cG9ydHNBdHRlbXB0cyIsInVpUm91dGVzIiwic2VuZEZpbGUiLCJhY2NlcHRSYW5nZXMiLCJ2ZXJzaW9uQ2hhbmdlUm91dGVzIiwiYWRtaW5Ub2tlbiIsInRva2VuIiwiYWN0aXZlU2VydmVycyIsIk1hcCIsImV4cHJlc3MiLCJzdGFydFNlcnZlciIsInNlcnZlck9wdGlvbnMiLCJ1cGxvYWRMaW1pdEJ5dGVzIiwic3RvcmFnZSIsIm11bHRlciIsIm1lbW9yeVN0b3JhZ2UiLCJ1cGxvYWQiLCJsaW1pdHMiLCJmaWVsZFNpemUiLCJkaXNhYmxlIiwiY29ycyIsIm1ldGhvZHMiLCJzZXQiLCJ1cmxlbmNvZGVkIiwiZXh0ZW5kZWQiLCJub25lIiwic3RhdGljIiwiaHR0cFNlcnZlciIsImNyZWF0ZVNlcnZlciIsIl9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwibGlzdGVuIiwiY2VydCIsImh0dHBzU2VydmVyIiwiY2xvc2VTZXJ2ZXJzIiwiZGVsZXRlIiwiZ2V0U2VydmVycyIsImdldEV4cHJlc3MiLCJnZXRBcHAiLCJlbmFibGVSYXRlTGltaXRpbmciLCJtaWRkbGV3YXJlcyIsInNodXRkb3duQ2xlYW5VcCIsImV4aXRDb2RlIiwiZXhpdCIsImluaXRFeHBvcnQiLCJpbml0T3B0aW9ucyIsIl9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyIsImNvZGUiXSwibWFwcGlu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 +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?path.normalize(e):path.resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.options=null,t.export.instr=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.options=null,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},_checkDataSize({export:t,customLogic:o}),postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}function _checkDataSize(e){const t=Buffer.byteLength(JSON.stringify(e),"utf-8");if(log(3,`[chart] The current total size of the data for the export process is around ${(t/1048576).toFixed(2)}MB.`),t>=104857600)throw new ExportError("[chart] The data for the export process exceeds 100MB limit.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`[validation] Request [${r}] - 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`[validation] Request [${r}] - 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: `HIGHCHARTS_ADMIN_TOKEN` is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIG5vcm1hbGl6ZSwgcmVzb2x2ZSB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBkaXJlY3RvcnkgcGF0aFxyXG5leHBvcnQgY29uc3QgX19kaXJuYW1lID0gZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuLi8uJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFuZCBzdGFuZGFyZGl6ZXMgdGV4dCBieSByZXBsYWNpbmcgbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZVxyXG4gKiBjaGFyYWN0ZXJzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIHRyaW1taW5nIGFueSBsZWFkaW5nIG9yIHRyYWlsaW5nXHJcbiAqIHdoaXRlc3BhY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhclRleHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyB0aGUgJy9cXHNcXHMrL2cnIFJlZ0V4cC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtyZXBsYWNlcj0nICddIC0gVGhlIHN0cmluZyB1c2VkIHRvIHJlcGxhY2UgbXVsdGlwbGVcclxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgJyAnIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyVGV4dCh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpIHtcclxuICByZXR1cm4gdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBkZWVwQ29weVxyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8QXJyYXkpfSBvYmpBcnIgLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fEFycmF5KX0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDb3B5KG9iakFycikge1xyXG4gIC8vIElmIHRoZSBgb2JqQXJyYCBpcyBudWxsIG9yIG5vdCBvZiB0aGUgYG9iamVjdGAgdHlwZSwgcmV0dXJuIGl0XHJcbiAgaWYgKG9iakFyciA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqQXJyICE9PSAnb2JqZWN0Jykge1xyXG4gICAgcmV0dXJuIG9iakFycjtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZWl0aGVyIGEgbmV3IGFycmF5IG9yIGEgbmV3IG9iamVjdFxyXG4gIGNvbnN0IG9iakFyckNvcHkgPSBBcnJheS5pc0FycmF5KG9iakFycikgPyBbXSA6IHt9O1xyXG5cclxuICAvLyBSZWN1cnNpdmVseSBjb3B5IGVhY2ggcHJvcGVydHlcclxuICBmb3IgKGNvbnN0IGtleSBpbiBvYmpBcnIpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqQXJyLCBrZXkpKSB7XHJcbiAgICAgIG9iakFyckNvcHlba2V5XSA9IGRlZXBDb3B5KG9iakFycltrZXldKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY29waWVkIG9iamVjdFxyXG4gIHJldHVybiBvYmpBcnJDb3B5O1xyXG59XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZXhwQmFja29mZlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBmdW5jdGlvbiB0byBiZSByZXRyaWVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYDBgLlxyXG4gKiBAcGFyYW0gey4uLnVua25vd259IGFyZ3MgLSBBcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSByZXN1bHRcclxuICogb2YgdGhlIGZ1bmN0aW9uIGlmIHN1Y2Nlc3NmdWwuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCBpZiB0aGUgbWF4aW11bSBudW1iZXIgb2YgYXR0ZW1wdHNcclxuICogaXMgcmVhY2hlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleHBCYWNrb2ZmKGZuLCBhdHRlbXB0ID0gMCwgLi4uYXJncykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBUcnkgdG8gY2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIHJldHVybiBhd2FpdCBmbiguLi5hcmdzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gQ2FsY3VsYXRlIGRlbGF5IGluIG1zXHJcbiAgICBjb25zdCBkZWxheUluTXMgPSAyICoqIGF0dGVtcHQgKiAxMDAwO1xyXG5cclxuICAgIC8vIElmIHRoZSBhdHRlbXB0IGV4Y2VlZHMgdGhlIG1heGltdW0gYXR0ZW1wdHMgb2YgcmVwZWF0LCB0aHJvdyBhbiBlcnJvclxyXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXYWl0IGdpdmVuIGFtb3VudCBvZiB0aW1lXHJcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIGRlbGF5SW5NcykpO1xyXG5cclxuICAgIC8vLyBUTyBETzogQ29ycmVjdFxyXG4gICAgLy8gLy8gSW5mb3JtYXRpb24gYWJvdXQgdGhlIHJlc291cmNlIHRpbWVvdXRcclxuICAgIC8vIGxvZyhcclxuICAgIC8vICAgMyxcclxuICAgIC8vICAgYFt1dGlsc10gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIG9mIElEOiAke2FyZ3NbMF19LmBcclxuICAgIC8vICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRqdXN0cyB0aGUgY29uc3RydWN0b3IgbmFtZSBieSB0cmFuc2Zvcm1pbmcgYW5kIG5vcm1hbGl6aW5nIGl0IGJhc2VkXHJcbiAqIG9uIGNvbW1vbiBjaGFydCB0eXBlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeENvbnN0clxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY29uc3RyIC0gVGhlIG9yaWdpbmFsIGNvbnN0cnVjdG9yIG5hbWUgdG8gYmUgZml4ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IgbmFtZSwgb3IgJ2NoYXJ0JyBpZiB0aGUgaW5wdXRcclxuICogaXMgbm90IHJlY29nbml6ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4Q29uc3RyKGNvbnN0cikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBGaXggdGhlIGNvbnN0cnVjdG9yIGJ5IGxvd2VyaW5nIGNhc2luZ1xyXG4gICAgY29uc3QgZml4ZWRDb25zdHIgPSBgJHtjb25zdHIudG9Mb3dlckNhc2UoKS5yZXBsYWNlKCdjaGFydCcsICcnKX1DaGFydGA7XHJcblxyXG4gICAgLy8gSGFuZGxlIHRoZSBjYXNlIHdoZXJlIHRoZSByZXN1bHQgaXMganVzdCAnQ2hhcnQnXHJcbiAgICBpZiAoZml4ZWRDb25zdHIgPT09ICdDaGFydCcpIHtcclxuICAgICAgZml4ZWRDb25zdHIudG9Mb3dlckNhc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZXR1cm4gdGhlIGNvcnJlY3RlZCBjb25zdHJ1Y3Rvciwgb3RoZXJ3aXNlIGRlZmF1bHQgdG8gJ2NoYXJ0J1xyXG4gICAgcmV0dXJuIFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10uaW5jbHVkZXMoXHJcbiAgICAgIGZpeGVkQ29uc3RyXHJcbiAgICApXHJcbiAgICAgID8gZml4ZWRDb25zdHJcclxuICAgICAgOiAnY2hhcnQnO1xyXG4gIH0gY2F0Y2gge1xyXG4gICAgLy8gRGVmYXVsdCB0byAnY2hhcnQnIGluIGNhc2Ugb2YgYW55IGVycm9yXHJcbiAgICByZXR1cm4gJ2NoYXJ0JztcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGaXhlcyB0aGUgb3V0ZmlsZSBiYXNlZCBvbiBwcm92aWRlZCB0eXBlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZml4T3V0ZmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSBvcmlnaW5hbCBleHBvcnQgdHlwZS5cclxuICogQHBhcmFtIHtzdHJpbmd9IG91dGZpbGUgLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgb3V0ZmlsZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhPdXRmaWxlKHR5cGUsIG91dGZpbGUpIHtcclxuICAvLyBHZXQgdGhlIGZpbGUgbmFtZSBmcm9tIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgY29uc3QgZmlsZU5hbWUgPSBnZXRBYnNvbHV0ZVBhdGgob3V0ZmlsZSB8fCAnY2hhcnQnKVxyXG4gICAgLnNwbGl0KCcuJylcclxuICAgIC5zaGlmdCgpO1xyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IG91dGZpbGVcclxuICByZXR1cm4gYCR7ZmlsZU5hbWV9LiR7dHlwZX1gO1xyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIGV4cG9ydCB0eXBlIGJhc2VkIG9uIE1JTUUgdHlwZXMgYW5kIGZpbGUgZXh0ZW5zaW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeFR5cGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbb3V0ZmlsZT1udWxsXSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYG51bGxgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeFR5cGUodHlwZSwgb3V0ZmlsZSA9IG51bGwpIHtcclxuICAvLyBNSU1FIHR5cGVzXHJcbiAgY29uc3QgbWltZVR5cGVzID0ge1xyXG4gICAgJ2ltYWdlL3BuZyc6ICdwbmcnLFxyXG4gICAgJ2ltYWdlL2pwZWcnOiAnanBlZycsXHJcbiAgICAnYXBwbGljYXRpb24vcGRmJzogJ3BkZicsXHJcbiAgICAnaW1hZ2Uvc3ZnK3htbCc6ICdzdmcnXHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IGZvcm1hdHNcclxuICBjb25zdCBmb3JtYXRzID0gT2JqZWN0LnZhbHVlcyhtaW1lVHlwZXMpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICAvLyBTdXBwb3J0IHRoZSBKUEcgdHlwZVxyXG4gICAgaWYgKG91dFR5cGUgPT09ICdqcGcnKSB7XHJcbiAgICAgIHR5cGUgPSAnanBlZyc7XHJcbiAgICB9IGVsc2UgaWYgKGZvcm1hdHMuaW5jbHVkZXMob3V0VHlwZSkgJiYgdHlwZSAhPT0gb3V0VHlwZSkge1xyXG4gICAgICB0eXBlID0gb3V0VHlwZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGNvcnJlY3QgdHlwZVxyXG4gIHJldHVybiBtaW1lVHlwZXNbdHlwZV0gfHwgZm9ybWF0cy5maW5kKCh0KSA9PiB0ID09PSB0eXBlKSB8fCAncG5nJztcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gcGF0aCBpcyByZWxhdGl2ZSBvciBhYnNvbHV0ZSBhbmQgcmV0dXJucyB0aGUgY29ycmVjdGVkLFxyXG4gKiBhYnNvbHV0ZSBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBYnNvbHV0ZVBhdGhcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byBiZSBjaGVja2VkIG9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgYWJzb2x1dGUgcGF0aC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBYnNvbHV0ZVBhdGgocGF0aCkge1xyXG4gIHJldHVybiBpc0Fic29sdXRlKHBhdGgpID8gbm9ybWFsaXplKHBhdGgpIDogcmVzb2x2ZShwYXRoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGlucHV0IGRhdGEgdG8gYSBCYXNlNjQgc3RyaW5nIGJhc2VkIG9uIHRoZSBleHBvcnQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJhc2U2NFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgaW5wdXQgdG8gYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZvcm1hdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBCYXNlNjQgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRCYXNlNjQoaW5wdXQsIHR5cGUpIHtcclxuICAvLyBGb3IgcGRmIGFuZCBzdmcgdHlwZXMgdGhlIGlucHV0IG11c3QgYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZyb20gYSBidWZmZXJcclxuICBpZiAodHlwZSA9PT0gJ3BkZicgfHwgdHlwZSA9PSAnc3ZnJykge1xyXG4gICAgcmV0dXJuIEJ1ZmZlci5mcm9tKGlucHV0LCAndXRmOCcpLnRvU3RyaW5nKCdiYXNlNjQnKTtcclxuICB9XHJcblxyXG4gIC8vIEZvciBwbmcgYW5kIGpwZWcgaW5wdXQgaXMgYWxyZWFkeSBhIEJhc2U2NCBzdHJpbmdcclxuICByZXR1cm4gaW5wdXQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHN0cmluZ2lmaWVkIGRhdGUgd2l0aG91dCB0aGUgR01UIHRleHQgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3RGF0ZSgpIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIHJldHVybiBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBzdG9yZWQgdGltZSB2YWx1ZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlVGltZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGVUaW1lKCkge1xyXG4gIHJldHVybiBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gaXRlbSBpcyBhbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgaXRlbSBpcyBhbiBvYmplY3QsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdChpdGVtKSB7XHJcbiAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChpdGVtKSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIG9iamVjdCBpcyBlbXB0eS5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW0gLSBUaGUgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdEVtcHR5KGl0ZW0pIHtcclxuICByZXR1cm4gKFxyXG4gICAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgICAhQXJyYXkuaXNBcnJheShpdGVtKSAmJlxyXG4gICAgaXRlbSAhPT0gbnVsbCAmJlxyXG4gICAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kLCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChpdGVtKSB7XHJcbiAgY29uc3QgcmVnZXhQYXR0ZXJucyA9IFtcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT9sb2NhbGhvc3RcXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEwXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMjdcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE3MlxcLigxWzYtOV18MlswLTldfDNbMC0xXSlcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xOTJcXC4xNjhcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiL1xyXG4gIF07XHJcblxyXG4gIHJldHVybiByZWdleFBhdHRlcm5zLnNvbWUoKHBhdHRlcm4pID0+IHBhdHRlcm4udGVzdChpdGVtKSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVdGlsaXR5IHRvIG1lYXN1cmUgZWxhcHNlZCB0aW1lIHVzaW5nIHRoZSBOb2RlLmpzIGBwcm9jZXNzLmhydGltZSgpYCBtZXRob2QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtZWFzdXJlVGltZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IEEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBlbGFwc2VkIHRpbWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1lYXN1cmVUaW1lKCkge1xyXG4gIGNvbnN0IHN0YXJ0ID0gcHJvY2Vzcy5ocnRpbWUuYmlnaW50KCk7XHJcbiAgcmV0dXJuICgpID0+IE51bWJlcihwcm9jZXNzLmhydGltZS5iaWdpbnQoKSAtIHN0YXJ0KSAvIDEwMDAwMDA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSb3VuZHMgYSBudW1iZXIgdG8gdGhlIHNwZWNpZmllZCBwcmVjaXNpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByb3VuZE51bWJlclxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBwcmVjaXNpb24gLSBUaGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIHRvIHJvdW5kIHRvLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgcm91bmRlZCBudW1iZXIuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcm91bmROdW1iZXIodmFsdWUsIHByZWNpc2lvbiA9IDEpIHtcclxuICBjb25zdCBtdWx0aXBsaWVyID0gTWF0aC5wb3coMTAsIHByZWNpc2lvbiB8fCAwKTtcclxuICByZXR1cm4gTWF0aC5yb3VuZCgrdmFsdWUgKiBtdWx0aXBsaWVyKSAvIG11bHRpcGxpZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBhIHZhbHVlIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHRvQm9vbGVhblxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSBib29sZWFuIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dCB2YWx1ZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB0b0Jvb2xlYW4oaXRlbSkge1xyXG4gIHJldHVybiBbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTicsICcwJywgJyddLmluY2x1ZGVzKGl0ZW0pXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6ICEhaXRlbTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFdyYXBzIGN1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgaXQgc2FmZWx5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gd3JhcEFyb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tQ29kZSAtIFRoZSBjdXN0b20gY29kZSB0byBiZSB3cmFwcGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEZsYWcgdG8gYWxsb3cgbG9hZGluZyBjb2RlIGZyb20gYSBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtpc0NhbGxiYWNrPWZhbHNlXSAtIEZsYWcgdGhhdCBpbmRpY2F0ZXMgdGhlIHJldHVybmVkIGNvZGVcclxuICogbXVzdCBiZSBpbiBhIGNhbGxiYWNrIGZvcm1hdC5cclxuICpcclxuICogQHJldHVybnMgeyhzdHJpbmd8bnVsbCl9IFRoZSB3cmFwcGVkIGN1c3RvbSBjb2RlIG9yIG51bGwgaWYgd3JhcHBpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gd3JhcEFyb3VuZChjdXN0b21Db2RlLCBhbGxvd0ZpbGVSZXNvdXJjZXMsIGlzQ2FsbGJhY2sgPSBmYWxzZSkge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICAvLyBMb2FkIGEgZmlsZSBpZiB0aGUgZmlsZSByZXNvdXJjZXMgYXJlIGFsbG93ZWRcclxuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICAgID8gd3JhcEFyb3VuZChcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChjdXN0b21Db2RlKSwgJ3V0ZjgnKSxcclxuICAgICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgICBpc0NhbGxiYWNrXHJcbiAgICAgICAgICApXHJcbiAgICAgICAgOiBudWxsO1xyXG4gICAgfSBlbHNlIGlmIChcclxuICAgICAgIWlzQ2FsbGJhY2sgJiZcclxuICAgICAgKGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24oKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbiAoKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpID0+JykpXHJcbiAgICApIHtcclxuICAgICAgLy8gVHJlYXQgYSBmdW5jdGlvbiBhcyBhIHNlbGYtaW52b2tpbmcgZXhwcmVzc2lvblxyXG4gICAgICByZXR1cm4gYCgke2N1c3RvbUNvZGV9KSgpYDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBPciByZXR1cm4gYXMgYSBzdHJpbmdpZmllZCBjb2RlXHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGRlZXBDb3B5LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4Q29uc3RyLFxyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIGdldE5ld0RhdGUsXHJcbiAgZ2V0TmV3RGF0ZVRpbWUsXHJcbiAgaXNPYmplY3QsXHJcbiAgaXNPYmplY3RFbXB0eSxcclxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxyXG4gIG1lYXN1cmVUaW1lLFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHRvQm9vbGVhbixcclxuICB3cmFwQXJvdW5kXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSBmb3IgbWFuYWdpbmcgbG9nZ2luZyBmdW5jdGlvbmFsaXR5IHdpdGggY3VzdG9taXphYmxlXHJcbiAqIGxvZyBsZXZlbHMsIGNvbnNvbGUgYW5kIGZpbGUgbG9nZ2luZyBvcHRpb25zLCBhbmQgZXJyb3IgaGFuZGxpbmcgc3VwcG9ydC5cclxuICogVGhlIG1vZHVsZSBhbHNvIGVuc3VyZXMgdGhhdCBmaWxlLWJhc2VkIGxvZ3MgYXJlIHN0b3JlZCBpbiBhIHN0cnVjdHVyZWRcclxuICogZGlyZWN0b3J5LCBjcmVhdGluZyB0aGUgbmVjZXNzYXJ5IHBhdGhzIGF1dG9tYXRpY2FsbHkgaWYgdGhleSBkbyBub3QgZXhpc3QuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYXBwZW5kRmlsZSwgZXhpc3RzU3luYywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGgsIGdldE5ld0RhdGUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmNvbnN0IGxvZ2dpbmcgPSB7XHJcbiAgLy8gRmxhZ3MgZm9yIGxvZ2dpbmcgc3RhdHVzXHJcbiAgdG9Db25zb2xlOiB0cnVlLFxyXG4gIHRvRmlsZTogZmFsc2UsXHJcbiAgcGF0aENyZWF0ZWQ6IGZhbHNlLFxyXG4gIC8vIEZ1bGwgcGF0aCB0byB0aGUgbG9nIGZpbGVcclxuICBwYXRoVG9Mb2c6ICcnLFxyXG4gIC8vIExvZyBsZXZlbHNcclxuICBsZXZlbHNEZXNjOiBbXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnZXJyb3InLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzBdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3dhcm5pbmcnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzFdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ25vdGljZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMl1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAndmVyYm9zZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbM11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnYmVuY2htYXJrJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1s0XVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGEgbWVzc2FnZSB3aXRoIGEgc3BlY2lmaWVkIGxvZyBsZXZlbC4gQWNjZXB0cyBhIHZhcmlhYmxlIG51bWJlclxyXG4gKiBvZiBhcmd1bWVudHMuIFRoZSBhcmd1bWVudHMgYWZ0ZXIgdGhlIGBsZXZlbGAgYXJlIHBhc3NlZCB0byBgY29uc29sZS5sb2dgXHJcbiAqIGFuZC9vciB1c2VkIHRvIGNvbnN0cnVjdCBhbmQgYXBwZW5kIG1lc3NhZ2VzIHRvIGEgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dcclxuICpcclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQW4gYXJyYXkgb2YgYXJndW1lbnRzIHdoZXJlIHRoZSBmaXJzdCBpcyB0aGUgbG9nXHJcbiAqIGxldmVsIGFuZCB0aGUgcmVtYWluaW5nIGFyZSBzdHJpbmdzIHVzZWQgdG8gYnVpbGQgdGhlIGxvZyBtZXNzYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nKC4uLmFyZ3MpIHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbHNEZXNjLCBsZXZlbCB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlIG9yIGlzIGl0IGEgYmVuY2htYXJrIGxvZ1xyXG4gIGlmIChcclxuICAgIG5ld0xldmVsICE9PSA1ICYmXHJcbiAgICAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKVxyXG4gICkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdCh0ZXh0cylcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIGFsb25nIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ1dpdGhTdGFja1xyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbG9nIGxldmVsLlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QgY29udGFpbmluZyB0aGUgc3RhY2sgdHJhY2UuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21NZXNzYWdlIC0gQW4gb3B0aW9uYWwgY3VzdG9tIG1lc3NhZ2UgdG8gYmUgaW5jbHVkZWRcclxuICogaW4gdGhlIGxvZyBhbG9uZ3NpZGUgdGhlIGVycm9yLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nV2l0aFN0YWNrKG5ld0xldmVsLCBlcnJvciwgY3VzdG9tTWVzc2FnZSkge1xyXG4gIC8vIEdldCB0aGUgbWFpbiBtZXNzYWdlXHJcbiAgY29uc3QgbWFpbk1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlIHx8IChlcnJvciAmJiBlcnJvci5tZXNzYWdlKSB8fCAnJztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQWRkIHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXHJcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID0gZXJyb3IgJiYgZXJyb3Iuc3RhY2s7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2UsIGlmIGV4aXN0c1xyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlXTtcclxuICBpZiAoc3RhY2tNZXNzYWdlKSB7XHJcbiAgICB0ZXh0cy5wdXNoKCdcXG4nLCBzdGFja01lc3NhZ2UpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcclxuICAgICAgICB0ZXh0cy5zaGlmdCgpW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAuLi50ZXh0c1xyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpbml0TG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgbG9nZ2luZ2Agb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpbml0TG9nZ2luZyhsb2dnaW5nT3B0aW9ucykge1xyXG4gIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGBsb2dnaW5nT3B0aW9uc2Agb2JqZWN0XHJcbiAgY29uc3QgeyBsZXZlbCwgZGVzdCwgZmlsZSwgdG9Db25zb2xlLCB0b0ZpbGUgfSA9IGxvZ2dpbmdPcHRpb25zO1xyXG5cclxuICAvLyBSZXNldCBmbGFncyB0byB0aGUgZGVmYXVsdCB2YWx1ZXNcclxuICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgbG9nZ2luZy5wYXRoVG9Mb2cgPSAnJztcclxuXHJcbiAgLy8gU2V0IHRoZSBsb2dnaW5nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobGV2ZWwpO1xyXG5cclxuICAvLyBTZXQgdGhlIGNvbnNvbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nKHRvQ29uc29sZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgZmlsZSBsb2dnaW5nXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGxvZyBsZXZlbCB0byB0aGUgc3BlY2lmaWVkIHZhbHVlLiBMb2cgbGV2ZWxzIGFyZSAoYDBgID0gbm8gbG9nZ2luZyxcclxuICogYDFgID0gZXJyb3IsIGAyYCA9IHdhcm5pbmcsIGAzYCA9IG5vdGljZSwgYDRgID0gdmVyYm9zZSwgb3IgYDVgID0gYmVuY2htYXJrKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldExvZ0xldmVsXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsb2cgbGV2ZWwgdG8gYmUgc2V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldExvZ0xldmVsKGxldmVsKSB7XHJcbiAgaWYgKFxyXG4gICAgTnVtYmVyLmlzSW50ZWdlcihsZXZlbCkgJiZcclxuICAgIGxldmVsID49IDAgJiZcclxuICAgIGxldmVsIDw9IGxvZ2dpbmcubGV2ZWxzRGVzYy5sZW5ndGhcclxuICApIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgbGV2ZWxgIG9wdGlvblxyXG4gICAgbG9nZ2luZy5sZXZlbCA9IGxldmVsO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgY29uc29sZSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB0b0NvbnNvbGUgLSBUaGUgZmxhZyBmb3Igc2V0dGluZyB0aGUgbG9nZ2luZyB0byB0aGUgY29uc29sZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpIHtcclxuICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYHRvQ29uc29sZWAgb3B0aW9uXHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSAhIXRvQ29uc29sZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUgbmFtZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBkZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggd2hlcmUgdGhlIGxvZyBmaWxlIHNob3VsZFxyXG4gKiBiZSBzYXZlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGZpbGUgLSBUaGUgbmFtZSBvZiB0aGUgbG9nIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9GaWxlIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2dnaW5nIHNob3VsZFxyXG4gKiBiZSBkaXJlY3RlZCB0byBhIGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0ZpbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9GaWxlID0gISF0b0ZpbGU7XHJcblxyXG4gIC8vIFNldCB0aGUgYGRlc3RgIGFuZCBgZmlsZWAgb3B0aW9ucyBvbmx5IGlmIHRoZSBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZFxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ID0gZGVzdCB8fCAnJztcclxuICAgIGxvZ2dpbmcuZmlsZSA9IGZpbGUgfHwgJyc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyB0aGUgcHJvdmlkZWQgdGV4dHMgdG8gYSBmaWxlLCBpZiBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZC4gSXQgY3JlYXRlc1xyXG4gKiB0aGUgbmVjZXNzYXJ5IGRpcmVjdG9yeSBzdHJ1Y3R1cmUgaWYgbm90IGFscmVhZHkgY3JlYXRlZCBhbmQgYXBwZW5kc1xyXG4gKiB0aGUgY29udGVudCwgaW5jbHVkaW5nIGFuIG9wdGlvbmFsIHByZWZpeCwgdG8gdGhlIHNwZWNpZmllZCBsb2cgZmlsZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2dUb0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBBbiBvcHRpb25hbCBwcmVmaXggdG8gYmUgYWRkZWQgdG8gZWFjaCBsb2cgZW50cnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpIHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpICYmXHJcbiAgICAgIG1rZGlyU3luYyhnZXRBYnNvbHV0ZVBhdGgobG9nZ2luZy5kZXN0KSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBmdWxsIHBhdGhcclxuICAgIGxvZ2dpbmcucGF0aFRvTG9nID0gZ2V0QWJzb2x1dGVQYXRoKGpvaW4obG9nZ2luZy5kZXN0LCBsb2dnaW5nLmZpbGUpKTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yICYmIGxvZ2dpbmcudG9GaWxlICYmIGxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29uZmlndXJhdGlvbiBtYW5hZ2VtZW50IG1vZHVsZSBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci5cclxuICogUHJvdmlkZXMgZGVmYXVsdCBjb25maWd1cmF0aW9ucyB0aGF0IHN1cHBvcnQgZW52aXJvbm1lbnQgdmFyaWFibGVzLCBDTElcclxuICogYXJndW1lbnRzLCBhbmQgaW50ZXJhY3RpdmUgcHJvbXB0cyBmb3IgY3VzdG9taXphdGlvbiBvZiBvcHRpb25zIGFuZCBmZWF0dXJlcy5cclxuICogQWRkaXRpb25hbGx5LCBpdCBtYXBzIGxlZ2FjeSBvcHRpb25zIHRvIG1vZGVybiBzdHJ1Y3R1cmVzLCBnZW5lcmF0ZXMgbmVzdGVkXHJcbiAqIGFyZ3VtZW50IG1hcHBpbmdzLCBhbmQgZGlzcGxheXMgQ0xJIHVzYWdlIGluZm9ybWF0aW9uLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBhbGwgYXZhaWxhYmxlIG9wdGlvbnMsIG9yZ2FuaXplZFxyXG4gKiBieSBzZWN0aW9ucy5cclxuICpcclxuICogVGhpcyBvYmplY3QgaW5jbHVkZXM6XHJcbiAqIC0gRGVmYXVsdCB2YWx1ZXMgZm9yIGVhY2ggb3B0aW9uXHJcbiAqIC0gRGF0YSB0eXBlcyBmb3IgdmFsaWRhdGlvblxyXG4gKiAtIE5hbWVzIG9mIGNvcnJlc3BvbmRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIC0gRGVzY3JpcHRpb25zIG9mIGVhY2ggcHJvcGVydHlcclxuICogLSBJbmZvcm1hdGlvbiB1c2VkIGZvciBwcm9tcHRzIGluIGludGVyYWN0aXZlIGNvbmZpZ3VyYXRpb25cclxuICogLSBbT3B0aW9uYWxdIENvcnJlc3BvbmRpbmcgQ0xJIGFyZ3VtZW50IG5hbWVzIGZvciBDTEkgdXNhZ2VcclxuICogLSBbT3B0aW9uYWxdIExlZ2FjeSBuYW1lcyBmcm9tIHRoZSBwcmV2aW91cyBQaGFudG9tSlMtYmFzZWQgc2VydmVyXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IHtcclxuICAgIGFyZ3M6IHtcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnLS1hbGxvdy1ydW5uaW5nLWluc2VjdXJlLWNvbnRlbnQnLFxyXG4gICAgICAgICctLWFzaC1uby1udWRnZXMnLFxyXG4gICAgICAgICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICAgICAgICctLWJsb2NrLW5ldy13ZWItY29udGVudHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYWNjZWxlcmF0ZWQtMmQtY2FudmFzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtbmV0d29ya2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLXRpbWVyLXRocm90dGxpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZGluZy1vY2NsdWRlZC13aW5kb3dzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJyZWFrcGFkJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNoZWNrZXItaW1hZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jbGllbnQtc2lkZS1waGlzaGluZy1kZXRlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LWV4dGVuc2lvbnMtd2l0aC1iYWNrZ3JvdW5kLXBhZ2VzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICAgICAgICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAgICAgICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZmVhdHVyZXM9Q2FsY3VsYXRlTmF0aXZlV2luT2NjbHVzaW9uLEludGVyZXN0RmVlZENvbnRlbnRTdWdnZXN0aW9ucyxXZWJPVFAnLFxyXG4gICAgICAgICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWlwYy1mbG9vZGluZy1wcm90ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWxvZ2dpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtbm90aWZpY2F0aW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1vZmZlci1zdG9yZS11bm1hc2tlZC13YWxsZXQtY2FyZHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtcG9wdXAtYmxvY2tpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJpbnQtcHJldmlldycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcm9tcHQtb24tcmVwb3N0JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXJlbmRlcmVyLWJhY2tncm91bmRpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2VhcmNoLWVuZ2luZS1jaG9pY2Utc2NyZWVuJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlc3Npb24tY3Jhc2hlZC1idWJibGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2l0ZS1pc29sYXRpb24tdHJpYWxzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICAgICAgICctLWRpc2FibGUtc3luYycsXHJcbiAgICAgICAgJy0tZW5hYmxlLXVuc2FmZS13ZWJncHUnLFxyXG4gICAgICAgICctLWhpZGUtY3Jhc2gtcmVzdG9yZS1idWJibGUnLFxyXG4gICAgICAgICctLWhpZGUtc2Nyb2xsYmFycycsXHJcbiAgICAgICAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgICAgICAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgICAgICAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAgICAgICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICAgICAgICctLW5vLXBpbmdzJyxcclxuICAgICAgICAnLS1uby1zYW5kYm94JyxcclxuICAgICAgICAnLS1uby1zdGFydHVwLXdpbmRvdycsXHJcbiAgICAgICAgJy0tbm8tenlnb3RlJyxcclxuICAgICAgICAnLS1wYXNzd29yZC1zdG9yZT1iYXNpYycsXHJcbiAgICAgICAgJy0tcHJvY2Vzcy1wZXItdGFiJyxcclxuICAgICAgICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuICAgICAgXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ1BVUFBFVEVFUl9BUkdTJyxcclxuICAgICAgY2xpTmFtZTogJ3B1cHBldGVlckFyZ3MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FycmF5IG9mIFB1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfVkVSU0lPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2RuVXJsOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NETl9VUkwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0NETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGbGFnIHRvIHJlZmV0Y2ggc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdG9yeSBwYXRoIGZvciBjYWNoZWQgSGlnaGNoYXJ0cyBzY3JpcHRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DT1JFX1NDUklQVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtb2R1bGVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ3N0b2NrJyxcclxuICAgICAgICAnbWFwJyxcclxuICAgICAgICAnZ2FudHQnLFxyXG4gICAgICAgICdleHBvcnRpbmcnLFxyXG4gICAgICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAgICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgICAgIC8vICdhbm5vdGF0aW9ucy1hZHZhbmNlZCcsXHJcbiAgICAgICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAgICAgJ2Jvb3N0JyxcclxuICAgICAgICAnZGF0YScsXHJcbiAgICAgICAgJ2RhdGEtdG9vbHMnLFxyXG4gICAgICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICAgICAnc3RhdGljLXNjYWxlJyxcclxuICAgICAgICAnYnJva2VuLWF4aXMnLFxyXG4gICAgICAgICdoZWF0bWFwJyxcclxuICAgICAgICAndGlsZW1hcCcsXHJcbiAgICAgICAgJ3RpbGVkd2VibWFwJyxcclxuICAgICAgICAndGltZWxpbmUnLFxyXG4gICAgICAgICd0cmVlbWFwJyxcclxuICAgICAgICAndHJlZWdyYXBoJyxcclxuICAgICAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgICAgICdkcmlsbGRvd24nLFxyXG4gICAgICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICAgICAnYnVsbGV0JyxcclxuICAgICAgICAnZnVubmVsJyxcclxuICAgICAgICAnZnVubmVsM2QnLFxyXG4gICAgICAgICdnZW9oZWF0bWFwJyxcclxuICAgICAgICAncHlyYW1pZDNkJyxcclxuICAgICAgICAnbmV0d29ya2dyYXBoJyxcclxuICAgICAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAgICAgJ3BhcmV0bycsXHJcbiAgICAgICAgJ3BhdHRlcm4tZmlsbCcsXHJcbiAgICAgICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAgICAgJ3ByaWNlLWluZGljYXRvcicsXHJcbiAgICAgICAgJ3NhbmtleScsXHJcbiAgICAgICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICAgICAnZGVwZW5kZW5jeS13aGVlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1sYWJlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1vbi1wb2ludCcsXHJcbiAgICAgICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICAgICAnc29uaWZpY2F0aW9uJyxcclxuICAgICAgICAvLyAnc3RvY2stdG9vbHMnLFxyXG4gICAgICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAgICAgJ3N1bmJ1cnN0JyxcclxuICAgICAgICAndmFyaWFibGUtcGllJyxcclxuICAgICAgICAndmFyaXdpZGUnLFxyXG4gICAgICAgICd2ZWN0b3InLFxyXG4gICAgICAgICd2ZW5uJyxcclxuICAgICAgICAnd2luZGJhcmInLFxyXG4gICAgICAgICd3b3JkY2xvdWQnLFxyXG4gICAgICAgICd4cmFuZ2UnLFxyXG4gICAgICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgICAgICdkcmFnLXBhbmVzJyxcclxuICAgICAgICAnZGVidWdnZXInLFxyXG4gICAgICAgICdkdW1iYmVsbCcsXHJcbiAgICAgICAgJ2xvbGxpcG9wJyxcclxuICAgICAgICAnY3lsaW5kZXInLFxyXG4gICAgICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgICAgICdkb3RwbG90JyxcclxuICAgICAgICAnbWFya2VyLWNsdXN0ZXJzJyxcclxuICAgICAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgICAgICdoZWlraW5hc2hpJyxcclxuICAgICAgICAnZmxvd21hcCcsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAnbmF2aWdhdG9yJyxcclxuICAgICAgICAndGV4dHBhdGgnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIG1vZHVsZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbmRpY2F0b3JTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2luZGljYXRvcnMtYWxsJ10sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGluZGljYXRvciBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjMwLjEvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuNDUvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBZGRpdGlvbmFsIGN1c3RvbSBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgICAgc2VwYXJhdG9yOiAnOydcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5GSUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0IGZpbGVuYW1lIHdpdGggdHlwZSwgZm9ybWF0dGVkIGNvcnJlY3RseSBhcyBKU09OIG9yIFNWRycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9JTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdmVycmlkZXMgdGhlIGBpbmZpbGVgIHdpdGggSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgU1ZHIGlucHV0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgb3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWxpYXMgZm9yIHRoZSBgaW5zdHJgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHN2Zzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TVkcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1NWRyBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IHRvIHJlbmRlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJhdGNoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0JBVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0JhdGNoIGpvYiBzdHJpbmcgd2l0aCBpbnB1dC9vdXRwdXQgcGFpcnM6IFwiaW49b3V0O2luPW91dDsuLi5cIicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG91dGZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1VURklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdXRwdXQgZmlsZW5hbWUgd2l0aCB0eXBlLiBDYW4gYmUganBlZywgcG5nLCBwZGYsIG9yIHN2ZyBhbmQgaWdub3JlcyBgdHlwZWAgb3B0aW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdHlwZToge1xyXG4gICAgICB2YWx1ZTogJ3BuZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1RZUEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0ZpbGUgZXhwb3J0IGZvcm1hdC4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3NlbGVjdCcsXHJcbiAgICAgICAgaGludDogJ0RlZmF1bHQ6IHBuZycsXHJcbiAgICAgICAgY2hvaWNlczogWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvbnN0cjoge1xyXG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfQ09OU1RSJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NoYXJ0IGNvbnN0cnVjdG9yLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBjaGFydCcsXHJcbiAgICAgICAgY2hvaWNlczogWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYjY0OiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0I2NCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byB0aGUgY2hhcnQgc2hvdWxkIGJlIHJlY2VpdmVkIGluIEJhc2U2NCBmb3JtYXQgaW5zdGVhZCBvZiBiaW5hcnknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vRG93bmxvYWQ6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfTk9fRE9XTkxPQUQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gaW5jbHVkZSBvciBleGNsdWRlIGF0dGFjaG1lbnQgaGVhZGVycyBpbiB0aGUgcmVzcG9uc2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgd2lkdGg6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfV0lEVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1dpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGVzIGNoYXJ0IHNldHRpbmdzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzY2FsZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TQ0FMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdTY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncy4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdEhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX0hFSUdIVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRXaWR0aDoge1xyXG4gICAgICB2YWx1ZTogNjAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWZhdWx0IHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0U2NhbGU6IHtcclxuICAgICAgdmFsdWU6IDEsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGVmYXVsdCBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldC4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBtaW46IDAuMSxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGdsb2JhbE9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0dMT0JBTF9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCBnbG9iYWwgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdGhlbWVPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9USEVNRV9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCB0aGVtZSBvcHRpb25zIGZvciBIaWdoY2hhcnRzLnNldE9wdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXN0ZXJpemF0aW9uVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMTUwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaWxsaXNlY29uZHMgdG8gd2FpdCBmb3Igd2VicGFnZSByZW5kZXJpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgY3VzdG9tTG9naWM6IHtcclxuICAgIGFsbG93Q29kZUV4ZWN1dGlvbjoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbGxvd3Mgb3IgZGlzYWxsb3dzIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBkdXJpbmcgZXhwb3J0aW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBpbmplY3Rpb24gb2YgZmlsZXN5c3RlbSByZXNvdXJjZXMgKGRpc2FibGVkIGluIHNlcnZlciBtb2RlKScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tQ29kZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DVVNUT01fQ09ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDdXN0b20gY29kZSB0byBleGVjdXRlIGJlZm9yZSBjaGFydCBpbml0aWFsaXphdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24sIGNvZGUgd3JhcHBlZCBpbiBhIGZ1bmN0aW9uLCBvciBhIC5qcyBmaWxlbmFtZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhbGxiYWNrOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0NBTExCQUNLJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0phdmFTY3JpcHQgY29kZSB0byBydW4gZHVyaW5nIGNvbnN0cnVjdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24gb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX1JFU09VUkNFUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBZGRpdGlvbmFsIHJlc291cmNlcyBhcyBKU09OLCBzdHJpbmdpZmllZCBKU09OLCBvciBmaWxlbmFtZSwgY29udGFpbmluZyBmaWxlcywganMsIGFuZCBjc3Mgc2VjdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsb2FkQ29uZmlnOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHJyxcclxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIHdpdGggYSBwcmUtZGVmaW5lZCBjb25maWd1cmF0aW9uIHRvIHVzZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZUNvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1Byb21wdC1iYXNlZCBvcHRpb24gc2V0dGluZywgc2F2ZWQgdG8gYSBwcm92aWRlZCBjb25maWcgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU3RhcnRzIHRoZSBzZXJ2ZXIgd2hlbiB0cnVlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIHZhbHVlOiAnMC4wLjAuMCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hvc3RuYW1lIG9mIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA3ODAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG51bWJlciBmb3IgdGhlIHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdXBsb2FkTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDMsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX1VQTE9BRF9MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSByZXF1ZXN0IGJvZHkgc2l6ZSBpbiBNQicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGlzcGxheXMgb3Igbm90IGFjdGlvbiBkdXJhdGlvbnMgaW4gbWlsbGlzZWNvbmRzIGR1cmluZyBzZXJ2ZXIgcmVxdWVzdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHByb3h5OiB7XHJcbiAgICAgIGhvc3Q6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfSE9TVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5SG9zdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdIb3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcG9ydDoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgb2YgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRpbWVvdXQ6IHtcclxuICAgICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX1RJTUVPVVQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVRpbWVvdXQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJhdGVMaW1pdGluZzoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdlbmFibGVSYXRlTGltaXRpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzZXJ2ZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBtYXhSZXF1ZXN0czoge1xyXG4gICAgICAgIHZhbHVlOiAxMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAncmF0ZUxpbWl0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgcGVyIG1pbnV0ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIHZhbHVlOiAxLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1cnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGltZSB3aW5kb3cgaW4gbWludXRlcyBmb3IgcmF0ZSBsaW1pdGluZycsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGRlbGF5OiB7XHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdEZWxheSBkdXJhdGlvbiBiZXR3ZWVuIHN1Y2Nlc3NpdmUgcmVxdWVzdHMgYmVmb3JlIHJlYWNoaW5nIHRoZSBsaW1pdCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRydXN0UHJveHk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTZXQgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBza2lwS2V5OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnS2V5IHRvIGJ5cGFzcyB0aGUgcmF0ZSBsaW1pdGVyLCB1c2VkIHdpdGggYHNraXBUb2tlbmAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTicsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUb2tlbiB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwS2V5YCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgU1NMIHByb3RvY29sJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgZm9yY2U6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0ZPUkNFJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsRm9yY2UnLFxyXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdzc2xPbmx5JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0ZvcmNlcyB0aGUgc2VydmVyIHRvIHVzZSBIVFRQUyBvbmx5IHdoZW4gdHJ1ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogNDQzLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX1BPUlQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgZm9yIHRoZSBTU0wgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgY2VydFBhdGg6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0NFUlRfUEFUSCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbENlcnRQYXRoJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsUGF0aCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHRoZSBTU0wgY2VydGlmaWNhdGUva2V5IGZpbGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX01JTl9XT1JLRVJTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaW5pbXVtIGFuZCBpbml0aWFsIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG1heFdvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDgsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NQVhfV09SS0VSUycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICd3b3JrZXJzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdvcmtMaW1pdDoge1xyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9XT1JLX0xJTUlUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdOdW1iZXIgb2YgdGFza3MgYSB3b3JrZXIgY2FuIGhhbmRsZSBiZWZvcmUgcmVzdGFydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWNxdWlyZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBhY3F1aXJpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3JlYXRlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGlkbGVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiAzMDAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0lETEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGRlc3Ryb3lpbmcgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWw6IHtcclxuICAgICAgdmFsdWU6IDIwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYmVmb3JlIHJldHJ5aW5nIHJlc291cmNlIGNyZWF0aW9uIG9uIGZhaWx1cmUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0ludGVydmFsIGluIG1pbGxpc2Vjb25kcyB0byBjaGVjayBhbmQgZGVzdHJveSBpZGxlIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAncG9vbEJlbmNobWFya2luZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU2hvd3Mgc3RhdGlzdGljcyBmb3IgdGhlIHBvb2wgb2YgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfTEVWRUwnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0xvZ2dpbmcgdmVyYm9zaXR5IGxldmVsJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIHJvdW5kOiAwLFxyXG4gICAgICAgIG1pbjogMCxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgdmFsdWU6ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdMb2cgZmlsZSBuYW1lLiBSZXF1aXJlcyBgbG9nVG9GaWxlYCBhbmQgYGxvZ0Rlc3RgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Q6IHtcclxuICAgICAgdmFsdWU6ICdsb2cnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfREVTVCcsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgdG8gYmUgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9Db25zb2xlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0NPTlNPTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9Db25zb2xlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGNvbnNvbGUgbG9nZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9GaWxlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGxvZ2dpbmcgdG8gYSBmaWxlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcm91dGU6IHtcclxuICAgICAgdmFsdWU6ICcvJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9ST1VURScsXHJcbiAgICAgIGNsaU5hbWU6ICd1aVJvdXRlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgZW5kcG9pbnQgcm91dGUgZm9yIHRoZSBVSScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgb3RoZXI6IHtcclxuICAgIG5vZGVFbnY6IHtcclxuICAgICAgdmFsdWU6ICdwcm9kdWN0aW9uJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT0RFX0VOVicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIE5vZGUuanMgZW52aXJvbm1lbnQgdHlwZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvUHJvY2Vzc0V4aXRzOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gYXR0YWNoIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbm9Mb2dvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9fTE9HTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGlzcGxheSBvciBza2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIHN0YXJ0dXAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhhcmRSZXNldFBhZ2U6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9IQVJEX1JFU0VUX1BBR0UnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHJlc2V0IHRoZSBwYWdlIGNvbnRlbnQgZW50aXJlbHknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJyb3dzZXJTaGVsbE1vZGU6IHtcclxuICAgICAgdmFsdWU6IHRydWUsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX0JST1dTRVJfU0hFTExfTU9ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBzaGVsbCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGRlYnVnOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlRGVidWcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIHVuZGVybHlpbmcgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaGVhZGxlc3M6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19IRUFETEVTUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byBzZXQgdGhlIGJyb3dzZXIgdG8gcnVuIGluIGhlYWRsZXNzIG1vZGUgZHVyaW5nIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGV2dG9vbHM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19ERVZUT09MUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBEZXZUb29scyBpbiBoZWFkZnVsIG1vZGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0xJU1RFTl9UT19DT05TT0xFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VuYWJsZXMgb3IgZGlzYWJsZXMgbGlzdGVuaW5nIHRvIGNvbnNvbGUgbWVzc2FnZXMgZnJvbSB0aGUgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZHVtcGlvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRFVNUElPJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1JlZGlyZWN0cyBvciBub3QgYnJvd3NlciBzdGRvdXQgYW5kIHN0ZGVyciB0byBwcm9jZXNzLnN0ZG91dCBhbmQgcHJvY2Vzcy5zdGRlcnInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNsb3dNbzoge1xyXG4gICAgICB2YWx1ZTogMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19TTE9XX01PJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWxheXMgUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBtaWxsaXNlY29uZHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlYnVnZ2luZ1BvcnQ6IHtcclxuICAgICAgdmFsdWU6IDkyMjIsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVCVUdHSU5HX1BPUlQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgdXNlZCBmb3IgZGVidWdnaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vLyBQcm9wZXJ0aWVzIG5lc3RpbmcgbGV2ZWwgb2YgYWxsIG9wdGlvbnNcclxuZXhwb3J0IGNvbnN0IG5lc3RlZFByb3BzID0gX2NyZWF0ZU5lc3RlZFByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLy8gUHJvcGVydGllcyBuYW1lcyB0aGF0IHNob3VsZCBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkXHJcbmV4cG9ydCBjb25zdCBhYnNvbHV0ZVByb3BzID0gX2NyZWF0ZUFic29sdXRlUHJvcHMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgZ2VuZXJhdGVzIGEgbWFwcGluZyBvZiBuZXN0ZWQgYXJndW1lbnQgY2hhaW5zIGZyb20gYSBuZXN0ZWRcclxuICogY29uZmlnIG9iamVjdC4gVGhpcyBmdW5jdGlvbiB0cmF2ZXJzZXMgYSBuZXN0ZWQgb2JqZWN0IGFuZCBjcmVhdGVzIGEgbWFwcGluZ1xyXG4gKiB3aGVyZSBlYWNoIGtleSBpcyBhbiBhcmd1bWVudCBuYW1lIChlaXRoZXIgZnJvbSBgY2xpTmFtZWAsIGBsZWdhY3lOYW1lYCxcclxuICogb3IgdGhlIG9yaWdpbmFsIGtleSkgYW5kIGVhY2ggdmFsdWUgaXMgYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBjaGFpblxyXG4gKiBvZiBuZXN0ZWQgcHJvcGVydGllcyBsZWFkaW5nIHRvIHRoYXQgYXJndW1lbnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtuZXN0ZWRQcm9wcz17fV0gLSBUaGUgYWNjdW11bGF0b3Igb2JqZWN0IGZvciBzdG9yaW5nXHJcbiAqIHRoZSByZXN1bHRpbmcgYXJndW1lbnRzIGNoYWlucy4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtwcm9wQ2hhaW49JyddIC0gVGhlIGN1cnJlbnQgY2hhaW4gb2YgbmVzdGVkIHByb3BlcnRpZXMsXHJcbiAqIHVzZWQgaW50ZXJuYWxseSBkdXJpbmcgcmVjdXJzaW9uLiAgVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgc3RyaW5nLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgbWFwcGluZyBhcmd1bWVudCBuYW1lcyB0byB0aGVpciBjb3JyZXNwb25kaW5nXHJcbiAqIG5lc3RlZCBwcm9wZXJ0eSBjaGFpbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHMoY29uZmlnLCBuZXN0ZWRQcm9wcyA9IHt9LCBwcm9wQ2hhaW4gPSAnJykge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHMgb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBfY3JlYXRlTmVzdGVkUHJvcHMoZW50cnksIG5lc3RlZFByb3BzLCBgJHtwcm9wQ2hhaW59LiR7a2V5fWApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIHRoZSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzXHJcbiAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmNsaU5hbWUgfHwga2V5XSA9IGAke3Byb3BDaGFpbn0uJHtrZXl9YC5zdWJzdHJpbmcoMSk7XHJcblxyXG4gICAgICAvLyBTdXBwb3J0IGZvciB0aGUgbGVnYWN5LCBQaGFudG9tSlMgcHJvcGVydGllcyBuYW1lc1xyXG4gICAgICBpZiAoZW50cnkubGVnYWN5TmFtZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgbmVzdGVkUHJvcHNbZW50cnkubGVnYWN5TmFtZV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb2JqZWN0IHdpdGggbmVzdGVkIGFyZ3VtZW50IGNoYWluc1xyXG4gIHJldHVybiBuZXN0ZWRQcm9wcztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdhdGhlcnMgdGhlIG5hbWVzIG9mIHByb3BlcnRpZXMgZnJvbSBhIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRoYXRcclxuICogY2FuIGJlIHRyZWF0ZWQgYXMgYWJzb2x1dGUgcHJvcGVydGllcy4gVGhlc2UgcHJvcGVydGllcyBoYXZlIHZhbHVlcyB0aGF0XHJcbiAqIGFyZSBvYmplY3RzIGFuZCBkbyBub3QgY29udGFpbiBmdXJ0aGVyIG5lc3RlZCBkZXB0aCB3aGVuIG1lcmdpbmcgYW4gb2JqZWN0XHJcbiAqIGNvbnRhaW5pbmcgdGhlc2Ugb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVBYnNvbHV0ZVByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IFthYnNvbHV0ZVByb3BzPVtdXSAtIEFuIGFycmF5IHRvIGNvbGxlY3QgdGhlIG5hbWVzXHJcbiAqIG9mIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPHN0cmluZz59IEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG5hbWVzIG9mIGFic29sdXRlXHJcbiAqIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wcyhjb25maWcsIGFic29sdXRlUHJvcHMgPSBbXSkge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS50eXBlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHNcclxuICAgICAgX2NyZWF0ZUFic29sdXRlUHJvcHMoZW50cnksIGFic29sdXRlUHJvcHMpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSWYgdGhlIG9wdGlvbiBjYW4gYmUgYW4gb2JqZWN0LCBzYXZlIGl0cyB0eXBlIGluIHRoZSBhcnJheVxyXG4gICAgICBpZiAoZW50cnkudHlwZXMuaW5jbHVkZXMoJ09iamVjdCcpKSB7XHJcbiAgICAgICAgYWJzb2x1dGVQcm9wcy5wdXNoKGtleSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBhcnJheSB3aXRoIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzXHJcbiAgcmV0dXJuIGFic29sdXRlUHJvcHM7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBkZWZhdWx0Q29uZmlnLFxyXG4gIG5lc3RlZFByb3BzLFxyXG4gIGFic29sdXRlUHJvcHNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgZmlsZSBpcyByZXNwb25zaWJsZSBmb3IgcGFyc2luZyB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIHdpdGggdGhlICd6b2QnIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkXHJcbiAqIHRvIGJlIHVzZWQgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIGBlbnZzYC4gV2Ugc2hvdWxkIG5vdCB1c2UgdGhlIGBwcm9jZXNzLmVudmBcclxuICogZGlyZWN0bHkgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuXHJcbiAqIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lclxyXG4gKiBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICovXHJcblxyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5cclxuaW1wb3J0IHsgZGVmYXVsdENvbmZpZyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuLy8gTG9hZCAuZW52IGludG8gZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbi8vIE9iamVjdCB3aXRoIGN1c3RvbSB2YWxpZGF0b3JzIGFuZCB0cmFuc2Zvcm1lcnMsIHRvIGF2b2lkIHJlcGV0aXRpb25cclxuLy8gaW4gdGhlIENvbmZpZyBvYmplY3RcclxuY29uc3QgdiA9IHtcclxuICAvLyBTcGxpdHMgc3RyaW5nIHZhbHVlIGludG8gZWxlbWVudHMgaW4gYW4gYXJyYXksIHRyaW1zIGV2ZXJ5IGVsZW1lbnQsIGNoZWNrc1xyXG4gIC8vIGlmIGFuIGFycmF5IGlzIGNvcnJlY3QsIGlmIGl0IGlzIGVtcHR5LCBhbmQgaWYgaXQgaXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgYXJyYXk6IChmaWx0ZXJBcnJheSkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlXHJcbiAgICAgICAgICAuc3BsaXQoJywnKVxyXG4gICAgICAgICAgLm1hcCgodmFsdWUpID0+IHZhbHVlLnRyaW0oKSlcclxuICAgICAgICAgIC5maWx0ZXIoKHZhbHVlKSA9PiBmaWx0ZXJBcnJheS5pbmNsdWRlcyh2YWx1ZSkpXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZS5sZW5ndGggPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3Mgb25seSB0cnVlLCBmYWxzZSBhbmQgY29ycmVjdGx5IHBhcnNlIHRoZSB2YWx1ZSB0byBib29sZWFuXHJcbiAgLy8gb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSB1bmRlZmluZWRcclxuICBib29sZWFuOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbJ3RydWUnLCAnZmFsc2UnLCAnJ10pXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgPT09ICd0cnVlJyA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3MgcGFzc2VkIHZhbHVlcyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsXHJcbiAgLy8gYmUgdW5kZWZpbmVkXHJcbiAgZW51bTogKHZhbHVlcykgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWy4uLnZhbHVlcywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIFRyaW1zIHRoZSBzdHJpbmcgdmFsdWUgYW5kIGNoZWNrcyBpZiBpdCBpcyBlbXB0eSBvciBjb250YWlucyBzdHJpbmdpZmllZFxyXG4gIC8vIHZhbHVlcyBzdWNoIGFzIGZhbHNlLCB1bmRlZmluZWQsIG51bGwsIE5hTiwgaWYgaXQgZG9lcywgcmV0dXJucyB1bmRlZmluZWRcclxuICBzdHJpbmc6ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgIVsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJ10uaW5jbHVkZXModmFsdWUpIHx8XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSBzdHJpbmcgY29udGFpbnMgZm9yYmlkZGVuIHZhbHVlcywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBvc2l0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIHBvc2l0aXZlTnVtOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBwb3NpdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG5vbi1uZWdhdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlXHJcbiAgLy8gd2lsbCBiZSB1bmRlZmluZWRcclxuICBub25OZWdhdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCksXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSB2YWx1ZSBtdXN0IGJlIG51bWVyaWMgYW5kIG5vbi1uZWdhdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKVxyXG59O1xyXG5cclxuZXhwb3J0IGNvbnN0IENvbmZpZyA9IHoub2JqZWN0KHtcclxuICAvLyBwdXBwZXRlZXJcclxuICBQVVBQRVRFRVJfQVJHUzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEhJR0hDSEFSVFNfVkVSU0lPTiBtdXN0IGJlICdsYXRlc3QnLCBhIG1ham9yIHZlcnNpb24sIG9yIGluIHRoZSBmb3JtIFhYLllZLlpaLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfQ0ROX1VSTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHA6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcbiAgSElHSENIQVJUU19GT1JDRV9GRVRDSDogdi5ib29sZWFuKCksXHJcbiAgSElHSENIQVJUU19DQUNIRV9QQVRIOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQURNSU5fVE9LRU46IHYuc3RyaW5nKCksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNvcmVTY3JpcHRzLnZhbHVlKSxcclxuICBISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLm1vZHVsZVNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuaW5kaWNhdG9yU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcbiAgSElHSENIQVJUU19DVVNUT01fU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jdXN0b21TY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuXHJcbiAgLy8gZXhwb3J0XHJcbiAgRVhQT1JUX0lORklMRTogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfSU5TVFI6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1NWRzogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfQkFUQ0g6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09VVEZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RZUEU6IHYuZW51bShbJ2pwZWcnLCAncG5nJywgJ3BkZicsICdzdmcnXSksXHJcbiAgRVhQT1JUX0NPTlNUUjogdi5lbnVtKFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10pLFxyXG4gIEVYUE9SVF9CNjQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9OT19ET1dOTE9BRDogdi5ib29sZWFuKCksXHJcbiAgRVhQT1JUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1dJRFRIOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfR0xPQkFMX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RIRU1FX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG5cclxuICAvLyBjdXN0b21cclxuICBDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT046IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUzogdi5ib29sZWFuKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NVU1RPTV9DT0RFOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19DQUxMQkFDSzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfUkVTT1VSQ0VTOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gc2VydmVyXHJcbiAgU0VSVkVSX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX0hPU1Q6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfVVBMT0FEX0xJTUlUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX0JFTkNITUFSS0lORzogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIHNlcnZlciBwcm94eVxyXG4gIFNFUlZFUl9QUk9YWV9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QUk9YWV9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1BST1hZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gc2VydmVyIHJhdGUgbGltaXRpbmdcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFk6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU46IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlciBzc2xcclxuICBTRVJWRVJfU1NMX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9GT1JDRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1NTTF9DRVJUX1BBVEg6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHBvb2xcclxuICBQT09MX01JTl9XT1JLRVJTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9NQVhfV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfV09SS19MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfREVTVFJPWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfUkVBUEVSX0lOVEVSVkFMOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBsb2dnZXJcclxuICBMT0dHSU5HX0xFVkVMOiB6XHJcbiAgICAuc3RyaW5nKClcclxuICAgIC50cmltKClcclxuICAgIC5yZWZpbmUoXHJcbiAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZSA9PT0gJycgfHxcclxuICAgICAgICAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPD0gNSksXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgTE9HR0lOR19MRVZFTC4gV2Ugb25seSBhY2NlcHQgdmFsdWVzIGZyb20gMCB0byA1IGFzIGxvZ2dpbmcgbGV2ZWxzLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpLFxyXG4gIExPR0dJTkdfRklMRTogdi5zdHJpbmcoKSxcclxuICBMT0dHSU5HX0RFU1Q6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19UT19DT05TT0xFOiB2LmJvb2xlYW4oKSxcclxuICBMT0dHSU5HX1RPX0ZJTEU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyB1aVxyXG4gIFVJX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgVUlfUk9VVEU6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIG90aGVyXHJcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcclxuICBPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfTk9fTE9HTzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcclxuICBPVEhFUl9CUk9XU0VSX1NIRUxMX01PREU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBkZWJ1Z2dlclxyXG4gIERFQlVHX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfSEVBRExFU1M6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RFVlRPT0xTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19MSVNURU5fVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfRFVNUElPOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19TTE9XX01POiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgREVCVUdfREVCVUdHSU5HX1BPUlQ6IHYucG9zaXRpdmVOdW0oKVxyXG59KTtcclxuXHJcbmV4cG9ydCBjb25zdCBlbnZzID0gQ29uZmlnLnBhcnRpYWwoKS5wYXJzZShwcm9jZXNzLmVudik7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBNYW5hZ2VzIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgYnkgbG9hZGluZ1xyXG4gKiBhbmQgbWVyZ2luZyBvcHRpb25zIGZyb20gbXVsdGlwbGUgc291cmNlcywgc3VjaCBhcyBkZWZhdWx0IHNldHRpbmdzLFxyXG4gKiBlbnZpcm9ubWVudCB2YXJpYWJsZXMsIHVzZXItcHJvdmlkZWQgb3B0aW9ucywgYW5kIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqIEVuc3VyZXMgdGhlIGdsb2JhbCBvcHRpb25zIGFyZSB1cC10by1kYXRlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHkgdmFsdWVzLlxyXG4gKiBQcm92aWRlcyBmdW5jdGlvbnMgZm9yIGFjY2Vzc2luZyBhbmQgdXBkYXRpbmcgY29uZmlndXJhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBkZWVwQ29weSwgZ2V0QWJzb2x1dGVQYXRoLCBpc09iamVjdCB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IHsgYWJzb2x1dGVQcm9wcywgbmVzdGVkUHJvcHMsIGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0T3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PXRydWVdIC0gU3BlY2lmaWVzIHdoZXRoZXIgdG8gcmV0dXJuIGEgY29waWVkXHJcbiAqIG9iamVjdCBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgKGB0cnVlYCkgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCBvciBhIHJlZmVyZW5jZVxyXG4gKiB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoZ2V0Q29weSA9IHRydWUpIHtcclxuICByZXR1cm4gZ2V0Q29weSA/IGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpIDogZ2xvYmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXBkYXRlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBuZXcgb3B0aW9ucyB0byBiZVxyXG4gKiBtZXJnZWQgaW50byB0aGUgZ2xvYmFsIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldENvcHk9ZmFsc2VdIC0gRGV0ZXJtaW5lcyB3aGV0aGVyIHRvIG1lcmdlIHRoZSBuZXdcclxuICogb3B0aW9ucyBpbnRvIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IChgdHJ1ZWApIG9yIGRpcmVjdGx5IGludG9cclxuICogdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0LCBlaXRoZXIgdGhlIG1vZGlmaWVkIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9yIGEgbW9kaWZpZWQgY29weSwgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGBnZXRDb3B5YC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVPcHRpb25zKG5ld09wdGlvbnMsIGdldENvcHkgPSBmYWxzZSkge1xyXG4gIC8vIE1lcmdlIG5ldyBvcHRpb25zIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyBvciBpdHMgY29weSBhbmQgcmV0dXJuIHRoZSByZXN1bHRcclxuICByZXR1cm4gX21lcmdlT3B0aW9ucyhnZXRPcHRpb25zKGdldENvcHkpLCBuZXdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggdmFsdWVzIHByb3ZpZGVkIHRocm91Z2ggdGhlIENMSSwga2VlcGluZ1xyXG4gKiB0aGUgcHJpbmNpcGxlIG9mIG9wdGlvbnMgbG9hZCBwcmlvcml0eS4gVGhpcyBmdW5jdGlvbiBhY2NlcHRzIGEgYGNsaUFyZ3NgXHJcbiAqIGFycmF5IGNvbnRhaW5pbmcgYXJndW1lbnRzIGZyb20gdGhlIENMSSwgd2hpY2ggd2lsbCBiZSB2YWxpZGF0ZWQgYW5kIGFwcGxpZWRcclxuICogaWYgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIFRoZSBwcmlvcml0eSBvcmRlciBmb3Igc2V0dGluZyB2YWx1ZXMgaXM6XHJcbiAqXHJcbiAqIDEuIFZhbHVlcyBmcm9tIGEgY3VzdG9tIEpTT04gZmlsZSAobG9hZGVkIGJ5IHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24pLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSB0aGUgY29tbWFuZCBsaW5lIGludGVyZmFjZSAoQ0xJKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldENsaU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQgbGluZSBhcmd1bWVudHMgdXNlZFxyXG4gKiBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHJlZmxlY3RpbmcgdGhlIG1lcmdlZFxyXG4gKiBjb25maWd1cmF0aW9uIGZyb20gc291cmNlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEkuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0Q2xpT3B0aW9ucyhjbGlBcmdzKSB7XHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzICYmIEFycmF5LmlzQXJyYXkoY2xpQXJncykgJiYgY2xpQXJncy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGxvYWRlZCB2aWEgdGhlIGAtLWxvYWRDb25maWdgXHJcbiAgICBjb25zdCBjb25maWdPcHRpb25zID0gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNvbmZpZ09wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNvbmZpZ09wdGlvbnMpO1xyXG5cclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIENMSVxyXG4gICAgY29uc3QgY2xpT3B0aW9ucyA9IF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncyk7XHJcblxyXG4gICAgLy8gVXBkYXRlIGdsb2JhbCBvcHRpb25zIHdpdGggdGhlIHZhbHVlcyBmcm9tIHRoZSBgY2xpT3B0aW9uc2BcclxuICAgIHVwZGF0ZU9wdGlvbnMoY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnZXRPcHRpb25zKGZhbHNlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG9sZE9wdGlvbnMpKSB7XHJcbiAgICAvLyBJdGVyYXRlIG92ZXIgZWFjaCBrZXktdmFsdWUgcGFpciBpbiB0aGUgb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgdGhlcmUgaXMgYSBuZXN0ZWQgbWFwcGluZywgc3BsaXQgaXQgaW50byBhIHByb3BlcnRpZXMgY2hhaW5cclxuICAgICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNba2V5XVxyXG4gICAgICAgID8gbmVzdGVkUHJvcHNba2V5XS5zcGxpdCgnLicpXHJcbiAgICAgICAgOiBbXTtcclxuXHJcbiAgICAgIC8vIElmIGl0IGlzIHRoZSBsYXN0IHByb3BlcnR5IGluIHRoZSBjaGFpbiwgYXNzaWduIHRoZSB2YWx1ZSwgb3RoZXJ3aXNlLFxyXG4gICAgICAvLyBjcmVhdGUgb3IgcmV1c2UgdGhlIG5lc3RlZCBvYmplY3RcclxuICAgICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cclxuICAgICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgICAgbmV3T3B0aW9uc1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgICdbY29uZmlnXSBObyBjb3JyZWN0IG9iamVjdCB3aXRoIG9wdGlvbnMgd2FzIHByb3ZpZGVkLiBSZXR1cm5pbmcgYW4gZW1wdHkgYXJyYXkuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgbmV3LCBzdHJ1Y3R1cmVkIG9wdGlvbnMgb2JqZWN0XHJcbiAgcmV0dXJuIG5ld09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBWYWxpZGF0ZXMsIHBhcnNlcywgYW5kIGNoZWNrcyBpZiB0aGUgcHJvdmlkZWQgY29uZmlnIGlzIGFsbG93ZWQgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0FsbG93ZWRDb25maWdcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBjb25maWcgLSBUaGUgY29uZmlnIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkIGFzIGEgc2V0XHJcbiAqIG9mIG9wdGlvbnMuIE11c3QgYmUgZWl0aGVyIGFuIG9iamVjdCBvciBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBbdG9TdHJpbmc9ZmFsc2VdIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCB2ZXJzaW9uXHJcbiAqIG9mIHRoZSBwYXJzZWQgY29uZmlnLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFthbGxvd0Z1bmN0aW9ucz1mYWxzZV0gLSBXaGV0aGVyIHRvIGFsbG93IGZ1bmN0aW9uc1xyXG4gKiBpbiB0aGUgcGFyc2VkIGNvbmZpZy4gSWYgdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWQuIE90aGVyd2lzZSwgd2hlblxyXG4gKiBhIGZ1bmN0aW9uIGlzIGZvdW5kLCBudWxsIGlzIHJldHVybmVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFZlcnNpb24obm9Mb2dvID0gZmFsc2UpIHtcclxuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGAuZW52YCBvciBmcm9tIGBwYWNrYWdlLmpzb25gXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuICApLnZlcnNpb247XHJcblxyXG4gIC8vIFByaW50IHRleHQgb25seVxyXG4gIGlmIChub0xvZ28pIHtcclxuICAgIGNvbnNvbGUubG9nKGBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259YCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFByaW50IHRoZSBsb2dvXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAnbXNnJywgJ3N0YXJ0dXAubXNnJyksICd1dGY4JykudG9TdHJpbmcoKVxyXG4gICAgICAgIC5ib2xkLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IGJhc2VkIG9uIHRoZSBwcm92aWRlZFxyXG4gKiBjb25maWd1cmF0aW9uLCBzZXR0aW5nIHZhbHVlcyBmcm9tIG5lc3RlZCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5LlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIChkZWZhdWx0cykuXHJcbiAqIDIuIFZhbHVlcyBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcyAoc3BlY2lmaWVkIGluIHRoZSBgLmVudmAgZmlsZSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaW5pdE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogdGhlIGdsb2JhbCBvcHRpb25zLiBJdCBzaG91bGQgaW5jbHVkZSBuZXN0ZWQgcHJvcGVydGllcyB3aXRoIGEgYHZhbHVlYFxyXG4gKiBhbmQgYW4gYGVudkxpbmtgIGZvciBsaW5raW5nIHRvIGVudmlyb25tZW50IHZhcmlhYmxlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGluaXRpYWxpemVkIGdsb2JhbCBvcHRpb25zIG9iamVjdCwgcG9wdWxhdGVkIHdpdGhcclxuICogdmFsdWVzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHlcclxuICogb3JkZXIuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaW5pdE9wdGlvbnMoY29uZmlnKSB7XHJcbiAgLy8gSW5pdCB0aGUgb2JqZWN0IGZvciBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBTdGFydCBpbml0aWFsaXppbmcgdGhlIGBvcHRpb25zYCBvYmplY3QgcmVjdXJzaXZlbHlcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBpdGVtXSBvZiBPYmplY3QuZW50cmllcyhjb25maWcpKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY29ycmVjdCB2YWx1ZSBiYXNlZCBvbiB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHkgb3JkZXJcclxuICAgICAgaWYgKGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gdW5kZWZpbmVkICYmIGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gbnVsbCkge1xyXG4gICAgICAgIC8vIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdmFsdWVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gZW52c1tpdGVtLmVudkxpbmtdO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRoZSB2YWx1ZSBmcm9tIHRoZSBjb25maWcgZmlsZVxyXG4gICAgICAgIG9wdGlvbnNbbmFtZV0gPSBpdGVtLnZhbHVlO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDcmVhdGUgYSBzZWN0aW9uIGluIHRoZSBvcHRpb25zXHJcbiAgICAgIG9wdGlvbnNbbmFtZV0gPSBfaW5pdE9wdGlvbnMoaXRlbSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNyZWF0ZWQgYG9wdGlvbnNgIG9iamVjdFxyXG4gIHJldHVybiBvcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogTWVyZ2VzIHR3byBzZXRzIG9mIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgY29uc2lkZXJpbmcgYWJzb2x1dGUgcHJvcGVydGllcy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9tZXJnZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9yaWdpbmFsT3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTWVyZ2VkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9ucywgbmV3T3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgb3JpZ2luYWxPcHRpb25zYCBhbmQgYG5ld09wdGlvbnNgIGFyZSBjb3JyZWN0IG9iamVjdHNcclxuICBpZiAoaXNPYmplY3Qob3JpZ2luYWxPcHRpb25zKSAmJiBpc09iamVjdChuZXdPcHRpb25zKSkge1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobmV3T3B0aW9ucykpIHtcclxuICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gPVxyXG4gICAgICAgIGlzT2JqZWN0KHZhbHVlKSAmJlxyXG4gICAgICAgICFhYnNvbHV0ZVByb3BzLmluY2x1ZGVzKGtleSkgJiZcclxuICAgICAgICBvcmlnaW5hbE9wdGlvbnNba2V5XSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICA/IF9tZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zW2tleV0sIHZhbHVlKVxyXG4gICAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgICAgOiBvcmlnaW5hbE9wdGlvbnNba2V5XSB8fCBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBvcmlnaW5hbCAobW9kaWZpZWQgb3Igbm90KSBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIHRoZSBwcm92aWRlZCBvcHRpb25zIG9iamVjdCB0byBhIEpTT04tZm9ybWF0dGVkIHN0cmluZ1xyXG4gKiB3aXRoIHRoZSBvcHRpb24gdG8gcHJlc2VydmUgZnVuY3Rpb25zLiBJbiBvcmRlciBmb3IgYSBmdW5jdGlvblxyXG4gKiB0byBiZSBwcmVzZXJ2ZWQsIGl0IG5lZWRzIHRvIGZvbGxvdyB0aGUgZm9ybWF0IGBmdW5jdGlvbiAoLi4uKSB7Li4ufWAuXHJcbiAqIFN1Y2ggYSBmdW5jdGlvbiBjYW4gYWxzbyBiZSBzdHJpbmdpZmllZC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9vcHRpb25zU3RyaW5naWZ5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IHRvIGJlIGNvbnZlcnRlZCB0byBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0Z1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHByZXNlcnZlZFxyXG4gKiBpbiB0aGUgb3V0cHV0LiBPdGhlcndpc2UgYW4gZXJyb3IgaXMgdGhyb3duLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHN0cmluZ2lmeUZ1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHNhdmVkXHJcbiAqIGFzIHN0cmluZ3MuIFRoZSBgYWxsb3dGdW5jdGlvbnNgIG11c3QgYmUgc2V0IHRvIHRydWUgYXMgd2VsbCBmb3IgdGhpcyB0byB0YWtlXHJcbiAqIGFuIGVmZmVjdC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCB3aGVuIGZ1bmN0aW9ucyBhcmUgbm90IGFsbG93ZWQgYnV0IGFyZVxyXG4gKiBmb3VuZCBpbiBwcm92aWRlZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeShvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucywgc3RyaW5naWZ5RnVuY3Rpb25zKSB7XHJcbiAgY29uc3QgcmVwbGFjZXJDYWxsYmFjayA9IChfLCB2YWx1ZSkgPT4ge1xyXG4gICAgLy8gVHJpbSBzdHJpbmcgdmFsdWVzXHJcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xyXG4gICAgICB2YWx1ZSA9IHZhbHVlLnRyaW0oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBJZiB2YWx1ZSBpcyBhIGZ1bmN0aW9uIG9yIHN0cmluZ2lmaWVkIGZ1bmN0aW9uXHJcbiAgICBpZiAoXHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJyB8fFxyXG4gICAgICAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJykgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIElmIGFsbG93RnVuY3Rpb25zIGlzIHNldCB0byB0cnVlLCBwcmVzZXJ2ZSBmdW5jdGlvbnNcclxuICAgICAgaWYgKGFsbG93RnVuY3Rpb25zKSB7XHJcbiAgICAgICAgLy8gQmFzZWQgb24gdGhlIGBzdHJpbmdpZnlGdW5jdGlvbnNgIG9wdGlvbnMsIHNldCBmdW5jdGlvbiB2YWx1ZXNcclxuICAgICAgICByZXR1cm4gc3RyaW5naWZ5RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IC8vIEFzIHN0cmluZ2lmaWVkIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgXCJFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5cImBcclxuICAgICAgICAgIDogLy8gQXMgZnVuY3Rpb25zXHJcbiAgICAgICAgICAgIGBFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5gO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRocm93IGFuIGVycm9yIG90aGVyd2lzZVxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcigpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSW4gYWxsIG90aGVyIGNhc2VzLCBzaW1wbHkgcmV0dXJuIHRoZSB2YWx1ZVxyXG4gICAgcmV0dXJuIHZhbHVlO1xyXG4gIH07XHJcblxyXG4gIC8vIFN0cmluZ2lmeSBvcHRpb25zIGFuZCBpZiByZXF1aXJlZCwgcmVwbGFjZSBzcGVjaWFsIGZ1bmN0aW9ucyBtYXJrc1xyXG4gIHJldHVybiBKU09OLnN0cmluZ2lmeShvcHRpb25zLCByZXBsYWNlckNhbGxiYWNrKS5yZXBsYWNlQWxsKFxyXG4gICAgc3RyaW5naWZ5RnVuY3Rpb25zID8gL1xcXFxcIkVYUF9GVU58RVhQX0ZVTlxcXFxcIi9nIDogL1wiRVhQX0ZVTnxFWFBfRlVOXCIvZyxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgcHJvdmlkZWQgdmlhXHJcbiAqIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gaW4gdGhlIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgdG8gc2VhcmNoXHJcbiAqIGZvciB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncykge1xyXG4gIC8vIEdldCB0aGUgYWxsb3cgZmxhZ3MgZm9yIHRoZSBjdXN0b20gbG9naWMgY2hlY2tcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiwgYWxsb3dGaWxlUmVzb3VyY2VzIH0gPSBnZXRPcHRpb25zKCkuY3VzdG9tTG9naWM7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiB2YWx1ZVxyXG4gIGNvbnN0IGNvbmZpZ0ZpbGVOYW1lID0gY29uZmlnSW5kZXggPiAtMSAmJiBjbGlBcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lICYmIGFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICByZXR1cm4gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7Y29uZmlnRmlsZU5hbWV9IGZpbGUuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gYWRkaXRpb25hbCBvcHRpb25zIHRvIHJldHVyblxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhcnNlcyBjb21tYW5kLWxpbmUgYXJndW1lbnRzIGFuZCBwYWlycyBlYWNoIGFyZ3VtZW50IHdpdGggaXRzIGNvcnJlc3BvbmRpbmdcclxuICogb3B0aW9uIGluIHRoZSBjb25maWd1cmF0aW9uLiBUaGUgdmFsdWVzIGFyZSBzdHJ1Y3R1cmVkIGludG8gYSBuZXN0ZWQgb3B0aW9uc1xyXG4gKiBvYmplY3QsIGJhc2VkIG9uIHByZWRlZmluZWQgbWFwcGluZ3MuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gbmVzdGVkUHJvcHMgLSBBbiBhcnJheSBvZiBuZXN0aW5nIGxldmVsIGZvciBhbGxcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQtbGluZSBhcmd1bWVudHNcclxuICogY29udGFpbmluZyBvcHRpb25zIGFuZCB0aGVpciBhc3NvY2lhdGVkIHZhbHVlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gdXBkYXRlZCBvcHRpb25zIG9iamVjdCB3aGVyZSBlYWNoIG9wdGlvbiBmcm9tXHJcbiAqIHRoZSBjb21tYW5kLWxpbmUgaXMgcGFpcmVkIHdpdGggaXRzIHZhbHVlLCBzdHJ1Y3R1cmVkIGludG8gbmVzdGVkIG9iamVjdHNcclxuICogYXMgZGVmaW5lZC5cclxuICovXHJcbmZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncykge1xyXG4gIC8vIEFuIGVtcHR5IG9iamVjdCB0byBjb2xsZWN0IGFuZCBzdHJ1Y3R1cml6ZSBkYXRhIGZyb20gdGhlIGFyZ3NcclxuICBjb25zdCBjbGlPcHRpb25zID0ge307XHJcblxyXG4gIC8vIEN5Y2xlIHRocm91Z2ggYWxsIENMSSBhcmdzIGFuZCBmaWx0ZXIgdGhlbVxyXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2xpQXJncy5sZW5ndGg7IGkrKykge1xyXG4gICAgY29uc3Qgb3B0aW9uID0gY2xpQXJnc1tpXS5yZXBsYWNlKC8tL2csICcnKTtcclxuXHJcbiAgICAvLyBGaW5kIHRoZSByaWdodCBwbGFjZSBmb3IgcHJvcGVydHkncyB2YWx1ZVxyXG4gICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZFByb3BzW29wdGlvbl0uc3BsaXQoJy4nKVxyXG4gICAgICA6IFtdO1xyXG5cclxuICAgIC8vIENyZWF0ZSBvcHRpb25zIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIENMSSBmb3IgbGF0ZXIgcGFyc2luZyBhbmQgbWVyZ2luZ1xyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZSgob2JqLCBwcm9wLCBpbmRleCkgPT4ge1xyXG4gICAgICBpZiAocHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4KSB7XHJcbiAgICAgICAgY29uc3QgdmFsdWUgPSBjbGlBcmdzWysraV07XHJcbiAgICAgICAgaWYgKCF2YWx1ZSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NvbmZpZ10gTWlzc2luZyB2YWx1ZSBmb3IgdGhlIENMSSAnLS0ke29wdGlvbn0nIGFyZ3VtZW50LiBVc2luZyB0aGUgZGVmYXVsdCB2YWx1ZS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgICBvYmpbcHJvcF0gPSB2YWx1ZSB8fCBudWxsO1xyXG4gICAgICB9IGVsc2UgaWYgKG9ialtwcm9wXSA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgb2JqW3Byb3BdID0ge307XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcclxuICAgIH0sIGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHBhcnNlZCBDTEkgb3B0aW9uc1xyXG4gIHJldHVybiBjbGlPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgdHJhdmVyc2VzIHRoZSBvcHRpb25zIG9iamVjdCB0byBwcmludCB0aGUgdXNhZ2UgaW5mb3JtYXRpb25cclxuICogZm9yIGVhY2ggb3B0aW9uIGNhdGVnb3J5IGFuZCBpbmRpdmlkdWFsIG9wdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBDTEkgb3B0aW9ucy4gSXQgbWF5XHJcbiAqIGluY2x1ZGUgbmVzdGVkIGNhdGVnb3JpZXMgYW5kIGluZGl2aWR1YWwgb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9ucykge1xyXG4gIGZvciAoY29uc3QgW25hbWUsIG9wdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcclxuICAgIC8vIElmIHRoZSBjdXJyZW50IGVudHJ5IGlzIGEgY2F0ZWdvcnkgYW5kIG5vdCBhIGxlYWYgb3B0aW9uLCByZWN1cnNlIGludG8gaXRcclxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9wdGlvbiwgJ3ZhbHVlJykpIHtcclxuICAgICAgX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb24pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gUHJlcGFyZSBkZXNjcmlwdGlvblxyXG4gICAgICBjb25zdCBkZXNjTmFtZSA9IGAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9YDtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdmFsdWVcclxuICAgICAgbGV0IG9wdGlvblZhbHVlID0gb3B0aW9uLnZhbHVlO1xyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGFycmF5IG9mIHN0cmluZ3NcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nW10nKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID1cclxuICAgICAgICAgICdbJyArIG9wdGlvblZhbHVlLm1hcCgoaXRlbSkgPT4gYCcke2l0ZW19J2ApLmpvaW4oJywgJykgKyAnXSc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhIHN0cmluZ1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmcnKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID0gYCcke29wdGlvblZhbHVlfSdgO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgIGRlc2NOYW1lLmdyZWVuLFxyXG4gICAgICAgIGAkeygnPCcgKyBvcHRpb24udHlwZXMuam9pbignfCcpICsgJz4nKS55ZWxsb3d9YCxcclxuICAgICAgICBgJHtTdHJpbmcob3B0aW9uVmFsdWUpLmJvbGR9YC5ibHVlLFxyXG4gICAgICAgIGAtICR7b3B0aW9uLmRlc2NyaXB0aW9ufS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIHNldENsaU9wdGlvbnMsXHJcbiAgbWFwVG9OZXdPcHRpb25zLFxyXG4gIGlzQWxsb3dlZENvbmZpZyxcclxuICBwcmludExpY2Vuc2UsXHJcbiAgcHJpbnRVc2FnZSxcclxuICBwcmludFZlcnNpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhUVFAgdXRpbGl0eSBtb2R1bGUgZm9yIGZldGNoaW5nIGFuZCBwb3N0aW5nIGRhdGEuIFN1cHBvcnRzIGJvdGhcclxuICogSFRUUCBhbmQgSFRUUFMgcHJvdG9jb2xzLCBwcm92aWRpbmcgbWV0aG9kcyB0byBtYWtlIEdFVCBhbmQgUE9TVCByZXF1ZXN0c1xyXG4gKiB3aXRoIGN1c3RvbWl6YWJsZSBvcHRpb25zLiBJbmNsdWRlcyBwcm90b2NvbCBkZXRlcm1pbmF0aW9uIGJhc2VkIG9uIFVSTFxyXG4gKiBhbmQgYXVnbWVudHMgcmVzcG9uc2Ugb2JqZWN0cyB3aXRoIGEgJ3RleHQnIHByb3BlcnR5IGZvciBlYXNpZXIgZGF0YSBhY2Nlc3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBkYXRhIGZyb20gdGhlIHNwZWNpZmllZCBVUkwgdXNpbmcgZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZmV0Y2hcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZmV0Y2ggZGF0YSBmcm9tLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZldGNoKHVybCwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAuZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIGlmICghcmVzcG9uc2VEYXRhKSB7XHJcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgUE9TVCByZXF1ZXN0IHRvIHRoZSBzcGVjaWZpZWQgVVJMIHdpdGggdGhlIHByb3ZpZGVkIEpTT04gYm9keSB1c2luZ1xyXG4gKiBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIHNlbmQgdGhlIFBPU1QgcmVxdWVzdCB0by5cclxuICogQHBhcmFtIHtPYmplY3R9IFtib2R5PXt9XSAtIFRoZSBKU09OIGJvZHkgdG8gaW5jbHVkZSBpbiB0aGUgUE9TVCByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcG9zdCh1cmwsIGJvZHkgPSB7fSwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoYm9keSk7XHJcblxyXG4gICAgLy8gU2V0IGRlZmF1bHQgaGVhZGVycyBhbmQgbWVyZ2Ugd2l0aCByZXF1ZXN0T3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oXHJcbiAgICAgIHtcclxuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcclxuICAgICAgICBoZWFkZXJzOiB7XHJcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxyXG4gICAgICAgICAgJ0NvbnRlbnQtTGVuZ3RoJzogZGF0YS5sZW5ndGhcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zXHJcbiAgICApO1xyXG5cclxuICAgIGNvbnN0IHJlcXVlc3QgPSBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAucmVxdWVzdCh1cmwsIG9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgIC8vIFdyaXRlIHRoZSByZXF1ZXN0IGJvZHkgYW5kIGVuZCB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC53cml0ZShkYXRhKTtcclxuICAgIHJlcXVlc3QuZW5kKCk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgVVJMLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGRldGVybWluZSB0aGUgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSAoaHR0cCBvciBodHRwcykuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKSB7XHJcbiAgcmV0dXJuIHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZmV0Y2gsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBlcnJvciBjbGFzcyBmb3IgaGFuZGxpbmcgZXhwb3J0LXJlbGF0ZWQgZXJyb3JzLiBFeHRlbmRzIHRoZSBuYXRpdmVcclxuICogYEVycm9yYCBjbGFzcyB0byBpbmNsdWRlIGFkZGl0aW9uYWwgcHJvcGVydGllcyBsaWtlIHN0YXR1cyBjb2RlIGFuZCBzdGFja1xyXG4gKiB0cmFjZSBkZXRhaWxzLlxyXG4gKi9cclxuY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gVGhlIGVycm9yIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkLlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gT3B0aW9uYWwgSFRUUCBzdGF0dXMgY29kZSBhc3NvY2lhdGVkXHJcbiAgICogd2l0aCB0aGUgZXJyb3IgKGUuZy4sIDQwMCwgNTAwKS5cclxuICAgKi9cclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXNDb2RlKSB7XHJcbiAgICBzdXBlcigpO1xyXG5cclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcblxyXG4gICAgaWYgKHN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgb3IgdXBkYXRlcyB0aGUgSFRUUCBzdGF0dXMgY29kZSBmb3IgdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBUaGUgSFRUUCBzdGF0dXMgY29kZSB0byBhc3NpZ24gdG8gdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgYWRkaXRpb25hbCBlcnJvciBkZXRhaWxzIGJhc2VkIG9uIGFuIGV4aXN0aW5nIGVycm9yIG9iamVjdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gQW4gZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgZGV0YWlscyB0byBwb3B1bGF0ZVxyXG4gICAqIHRoZSBgRXhwb3J0RXJyb3JgIGluc3RhbmNlLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG5cclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoZSBjYWNoZSBtYW5hZ2VyIGlzIHJlc3BvbnNpYmxlIGZvciBoYW5kbGluZyBhbmQgbWFuYWdpbmdcclxuICogdGhlIEhpZ2hjaGFydHMgbGlicmFyeSBhbG9uZyB3aXRoIGl0cyBkZXBlbmRlbmNpZXMuIEl0IGVuc3VyZXMgdGhhdCB0aGVzZVxyXG4gKiByZXNvdXJjZXMgYXJlIHN0b3JlZCBhbmQgcmV0cmlldmVkIGVmZmljaWVudGx5IHRvIG9wdGltaXplIHBlcmZvcm1hbmNlXHJcbiAqIGFuZCByZWR1Y2UgcmVkdW5kYW50IG5ldHdvcmsgcmVxdWVzdHMuIFRoZSBjYWNoZSBpcyBzdG9yZWQgaW4gdGhlIGAuY2FjaGVgXHJcbiAqIGRpcmVjdG9yeSBieSBkZWZhdWx0LCB3aGljaCBzZXJ2ZXMgYXMgYSBkZWRpY2F0ZWQgZm9sZGVyIGZvciBrZWVwaW5nIGNhY2hlZFxyXG4gKiBmaWxlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnNcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNhY2hlIHBhdGhcclxuICAgIGNvbnN0IGNhY2hlUGF0aCA9IGdldENhY2hlUGF0aCgpO1xyXG5cclxuICAgIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgICBjb25zdCBtYW5pZmVzdFBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKTtcclxuICAgIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ3NvdXJjZXMuanMnKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcclxuXHJcbiAgICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIHRoZSBgbWFuaWZlc3QuanNvbmAgZG9lcyBub3QgZXhpc3RcclxuICAgIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICAgIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGhpZ2hjaGFydHNPcHRpb25zLmZvcmNlRmV0Y2gpIHtcclxuICAgICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAgIC8vIFJlYWQgdGhlIG1hbmlmZXN0IEpTT05cclxuICAgICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpLCAndXRmOCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG1vZHVsZXMgaXMgYW4gYXJyYXksIGlmIHNvLCB3ZSByZXdyaXRlIGl0IHRvIGEgbWFwIHRvIG1ha2VcclxuICAgICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIGFjdHVhbCBudW1iZXIgb2Ygc2NyaXB0cyB0byBiZSBmZXRjaGVkXHJcbiAgICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9XHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnM7XHJcbiAgICAgIGNvbnN0IG51bWJlck9mTW9kdWxlcyA9XHJcbiAgICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcclxuICAgICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24pIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAgIE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzXHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgX3VwZGF0ZUNhY2hlKFxyXG4gICAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgICBzb3VyY2VQYXRoXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xyXG5cclxuICAgICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEZpbmFsbHksIHNhdmUgdGhlIG5ldyBtYW5pZmVzdCwgd2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjdXJyZW50IGNvbmZpZ1xyXG4gICAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgICBhd2FpdCBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY29uZmlndXJlIGNhY2hlIGFuZCBjcmVhdGUgb3IgdXBkYXRlIHRoZSBjb25maWcgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIFVwZGF0ZSB0byB0aGUgbmV3IHZlcnNpb25cclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICBoaWdoY2hhcnRzOiB7XHJcbiAgICAgIHZlcnNpb246IG5ld1ZlcnNpb25cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgsICd1dGY4Jyk7IC8vICM1NjJcclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBgZmV0Y2hlZE1vZHVsZXNgIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxyXG4gKiB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxyXG4gKiBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuIFRoZSBkZWZhdWx0XHJcbiAqIHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbVxyXG4gKiB3aXRoIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxyXG4gIHNjcmlwdCxcclxuICByZXF1ZXN0T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlcyxcclxuICBzaG91bGRUaHJvd0Vycm9yID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgLmpzIGZyb20gdGhlIGN1c3RvbSBzdHJpbmdzXHJcbiAgaWYgKHNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgIHNjcmlwdCA9IHNjcmlwdC5zdWJzdHJpbmcoMCwgc2NyaXB0Lmxlbmd0aCAtIDMpO1xyXG4gIH1cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gIH1cclxuXHJcbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYCxcclxuICAgICAgNDA0XHJcbiAgICApLnNldEVycm9yKHJlc3BvbnNlKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uLmBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3RcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbZmV0Y2hlZE1vZHVsZXM9e31dIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzT3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMgPSB7fSkge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzXHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihnZXRDYWNoZVBhdGgoKSwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgYHNjcmlwdHNgIGFuZCBgY3VzdG9tU2NyaXB0c2AgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNvcmVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBjb3JlIHNjcmlwdHMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG1vZHVsZVNjcmlwdHMgLSBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGN1c3RvbVNjcmlwdHMgLSBDdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsXHJcbiAqIFVSTHMpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmZXRjaGVkIHNjcmlwdHNcclxuICogY29udGVudCBqb2luZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogc2V0dGluZyBhbiBIVFRQIEFnZW50IGZvciBwcm94eS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHMoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pIHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gc2VydmVyUHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gc2VydmVyUHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiBleGlzdHMsIGFkZCBwcm94eSBhZ2VudCB0byByZXF1ZXN0IG9wdGlvbnNcclxuICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHByb3h5QWdlbnRcclxuICAgID8ge1xyXG4gICAgICAgIGFnZW50OiBwcm94eUFnZW50LFxyXG4gICAgICAgIHRpbWVvdXQ6IHNlcnZlclByb3h5T3B0aW9ucy50aW1lb3V0XHJcbiAgICAgIH1cclxuICAgIDoge307XHJcblxyXG4gIGNvbnN0IGFsbEZldGNoUHJvbWlzZXMgPSBbXHJcbiAgICAuLi5jb3JlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcylcclxuICAgICksXHJcbiAgICAuLi5jdXN0b21TY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucylcclxuICAgIClcclxuICBdO1xyXG5cclxuICBjb25zdCBmZXRjaGVkU2NyaXB0cyA9IGF3YWl0IFByb21pc2UuYWxsKGFsbEZldGNoUHJvbWlzZXMpO1xyXG4gIHJldHVybiBmZXRjaGVkU2NyaXB0cy5qb2luKCc7XFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgcmVwcmVzZW50aW5nXHJcbiAqIHRoZSBmZXRjaGVkIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF91cGRhdGVDYWNoZShoaWdoY2hhcnRzT3B0aW9ucywgc2VydmVyUHJveHlPcHRpb25zLCBzb3VyY2VQYXRoKSB7XHJcbiAgLy8gR2V0IEhpZ2hjaGFydHMgdmVyc2lvbiBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGhjVmVyc2lvbiA9XHJcbiAgICBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uID09PSAnbGF0ZXN0J1xyXG4gICAgICA/IG51bGxcclxuICAgICAgOiBgJHtoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9ufWA7XHJcblxyXG4gIC8vIEdldCB0aGUgQ0ROIHVybCBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGNkblVybCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVybCB8fCBjYWNoZS5jZG5Vcmw7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5zb3VyY2VzID0gYXdhaXQgX2ZldGNoU2NyaXB0cyhcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoYykgPT5cclxuICAgICAgICAgIGhjVmVyc2lvbiA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259LyR7Y31gIDogYCR7Y2RuVXJsfS8ke2N9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vbWFwcy8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tYXBzL21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgOiBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICksXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGkpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb25cclxuICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L3N0b2NrLyR7aGNWZXJzaW9ufS9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9zdG9jay9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldEhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIGV4dHJhY3RWZXJzaW9uLFxyXG4gIGV4dHJhY3RNb2R1bGVOYW1lLFxyXG4gIGdldENhY2hlLFxyXG4gIGdldENhY2hlUGF0aFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWV0aG9kcyBmb3IgaW5pdGlhbGl6aW5nIEhpZ2hjaGFydHMgd2l0aCBjdXN0b21pemVkXHJcbiAqIGFuaW1hdGlvbiBzZXR0aW5ncyBhbmQgdHJpZ2dlcmluZyB0aGUgY3JlYXRpb24gb2YgSGlnaGNoYXJ0cyBjaGFydHMgd2l0aFxyXG4gKiBleHBvcnQtc3BlY2lmaWMgY29uZmlndXJhdGlvbnMgaW4gdGhlIHBhZ2UgY29udGV4dC4gU3VwcG9ydHMgZHluYW1pYyBvcHRpb25cclxuICogbWVyZ2luZywgY3VzdG9tIGxvZ2ljIGluamVjdGlvbiwgYW5kIGNvbnRyb2wgb3ZlciByZW5kZXJpbmcgYmVoYXZpb3JzLiBVc2VkXHJcbiAqIGJ5IHRoZSBQdXBwZXRlZXIgcGFnZS5cclxuICovXHJcblxyXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xyXG5cclxuLyoqXHJcbiAqIFNldHRpbmcgdGhlIGBIaWdoY2hhcnRzLmFuaW1PYmplY3RgIGZ1bmN0aW9uLiBDYWxsZWQgd2hlbiBpbml0aW5nIHRoZSBwYWdlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xyXG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcclxuICAgIHJldHVybiB7IGR1cmF0aW9uOiAwIH07XHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBIaWdoY2hhcnRzIGNoYXJ0IG9uIGEgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVDaGFydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQ2hhcnQoZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgc2V0T3B0aW9ucywgbWVyZ2UsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHRoZSBgdXNlck9wdGlvbnNgIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB0aGUgYHVzZXJPcHRpb25zYCB3aGVuIGBmb3JFeHBvcnRgIGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBTb21lIG1hbmRhdG9yeSBhZGRpdGlvbmFsIGBjaGFydGAgYW5kIGBleHBvcnRpbmdgIG9wdGlvbnNcclxuICBjb25zdCBhZGRpdGlvbmFsT3B0aW9ucyA9IHtcclxuICAgIGNoYXJ0OiB7XHJcbiAgICAgIC8vIEJ5IGRlZmF1bHQgYW5pbWF0aW9uIGlzIGRpc2FibGVkXHJcbiAgICAgIGFuaW1hdGlvbjogZmFsc2UsXHJcbiAgICAgIC8vIEdldCB0aGUgcmlnaHQgc2l6ZSB2YWx1ZXNcclxuICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcclxuICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmluc3RyfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYHRoZW1lT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgdGhlbWVPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9uc31gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb25cclxuICBjb25zdCBnbG9iYWxPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gTWVyZ2UgdGhlIGZvbGxvd2luZyBvcHRpb25zIG9iamVjdHMgdG8gY3JlYXRlIGZpbmFsIG9wdGlvbnNcclxuICBjb25zdCBmaW5hbE9wdGlvbnMgPSBtZXJnZShcclxuICAgIGZhbHNlLFxyXG4gICAgdGhlbWVPcHRpb25zLFxyXG4gICAgdXNlck9wdGlvbnMsXHJcbiAgICAvLyBQbGFjZWQgaXQgaGVyZSBpbnN0ZWFkIGluIHRoZSBpbml0IGJlY2F1c2Ugb2YgdGhlIHNpemUgaXNzdWVzXHJcbiAgICBhZGRpdGlvbmFsT3B0aW9uc1xyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjYWxsYmFja2Agb3B0aW9uXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke2N1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja31gKSgpXHJcbiAgICA6IG51bGw7XHJcblxyXG4gIC8vIFRyaWdnZXIgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbignb3B0aW9ucycsIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbZXhwb3J0T3B0aW9ucy5jb25zdHJdKCdjb250YWluZXInLCBmaW5hbE9wdGlvbnMsIGZpbmFsQ2FsbGJhY2spO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBgc2V0T3B0aW9uc2Agd2FzIHVzZWQgaW4gdGhlIGBjdXN0b21Db2RlYClcclxuICBmb3IgKGNvbnN0IHByb3AgaW4gZGVmYXVsdE9wdGlvbnMpIHtcclxuICAgIGlmICh0eXBlb2YgZGVmYXVsdE9wdGlvbnNbcHJvcF0gIT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgZGVsZXRlIGRlZmF1bHRPcHRpb25zW3Byb3BdO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBkZWZhdWx0IG9wdGlvbnMgYmFja1xyXG4gIHNldE9wdGlvbnMoSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqKTtcclxuXHJcbiAgLy8gRW1wdHkgdGhlIGN1c3RvbSBnbG9iYWwgb3B0aW9ucyBvYmplY3RcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSB7fTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNldHVwSGlnaGNoYXJ0cyxcclxuICBjcmVhdGVDaGFydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZSwgY3JlYXRpbmcgYW5kIGNsZWFyaW5nIHBhZ2VzLCBpbmplY3RpbmcgY3VzdG9tIHJlc291cmNlcyxcclxuICogYW5kIHNldHRpbmcgdXAgSGlnaGNoYXJ0cyBmb3Igc2VydmVyLXNpZGUgcmVuZGVyaW5nLiBUaGUgbW9kdWxlIGVuc3VyZXNcclxuICogdGhhdCByZXNvdXJjZXMgYXJlIGNvcnJlY3RseSBtYW5hZ2VkIGFuZCBjYW4gaGFuZGxlIGZhaWx1cmVzIGR1cmluZ1xyXG4gKiBvcGVyYXRpb25zIGxpa2UgbGF1bmNoaW5nIHRoZSBicm93c2VyIG9yIGNyZWF0aW5nIG5ldyBwYWdlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciBwYWdlc1xyXG5jb25zdCB0ZW1wbGF0ZSA9IHJlYWRGaWxlU3luYyhcclxuICBqb2luKF9fZGlybmFtZSwgJ3RlbXBsYXRlcycsICd0ZW1wbGF0ZS5odG1sJyksXHJcbiAgJ3V0ZjgnXHJcbik7XHJcblxyXG4vLyBUbyBzYXZlIHRoZSBicm93c2VyXHJcbmxldCBicm93c2VyID0gbnVsbDtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QnJvd3NlclxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBubyB2YWxpZCBicm93c2VyXHJcbiAqIGhhcyBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QnJvd3NlcigpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nLCA1MDApO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXJcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgY3JlYXRlZCBQdXBwZXRlZXJcclxuICogYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG1heCByZXRyaWVzIHRvIG9wZW5cclxuICogYSBicm93c2VyIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyXHJcbiAqIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2AgYW5kIGBvdGhlcmAgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWdPcHRpb25zIH0gPSBkZWJ1ZztcclxuXHJcbiAgLy8gTGF1bmNoIG9wdGlvbnMgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgY29uc3QgbGF1bmNoT3B0aW9ucyA9IHtcclxuICAgIGhlYWRsZXNzOiBvdGhlci5icm93c2VyU2hlbGxNb2RlID8gJ3NoZWxsJyA6IHRydWUsXHJcbiAgICB1c2VyRGF0YURpcjogJ3RtcCcsXHJcbiAgICBhcmdzOiBwdXBwZXRlZXJBcmdzIHx8IFtdLFxyXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHSFVQOiBmYWxzZSxcclxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXHJcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXHJcbiAgICAuLi4oZW5hYmxlZERlYnVnICYmIGRlYnVnT3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICAvLyBBIGNvdW50ZXIgZm9yIHRoZSBicm93c2VyJ3MgbGF1bmNoIHJldHJpZXNcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFticm93c2VyXSBBdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeSAkeysrdHJ5Q291bnR9KS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gTGF1bmNoIHRoZSBicm93c2VyXHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgJ1ticm93c2VyXSBGYWlsZWQgdG8gbGF1bmNoIGEgYnJvd3NlciBpbnN0YW5jZS4nXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0cnkgdG8gbGF1bmNoIGJyb3dzZXIgdW50aWwgcmVhY2hpbmcgbWF4IGF0dGVtcHRzXHJcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcclxuICAgICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIFJldHJ5IHRvIG9wZW4gYSBicm93c2VyICgke3RyeUNvdW50fSBvdXQgb2YgMjUpLmApO1xyXG5cclxuICAgICAgICAgIC8vIFdhaXQgZm9yIGEgNCBzZWNvbmRzIGJlZm9yZSB0cnlpbmcgYWdhaW5cclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG5cclxuICAgICAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGxhdW5jaE9wdGlvbnMuaGVhZGxlc3MgPT09ICdzaGVsbCcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIHNoZWxsIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERlYnVnIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChlbmFibGVkRGVidWcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIGRlYnVnIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2Jyb3dzZXJdIE1heGltdW0gcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlciBpbnN0YW5jZSByZWFjaGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nLCA1MDApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xvc2VCcm93c2VyXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2VCcm93c2VyKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIgJiYgYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgYnJvd3NlciA9IG51bGw7XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgcGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHRoZSBgX3NldFBhZ2VDb250ZW50KClgLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBuZXdQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIGBpZGAsXHJcbiAqIGB3b3JrQ291bnRgLCBhbmQgYHBhZ2VgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjb25uZWN0ZWQgb3IgaWYgYSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG5ld1BhZ2UocG9vbFJlc291cmNlKSB7XHJcbiAgLy8gRXJyb3IgaW4gY2FzZSBvZiBubyBjb25uZWN0ZWQgYnJvd3NlclxyXG4gIGlmICghYnJvd3NlciB8fCAhYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW2Jyb3dzZXJdIEJyb3dzZXIgaXMgbm90IHlldCBjb25uZWN0ZWQuYCwgNTAwKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBwb29sUmVzb3VyY2UucGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBEaXNhYmxlIGNhY2hlXHJcbiAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXHJcbiAgX3NldFBhZ2VFdmVudHMocG9vbFJlc291cmNlLnBhZ2UpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgcGFnZSBpcyBjb3JyZWN0bHkgY3JlYXRlZFxyXG4gIGlmICghcG9vbFJlc291cmNlLnBhZ2UgfHwgcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgdGhlIGNvbnRlbnQgb2YgYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG1vZGUuIExvZ3NcclxuICogdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIG9mIGEgcGFnZSdzIGNvbnRlbnQgZmFpbHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIHBhZ2UgYW5kIGlkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtoYXJkUmVzZXQ9ZmFsc2VdIC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gYGFib3V0OmJsYW5rYCBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgd2hlbiBwYWdlXHJcbiAqIGlzIGNvcnJlY3RseSBjbGVhcmVkIGFuZCBmYWxzZSB3aGVuIGl0IGlzIG5vdC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2UocG9vbFJlc291cmNlLCBoYXJkUmVzZXQgPSBmYWxzZSkge1xyXG4gIHRyeSB7XHJcbiAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGBhYm91dDpibGFua2BcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHtcclxuICAgICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBfc2V0UGFnZUNvbnRlbnQocG9vbFJlc291cmNlLnBhZ2UpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gQ29udGVudCBvZiB0aGUgcGFnZSBjb3VsZCBub3QgYmUgY2xlYXJlZC5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICBwb29sUmVzb3VyY2Uud29ya0NvdW50ID0gZ2V0T3B0aW9ucygpLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICB9XHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyBjdXN0b20gSlMgYW5kIENTUyByZXNvdXJjZXMgdG8gYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYWRkUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9IGZpbGUuc3RhcnRzV2l0aCgnaHR0cCcpID8gZmFsc2UgOiB0cnVlO1xyXG5cclxuICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gc2NyaXB0IGZyb20gcmVzb3VyY2VzJyBmaWxlc1xyXG4gICAgICAgIGluamVjdGVkSnMucHVzaChcclxuICAgICAgICAgIGlzTG9jYWxcclxuICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICBjb250ZW50OiByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGZpbGUpLCAndXRmOCcpXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA6IHtcclxuICAgICAgICAgICAgICAgIHVybDogZmlsZVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgZm9yIChjb25zdCBqc1Jlc291cmNlIG9mIGluamVjdGVkSnMpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU2NyaXB0VGFnKGpzUmVzb3VyY2UpKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gVGhlIEpTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGluamVjdGVkSnMubGVuZ3RoID0gMDtcclxuXHJcbiAgICAvLyBMb2FkIENTU1xyXG4gICAgY29uc3QgaW5qZWN0ZWRDc3MgPSBbXTtcclxuICAgIGlmIChyZXNvdXJjZXMuY3NzKSB7XHJcbiAgICAgIGxldCBjc3NJbXBvcnRzID0gcmVzb3VyY2VzLmNzcy5tYXRjaCgvQGltcG9ydFxccyooW147XSopOy9nKTtcclxuICAgICAgaWYgKGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAvLyBIYW5kbGUgY3NzIHNlY3Rpb25cclxuICAgICAgICBmb3IgKGxldCBjc3NJbXBvcnRQYXRoIG9mIGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoKSB7XHJcbiAgICAgICAgICAgIGNzc0ltcG9ydFBhdGggPSBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ3VybCgnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgnQGltcG9ydCcsICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvJy9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvOy8sICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cXCkvZywgJycpXHJcbiAgICAgICAgICAgICAgLnRyaW0oKTtcclxuXHJcbiAgICAgICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBjc3MgZnJvbSByZXNvdXJjZXNcclxuICAgICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGguc3RhcnRzV2l0aCgnaHR0cCcpKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICBwYXRoOiBnZXRBYnNvbHV0ZVBhdGgoY3NzSW1wb3J0UGF0aClcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVGhlIHJlc3Qgb2YgdGhlIENTUyBzZWN0aW9uIHdpbGwgYmUgY29udGVudCBieSBub3dcclxuICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmNzcy5yZXBsYWNlKC9AaW1wb3J0XFxzKihbXjtdKik7L2csICcnKSB8fCAnICdcclxuICAgICAgfSk7XHJcblxyXG4gICAgICBmb3IgKGNvbnN0IGNzc1Jlc291cmNlIG9mIGluamVjdGVkQ3NzKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyhjc3NSZXNvdXJjZSkpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICBgW2Jyb3dzZXJdIFRoZSBDU1MgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBpbmplY3RlZENzcy5sZW5ndGggPSAwO1xyXG4gICAgfVxyXG4gIH1cclxuICByZXR1cm4gaW5qZWN0ZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgb3V0IGFsbCBzdGF0ZSBzZXQgb24gdGhlIHBhZ2Ugd2l0aCBgYWRkU2NyaXB0VGFnYCBhbmQgYGFkZFN0eWxlVGFnYC5cclxuICogUmVtb3ZlcyBpbmplY3RlZCByZXNvdXJjZXMgYW5kIHJlc2V0cyBDU1MgYW5kIHNjcmlwdCB0YWdzIG9uIHRoZSBwYWdlLlxyXG4gKiBBZGRpdGlvbmFsbHksIGl0IGRlc3Ryb3lzIHByZXZpb3VzbHkgZXhpc3RpbmcgY2hhcnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgZnJvbSB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxPYmplY3Q+fSBpbmplY3RlZFJlc291cmNlcyAtIEFycmF5IG9mIGluamVjdGVkIHJlc291cmNlc1xyXG4gKiB0byBiZSBjbGVhcmVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcykge1xyXG4gIHRyeSB7XHJcbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlc291cmNlLmRpc3Bvc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IG9sZCBjaGFydHMgYWZ0ZXIgZXhwb3J0IGlzIGRvbmUgYW5kIHJlc2V0IGFsbCBDU1MgYW5kIHNjcmlwdCB0YWdzXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgLy8gV2UgYXJlIG5vdCBndWFyYW50ZWVkIHRoYXQgSGlnaGNoYXJ0cyBpcyBsb2FkZWQsIHdoZW4gZG9pbmcgU1ZHIGV4cG9ydHNcclxuICAgICAgaWYgKHR5cGVvZiBIaWdoY2hhcnRzICE9PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgIGNvbnN0IG9sZENoYXJ0cyA9IEhpZ2hjaGFydHMuY2hhcnRzO1xyXG5cclxuICAgICAgICAvLyBDaGVjayBpbiBhbnkgYWxyZWFkeSBleGlzdGluZyBjaGFydHNcclxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShvbGRDaGFydHMpICYmIG9sZENoYXJ0cy5sZW5ndGgpIHtcclxuICAgICAgICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0c1xyXG4gICAgICAgICAgZm9yIChjb25zdCBvbGRDaGFydCBvZiBvbGRDaGFydHMpIHtcclxuICAgICAgICAgICAgb2xkQ2hhcnQgJiYgb2xkQ2hhcnQuZGVzdHJveSgpO1xyXG4gICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgICAgSGlnaGNoYXJ0cy5jaGFydHMuc2hpZnQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gQ291bGQgbm90IGNsZWFyIHBhZ2UncyByZXNvdXJjZXMuYCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgY29udGVudCBmb3IgYSBQdXBwZXRlZXIgUGFnZSB1c2luZyBhIHByZWRlZmluZWQgdGVtcGxhdGVcclxuICogYW5kIGFkZGl0aW9uYWwgc2NyaXB0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBjb250ZW50XHJcbiAqIGlzIGJlaW5nIHNldC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zZXRQYWdlQ29udGVudChwYWdlKSB7XHJcbiAgLy8gU2V0IHRoZSBpbml0aWFsIHBhZ2UgY29udGVudFxyXG4gIGF3YWl0IHBhZ2Uuc2V0Q29udGVudCh0ZW1wbGF0ZSwgeyB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJyB9KTtcclxuXHJcbiAgLy8gQWRkIGFsbCByZWdpc3RlcmVkIEhpZ2NoYXJ0cyBzY3JpcHRzLCBxdWl0ZSBkZW1hbmRpbmdcclxuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdzb3VyY2VzLmpzJykgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBgYW5pbU9iamVjdGAgZm9yIEhpZ2hjaGFydHNcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKHNldHVwSGlnaGNoYXJ0cyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgZXZlbnRzIChsaWtlIGBwYWdlZXJyb3JgIGFuZCBgY29uc29sZWApIGZvciBhIFB1cHBldGVlciBQYWdlIGluIG9yZGVyXHJcbiAqIHRvIGNhdGNoIGFuZCBkaXNwbGF5IGVycm9ycyBhbmQgY29uc29sZSBsb2dzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VFdmVudHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBsaXN0ZW5lcnNcclxuICogYXJlIGJlaW5nIHNldC5cclxuICovXHJcbmZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzKHBhZ2UpIHtcclxuICAvLyBHZXQgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBkZWJ1ZyB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBTZXQgdGhlIGBwYWdlZXJyb3JgIGxpc3RlbmVyXHJcbiAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKCkgPT4ge1xyXG4gICAgLy8gSXQgd291bGQgc2VlbSBsaWtlIHRoaXMgbWF5IGZpcmUgYXQgdGhlIHNhbWUgdGltZSBvciBzaG9ydGx5IGJlZm9yZVxyXG4gICAgLy8gYSBwYWdlIGlzIGNsb3NlZC5cclxuICAgIGlmIChwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGBjb25zb2xlYCBsaXN0ZW5lciwgaWYgbmVlZGVkXHJcbiAgaWYgKGRlYnVnLmVuYWJsZSAmJiBkZWJ1Zy5saXN0ZW5Ub0NvbnNvbGUpIHtcclxuICAgIHBhZ2Uub24oJ2NvbnNvbGUnLCAobWVzc2FnZSkgPT4ge1xyXG4gICAgICBjb25zb2xlLmxvZyhgW2RlYnVnXSAke21lc3NhZ2UudGV4dCgpfWApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0QnJvd3NlcixcclxuICBjcmVhdGVCcm93c2VyLFxyXG4gIGNsb3NlQnJvd3NlcixcclxuICBuZXdQYWdlLFxyXG4gIGNsZWFyUGFnZSxcclxuICBhZGRQYWdlUmVzb3VyY2VzLFxyXG4gIGNsZWFyUGFnZVJlc291cmNlc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgQ1NTIHRvIGJlIHVzZWQgb24gdGhlIGV4cG9ydGVkIHBhZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBDU1MgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0ICgpID0+IGBcclxuXHJcbmh0bWwsIGJvZHkge1xyXG4gIG1hcmdpbjogMDtcclxuICBwYWRkaW5nOiAwO1xyXG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XHJcbn1cclxuXHJcbiN0YWJsZS1kaXYsICNzbGlkZXJzLCAjZGF0YXRhYmxlLCAjY29udHJvbHMsIC5sZC1yb3cge1xyXG4gIGRpc3BsYXk6IG5vbmU7XHJcbiAgaGVpZ2h0OiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyIHtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG4gIG1hcmdpbjogMDtcclxuICBvdmVyZmxvdzogYXV0bztcclxuICBmb250LXNpemU6IDA7XHJcbn1cclxuXHJcbiNjaGFydC1jb250YWluZXIgPiBmaWd1cmUsIGRpdiB7XHJcbiAgbWFyZ2luLXRvcDogMCAhaW1wb3J0YW50O1xyXG4gIG1hcmdpbi1ib3R0b206IDAgIWltcG9ydGFudDtcclxufVxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY3NzVGVtcGxhdGUgZnJvbSAnLi9jc3MuanMnO1xyXG5cclxuLyoqXHJcbiAqIFRoZSBTVkcgdGVtcGxhdGUgdG8gdXNlIHdoZW4gbG9hZGluZyBTVkcgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHN2ZyAtIFRoZSBTVkcgaW5wdXQgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIFNWRyB0ZW1wbGF0ZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChzdmcpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNoYXJ0cyBFeHBvcnQ8L3RpdGxlPlxyXG4gIDwvaGVhZD5cclxuICA8c3R5bGU+XHJcbiAgICAke2Nzc1RlbXBsYXRlKCl9XHJcbiAgPC9zdHlsZT5cclxuICA8Ym9keT5cclxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cclxuICAgICAgJHtzdmd9XHJcbiAgICA8L2Rpdj5cclxuICA8L2JvZHk+XHJcbjwvaHRtbD5cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBoYW5kbGVzIGNoYXJ0IGV4cG9ydCBmdW5jdGlvbmFsaXR5IHVzaW5nIFB1cHBldGVlci5cclxuICogSXQgc3VwcG9ydHMgZXhwb3J0aW5nIGNoYXJ0cyBhcyBTVkcsIFBORywgSlBFRywgYW5kIFBERiBmb3JtYXRzLiBUaGUgbW9kdWxlXHJcbiAqIG1hbmFnZXMgcGFnZSByZXNvdXJjZXMsIHNldHMgdXAgdGhlIGV4cG9ydCBlbnZpcm9ubWVudCwgYW5kIHByb2Nlc3NlcyBjaGFydFxyXG4gKiBjb25maWd1cmF0aW9ucyBvciBTVkcgaW5wdXRzIGZvciByZW5kZXJpbmcuIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZVxyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYWRkUGFnZVJlc291cmNlcywgY2xlYXJQYWdlUmVzb3VyY2VzIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgY3JlYXRlQ2hhcnQgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgc3ZnVGVtcGxhdGUgZnJvbSAnLi4vdGVtcGxhdGVzL3N2Z0V4cG9ydC9zdmdFeHBvcnQuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2UgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTwoc3RyaW5nfEJ1ZmZlcnxFeHBvcnRFcnJvcik+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlc1xyXG4gKiB0byB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgZXhwb3J0IHRvIGFuIHVuc3VwcG9ydGVkXHJcbiAqIG91dHB1dCBmb3JtYXQgb2NjdXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydChwYWdlLCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXkgKGFkZGl0aW9uYWwgSlMgYW5kIENTUylcclxuICBjb25zdCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICB0cnkge1xyXG4gICAgbGV0IGlzU1ZHID0gZmFsc2U7XHJcblxyXG4gICAgLy8gRGVjaWRlIG9uIHRoZSBleHBvcnQgbWV0aG9kXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy5zdmcpIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgYHR5cGVgIGlzIGFsc28gU1ZHLCByZXR1cm4gdGhlIGlucHV0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGV4cG9ydE9wdGlvbnMuc3ZnO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBNYXJrIGFzIFNWRyBleHBvcnQgZm9yIHRoZSBsYXRlciBzaXplIGNvcnJlY3Rpb25zXHJcbiAgICAgIGlzU1ZHID0gdHJ1ZTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRcclxuICAgICAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKGV4cG9ydE9wdGlvbnMuc3ZnKSwge1xyXG4gICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgIH0pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBKU09OIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoY3JlYXRlQ2hhcnQsIGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXHJcbiAgICAvLyBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAuLi4oYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBjdXN0b21Mb2dpY09wdGlvbnMpKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZSBhbmQgc2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XHJcbiAgICBjb25zdCBzaXplID0gaXNTVkdcclxuICAgICAgPyBhd2FpdCBwYWdlLmV2YWx1YXRlKChzY2FsZSkgPT4ge1xyXG4gICAgICAgICAgY29uc3Qgc3ZnRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXHJcbiAgICAgICAgICAgICcjY2hhcnQtY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJ1xyXG4gICAgICAgICAgKTtcclxuXHJcbiAgICAgICAgICAvLyBHZXQgdGhlIHZhbHVlcyBjb3JyZWN0bHkgc2NhbGVkXHJcbiAgICAgICAgICBjb25zdCBjaGFydEhlaWdodCA9IHN2Z0VsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0V2lkdGggPSBzdmdFbGVtZW50LndpZHRoLmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBJbiBjYXNlIG9mIFNWRyB0aGUgem9vbSBtdXN0IGJlIHNldCBkaXJlY3RseSBmb3IgYm9keSBhcyBzY2FsZVxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBTZXQgdGhlIG1hcmdpbiB0byAwcHhcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcclxuXHJcbiAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICBjaGFydEhlaWdodCxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aFxyXG4gICAgICAgICAgfTtcclxuICAgICAgICB9LCBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpKVxyXG4gICAgICA6IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBjb25zdCB7IGNoYXJ0SGVpZ2h0LCBjaGFydFdpZHRoIH0gPSB3aW5kb3cuSGlnaGNoYXJ0cy5jaGFydHNbMF07XHJcblxyXG4gICAgICAgICAgLy8gTm8gbmVlZCBmb3Igc3VjaCBzY2FsZSBtYW5pcHVsYXRpb24gaW4gY2FzZSBvZiBvdGhlciB0eXBlc1xyXG4gICAgICAgICAgLy8gb2YgZXhwb3J0cy4gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjbGlwIHJlZ2lvbiBmb3IgdGhlIHBhZ2VcclxuICAgIGNvbnN0IHsgeCwgeSB9ID0gYXdhaXQgX2dldENsaXBSZWdpb24ocGFnZSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGBoZWlnaHRgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRIZWlnaHQgfHwgZXhwb3J0T3B0aW9ucy5oZWlnaHQpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgd2lkdGhgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRXaWR0aCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcclxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgIH0pO1xyXG5cclxuICAgIGxldCByZXN1bHQ7XHJcbiAgICAvLyBSYXN0ZXJpemF0aW9uIHByb2Nlc3NcclxuICAgIHN3aXRjaCAoZXhwb3J0T3B0aW9ucy50eXBlKSB7XHJcbiAgICAgIGNhc2UgJ3N2Zyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVNWRyhwYWdlKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncG5nJzpcclxuICAgICAgY2FzZSAnanBlZyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZUltYWdlKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMudHlwZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIHgsXHJcbiAgICAgICAgICAgIHlcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncGRmJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlUERGKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBkZWZhdWx0OlxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBVbnN1cHBvcnRlZCBvdXRwdXQgZm9ybWF0OiAke2V4cG9ydE9wdGlvbnMudHlwZX0uYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2xlYXIgcHJldmlvdXNseSBpbmplY3RlZCBKUyBhbmQgQ1NTIHJlc291cmNlc1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMgb2YgdGhlIHNwZWNpZmllZCBwYWdlIGVsZW1lbnRcclxuICogd2l0aCB0aGUgJ2NoYXJ0LWNvbnRhaW5lcicgaWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2dldENsaXBSZWdpb25cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGB4YCwgYHlgLCBgd2lkdGhgLCBhbmQgYGhlaWdodGAgcHJvcGVydGllcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XHJcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB4LFxyXG4gICAgICB5LFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXHJcbiAgICB9O1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBTVkcgYnkgZXZhbHVhdGluZyB0aGUgYG91dGVySFRNTGAgb2YgdGhlIGZpcnN0ICdzdmcnIGVsZW1lbnRcclxuICogaW5zaWRlIGFuIGVsZW1lbnQgd2l0aCB0aGUgaWQgJ2NvbnRhaW5lcicuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVNWR1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFNWRyBzdHJpbmcuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlU1ZHKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbChcclxuICAgICcjY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJyxcclxuICAgIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIGltYWdlIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgYHNjcmVlbnNob3RgIGZ1bmN0aW9uYWxpdHkgd2l0aFxyXG4gKiBzcGVjaWZpZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlSW1hZ2VcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gSW1hZ2UgdHlwZS5cclxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgaW1hZ2UgYnVmZmVyXHJcbiAqIG9yIHJlamVjdGluZyB3aXRoIGFuIGBFeHBvcnRFcnJvcmAgZm9yIHRpbWVvdXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlSW1hZ2UocGFnZSwgdHlwZSwgY2xpcCwgcmFzdGVyaXphdGlvblRpbWVvdXQpIHtcclxuICByZXR1cm4gUHJvbWlzZS5yYWNlKFtcclxuICAgIHBhZ2Uuc2NyZWVuc2hvdCh7XHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGNsaXAsXHJcbiAgICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgICAgZnVsbFBhZ2U6IGZhbHNlLFxyXG4gICAgICBvcHRpbWl6ZUZvclNwZWVkOiB0cnVlLFxyXG4gICAgICBjYXB0dXJlQmV5b25kVmlld3BvcnQ6IHRydWUsXHJcbiAgICAgIC4uLih0eXBlICE9PSAncG5nJyA/IHsgcXVhbGl0eTogODAgfSA6IHt9KSxcclxuICAgICAgLy8gQWx3YXlzIHJlbmRlciBvbiBhIHRyYW5zcGFyZW50IHBhZ2UgaWYgdGhlIGV4cGVjdGVkIHR5cGUgZm9ybWF0IGlzIFBOR1xyXG4gICAgICBvbWl0QmFja2dyb3VuZDogdHlwZSA9PSAncG5nJyAvLyAjNDQ3LCAjNDYzXHJcbiAgICB9KSxcclxuICAgIG5ldyBQcm9taXNlKChfcmVzb2x2ZSwgcmVqZWN0KSA9PlxyXG4gICAgICBzZXRUaW1lb3V0KFxyXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcsIDQwOCkpLFxyXG4gICAgICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICAgICAgKVxyXG4gICAgKVxyXG4gIF0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIFBERiB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBwZGZgIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlUERGXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0IC0gUERGIGhlaWdodC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoIC0gUERGIHdpZHRoLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFBERiBidWZmZXIuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlUERGKHBhZ2UsIGhlaWdodCwgd2lkdGgsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgYXdhaXQgcGFnZS5lbXVsYXRlTWVkaWFUeXBlKCdzY3JlZW4nKTtcclxuICByZXR1cm4gcGFnZS5wZGYoe1xyXG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXHJcbiAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICB3aWR0aCxcclxuICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgIHRpbWVvdXQ6IHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHB1cHBldGVlckV4cG9ydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgYSB3b3JrZXIgcG9vbCBpbXBsZW1lbnRhdGlvbiBmb3IgbWFuYWdpbmdcclxuICogdGhlIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHBhZ2VzLCBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIHVzZSB3aXRoXHJcbiAqIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuIEl0IG9wdGltaXplcyByZXNvdXJjZXMgdXNhZ2UgYW5kIHBlcmZvcm1hbmNlXHJcbiAqIGJ5IG1haW50YWluaW5nIGEgcG9vbCBvZiB3b3JrZXJzIHRoYXQgY2FuIGhhbmRsZSBjb25jdXJyZW50IGV4cG9ydCB0YXNrc1xyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgUG9vbCB9IGZyb20gJ3Rhcm4nO1xyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBjcmVhdGVCcm93c2VyLCBjbG9zZUJyb3dzZXIsIG5ld1BhZ2UsIGNsZWFyUGFnZSB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IHB1cHBldGVlckV4cG9ydCB9IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldE5ld0RhdGVUaW1lLCBtZWFzdXJlVGltZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBwb29sIGluc3RhbmNlXHJcbmxldCBwb29sID0gbnVsbDtcclxuXHJcbi8vIFBvb2wgc3RhdGlzdGljc1xyXG5jb25zdCBwb29sU3RhdHMgPSB7XHJcbiAgZXhwb3J0c0F0dGVtcHRlZDogMCxcclxuICBleHBvcnRzUGVyZm9ybWVkOiAwLFxyXG4gIGV4cG9ydHNEcm9wcGVkOiAwLFxyXG4gIGV4cG9ydHNGcm9tU3ZnOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uczogMCxcclxuICBleHBvcnRzRnJvbVN2Z0F0dGVtcHRzOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzOiAwLFxyXG4gIHRpbWVTcGVudDogMCxcclxuICB0aW1lU3BlbnRBdmVyYWdlOiAwXHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwb29sIHdpdGggdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24sIGNyZWF0aW5nXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhbmQgc2V0dGluZyB1cCB3b3JrZXIgcmVzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGluaXRQb29sXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlclxyXG4gKiBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBlbmRpbmcgdGhlIGZ1bmN0aW9uXHJcbiAqIGV4ZWN1dGlvbiB3aGVuIGFuIGFscmVhZHkgaW5pdGlhbGl6ZWQgcG9vbCBvZiByZXNvdXJjZXMgaXMgZm91bmQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY3JlYXRlIHRoZSBwb29sXHJcbiAqIG9mIHdvcmtlcnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdFBvb2wocG9vbE9wdGlvbnMsIHB1cHBldGVlckFyZ3MpIHtcclxuICAvLyBDcmVhdGUgYSBicm93c2VyIGluc3RhbmNlIHdpdGggdGhlIHB1cHBldGVlciBhcmd1bWVudHNcclxuICBhd2FpdCBjcmVhdGVCcm93c2VyKHB1cHBldGVlckFyZ3MpO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIEluaXRpYWxpemluZyBwb29sIHdpdGggd29ya2VyczogbWluICR7cG9vbE9wdGlvbnMubWluV29ya2Vyc30sIG1heCAke3Bvb2xPcHRpb25zLm1heFdvcmtlcnN9LmBcclxuICAgICk7XHJcblxyXG4gICAgaWYgKHBvb2wpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXAgYW4gZXllIG9uIGEgY29ycmVjdCBtaW4gYW5kIG1heCB3b3JrZXJzIG51bWJlclxyXG4gICAgaWYgKHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzKSB7XHJcbiAgICAgIHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPSBwb29sT3B0aW9ucy5tYXhXb3JrZXJzO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENyZWF0ZSBhIHBvb2wgYWxvbmcgd2l0aCBhIG1pbmltYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgcG9vbCA9IG5ldyBQb29sKHtcclxuICAgICAgLy8gR2V0IHRoZSBgY3JlYXRlYCwgYHZhbGlkYXRlYCwgYW5kIGBkZXN0cm95YCBmdW5jdGlvbnNcclxuICAgICAgLi4uX2ZhY3RvcnkocG9vbE9wdGlvbnMpLFxyXG4gICAgICBtaW46IHBvb2xPcHRpb25zLm1pbldvcmtlcnMsXHJcbiAgICAgIG1heDogcG9vbE9wdGlvbnMubWF4V29ya2VycyxcclxuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmFjcXVpcmVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVUaW1lb3V0LFxyXG4gICAgICBkZXN0cm95VGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuZGVzdHJveVRpbWVvdXQsXHJcbiAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5pZGxlVGltZW91dCxcclxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlUmV0cnlJbnRlcnZhbCxcclxuICAgICAgcmVhcEludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5yZWFwZXJJbnRlcnZhbCxcclxuICAgICAgcHJvcGFnYXRlQ3JlYXRlRXJyb3I6IGZhbHNlXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBTZXQgZXZlbnRzXHJcbiAgICBwb29sLm9uKCdyZWxlYXNlJywgYXN5bmMgKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIENsZWFyIHBhZ2VcclxuICAgICAgY29uc3QgY2xlYXJTdGF0dXMgPSBhd2FpdCBjbGVhclBhZ2UocmVzb3VyY2UsIGZhbHNlKTtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBSZWxlYXNpbmcgYSB3b3JrZXIuIENsZWFyIHBhZ2Ugc3RhdHVzOiAke2NsZWFyU3RhdHVzfS5gXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChfZXZlbnRJZCwgcmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBEZXN0cm95ZWQgYSB3b3JrZXIgc3VjY2Vzc2Z1bGx5LmBcclxuICAgICAgKTtcclxuICAgICAgcmVzb3VyY2UucGFnZSA9IG51bGw7XHJcbiAgICB9KTtcclxuXHJcbiAgICBjb25zdCBpbml0aWFsUmVzb3VyY2VzID0gW107XHJcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBvb2xPcHRpb25zLm1pbldvcmtlcnM7IGkrKykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuICAgICAgICBpbml0aWFsUmVzb3VyY2VzLnB1c2gocmVzb3VyY2UpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIGFuIGluaXRpYWwgcmVzb3VyY2UuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXMgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgaW5pdGlhbFJlc291cmNlcy5mb3JFYWNoKChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICBwb29sLnJlbGVhc2UocmVzb3VyY2UpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIFRoZSBwb29sIGlzIHJlYWR5JHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aCA/IGAgd2l0aCAke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RofSBpbml0aWFsIHJlc291cmNlcyB3YWl0aW5nLmAgOiAnLid9YFxyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3Bvb2xdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogVGVybWluYXRlcyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24ga2lsbFBvb2xcclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgYWxsIHdvcmtlcnMgYXJlXHJcbiAqIHRlcm1pbmF0ZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIHN1Y2Nlc3NmdWxseSBjbG9zZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XHJcbiAgbG9nKDMsICdbcG9vbF0gS2lsbGluZyBwb29sIHdpdGggYWxsIHdvcmtlcnMgYW5kIGNsb3NpbmcgYnJvd3Nlci4nKTtcclxuXHJcbiAgLy8gSWYgc3RpbGwgYWxpdmUsIGRlc3Ryb3kgdGhlIHBvb2wgb2YgcGFnZXMgYmVmb3JlIGNsb3NpbmcgYSBicm93c2VyXHJcbiAgaWYgKHBvb2wpIHtcclxuICAgIC8vIEZyZWUgdXAgbm90IHJlbGVhc2VkIHdvcmtlcnNcclxuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHBvb2wudXNlZCkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VyLnJlc291cmNlKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IHRoZSBwb29sIGlmIGl0IGlzIHN0aWxsIGF2YWlsYWJsZVxyXG4gICAgaWYgKCFwb29sLmRlc3Ryb3llZCkge1xyXG4gICAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gRGVzdHJveWVkIHRoZSBwb29sIG9mIHJlc291cmNlcy4nKTtcclxuICAgIH1cclxuICAgIHBvb2wgPSBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICBhd2FpdCBjbG9zZUJyb3dzZXIoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByb2Nlc3NlcyB0aGUgZXhwb3J0IHdvcmsgdXNpbmcgYSB3b3JrZXIgZnJvbSB0aGUgcG9vbC4gQWNxdWlyZXMgYSB3b3JrZXJcclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcclxuICogdGhlIHdvcmtlciBoYW5kbGUgYmFjayB0byB0aGUgcG9vbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0V29ya1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZXhwb3J0IHJlc3VsdFxyXG4gKiBhbmQgb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3RXb3JrKG9wdGlvbnMpIHtcclxuICBsZXQgd29ya2VySGFuZGxlO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgICAvLyBBbiBleHBvcnQgYXR0ZW1wdCBjb3VudGVkXHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzQXR0ZW1wdGVkO1xyXG5cclxuICAgIC8vIERpc3BsYXkgdGhlIHBvb2wgaW5mb3JtYXRpb24gaWYgbmVlZGVkXHJcbiAgICBpZiAob3B0aW9ucy5wb29sLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGluIGNhc2Ugb2YgbGFja2luZyB0aGUgcG9vbCBpbnN0YW5jZVxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhlIGFjcXVpcmUgY291bnRlclxyXG4gICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFRyeSB0byBhY3F1aXJlIHRoZSB3b3JrZXIgYWxvbmcgd2l0aCB0aGUgaWQsIHdvcmtzIGNvdW50IGFuZCBwYWdlXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgICAgLy8gQWNxdWlyZSBhIHBvb2wgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxyXG4gICAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgICAgYEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUgdG9vayAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFJlc29sdmVkIHdvcmtlciBwYWdlIGlzIGludmFsaWQ6IHRoZSBwb29sIHNldHVwIGlzIHdvbmt5LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgY29uc3Qgd29ya1N0YXJ0ID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHt3b3JrZXJIYW5kbGUuaWR9XSAtIFN0YXJ0aW5nIHdvcmsgb24gdGhpcyBwb29sIGVudHJ5LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU3RhcnQgbWVhc3VyaW5nIGV4cG9ydCB0aW1lXHJcbiAgICBjb25zdCBleHBvcnRDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KFxyXG4gICAgICB3b3JrZXJIYW5kbGUucGFnZSxcclxuICAgICAgb3B0aW9ucy5leHBvcnQsXHJcbiAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKHJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIElmIHRoZXJlJ3MgYSByYXN0ZXJpemF0aW9uIHRpbWVvdXQsIHdlIHdhbnQgbmVlZCB0byBmbHVzaCB0aGUgcGFnZS5cclxuICAgICAgLy8gVGhpcyBpcyBiZWNhdXNlIHRoZSBwYWdlIG1heSBiZSBpbiBhIHN0YXRlIHdoZXJlIGl0J3Mgd2FpdGluZyBmb3JcclxuICAgICAgLy8gdGhlIHNjcmVlbnNob3QgdG8gZmluaXNoIGV2ZW4gdGhvdWdoIHRoZSB0aW1lb3V0IGhhcyBvY2N1cmVkLlxyXG4gICAgICAvLyBXaGljaCBvZiBjb3Vyc2UgY2F1c2VzIGEgbG90IG9mIGlzc3VlcyB3aXRoIHRoZSBldmVudCBzeXN0ZW0sXHJcbiAgICAgIC8vIGFuZCBwYWdlIGNvbnNpc3RlbmN5LlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBPbmx5IHBhZ2Uuc2NyZWVuc2hvdCB3aWxsIHRocm93IHRoaXMsIHRpbWVvdXRzIGZvciBQREYncyBhcmVcclxuICAgICAgLy8gaGFuZGxlZCBieSB0aGUgcGFnZS5wZGYgZnVuY3Rpb24gaXRzZWxmLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyAuLi55ZXMsIHRoaXMgaXMgdWdseS5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlID0gbnVsbDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJlc3VsdC5uYW1lID09PSAnVGltZW91dEVycm9yJyB8fFxyXG4gICAgICAgIHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0J1xyXG4gICAgICApIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfVJhc3Rlcml6YXRpb24gdGltZW91dDogeW91ciBjaGFydCBtYXkgYmUgdG9vIGNvbXBsZXggb3IgbGFyZ2UsIGFuZCBmYWlsZWQgdG8gcmVuZGVyIHdpdGhpbiB0aGUgYWxsb3R0ZWQgdGltZS5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGV4cG9ydDogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIHRoZSBQdXBwZXRlZXIgZXhwb3J0IHRpbWVcclxuICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDUsXHJcbiAgICAgICAgYFtiZW5jaG1hcmtdICR7b3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnfWAsXHJcbiAgICAgICAgYEV4cG9ydGluZyBhIGNoYXJ0IHN1Y2Vzc2Z1bGx5IHRvb2sgJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIHJlc291cmNlIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG5cclxuICAgIC8vIFVzZWQgZm9yIHN0YXRpc3RpY3MgaW4gYXZlcmFnZVRpbWUgYW5kIHByb2Nlc3NlZFdvcmtDb3VudCwgd2hpY2hcclxuICAgIC8vIGluIHR1cm4gaXMgdXNlZCBieSB0aGUgL2hlYWx0aCByb3V0ZS5cclxuICAgIGNvbnN0IHdvcmtFbmQgPSBnZXROZXdEYXRlVGltZSgpO1xyXG4gICAgY29uc3QgZXhwb3J0VGltZSA9IHdvcmtFbmQgLSB3b3JrU3RhcnQ7XHJcblxyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UgPVxyXG4gICAgICBwb29sU3RhdHMudGltZVNwZW50IC8gKytwb29sU3RhdHMuZXhwb3J0c1BlcmZvcm1lZDtcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBXb3JrIGNvbXBsZXRlZCBpbiAke2V4cG9ydFRpbWV9bXMuYCk7XHJcblxyXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICByZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICsrcG9vbFN0YXRzLmV4cG9ydHNEcm9wcGVkO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhyb3cgZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fG51bGwpfSBUaGUgY3VycmVudCBwb29sIGluc3RhbmNlIGlmIGluaXRpYWxpemVkLCBvciBudWxsXHJcbiAqIGlmIHRoZSBwb29sIGhhcyBub3QgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2woKSB7XHJcbiAgcmV0dXJuIHBvb2w7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBzdGF0aXN0aWMgb2YgYSBwb29sIGluc3RhY2UgYWJvdXQgZXhwb3J0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xTdGF0c1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY3VycmVudCBwb29sIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbFN0YXRzKCkge1xyXG4gIHJldHVybiBwb29sU3RhdHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgcG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdCwgaW5jbHVkaW5nIG1pbmltdW0gYW5kIG1heGltdW1cclxuICogd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OKCkge1xyXG4gIHJldHVybiB7XHJcbiAgICBtaW46IHBvb2wubWluLFxyXG4gICAgbWF4OiBwb29sLm1heCxcclxuICAgIHVzZWQ6IHBvb2wubnVtVXNlZCgpLFxyXG4gICAgYXZhaWxhYmxlOiBwb29sLm51bUZyZWUoKSxcclxuICAgIGFsbENyZWF0ZWQ6IHBvb2wubnVtVXNlZCgpICsgcG9vbC5udW1GcmVlKCksXHJcbiAgICBwZW5kaW5nQWNxdWlyZXM6IHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCksXHJcbiAgICBwZW5kaW5nQ3JlYXRlczogcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zOiBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzOiBwb29sLnBlbmRpbmdEZXN0cm95cy5sZW5ndGgsXHJcbiAgICBhYnNvbHV0ZUFsbDpcclxuICAgICAgcG9vbC5udW1Vc2VkKCkgK1xyXG4gICAgICBwb29sLm51bUZyZWUoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdDcmVhdGVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpICtcclxuICAgICAgcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoXHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHBvb2wsIGluY2x1ZGluZyB0aGUgbWluaW11bVxyXG4gKiBhbmQgbWF4aW11bSB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmVcclxuICogcmVxdWVzdHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sSW5mb1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvKCkge1xyXG4gIGNvbnN0IHtcclxuICAgIG1pbixcclxuICAgIG1heCxcclxuICAgIHVzZWQsXHJcbiAgICBhdmFpbGFibGUsXHJcbiAgICBhbGxDcmVhdGVkLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXMsXHJcbiAgICBwZW5kaW5nVmFsaWRhdGlvbnMsXHJcbiAgICBwZW5kaW5nRGVzdHJveXMsXHJcbiAgICBhYnNvbHV0ZUFsbFxyXG4gIH0gPSBnZXRQb29sSW5mb0pTT04oKTtcclxuXHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1pbmltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWlufS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttYXh9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgdXNlZCByZXNvdXJjZXM6ICR7dXNlZH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBmcmVlIHJlc291cmNlczogJHthdmFpbGFibGV9LmApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIGNyZWF0ZWQgKHVzZWQgYW5kIGZyZWUpIHJlc291cmNlczogJHthbGxDcmVhdGVkfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGFjcXVpcmVkOiAke3BlbmRpbmdBY3F1aXJlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBjcmVhdGVkOiAke3BlbmRpbmdDcmVhdGVzfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIHZhbGlkYXRlZDogJHtwZW5kaW5nVmFsaWRhdGlvbnN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgZGVzdHJveWVkOiAke3BlbmRpbmdEZXN0cm95c30uYFxyXG4gICk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgcmVzb3VyY2VzOiAke2Fic29sdXRlQWxsfS5gKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZhY3RvcnkgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGFuIG9iamVjdCB3aXRoIGBjcmVhdGVgLCBgdmFsaWRhdGVgLFxyXG4gKiBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9ucyBmb3IgdGhlIHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmFjdG9yeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9mYWN0b3J5KHBvb2xPcHRpb25zKSB7XHJcbiAgcmV0dXJuIHtcclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyB3b3JrZXIgcGFnZSBmb3IgdGhlIGV4cG9ydCBwb29sLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGNyZWF0ZVxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdFxyXG4gICAgICogY29udGFpbmluZyB0aGUgd29ya2VyIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgaW5pdGlhbFxyXG4gICAgICogd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gZXJyb3IgZHVyaW5nXHJcbiAgICAgKiB0aGUgY3JlYXRpb24gb2YgdGhlIG5ldyBwYWdlLlxyXG4gICAgICovXHJcbiAgICBjcmVhdGU6IGFzeW5jICgpID0+IHtcclxuICAgICAgLy8gSW5pdCB0aGUgcmVzb3VyY2Ugd2l0aCB1bmlxdWUgaWQgYW5kIHdvcmsgY291bnRcclxuICAgICAgY29uc3QgcG9vbFJlc291cmNlID0ge1xyXG4gICAgICAgIGlkOiB1dWlkKCksXHJcbiAgICAgICAgLy8gVHJ5IHRvIGRpc3RyaWJ1dGUgdGhlIGluaXRpYWwgd29yayBjb3VudFxyXG4gICAgICAgIHdvcmtDb3VudDogTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogKHBvb2xPcHRpb25zLndvcmtMaW1pdCAvIDIpKVxyXG4gICAgICB9O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBTdGFydCBtZWFzdXJpbmcgYSBwYWdlIGNyZWF0aW9uIHRpbWVcclxuICAgICAgICBjb25zdCBzdGFydERhdGUgPSBnZXROZXdEYXRlVGltZSgpO1xyXG5cclxuICAgICAgICAvLyBDcmVhdGUgYSBuZXcgcGFnZVxyXG4gICAgICAgIGF3YWl0IG5ld1BhZ2UocG9vbFJlc291cmNlKTtcclxuXHJcbiAgICAgICAgLy8gTWVhc3VyZSB0aGUgdGltZSBvZiBmdWxsIGNyZWF0aW9uIGFuZCBjb25maWd1cmF0aW9uIG9mIGEgcGFnZVxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBhIHdvcmtlciwgdG9vayAke1xyXG4gICAgICAgICAgICBnZXROZXdEYXRlVGltZSgpIC0gc3RhcnREYXRlXHJcbiAgICAgICAgICB9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHVybiByZWFkeSBwb29sIHJlc291cmNlXHJcbiAgICAgICAgcmV0dXJuIHBvb2xSZXNvdXJjZTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBjcmVhdGluZyBhIG5ldyBwYWdlLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcclxuICAgICAqIHRoZSB3b3JrIGxpbWl0LlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIHZhbGlkYXRlXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHBvb2xSZXNvdXJjZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAgICogdGhlIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBpZiB0aGUgd29ya2VyXHJcbiAgICAgKiBpcyB2YWxpZCBhbmQgd2l0aGluIHRoZSB3b3JrIGxpbWl0OyBvdGhlcndpc2UsIHRvIGZhbHNlLlxyXG4gICAgICovXHJcbiAgICB2YWxpZGF0ZTogYXN5bmMgKHBvb2xSZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJbiBjZXJ0YWluIGNhc2VzIGFjcXVpcmluZyB0aHJvd3MgYSBUYXJnZXRDbG9zZUVycm9yLCB3aGljaCBtYXlcclxuICAgICAgLy8gYmUgY2F1c2VkIGJ5IHR3byB0aGluZ3M6XHJcbiAgICAgIC8vIC0gVGhlIHBhZ2UgaXMgY2xvc2VkIGFuZCBhdHRlbXB0ZWQgdG8gYmUgcmV1c2VkLlxyXG4gICAgICAvLyAtIExvc3QgY29udGFjdCB3aXRoIHRoZSBicm93c2VyLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBXaGF0IHdlJ3JlIHNlZWluZyBpbiBsb2dzIGlzIHRoYXQgc3VjY2Vzc2l2ZSBleHBvcnRzIHR5cGljYWxseVxyXG4gICAgICAvLyBzdWNjZWVkcywgYW5kIHRoZSBzZXJ2ZXIgcmVjb3ZlcnMsIGluZGljYXRpbmcgdGhhdCBpdCdzIGxpa2VseVxyXG4gICAgICAvLyB0aGUgZmlyc3QgY2FzZS4gVGhpcyBpcyBhbiBhdHRlbXB0IGF0IGFsbGlldmF0aW5nIHRoZSBpc3N1ZSBieVxyXG4gICAgICAvLyBzaW1wbHkgbm90IHZhbGlkYXRpbmcgdGhlIHdvcmtlciBpZiB0aGUgcGFnZSBpcyBudWxsIG9yIGNsb3NlZC5cclxuICAgICAgLy9cclxuICAgICAgLy8gVGhlIGFjdHVhbCByZXN1bHQgZnJvbSB3aGVuIHRoaXMgaGFwcGVuZWQsIHdhcyB0aGF0IGEgd29ya2VyIHdvdWxkXHJcbiAgICAgIC8vIGJlIGNvbXBsZXRlbHkgbG9ja2VkLCBzdG9wcGluZyBpdCBmcm9tIGJlaW5nIGFjcXVpcmVkIHVudGlsXHJcbiAgICAgIC8vIGl0cyB3b3JrIGNvdW50IHJlYWNoZWQgdGhlIGxpbWl0LlxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBwYWdlYCBpcyB2YWxpZFxyXG4gICAgICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChubyB2YWxpZCBwYWdlIGlzIGZvdW5kKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgY2xvc2VkXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlIGlzIGNsb3NlZCBvciBpbnZhbGlkKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgbWFpbkZyYW1lYCBpcyBkZXRhY2hlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UubWFpbkZyYW1lKCkuZGV0YWNoZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKHBhZ2UncyBmcmFtZSBpcyBkZXRhY2hlZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHdvcmtMaW1pdGAgaXMgZXhjZWVkZWRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHBvb2xPcHRpb25zLndvcmtMaW1pdCAmJlxyXG4gICAgICAgICsrcG9vbFJlc291cmNlLndvcmtDb3VudCA+IHBvb2xPcHRpb25zLndvcmtMaW1pdFxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKGV4Y2VlZGVkIHRoZSAke3Bvb2xPcHRpb25zLndvcmtMaW1pdH0gd29ya3MgcGVyIHJlc291cmNlIGxpbWl0KS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSBgcG9vbFJlc291cmNlYCBpcyB2YWxpZGF0ZWRcclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gZGVzdHJveVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKi9cclxuICAgIGRlc3Ryb3k6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRGVzdHJveWluZyBhIHdvcmtlci5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgLy8gUmVtb3ZlIGFsbCBhdHRhY2hlZCBldmVudCBsaXN0ZW5lcnMgZnJvbSB0aGUgcmVzb3VyY2VcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygncGFnZWVycm9yJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2NvbnNvbGUnKTtcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygnZnJhbWVkZXRhY2hlZCcpO1xyXG5cclxuICAgICAgICAgIC8vIFdlIG5lZWQgdG8gd2FpdCBhcm91bmQgZm9yIHRoaXNcclxuICAgICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gUGFnZSBjb3VsZCBub3QgYmUgY2xvc2VkIHVwb24gZGVzdHJveWluZy5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGluaXRQb29sLFxyXG4gIGtpbGxQb29sLFxyXG4gIHBvc3RXb3JrLFxyXG4gIGdldFBvb2wsXHJcbiAgZ2V0UG9vbFN0YXRzLFxyXG4gIGdldFBvb2xJbmZvLFxyXG4gIGdldFBvb2xJbmZvSlNPTlxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVXNlZCB0byBzYW5pdGl6ZSB0aGUgc3RyaW5ncyBjb21pbmcgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZVxyXG4gKiB0byBwcmV2ZW50IFhTUyBhdHRhY2tzICh3aXRoIHRoZSBET01QdXJpZnkgbGlicmFyeSkuXHJcbiAqL1xyXG5cclxuaW1wb3J0IERPTVB1cmlmeSBmcm9tICdkb21wdXJpZnknO1xyXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcclxuXHJcbi8qKlxyXG4gKiBTYW5pdGl6ZXMgYSBnaXZlbiBIVE1MIHN0cmluZyBieSByZW1vdmluZyA8c2NyaXB0PiB0YWdzLiBUaGlzIGZ1bmN0aW9uIHVzZXNcclxuICogYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gZmluZCBhbmQgcmVtb3ZlIGFsbCBvY2N1cnJlbmNlcyBvZiA8c2NyaXB0Pjwvc2NyaXB0PlxyXG4gKiB0YWdzIGFuZCBhbnkgY29udGVudCB3aXRoaW4gdGhlbS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNhbml0aXplXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCAtIFRoZSBIVE1MIHN0cmluZyB0byBiZSBzYW5pdGl6ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTCBzdHJpbmcuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXQpIHtcclxuICAvLyBHZXQgdGhlIHZpcnR1YWwgRE9NXHJcbiAgY29uc3Qgd2luZG93ID0gbmV3IEpTRE9NKCcnKS53aW5kb3c7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHB1cmlmeWluZyBpbnN0YW5jZVxyXG4gIGNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cpO1xyXG5cclxuICAvLyBSZXR1cm4gc2FuaXRpemVkIGlucHV0XHJcbiAgcmV0dXJuIHB1cmlmeS5zYW5pdGl6ZShpbnB1dCwgeyBBRERfVEFHUzogWydmb3JlaWduT2JqZWN0J10gfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzYW5pdGl6ZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIHRvIHByZXBhcmUgZm9yIHRoZSBleHBvcnRpbmcgY2hhcnRzXHJcbiAqIGludG8gdmFyaW91cyBpbWFnZSBvdXRwdXQgZm9ybWF0cyBzdWNoIGFzIEpQRUcsIFBORywgUERGLCBhbmQgU1ZHcy5cclxuICogSXQgc3VwcG9ydHMgc2luZ2xlIGFuZCBiYXRjaCBleHBvcnQgb3BlcmF0aW9ucyBhbmQgYWxsb3dzIGN1c3RvbWl6YXRpb25cclxuICogdGhyb3VnaCBvcHRpb25zIHBhc3NlZCBmcm9tIGNvbmZpZ3VyYXRpb25zIG9yIEFQSXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnLCB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBpc09iamVjdCxcclxuICByb3VuZE51bWJlcixcclxuICB3cmFwQXJvdW5kXHJcbn0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGdsb2JhbCBmbGFnIGZvciB0aGUgY29kZSBleGVjdXRpb24gcGVybWlzc2lvblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucyBhbmQgc2F2ZXNcclxuICogdGhlIHJlc3VsdGluZyBpbWFnZSB0byB0aGUgcHJvdmlkZWQgb3V0cHV0IGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIFRoZSBvYmplY3QgbXVzdCBjb250YWluIGF0IGxlYXN0IG9uZVxyXG4gKiBvZiB0aGUgZm9sbG93aW5nIGBleHBvcnRgIHByb3BlcnRpZXM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgXHJcbiAqIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpbmdsZUV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0KSB7XHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQoXHJcbiAgICAgIHsgZXhwb3J0OiBvcHRpb25zLmV4cG9ydCwgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWMgfSxcclxuICAgICAgYXN5bmMgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSBvbmUgb2YgdGhlIGZvbGxvd2luZyBvcHRpb25zOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIGluZm9ybWF0aW9uXHJcbiAqIHByb3ZpZGVkIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZ1xyXG4gKiBmb3JtYXQ6IFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzXHJcbiAqIGFyZSBzYXZlZCB0byB0aGUgc3BlY2lmaWVkIG91dHB1dCBmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIGZyb21cclxuICogdGhlIGBleHBvcnRgIHNlY3Rpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3NlcyBhcmUgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJhdGNoRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQgJiYgb3B0aW9ucy5leHBvcnQuYmF0Y2gpIHtcclxuICAgIC8vIEFuIGFycmF5IGZvciBjb2xsZWN0aW5nIGJhdGNoIGV4cG9ydHNcclxuICAgIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIGBiYXRjaGAgYXJndW1lbnRzXHJcbiAgICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykgfHwgW10pIHtcclxuICAgICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgaW1hZ2VPcHRpb25zYCBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXRcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgcGFydGlhbFxyXG4gKiBvcHRpb25zIGFyZSBwcm92aWRlZCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICpcclxuICogVGhlIGBlbmRDYWxsYmFja2AgZnVuY3Rpb24gaXMgaW52b2tlZCB1cG9uIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBleHBvcnQsXHJcbiAqIGVpdGhlciBzdWNjZXNzZnVsbHkgb3Igd2l0aCBhbiBlcnJvci4gVGhlIGBlcnJvcmAgb2JqZWN0IGlzIHByb3ZpZGVkXHJcbiAqIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIGNvbnRhaW5pbmdcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5XHJcbiAqIGFuZCB0aGUgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW1hZ2VPcHRpb25zIC0gVGhlIGBpbWFnZU9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkXHJcbiAqIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgdGhlIHByb3ZpZGVkXHJcbiAqIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHRoZSBleHBvcnQgcHJvY2VzcyBvciB1cG9uIGVuY291bnRlcmluZyBhbiBlcnJvci4gVGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGlzIHRoZSBgZXJyb3JgIG9iamVjdCwgYW5kIHRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIGBkYXRhYCBvYmplY3QsXHJcbiAqIHdoaWNoIGluY2x1ZGVzIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YFxyXG4gKiBwcm9wZXJ0eSBhbmQgdGhlIGZ1bGwgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGltYWdlT3B0aW9ucywgZW5kQ2FsbGJhY2spIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgb3B0aW9ucyBhcmUgaW4gYW4gb2JqZWN0XHJcbiAgICBpZiAoIWlzT2JqZWN0KGltYWdlT3B0aW9ucykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgcHJvdmlkZWQgYGltYWdlT3B0aW9uc2AuIE5lZWRzIHRvIGJlIGFuIG9iamVjdC4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE1lcmdlIGFkZGl0aW9uYWwgb3B0aW9ucyB0byB0aGUgY29weSBvZiB0aGUgaW5zdGFuY2Ugb3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoXHJcbiAgICAgIHtcclxuICAgICAgICBleHBvcnQ6IGltYWdlT3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgY3VzdG9tTG9naWM6IGltYWdlT3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcbiAgICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnMgPSBudWxsO1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBpbWFnZSBvcHRpb25zIG9iamVjdCBkb2VzIG5vdCBleGNlZWQgdGhlIHNpemUgbGltaXRcclxuICBfY2hlY2tEYXRhU2l6ZSh7IGV4cG9ydDogZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWM6IGN1c3RvbUxvZ2ljT3B0aW9ucyB9KTtcclxuXHJcbiAgLy8gUG9zdCB0aGUgd29yayB0byB0aGUgcG9vbFxyXG4gIHJldHVybiBwb3N0V29yayhvcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgdGhlIGBoZWlnaHRgLCBgd2lkdGhgIGFuZCBgc2NhbGVgIGZvciBjaGFydCBleHBvcnRzIGJhc2VkXHJcbiAqIG9uIHRoZSBwcm92aWRlZCBleHBvcnQgb3B0aW9ucy5cclxuICpcclxuICogVGhlIGZ1bmN0aW9uIHByaW9yaXRpemVzIHZhbHVlcyBpbiB0aGUgZm9sbG93aW5nIG9yZGVyOlxyXG4gKiAxLiBUaGUgYGhlaWdodGAsIGB3aWR0aGAsIGBzY2FsZWAgZnJvbSB0aGUgYGV4cG9ydE9wdGlvbnNgLlxyXG4gKiAyLiBPcHRpb25zIGZyb20gdGhlIGNoYXJ0IGNvbmZpZ3VyYXRpb24gKGZyb20gYGV4cG9ydGluZ2AgYW5kIGBjaGFydGApLlxyXG4gKiAzLiBPcHRpb25zIGZyb20gdGhlIGdsb2JhbCBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogNC4gT3B0aW9ucyBmcm9tIHRoZSB0aGVtZSBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgIHNlY3Rpb25zKS5cclxuICogNS4gRmFsbGJhY2sgZGVmYXVsdCB2YWx1ZXMgKGBoZWlnaHQgPSA0MDBgLCBgd2lkdGggPSA2MDBgLCBgc2NhbGUgPSAxYCkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmluZENoYXJ0U2l6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBvYmplY3QgY29udGFpbmluZyBjYWxjdWxhdGVkIGBoZWlnaHRgLCBgd2lkdGhgXHJcbiAqIGFuZCBgc2NhbGVgIHZhbHVlcyBmb3IgdGhlIGNoYXJ0IGV4cG9ydC5cclxuICovXHJcbmZ1bmN0aW9uIF9maW5kQ2hhcnRTaXplKGV4cG9ydE9wdGlvbnMpIHtcclxuICAvLyBDaGVjayB0aGUgYG9wdGlvbnNgIGFuZCBgaW5zdHJgIGZvciBjaGFydCBhbmQgZXhwb3J0aW5nIHNlY3Rpb25zXHJcbiAgY29uc3QgeyBjaGFydDogb3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IG9wdGlvbnNFeHBvcnRpbmcgfSA9XHJcbiAgICBpc0FsbG93ZWRDb25maWcoZXhwb3J0T3B0aW9ucy5pbnN0cikgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgZ2xvYmFsT3B0aW9uc2AgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiBnbG9iYWxPcHRpb25zQ2hhcnQsIGV4cG9ydGluZzogZ2xvYmFsT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnMpIHx8IGZhbHNlO1xyXG5cclxuICAvLyBDaGVjayB0aGUgYHRoZW1lT3B0aW9uc2AgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiB0aGVtZU9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiB0aGVtZU9wdGlvbnNFeHBvcnRpbmcgfSA9XHJcbiAgICBpc0FsbG93ZWRDb25maWcoZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnMpIHx8IGZhbHNlO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgc2NhbGVgIHZhbHVlOlxyXG4gIC8vIC0gSXQgY2Fubm90IGJlIGxvd2VyIHRoYW4gMC4xXHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgaGlnaGVyIHRoYW4gNS4wXHJcbiAgLy8gLSBJdCBtdXN0IGJlIHJvdW5kZWQgdG8gMiBkZWNpbWFsIHBsYWNlcyAoZS5nLiAwLjIzMjM0IC0+IDAuMjMpXHJcbiAgY29uc3Qgc2NhbGUgPSByb3VuZE51bWJlcihcclxuICAgIE1hdGgubWF4KFxyXG4gICAgICAwLjEsXHJcbiAgICAgIE1hdGgubWluKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc2NhbGUgfHxcclxuICAgICAgICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5kZWZhdWx0U2NhbGUgfHxcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgNS4wXHJcbiAgICAgIClcclxuICAgICksXHJcbiAgICAyXHJcbiAgKTtcclxuXHJcbiAgLy8gRmluZCB0aGUgYGhlaWdodGAgdmFsdWVcclxuICBjb25zdCBoZWlnaHQgPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5oZWlnaHQgfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8uaGVpZ2h0IHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRIZWlnaHQgfHxcclxuICAgIDQwMDtcclxuXHJcbiAgLy8gRmluZCB0aGUgYHdpZHRoYCB2YWx1ZVxyXG4gIGNvbnN0IHdpZHRoID1cclxuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py53aWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRXaWR0aCB8fFxyXG4gICAgNjAwO1xyXG5cclxuICAvLyBHYXRoZXIgYGhlaWdodGAsIGB3aWR0aGAgYW5kIGBzY2FsZWAgaW5mb3JtYXRpb24gaW4gb25lIG9iamVjdFxyXG4gIGNvbnN0IHNpemUgPSB7IGhlaWdodCwgd2lkdGgsIHNjYWxlIH07XHJcblxyXG4gIC8vIEdldCByaWQgb2YgcG90ZW50aWFsIGBweGAgYW5kIGAlYFxyXG4gIGZvciAobGV0IFtwYXJhbSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHNpemUpKSB7XHJcbiAgICBzaXplW3BhcmFtXSA9XHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgPyArdmFsdWUucmVwbGFjZSgvcHh8JS9naSwgJycpIDogdmFsdWU7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIHNpemUgb2JqZWN0XHJcbiAgcmV0dXJuIHNpemU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleGVjdXRpb24gb2YgY3VzdG9tIGxvZ2ljIG9wdGlvbnMsIGluY2x1ZGluZyBsb2FkaW5nIGByZXNvdXJjZXNgLFxyXG4gKiBgY3VzdG9tQ29kZWAsIGFuZCBgY2FsbGJhY2tgLiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLCBpdCBwcm9jZXNzZXNcclxuICogdGhlIGN1c3RvbSBsb2dpYyBvcHRpb25zIGFjY29yZGluZ2x5LiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBub3QgYWxsb3dlZCxcclxuICogaXQgZGlzYWJsZXMgdGhlIHVzYWdlIG9mIHJlc291cmNlcywgY3VzdG9tIGNvZGUgYW5kIGNhbGxiYWNrLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY29kZSBleGVjdXRpb25cclxuICogaXMgbm90IGFsbG93ZWQgYnV0IGN1c3RvbSBsb2dpYyBvcHRpb25zIGFyZSBzdGlsbCBwcm92aWRlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVDdXN0b21Mb2dpYyhjdXN0b21Mb2dpY09wdGlvbnMsIGFsbG93Q29kZUV4ZWN1dGlvbikge1xyXG4gIC8vIEluIGNhc2Ugb2YgYWxsb3dpbmcgY29kZSBleGVjdXRpb25cclxuICBpZiAoYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgcmVzb3VyY2VzYCBvcHRpb25cclxuICAgIGlmICh0eXBlb2YgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gQ3VzdG9tIHN0cmluZ2lmaWVkIHJlc291cmNlc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmICghY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIExvYWQgdGhlIGRlZmF1bHQgb25lXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9IF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKCdyZXNvdXJjZXMuanNvbicpLCAndXRmOCcpLFxyXG4gICAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgIHRydWVcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZygyLCAnW2NoYXJ0XSBVbmFibGUgdG8gbG9hZCB0aGUgZGVmYXVsdCBgcmVzb3VyY2VzLmpzb25gIGZpbGUuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBUcnkgdG8gbG9hZCBjdXN0b20gY29kZSBhbmQgd3JhcCBhcm91bmQgaXQgaW4gYSBzZWxmIGludm9raW5nIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY3VzdG9tQ29kZWAgY2Fubm90IGJlIGxvYWRlZC4nKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUHJvY2VzcyB0aGUgYGNhbGxiYWNrYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGNhbGxiYWNrIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IHdyYXBBcm91bmQoXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW2NoYXJ0XSBUaGUgYGNhbGxiYWNrYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjdXN0b21Db2RlYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBjdXN0b21Db2RlYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjYWxsYmFja2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjaykpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGNhbGxiYWNrYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGByZXNvdXJjZXNgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgcmVzb3VyY2VzYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIElmIHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBmbGFnIGlzIHNldCB0byBmYWxzZSwgd2Ugc2hvdWxkIHJlZnVzZVxyXG4gICAgLy8gdGhlIHVzYWdlIG9mIHRoZSBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYCwgYW5kIGBjdXN0b21Db2RlYCBvcHRpb25zLlxyXG4gICAgLy8gQWRkaXRpb25hbGx5LCB0aGUgd29ya2VyIHdpbGwgcmVmdXNlIHRvIHJ1biBhcmJpdHJhcnkgSmF2YVNjcmlwdC5cclxuICAgIGlmIChcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGVcclxuICAgICkge1xyXG4gICAgICAvLyBSZXNldCBhbGwgY3VzdG9tIGNvZGUgb3B0aW9uc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gbnVsbDtcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBudWxsO1xyXG5cclxuICAgICAgLy8gU2VuZCBhIG1lc3NhZ2Ugc2F5aW5nIHRoYXQgdGhlIGV4cG9ydGVyIGRvZXMgbm90IHN1cHBvcnQgdGhlc2Ugc2V0dGluZ3NcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJywgYW5kICdjdXN0b21Db2RlJyBvcHRpb25zIGhhdmUgYmVlbiBkaXNhYmxlZCBmb3IgdGhpcyBzZXJ2ZXIuYCxcclxuICAgICAgICA0MDNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIGFuZCB2YWxpZGF0ZXMgcmVzb3VyY2VzIGZyb20gdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3IgZXhwb3J0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8c3RyaW5nfG51bGwpfSBbcmVzb3VyY2VzPW51bGxdIC0gVGhlIHJlc291cmNlcyB0byBiZSBoYW5kbGVkLlxyXG4gKiBDYW4gYmUgZWl0aGVyIGEgSlNPTiBvYmplY3QsIHN0cmluZ2lmaWVkIEpTT04sIGEgcGF0aCB0byBhIEpTT04gZmlsZSxcclxuICogb3IgbnVsbC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYG51bGxgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8bnVsbCl9IFRoZSBoYW5kbGVkIHJlc291cmNlcyBvciBudWxsIGlmIG5vIHZhbGlkIHJlc291cmNlc1xyXG4gKiBhcmUgZm91bmQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gIHJlc291cmNlcyA9IG51bGwsXHJcbiAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4pIHtcclxuICAvLyBMaXN0IG9mIGFsbG93ZWQgc2VjdGlvbnMgaW4gdGhlIHJlc291cmNlcyBKU09OXHJcbiAgY29uc3QgYWxsb3dlZFByb3BzID0gWydqcycsICdjc3MnLCAnZmlsZXMnXTtcclxuXHJcbiAgbGV0IGhhbmRsZWRSZXNvdXJjZXMgPSByZXNvdXJjZXM7XHJcbiAgbGV0IGNvcnJlY3RSZXNvdXJjZXMgPSBmYWxzZTtcclxuXHJcbiAgLy8gVHJ5IHRvIGxvYWQgcmVzb3VyY2VzIGZyb20gYSBmaWxlXHJcbiAgaWYgKGFsbG93RmlsZVJlc291cmNlcyAmJiByZXNvdXJjZXMuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChyZXNvdXJjZXMpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCB7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0FsbG93ZWRDb25maWcocmVzb3VyY2VzLCBmYWxzZSwgYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgICAvLyBHZXQgcmlkIG9mIHRoZSBmaWxlcyBzZWN0aW9uXHJcbiAgICBpZiAoaGFuZGxlZFJlc291cmNlcyAmJiAhYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRmlsdGVyIGZyb20gdW5uZWNlc3NhcnkgcHJvcGVydGllc1xyXG4gIGZvciAoY29uc3QgcHJvcE5hbWUgaW4gaGFuZGxlZFJlc291cmNlcykge1xyXG4gICAgaWYgKCFhbGxvd2VkUHJvcHMuaW5jbHVkZXMocHJvcE5hbWUpKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzW3Byb3BOYW1lXTtcclxuICAgIH0gZWxzZSBpZiAoIWNvcnJlY3RSZXNvdXJjZXMpIHtcclxuICAgICAgY29ycmVjdFJlc291cmNlcyA9IHRydWU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBhdCBsZWFzdCBvbmUgb2YgYWxsb3dlZCBwcm9wZXJ0aWVzIGlzIHByZXNlbnRcclxuICBpZiAoIWNvcnJlY3RSZXNvdXJjZXMpIHtcclxuICAgIHJldHVybiBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gSGFuZGxlIGZpbGVzIHNlY3Rpb25cclxuICBpZiAoaGFuZGxlZFJlc291cmNlcy5maWxlcykge1xyXG4gICAgaGFuZGxlZFJlc291cmNlcy5maWxlcyA9IGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMubWFwKChpdGVtKSA9PiBpdGVtLnRyaW0oKSk7XHJcbiAgICBpZiAoIWhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgfHwgaGFuZGxlZFJlc291cmNlcy5maWxlcy5sZW5ndGggPD0gMCkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiByZXNvdXJjZXNcclxuICByZXR1cm4gaGFuZGxlZFJlc291cmNlcztcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGxvYWRpbmcgYW5kIHZhbGlkYXRpb24gb2YgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2BcclxuICogaW4gdGhlIGV4cG9ydCBvcHRpb25zLiBJZiB0aGUgb3B0aW9uIGlzIGEgc3RyaW5nIGFuZCByZWZlcmVuY2VzIGEgSlNPTiBmaWxlXHJcbiAqICh3aGVuIHRoZSBgYWxsb3dGaWxlUmVzb3VyY2VzYCBpcyB0cnVlKSwgaXQgcmVhZHMgYW5kIHBhcnNlcyB0aGUgZmlsZS5cclxuICogT3RoZXJ3aXNlLCBpdCBhdHRlbXB0cyB0byBwYXJzZSB0aGUgc3RyaW5nIG9yIG9iamVjdCBhcyBKU09OLiBJZiBhbnkgZXJyb3JzXHJcbiAqIG9jY3VyIGR1cmluZyB0aGlzIHByb2Nlc3MsIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuIElmIHRoZXJlIGlzIGFuIGVycm9yXHJcbiAqIGxvYWRpbmcgb3IgcGFyc2luZyB0aGUgYGdsb2JhbE9wdGlvbnNgIG9yIGB0aGVtZU9wdGlvbnNgLCB0aGUgZXJyb3IgaXMgbG9nZ2VkXHJcbiAqIGFuZCB0aGUgb3B0aW9uIGlzIHNldCB0byBudWxsLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUdsb2JhbEFuZFRoZW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGxvYWRpbmdcclxuICogcmVzb3VyY2VzIGZyb20gZmlsZXMgaXMgYWxsb3dlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWUoXHJcbiAgZXhwb3J0T3B0aW9ucyxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIENoZWNrIHRoZSBgZ2xvYmFsT3B0aW9uc2AgYW5kIGB0aGVtZU9wdGlvbnNgIG9wdGlvbnNcclxuICBbJ2dsb2JhbE9wdGlvbnMnLCAndGhlbWVPcHRpb25zJ10uZm9yRWFjaCgob3B0aW9uc05hbWUpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBvcHRpb24gZXhpc3RzXHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSkge1xyXG4gICAgICAgIC8vIENoZWNrIGlmIGl0IGlzIGEgc3RyaW5nIGFuZCBhIGZpbGUgbmFtZSB3aXRoIHRoZSBgLmpzb25gIGV4dGVuc2lvblxyXG4gICAgICAgIGlmIChcclxuICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyAmJlxyXG4gICAgICAgICAgdHlwZW9mIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0uZW5kc1dpdGgoJy5qc29uJylcclxuICAgICAgICApIHtcclxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSBmaWxlIGNvbnRlbnQgY2FuIGJlIGEgY29uZmlnLCBhbmQgc2F2ZSBpdCBhcyBhIHN0cmluZ1xyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0pLCAndXRmOCcpLFxyXG4gICAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSB2YWx1ZSBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0sXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGVycm9yLFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSBcXGAke29wdGlvbnNOYW1lfVxcYCBjYW5ub3QgYmUgbG9hZGVkLmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IG51bGw7XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGlzIHRoZSBgZ2xvYmFsT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnMpKSB7XHJcbiAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3B0aW9uIGZvdW5kLicpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGB0aGVtZU9wdGlvbnNgIHByZXNlbnRcclxuICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnMpKSB7XHJcbiAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgdGhlbWVPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogVmFsaWRhdGVzIHRoZSBzaXplIG9mIHRoZSBkYXRhIGZvciB0aGUgZXhwb3J0IHByb2Nlc3MgYWdhaW5zdCBhIGZpeGVkIGxpbWl0XHJcbiAqIG9mIDEwME1CLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NoZWNrRGF0YVNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGltYWdlT3B0aW9ucyAtIFRoZSBkYXRhIG9iamVjdCwgd2hpY2ggaW5jbHVkZXMgb3B0aW9ucyBmcm9tXHJcbiAqIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucyBhbmQgd2lsbCBiZSBzZW50IHRvIGEgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgc2l6ZSBvZiB0aGUgZGF0YSBmb3JcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzIG9iamVjdCBleGNlZWRzIHRoZSAxMDBNQiBsaW1pdC5cclxuICovXHJcbmZ1bmN0aW9uIF9jaGVja0RhdGFTaXplKGltYWdlT3B0aW9ucykge1xyXG4gIC8vIFNldCB0aGUgZml4ZWQgZGF0YSBsaW1pdCAoMTAwTUIpIGZvciB0aGUgZGV2LXRvb2xzIHByb3RvY29sXHJcbiAgY29uc3QgZGF0YUxpbWl0ID0gMTAwICogMTAyNCAqIDEwMjQ7XHJcblxyXG4gIC8vIEdldCB0aGUgc2l6ZSBvZiB0aGUgZGF0YVxyXG4gIGNvbnN0IHRvdGFsU2l6ZSA9IEJ1ZmZlci5ieXRlTGVuZ3RoKEpTT04uc3RyaW5naWZ5KGltYWdlT3B0aW9ucyksICd1dGYtOCcpO1xyXG5cclxuICAvLyBMb2cgdGhlIHNpemUgaW4gTUJcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1cnJlbnQgdG90YWwgc2l6ZSBvZiB0aGUgZGF0YSBmb3IgdGhlIGV4cG9ydCBwcm9jZXNzIGlzIGFyb3VuZCAkeyhcclxuICAgICAgdG90YWxTaXplIC9cclxuICAgICAgKDEwMjQgKiAxMDI0KVxyXG4gICAgKS50b0ZpeGVkKDIpfU1CLmBcclxuICApO1xyXG5cclxuICAvLyBDaGVjayB0aGUgc2l6ZSBvZiBkYXRhIGJlZm9yZSBwYXNzaW5nIHRvIGEgcGFnZVxyXG4gIGlmICh0b3RhbFNpemUgPj0gZGF0YUxpbWl0KSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgIGBbY2hhcnRdIFRoZSBkYXRhIGZvciB0aGUgZXhwb3J0IHByb2Nlc3MgZXhjZWVkcyAxMDBNQiBsaW1pdC5gXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBiYXRjaEV4cG9ydCxcclxuICBzdGFydEV4cG9ydCxcclxuICBnZXRBbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBwcm92aWRlcyB1dGlsaXR5IGZ1bmN0aW9ucyBmb3IgbWFuYWdpbmcgaW50ZXJ2YWxzXHJcbiAqIGFuZCB0aW1lb3V0cyBpbiBhIGNlbnRyYWxpemVkIG1hbm5lci4gSXQgbWFpbnRhaW5zIGEgcmVnaXN0cnkgb2YgYWxsIGFjdGl2ZVxyXG4gKiB0aW1lcnMgYW5kIGFsbG93cyBmb3IgdGhlaXIgZWZmaWNpZW50IGNsZWFudXAgd2hlbiBuZWVkZWQuIFRoaXMgY2FuIGJlIHVzZWZ1bFxyXG4gKiBpbiBhcHBsaWNhdGlvbnMgd2hlcmUgcHJvcGVyIHJlc291cmNlIG1hbmFnZW1lbnQgYW5kIGNsZWFuIHNodXRkb3duIG9mIHRpbWVyc1xyXG4gKiBhcmUgY3JpdGljYWwgdG8gYXZvaWQgbWVtb3J5IGxlYWtzIG9yIHVuaW50ZW5kZWQgYmVoYXZpb3IuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuLy8gQXJyYXkgdGhhdCBjb250YWlucyBpZHMgb2YgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0c1xyXG5jb25zdCB0aW1lcklkcyA9IFtdO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgaWQgb2YgdGhlIGBzZXRJbnRlcnZhbGAgb3IgYHNldFRpbWVvdXRgIGFuZCB0byB0aGUgYHRpbWVySWRzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIGFkZFRpbWVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7Tm9kZUpTLlRpbWVvdXR9IGlkIC0gSWQgb2YgYW4gaW50ZXJ2YWwgb3IgYSB0aW1lb3V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGFkZFRpbWVyKGlkKSB7XHJcbiAgdGltZXJJZHMucHVzaChpZCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgYWxsIG9mIG9uZ29pbmcgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cyBieSBpZHMgZ2F0aGVyZWRcclxuICogaW4gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhckFsbFRpbWVyc1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyQWxsVGltZXJzKCkge1xyXG4gIGxvZyg0LCBgW3RpbWVyXSBDbGVhcmluZyBhbGwgcmVnaXN0ZXJlZCBpbnRlcnZhbHMgYW5kIHRpbWVvdXRzLmApO1xyXG4gIGZvciAoY29uc3QgaWQgb2YgdGltZXJJZHMpIHtcclxuICAgIGNsZWFySW50ZXJ2YWwoaWQpO1xyXG4gICAgY2xlYXJUaW1lb3V0KGlkKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBhZGRUaW1lcixcclxuICBjbGVhckFsbFRpbWVyc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWlkZGxld2FyZSBmdW5jdGlvbnMgZm9yIGxvZ2dpbmcgZXJyb3JzIHdpdGggc3RhY2sgdHJhY2VzXHJcbiAqIGFuZCBoYW5kbGluZyBlcnJvciByZXNwb25zZXMgaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlIGFuZCBoYW5kbGluZyBlcnJvciByZXNwb25zZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ0Vycm9yTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uIHdpdGhcclxuICogdGhlIHBhc3NlZCBlcnJvci5cclxuICovXHJcbmZ1bmN0aW9uIGxvZ0Vycm9yTWlkZGxld2FyZShlcnJvciwgcmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICAvLyBEaXNwbGF5IHRoZSBlcnJvciB3aXRoIHN0YWNrIGluIGEgY29ycmVjdCBmb3JtYXRcclxuICBsb2dXaXRoU3RhY2soMSwgZXJyb3IpO1xyXG5cclxuICAvLyBEZWxldGUgdGhlIHN0YWNrIGZvciB0aGUgZW52aXJvbm1lbnQgb3RoZXIgdGhhbiB0aGUgZGV2ZWxvcG1lbnRcclxuICBpZiAoZ2V0T3B0aW9ucygpLm90aGVyLm5vZGVFbnYgIT09ICdkZXZlbG9wbWVudCcpIHtcclxuICAgIGRlbGV0ZSBlcnJvci5zdGFjaztcclxuICB9XHJcblxyXG4gIC8vIENhbGwgdGhlIGByZXR1cm5FcnJvck1pZGRsZXdhcmVgIG1pZGRsZXdhcmVcclxuICByZXR1cm4gbmV4dChlcnJvcik7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciByZXR1cm5pbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICovXHJcbmZ1bmN0aW9uIHJldHVybkVycm9yTWlkZGxld2FyZShlcnJvciwgcmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICAvLyBHYXRoZXIgYWxsIHJlcXVpZWQgaW5mb3JtYXRpb24gZm9yIHRoZSByZXNwb25zZVxyXG4gIGNvbnN0IHsgbWVzc2FnZSwgc3RhY2sgfSA9IGVycm9yO1xyXG5cclxuICAvLyBVc2UgdGhlIGVycm9yJ3Mgc3RhdHVzIGNvZGUgb3IgdGhlIGRlZmF1bHQgNDAwXHJcbiAgY29uc3Qgc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGUgfHwgNDAwO1xyXG5cclxuICAvLyBTZXQgYW5kIHJldHVybiByZXNwb25zZVxyXG4gIHJlc3BvbnNlLnN0YXR1cyhzdGF0dXNDb2RlKS5qc29uKHsgc3RhdHVzQ29kZSwgbWVzc2FnZSwgc3RhY2sgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBlcnJvciBtaWRkbGV3YXJlcyB0byB0aGUgcGFzc2VkIGV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGVycm9yTWlkZGxld2FyZShhcHApIHtcclxuICAvLyBBZGQgbG9nIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKGxvZ0Vycm9yTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCBzZXQgc3RhdHVzIGFuZCByZXR1cm4gZXJyb3IgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UocmV0dXJuRXJyb3JNaWRkbGV3YXJlKTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWlkZGxld2FyZSBmdW5jdGlvbnMgZm9yIGNvbmZpZ3VyaW5nIGFuZCBlbmFibGluZyByYXRlXHJcbiAqIGxpbWl0aW5nIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHJhdGVMaW1pdCBmcm9tICdleHByZXNzLXJhdGUtbGltaXQnO1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIGVuYWJsaW5nIHJhdGUgbGltaXRpbmcgb24gdGhlIHNwZWNpZmllZCBFeHByZXNzIGFwcC5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSByYXRlTGltaXRpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHJhdGVMaW1pdGluZ2Agb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGNvdWxkIG5vdCBjb25maWd1cmUgYW5kIHNldFxyXG4gKiB0aGUgcmF0ZSBsaW1pdGluZyBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gcmF0ZUxpbWl0aW5nTWlkZGxld2FyZShhcHAsIHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgdGhlIHJhdGUgbGltaXRpbmcgaXMgZW5hYmxlZCBhbmQgdGhlIGFwcCBleGlzdHNcclxuICAgIGlmIChhcHAgJiYgcmF0ZUxpbWl0aW5nT3B0aW9ucy5lbmFibGUpIHtcclxuICAgICAgY29uc3QgbWVzc2FnZSA9XHJcbiAgICAgICAgJ1RvbyBtYW55IHJlcXVlc3RzLCB5b3UgaGF2ZSBiZWVuIHJhdGUgbGltaXRlZC4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4nO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBmb3IgdGhlIHJhdGUgbGltaXRlclxyXG4gICAgICBjb25zdCByYXRlT3B0aW9ucyA9IHtcclxuICAgICAgICB3aW5kb3c6IHJhdGVMaW1pdGluZ09wdGlvbnMud2luZG93IHx8IDEsXHJcbiAgICAgICAgbWF4UmVxdWVzdHM6IHJhdGVMaW1pdGluZ09wdGlvbnMubWF4UmVxdWVzdHMgfHwgMzAsXHJcbiAgICAgICAgZGVsYXk6IHJhdGVMaW1pdGluZ09wdGlvbnMuZGVsYXkgfHwgMCxcclxuICAgICAgICB0cnVzdFByb3h5OiByYXRlTGltaXRpbmdPcHRpb25zLnRydXN0UHJveHkgfHwgZmFsc2UsXHJcbiAgICAgICAgc2tpcEtleTogcmF0ZUxpbWl0aW5nT3B0aW9ucy5za2lwS2V5IHx8IG51bGwsXHJcbiAgICAgICAgc2tpcFRva2VuOiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBUb2tlbiB8fCBudWxsXHJcbiAgICAgIH07XHJcblxyXG4gICAgICAvLyBTZXQgaWYgYmVoaW5kIGEgcHJveHlcclxuICAgICAgaWYgKHJhdGVPcHRpb25zLnRydXN0UHJveHkpIHtcclxuICAgICAgICBhcHAuZW5hYmxlKCd0cnVzdCBwcm94eScpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDcmVhdGUgYSBsaW1pdGVyXHJcbiAgICAgIGNvbnN0IGxpbWl0ZXIgPSByYXRlTGltaXQoe1xyXG4gICAgICAgIC8vIFRpbWUgZnJhbWUgZm9yIHdoaWNoIHJlcXVlc3RzIGFyZSBjaGVja2VkIGFuZCByZW1lbWJlcmVkXHJcbiAgICAgICAgd2luZG93TXM6IHJhdGVPcHRpb25zLndpbmRvdyAqIDYwICogMTAwMCxcclxuICAgICAgICAvLyBMaW1pdCBlYWNoIElQIHRvIDEwMCByZXF1ZXN0cyBwZXIgYHdpbmRvd01zYFxyXG4gICAgICAgIGxpbWl0OiByYXRlT3B0aW9ucy5tYXhSZXF1ZXN0cyxcclxuICAgICAgICAvLyBEaXNhYmxlIGRlbGF5aW5nLCBmdWxsIHNwZWVkIHVudGlsIHRoZSBtYXggbGltaXQgaXMgcmVhY2hlZFxyXG4gICAgICAgIGRlbGF5TXM6IHJhdGVPcHRpb25zLmRlbGF5LFxyXG4gICAgICAgIGhhbmRsZXI6IChyZXF1ZXN0LCByZXNwb25zZSkgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2UuZm9ybWF0KHtcclxuICAgICAgICAgICAganNvbjogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQoeyBtZXNzYWdlIH0pO1xyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICBkZWZhdWx0OiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZChtZXNzYWdlKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSk7XHJcbiAgICAgICAgfSxcclxuICAgICAgICBza2lwOiAocmVxdWVzdCkgPT4ge1xyXG4gICAgICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcclxuICAgICAgICAgIGlmIChcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcEtleSAhPT0gbnVsbCAmJlxyXG4gICAgICAgICAgICByYXRlT3B0aW9ucy5za2lwVG9rZW4gIT09IG51bGwgJiZcclxuICAgICAgICAgICAgcmVxdWVzdC5xdWVyeS5rZXkgPT09IHJhdGVPcHRpb25zLnNraXBLZXkgJiZcclxuICAgICAgICAgICAgcmVxdWVzdC5xdWVyeS5hY2Nlc3NfdG9rZW4gPT09IHJhdGVPcHRpb25zLnNraXBUb2tlblxyXG4gICAgICAgICAgKSB7XHJcbiAgICAgICAgICAgIGxvZyg0LCAnW3JhdGUgbGltaXRpbmddIFNraXBwaW5nIHJhdGUgbGltaXRlci4nKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIC8vIFVzZSBhIGxpbWl0ZXIgYXMgYSBtaWRkbGV3YXJlXHJcbiAgICAgIGFwcC51c2UobGltaXRlcik7XHJcblxyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3JhdGUgbGltaXRpbmddIEVuYWJsZWQgcmF0ZSBsaW1pdGluZyB3aXRoICR7cmF0ZU9wdGlvbnMubWF4UmVxdWVzdHN9IHJlcXVlc3RzIHBlciAke3JhdGVPcHRpb25zLndpbmRvd30gbWludXRlIGZvciBlYWNoIElQLCB0cnVzdGluZyBwcm94eTogJHtyYXRlT3B0aW9ucy50cnVzdFByb3h5fS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tyYXRlIGxpbWl0aW5nXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWlkZGxld2FyZSBmdW5jdGlvbnMgZm9yIHZhbGlkYXRpbmcgaW5jb21pbmcgSFRUUCByZXF1ZXN0c1xyXG4gKiBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLiBUaGlzIG1vZHVsZSBlbnN1cmVzIHRoYXQgcmVxdWVzdHMgY29udGFpblxyXG4gKiBhcHByb3ByaWF0ZSBjb250ZW50IHR5cGVzIGFuZCB2YWxpZCByZXF1ZXN0IGJvZGllcywgaW5jbHVkaW5nIHByb3BlciBKU09OXHJcbiAqIHN0cnVjdHVyZXMgYW5kIGNoYXJ0IGRhdGEgZm9yIGV4cG9ydHMuIEl0IGNoZWNrcyBmb3IgcG90ZW50aWFsIGlzc3VlcyBzdWNoXHJcbiAqIGFzIG1pc3Npbmcgb3IgbWFsZm9ybWVkIGRhdGEsIHByaXZhdGUgcmFuZ2UgVVJMcyBpbiBTVkcgcGF5bG9hZHMsIGFuZCBhbGxvd3NcclxuICogZm9yIGZsZXhpYmxlIG9wdGlvbnMgdmFsaWRhdGlvbi4gVGhlIG1pZGRsZXdhcmUgbG9ncyBkZXRhaWxlZCBpbmZvcm1hdGlvblxyXG4gKiBhbmQgaGFuZGxlcyBlcnJvcnMgcmVsYXRlZCB0byBpbmNvcnJlY3QgcGF5bG9hZHMsIGNoYXJ0IGRhdGEsIGFuZCBwcml2YXRlIFVSTFxyXG4gKiB1c2FnZS5cclxuICovXHJcblxyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBbGxvd0NvZGVFeGVjdXRpb24gfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGlzQWxsb3dlZENvbmZpZyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGlzT2JqZWN0RW1wdHksIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciB2YWxpZGF0aW5nIHRoZSBjb250ZW50LXR5cGUgaGVhZGVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY29udGVudFR5cGVNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGNvbnRlbnQtdHlwZVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICovXHJcbmZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZShyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIGNvbnRlbnQgdHlwZSBoZWFkZXJcclxuICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gcmVxdWVzdC5oZWFkZXJzWydjb250ZW50LXR5cGUnXSB8fCAnJztcclxuXHJcbiAgICAvLyBBbGxvdyBvbmx5IEpTT04sIFVSTC1lbmNvZGVkIGFuZCBmb3JtIGRhdGEgd2l0aG91dCBmaWxlcyB0eXBlcyBvZiBkYXRhXHJcbiAgICBpZiAoXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24vanNvbicpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJykgJiZcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdtdWx0aXBhcnQvZm9ybS1kYXRhJylcclxuICAgICkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1t2YWxpZGF0aW9uXSBDb250ZW50LVR5cGUgbXVzdCBiZSBhcHBsaWNhdGlvbi9qc29uLCBhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQsIG9yIG11bHRpcGFydC9mb3JtLWRhdGEuJyxcclxuICAgICAgICA0MTVcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDYWxsIHRoZSBgcmVxdWVzdEJvZHlNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgICByZXR1cm4gbmV4dCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgcmVxdWVzdCdzIGJvZHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9IFRoZSBjYWxsIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgYm9keSBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBjaGFydCBkYXRhIGZyb20gdGhlIGJvZHlcclxuICogaXMgbm90IGNvcnJlY3QuXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpbiBjYXNlIG9mIHRoZSBwcml2YXRlIHJhbmdlXHJcbiAqIHVybCBlcnJvci5cclxuICovXHJcbmZ1bmN0aW9uIHJlcXVlc3RCb2R5TWlkZGxld2FyZShyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3QgYm9keVxyXG4gICAgY29uc3QgYm9keSA9IHJlcXVlc3QuYm9keTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSB1bmlxdWUgSUQgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgcmVxdWVzdElkID0gdXVpZCgpO1xyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRoZXJlIGlzIG5vIGNvcnJlY3QgYm9keVxyXG4gICAgaWYgKCFib2R5IHx8IGlzT2JqZWN0RW1wdHkoYm9keSkpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fCByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcGF5bG9hZCBpcyBlbXB0eS5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgYm9keSBpcyByZXF1aXJlZC4gUGxlYXNlIGVuc3VyZSB0aGF0IHlvdXIgQ29udGVudC1UeXBlIGhlYWRlciBpcyBjb3JyZWN0LiBBY2NlcHRlZCB0eXBlcyBhcmUgJ2FwcGxpY2F0aW9uL2pzb24nIGFuZCAnbXVsdGlwYXJ0L2Zvcm0tZGF0YScuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBvcHRpb24gZm9yIHRoZSBzZXJ2ZXJcclxuICAgIGNvbnN0IGFsbG93Q29kZUV4ZWN1dGlvbiA9IGdldEFsbG93Q29kZUV4ZWN1dGlvbigpO1xyXG5cclxuICAgIC8vIEZpbmQgYSBjb3JyZWN0IGNoYXJ0IG9wdGlvbnNcclxuICAgIGNvbnN0IGluc3RyID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAvLyBVc2Ugb25lIG9mIHRoZSBiZWxvd1xyXG4gICAgICBib2R5Lmluc3RyIHx8IGJvZHkub3B0aW9ucyB8fCBib2R5LmluZmlsZSB8fCBib2R5LmRhdGEsXHJcbiAgICAgIC8vIFN0cmluZ2lmeSBvcHRpb25zXHJcbiAgICAgIHRydWUsXHJcbiAgICAgIC8vIEFsbG93IG9yIGRpc2FsbG93IGZ1bmN0aW9uc1xyXG4gICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICk7XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgaXMgbm8gY29ycmVjdCBjaGFydCBkYXRhXHJcbiAgICBpZiAoaW5zdHIgPT09IG51bGwgJiYgIWJvZHkuc3ZnKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgbWlzc2luZyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm9yIGV4cG9ydDogJHtKU09OLnN0cmluZ2lmeShib2R5KX0uYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIE5vIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3VuZC4gRW5zdXJlIHRoYXQgeW91IGFyZSB1c2luZyBlaXRoZXIgYXBwbGljYXRpb24vanNvbiBvciBtdWx0aXBhcnQvZm9ybS1kYXRhIGhlYWRlcnMuIElmIHNlbmRpbmcgSlNPTiwgbWFrZSBzdXJlIHRoZSBjaGFydCBkYXRhIGlzIGluIHRoZSAnaW5maWxlJywgJ29wdGlvbnMnLCBvciAnZGF0YScgYXR0cmlidXRlLiBJZiBzZW5kaW5nIFNWRywgZW5zdXJlIGl0IGlzIGluIHRoZSAnc3ZnJyBhdHRyaWJ1dGUuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0ZXN0IG9mIHhsaW5rOmhyZWYgZWxlbWVudHMgZnJvbSBwYXlsb2FkJ3MgU1ZHIGZhaWxzXHJcbiAgICBpZiAoYm9keS5zdmcgJiYgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChib2R5LnN2ZykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFNWRyBwb3RlbnRpYWxseSBjb250YWluIGF0IGxlYXN0IG9uZSBmb3JiaWRkZW4gVVJMIGluICd4bGluazpocmVmJyBlbGVtZW50LiBQbGVhc2UgcmV2aWV3IHRoZSBTVkcgY29udGVudCBhbmQgZW5zdXJlIHRoYXQgYWxsIHJlZmVyZW5jZWQgVVJMcyBjb21wbHkgd2l0aCBzZWN1cml0eSBwb2xpY2llcy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgcmVxdWVzdCBvcHRpb25zIGFuZCBzdG9yZSBwYXJzZWQgc3RydWN0dXJlIGluIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnMgPSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY3JlYXRlZCBJRCBhcyBhIGByZXF1ZXN0SWRgIHByb3BlcnR5IGluIHRoZSBvcHRpb25zXHJcbiAgICAgIHJlcXVlc3RJZCxcclxuICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgaW5zdHIsXHJcbiAgICAgICAgc3ZnOiBib2R5LnN2ZyxcclxuICAgICAgICBvdXRmaWxlOlxyXG4gICAgICAgICAgYm9keS5vdXRmaWxlIHx8XHJcbiAgICAgICAgICBgJHtyZXF1ZXN0LnBhcmFtcy5maWxlbmFtZSB8fCAnY2hhcnQnfS4ke2JvZHkudHlwZSB8fCAncG5nJ31gLFxyXG4gICAgICAgIHR5cGU6IGJvZHkudHlwZSxcclxuICAgICAgICBjb25zdHI6IGJvZHkuY29uc3RyLFxyXG4gICAgICAgIGI2NDogYm9keS5iNjQsXHJcbiAgICAgICAgbm9Eb3dubG9hZDogYm9keS5ub0Rvd25sb2FkLFxyXG4gICAgICAgIGhlaWdodDogYm9keS5oZWlnaHQsXHJcbiAgICAgICAgd2lkdGg6IGJvZHkud2lkdGgsXHJcbiAgICAgICAgc2NhbGU6IGJvZHkuc2NhbGUsXHJcbiAgICAgICAgZ2xvYmFsT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS5nbG9iYWxPcHRpb25zLFxyXG4gICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICksXHJcbiAgICAgICAgdGhlbWVPcHRpb25zOiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICBib2R5LnRoZW1lT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApXHJcbiAgICAgIH0sXHJcbiAgICAgIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gICAgICAgIGFsbG93RmlsZVJlc291cmNlczogZmFsc2UsXHJcbiAgICAgICAgY3VzdG9tQ29kZTogYm9keS5jdXN0b21Db2RlLFxyXG4gICAgICAgIGNhbGxiYWNrOiBib2R5LmNhbGxiYWNrLFxyXG4gICAgICAgIHJlc291cmNlczogaXNBbGxvd2VkQ29uZmlnKGJvZHkucmVzb3VyY2VzLCB0cnVlLCBhbGxvd0NvZGVFeGVjdXRpb24pXHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgbmV4dCBtaWRkbGV3YXJlXHJcbiAgICByZXR1cm4gbmV4dCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlcyB0byB0aGUgcGFzc2VkIGV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCkge1xyXG4gIC8vIEFkZCBjb250ZW50IHR5cGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgYXBwLnBvc3QoWycvJywgJy86ZmlsZW5hbWUnXSwgY29udGVudFR5cGVNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHJlcXVlc3QgYm9keSByZXF1ZXN0IHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIHJlcXVlc3RCb2R5TWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgdGhlIGV4cG9ydCByb3V0ZXMgYW5kIGxvZ2ljIGZvciBoYW5kbGluZyBjaGFydCBleHBvcnRcclxuICogcmVxdWVzdHMgaW4gYW4gRXhwcmVzcyBzZXJ2ZXIuIFRoaXMgbW9kdWxlIHByb2Nlc3NlcyBpbmNvbWluZyByZXF1ZXN0c1xyXG4gKiB0byBleHBvcnQgY2hhcnRzIGluIHZhcmlvdXMgZm9ybWF0cyAoZS5nLiBKUEVHLCBQTkcsIFBERiwgU1ZHKS4gSXQgaW50ZWdyYXRlc1xyXG4gKiB3aXRoIEhpZ2hjaGFydHMnIGNvcmUgZnVuY3Rpb25hbGl0aWVzIGFuZCBzdXBwb3J0cyBib3RoIGltbWVkaWF0ZSBkb3dubG9hZFxyXG4gKiByZXNwb25zZXMgYW5kIEJhc2U2NC1lbmNvZGVkIGNvbnRlbnQgcmV0dXJucy4gVGhlIGNvZGUgYWxzbyBmZWF0dXJlc1xyXG4gKiBiZW5jaG1hcmtpbmcgZm9yIHBlcmZvcm1hbmNlIG1vbml0b3JpbmcuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgc3RhcnRFeHBvcnQgfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldEJhc2U2NCwgbWVhc3VyZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFJldmVyc2VkIE1JTUUgdHlwZXNcclxuY29uc3QgcmV2ZXJzZWRNaW1lID0ge1xyXG4gIHBuZzogJ2ltYWdlL3BuZycsXHJcbiAganBlZzogJ2ltYWdlL2pwZWcnLFxyXG4gIGdpZjogJ2ltYWdlL2dpZicsXHJcbiAgcGRmOiAnYXBwbGljYXRpb24vcGRmJyxcclxuICBzdmc6ICdpbWFnZS9zdmcreG1sJ1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4cG9ydCByZXF1ZXN0cyBmcm9tIHRoZSBjbGllbnQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAqIGlzIGNvbXBsZXRlLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gcmVxdWVzdEV4cG9ydChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBTdGFydCBjb3VudGluZyB0aW1lIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBJbiBjYXNlIHRoZSBjb25uZWN0aW9uIGlzIGNsb3NlZCwgZm9yY2UgdG8gYWJvcnQgZnVydGhlciBhY3Rpb25zXHJcbiAgICBsZXQgY29ubmVjdGlvbkFib3J0ZWQgPSBmYWxzZTtcclxuICAgIHJlcXVlc3Quc29ja2V0Lm9uKCdjbG9zZScsIChoYWRFcnJvcnMpID0+IHtcclxuICAgICAgaWYgKGhhZEVycm9ycykge1xyXG4gICAgICAgIGNvbm5lY3Rpb25BYm9ydGVkID0gdHJ1ZTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBvcHRpb25zIHByZXZpb3VzbHkgdmFsaWRhdGVkIGluIHRoZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICAgIGNvbnN0IG9wdGlvbnMgPSByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGlkXHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSBvcHRpb25zLnJlcXVlc3RJZDtcclxuXHJcbiAgICAvLyBJbmZvIGFib3V0IGFuIGluY29taW5nIHJlcXVlc3Qgd2l0aCBjb3JyZWN0IGRhdGFcclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3QuYCk7XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChvcHRpb25zLCAoZXJyb3IsIGRhdGEpID0+IHtcclxuICAgICAgLy8gUmVtb3ZlIHRoZSBjbG9zZSBldmVudCBmcm9tIHRoZSBzb2NrZXRcclxuICAgICAgcmVxdWVzdC5zb2NrZXQucmVtb3ZlQWxsTGlzdGVuZXJzKCdjbG9zZScpO1xyXG5cclxuICAgICAgLy8gSWYgdGhlIGNvbm5lY3Rpb24gd2FzIGNsb3NlZCwgZG8gbm90aGluZ1xyXG4gICAgICBpZiAoY29ubmVjdGlvbkFib3J0ZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgY2xpZW50IGNsb3NlZCB0aGUgY29ubmVjdGlvbiBiZWZvcmUgdGhlIGNoYXJ0IGZpbmlzaGVkIHByb2Nlc3NpbmcuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBlcnJvciwgbG9nIGl0IGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBkYXRhIGlzIG1pc3NpbmcsIGxvZyB0aGUgbWVzc2FnZSBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoIWRhdGEgfHwgIWRhdGEucmVzdWx0KSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gUmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHxcclxuICAgICAgICAgICAgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcmVzdWx0IGlzICR7ZGF0YS5yZXN1bHR9LmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFVuZXhwZWN0ZWQgcmV0dXJuIG9mIHRoZSBleHBvcnQgcmVzdWx0IGZyb20gdGhlIGNoYXJ0IGdlbmVyYXRpb24uIFBsZWFzZSBjaGVjayB5b3VyIHJlcXVlc3QgZGF0YS5gLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gUmV0dXJuIHRoZSByZXN1bHQgaW4gYW4gYXBwcm9wcmlhdGUgZm9ybWF0XHJcbiAgICAgIGlmIChkYXRhLnJlc3VsdCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSB3aG9sZSBleHBvcnRpbmcgcHJvY2VzcyB0b29rICR7cmVxdWVzdENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBgdHlwZWAsIGBiNjRgLCBgbm9Eb3dubG9hZGAsIGFuZCBgb3V0ZmlsZWAgZnJvbSBvcHRpb25zXHJcbiAgICAgICAgY29uc3QgeyB0eXBlLCBiNjQsIG5vRG93bmxvYWQsIG91dGZpbGUgfSA9IGRhdGEub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgICAgIC8vIElmIG9ubHkgQmFzZTY0IGlzIHJlcXVpcmVkLCByZXR1cm4gaXRcclxuICAgICAgICBpZiAoYjY0KSB7XHJcbiAgICAgICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChnZXRCYXNlNjQoZGF0YS5yZXN1bHQsIHR5cGUpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFNldCBjb3JyZWN0IGNvbnRlbnQgdHlwZVxyXG4gICAgICAgIHJlc3BvbnNlLmhlYWRlcignQ29udGVudC1UeXBlJywgcmV2ZXJzZWRNaW1lW3R5cGVdIHx8ICdpbWFnZS9wbmcnKTtcclxuXHJcbiAgICAgICAgLy8gRGVjaWRlIHdoZXRoZXIgdG8gZG93bmxvYWQgb3Igbm90IGNoYXJ0IGZpbGVcclxuICAgICAgICBpZiAoIW5vRG93bmxvYWQpIHtcclxuICAgICAgICAgIHJlc3BvbnNlLmF0dGFjaG1lbnQob3V0ZmlsZSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBJZiBTVkcsIHJldHVybiBwbGFpbiBjb250ZW50LCBvdGhlcndpc2UgYSBiNjQgc3RyaW5nIGZyb20gYSBidWZmZXJcclxuICAgICAgICByZXR1cm4gdHlwZSA9PT0gJ3N2ZydcclxuICAgICAgICAgID8gcmVzcG9uc2Uuc2VuZChkYXRhLnJlc3VsdClcclxuICAgICAgICAgIDogcmVzcG9uc2Uuc2VuZChCdWZmZXIuZnJvbShkYXRhLnJlc3VsdCwgJ2Jhc2U2NCcpKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgZXhwb3J0YCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHBvcnRSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBleHBvcnRSb3V0ZXMoYXBwKSB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnLycgLSBBIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIGF0IHRoZSByb290XHJcbiAgICogZW5kcG9pbnQuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy8nLCByZXF1ZXN0RXhwb3J0KTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnLzpmaWxlbmFtZScgLSBBIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIHdpdGhcclxuICAgKiBhIHNwZWNpZmllZCBmaWxlbmFtZSBwYXJhbWV0ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy86ZmlsZW5hbWUnLCByZXF1ZXN0RXhwb3J0KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciBzZXJ2ZXIgaGVhbHRoIG1vbml0b3JpbmcsIGluY2x1ZGluZ1xyXG4gKiB1cHRpbWUsIHN1Y2Nlc3MgcmF0ZXMsIGFuZCBvdGhlciBzZXJ2ZXIgc3RhdGlzdGljcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldEhpZ2hjaGFydHNWZXJzaW9uIH0gZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRQb29sU3RhdHMsIGdldFBvb2xJbmZvSlNPTiB9IGZyb20gJy4uLy4uL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBhZGRUaW1lciB9IGZyb20gJy4uLy4uL3RpbWVyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXROZXdEYXRlVGltZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbi8vIFNldCB0aGUgc3RhcnQgZGF0ZSBvZiB0aGUgc2VydmVyXHJcbmNvbnN0IHNlcnZlclN0YXJ0VGltZSA9IG5ldyBEYXRlKCk7XHJcblxyXG4vLyBHZXQgdGhlIGBwYWNrYWdlLmpzb25gIGNvbnRlbnRcclxuY29uc3QgcGFja2FnZUZpbGUgPSBKU09OLnBhcnNlKFxyXG4gIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ3BhY2thZ2UuanNvbicpLCAndXRmOCcpXHJcbik7XHJcblxyXG4vLyBBbiBhcnJheSBmb3Igc3VjY2VzcyByYXRlIHJhdGlvc1xyXG5jb25zdCBzdWNjZXNzUmF0ZXMgPSBbXTtcclxuXHJcbi8vIFJlY29yZCBldmVyeSBtaW51dGVcclxuY29uc3QgcmVjb3JkSW50ZXJ2YWwgPSA2MCAqIDEwMDA7XHJcblxyXG4vLyAzMCBtaW51dGVzXHJcbmNvbnN0IHdpbmRvd1NpemUgPSAzMDtcclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIG1vdmluZyBhdmVyYWdlIGluZGljYXRvciBiYXNlZCBvbiB0aGUgZGF0YSBmcm9tIHRoZSBgc3VjY2Vzc1JhdGVzYFxyXG4gKiBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtudW1iZXJ9IEEgbW92aW5nIGF2ZXJhZ2UgZm9yIHN1Y2Nlc3MgcmF0aW8gb2YgdGhlIHNlcnZlciBleHBvcnRzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2UoKSB7XHJcbiAgcmV0dXJuIHN1Y2Nlc3NSYXRlcy5yZWR1Y2UoKGEsIGIpID0+IGEgKyBiLCAwKSAvIHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgdGhlIGludGVydmFsIHJlc3BvbnNpYmxlIGZvciBjYWxjdWxhdGluZyBjdXJyZW50IHN1Y2Nlc3MgcmF0ZSByYXRpb1xyXG4gKiBhbmQgY29sbGVjdHMgcmVjb3JkcyB0byB0aGUgYHN1Y2Nlc3NSYXRlc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfc3RhcnRTdWNjZXNzUmF0ZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Tm9kZUpTLlRpbWVvdXR9IElkIG9mIGFuIGludGVydmFsLlxyXG4gKi9cclxuZnVuY3Rpb24gX3N0YXJ0U3VjY2Vzc1JhdGUoKSB7XHJcbiAgcmV0dXJuIHNldEludGVydmFsKCgpID0+IHtcclxuICAgIGNvbnN0IHN0YXRzID0gZ2V0UG9vbFN0YXRzKCk7XHJcbiAgICBjb25zdCBzdWNjZXNzUmF0aW8gPVxyXG4gICAgICBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkID09PSAwXHJcbiAgICAgICAgPyAxXHJcbiAgICAgICAgOiAoc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCAvIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQpICogMTAwO1xyXG5cclxuICAgIHN1Y2Nlc3NSYXRlcy5wdXNoKHN1Y2Nlc3NSYXRpbyk7XHJcbiAgICBpZiAoc3VjY2Vzc1JhdGVzLmxlbmd0aCA+IHdpbmRvd1NpemUpIHtcclxuICAgICAgc3VjY2Vzc1JhdGVzLnNoaWZ0KCk7XHJcbiAgICB9XHJcbiAgfSwgcmVjb3JkSW50ZXJ2YWwpO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYGhlYWx0aGAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaGVhbHRoUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gaGVhbHRoUm91dGVzKGFwcCkge1xyXG4gIC8vIFN0YXJ0IHByb2Nlc3Npbmcgc3VjY2VzcyByYXRlIHJhdGlvIGludGVydmFsIGFuZCBzYXZlIGl0cyBpZCB0byB0aGUgYXJyYXlcclxuICAvLyBmb3IgdGhlIGdyYWNlZnVsIGNsZWFyaW5nIG9uIHNodXRkb3duIHdpdGggaW5qZWN0ZWQgYGFkZFRpbWVyYCBmdW50aW9uXHJcbiAgYWRkVGltZXIoX3N0YXJ0U3VjY2Vzc1JhdGUoKSk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIEdFVCAnL2hlYWx0aCcgLSBBIHJvdXRlIGZvciBnZXR0aW5nIHRoZSBiYXNpYyBzdGF0cyBvZiB0aGUgc2VydmVyLlxyXG4gICAqL1xyXG4gIGFwcC5nZXQoJy9oZWFsdGgnLCAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW2hlYWx0aF0gUmV0dXJuaW5nIHNlcnZlciBoZWFsdGguJyk7XHJcblxyXG4gICAgICBjb25zdCBzdGF0cyA9IGdldFBvb2xTdGF0cygpO1xyXG4gICAgICBjb25zdCBwZXJpb2QgPSBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG4gICAgICBjb25zdCBtb3ZpbmdBdmVyYWdlID0gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2UoKTtcclxuXHJcbiAgICAgIC8vIFNlbmQgdGhlIHNlcnZlcidzIHN0YXRpc3RpY3NcclxuICAgICAgcmVzcG9uc2Uuc2VuZCh7XHJcbiAgICAgICAgLy8gU3RhdHVzIGFuZCB0aW1lc1xyXG4gICAgICAgIHN0YXR1czogJ09LJyxcclxuICAgICAgICBib290VGltZTogc2VydmVyU3RhcnRUaW1lLFxyXG4gICAgICAgIHVwdGltZTogYCR7TWF0aC5mbG9vcigoZ2V0TmV3RGF0ZVRpbWUoKSAtIHNlcnZlclN0YXJ0VGltZS5nZXRUaW1lKCkpIC8gMTAwMCAvIDYwKX0gbWludXRlc2AsXHJcblxyXG4gICAgICAgIC8vIFZlcnNpb25zXHJcbiAgICAgICAgc2VydmVyVmVyc2lvbjogcGFja2FnZUZpbGUudmVyc2lvbixcclxuICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuXHJcbiAgICAgICAgLy8gRXhwb3J0c1xyXG4gICAgICAgIGF2ZXJhZ2VFeHBvcnRUaW1lOiBzdGF0cy50aW1lU3BlbnRBdmVyYWdlLFxyXG4gICAgICAgIGF0dGVtcHRlZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQsXHJcbiAgICAgICAgcGVyZm9ybWVkRXhwb3J0czogc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCxcclxuICAgICAgICBmYWlsZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzRHJvcHBlZCxcclxuICAgICAgICBzdWNlc3NSYXRpbzogKHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQgLyBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkKSAqIDEwMCxcclxuXHJcbiAgICAgICAgLy8gUG9vbFxyXG4gICAgICAgIHBvb2w6IGdldFBvb2xJbmZvSlNPTigpLFxyXG5cclxuICAgICAgICAvLyBNb3ZpbmcgYXZlcmFnZVxyXG4gICAgICAgIHBlcmlvZCxcclxuICAgICAgICBtb3ZpbmdBdmVyYWdlLFxyXG4gICAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgICBpc05hTihtb3ZpbmdBdmVyYWdlKSB8fCAhc3VjY2Vzc1JhdGVzLmxlbmd0aFxyXG4gICAgICAgICAgICA/ICdUb28gZWFybHkgdG8gcmVwb3J0LiBObyBleHBvcnRzIG1hZGUgeWV0LiBQbGVhc2UgY2hlY2sgYmFjayBzb29uLidcclxuICAgICAgICAgICAgOiBgTGFzdCAke3BlcmlvZH0gbWludXRlcyBoYWQgYSBzdWNjZXNzIHJhdGUgb2YgJHttb3ZpbmdBdmVyYWdlLnRvRml4ZWQoMil9JS5gLFxyXG5cclxuICAgICAgICAvLyBTVkcgYW5kIEpTT04gZXhwb3J0c1xyXG4gICAgICAgIHN2Z0V4cG9ydHM6IHN0YXRzLmV4cG9ydHNGcm9tU3ZnLFxyXG4gICAgICAgIGpzb25FeHBvcnRzOiBzdGF0cy5leHBvcnRzRnJvbU9wdGlvbnMsXHJcbiAgICAgICAgc3ZnRXhwb3J0c0F0dGVtcHRzOiBzdGF0cy5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzLFxyXG4gICAgICAgIGpzb25FeHBvcnRzQXR0ZW1wdHM6IHN0YXRzLmV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzXHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHNlcnZpbmcgdGhlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlclxyXG4gKiB3aGVuIGVuYWJsZWQuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgdWlgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIHVpUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdWlSb3V0ZXMoYXBwKSB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgR0VUICcvJyAtIEEgcm91dGUgZm9yIGEgVUkgd2hlbiBlbmFibGVkIG9uIHRoZSBleHBvcnQgc2VydmVyLlxyXG4gICAqL1xyXG4gIGFwcC5nZXQoZ2V0T3B0aW9ucygpLnVpLnJvdXRlIHx8ICcvJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1t1aV0gUmV0dXJuaW5nIFVJIGZvciB0aGUgZXhwb3J0LicpO1xyXG5cclxuICAgICAgcmVzcG9uc2Uuc2VuZEZpbGUoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnLCAnaW5kZXguaHRtbCcpLCB7XHJcbiAgICAgICAgYWNjZXB0UmFuZ2VzOiBmYWxzZVxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciB1cGRhdGluZyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uXHJcbiAqIG9uIHRoZSBzZXJ2ZXIsIHdpdGggYXV0aGVudGljYXRpb24gYW5kIHZhbGlkYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24sIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIH0gZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi4vLi4vZW52cy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgdmVyc2lvbl9jaGFuZ2VgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIHZlcnNpb25DaGFuZ2VSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicgLSBBIHJvdXRlIGZvciBjaGFuZ2luZ1xyXG4gICAqIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gb24gdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnL3ZlcnNpb25fY2hhbmdlLzpuZXdWZXJzaW9uJywgYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1t2ZXJzaW9uXSBDaGFuZ2luZyBIaWdoY2hhcnRzIHZlcnNpb24uJyk7XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIHRva2VuIGRpcmVjdGx5IGZyb20gZW52c1xyXG4gICAgICBjb25zdCBhZG1pblRva2VuID0gZW52cy5ISUdIQ0hBUlRTX0FETUlOX1RPS0VOO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGV4aXN0ZW5jZSBvZiB0aGUgdG9rZW5cclxuICAgICAgaWYgKCFhZG1pblRva2VuIHx8ICFhZG1pblRva2VuLmxlbmd0aCkge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbdmVyc2lvbl0gVGhlIHNlcnZlciBpcyBub3QgY29uZmlndXJlZCB0byBwZXJmb3JtIHJ1bi10aW1lIHZlcnNpb24gY2hhbmdlczogYEhJR0hDSEFSVFNfQURNSU5fVE9LRU5gIGlzIG5vdCBzZXQuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZnJvbSB0aGUgaGMtYXV0aCBoZWFkZXJcclxuICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXHJcbiAgICAgIGlmICghdG9rZW4gfHwgdG9rZW4gIT09IGFkbWluVG9rZW4pIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIEludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgbmV3IHZlcnNpb24gZnJvbSB0aGUgcGFyYW1zXHJcbiAgICAgIGNvbnN0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG5cclxuICAgICAgLy8gVXBkYXRlIHZlcnNpb25cclxuICAgICAgaWYgKG5ld1ZlcnNpb24pIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgYXdhaXQgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbik7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgYFt2ZXJzaW9uXSBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgIDQwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbdmVyc2lvbl0gTm8gbmV3IHZlcnNpb24gc3VwcGxpZWQuJywgNDAwKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSB0aGF0IHNldHMgdXAgYW5kIG1hbmFnZXMgSFRUUCBhbmQgSFRUUFMgc2VydmVyc1xyXG4gKiBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci4gSXQgaGFuZGxlcyBzZXJ2ZXIgaW5pdGlhbGl6YXRpb24sXHJcbiAqIGNvbmZpZ3VyYXRpb24sIGVycm9yIGhhbmRsaW5nLCBtaWRkbGV3YXJlIHNldHVwLCByb3V0ZSBkZWZpbml0aW9uLCBhbmQgcmF0ZVxyXG4gKiBsaW1pdGluZy4gVGhlIG1vZHVsZSBleHBvcnRzIGZ1bmN0aW9ucyB0byBzdGFydCwgc3RvcCwgYW5kIG1hbmFnZSBzZXJ2ZXJcclxuICogaW5zdGFuY2VzLCBhcyB3ZWxsIGFzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBkZWZpbmluZyByb3V0ZXMgYW5kIGF0dGFjaGluZ1xyXG4gKiBtaWRkbGV3YXJlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBjb3JzIGZyb20gJ2NvcnMnO1xyXG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcbmltcG9ydCBtdWx0ZXIgZnJvbSAnbXVsdGVyJztcclxuXHJcbmltcG9ydCB7IHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IGVycm9yTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL2Vycm9yLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdGluZ01pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy9yYXRlTGltaXRpbmcuanMnO1xyXG5pbXBvcnQgdmFsaWRhdGlvbk1pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzJztcclxuXHJcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcclxuaW1wb3J0IGhlYWx0aFJvdXRlcyBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xyXG5pbXBvcnQgdmVyc2lvbkNoYW5nZVJvdXRlcyBmcm9tICcuL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcclxuY29uc3QgYWN0aXZlU2VydmVycyA9IG5ldyBNYXAoKTtcclxuXHJcbi8vIENyZWF0ZSBleHByZXNzIGFwcFxyXG5jb25zdCBhcHAgPSBleHByZXNzKCk7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIEhUVFAgYW5kL29yIEhUVFBTIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi5cclxuICogVGhlIGBzZXJ2ZXJPcHRpb25zYCBvYmplY3QgY29udGFpbnMgc2VydmVyLXJlbGF0ZWQgcHJvcGVydGllcyAocmVmZXJcclxuICogdG8gdGhlIGBzZXJ2ZXJgIHNlY3Rpb24gaW4gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgZGV0YWlscykuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRTZXJ2ZXJcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlck9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIHRoZSBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBkZWZhdWx0XHJcbiAqIHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGVpdGhlclxyXG4gKiBub3QgZW5hYmxlZCBvciBubyB2YWxpZCBFeHByZXNzIGFwcCBpcyBmb3VuZCwgc2lnbmFsaW5nIHRoZSBlbmQgb2YgdGhlXHJcbiAqIGZ1bmN0aW9uJ3MgZXhlY3V0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIHNlcnZlciBjYW5ub3RcclxuICogYmUgY29uZmlndXJlZCBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFNlcnZlcihzZXJ2ZXJPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgc2VydmVyOiBzZXJ2ZXJPcHRpb25zXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBVc2UgdmFsaWRhdGVkIG9wdGlvbnNcclxuICAgIHNlcnZlck9wdGlvbnMgPSBvcHRpb25zLnNlcnZlcjtcclxuXHJcbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXHJcbiAgICBpZiAoIXNlcnZlck9wdGlvbnMuZW5hYmxlIHx8ICFhcHApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbc2VydmVyXSBTZXJ2ZXIgY2Fubm90IGJlIHN0YXJ0ZWQgKG5vdCBlbmFibGVkIG9yIG5vIGNvcnJlY3QgRXhwcmVzcyBhcHAgZm91bmQpLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVG9vIGJpZyBsaW1pdHMgbGVhZCB0byB0aW1lb3V0cyBpbiB0aGUgZXhwb3J0IHByb2Nlc3Mgd2hlblxyXG4gICAgLy8gdGhlIHJhc3Rlcml6YXRpb24gdGltZW91dCBpcyBzZXQgdG9vIGxvd1xyXG4gICAgY29uc3QgdXBsb2FkTGltaXRCeXRlcyA9IHNlcnZlck9wdGlvbnMudXBsb2FkTGltaXQgKiAxMDI0ICogMTAyNDtcclxuXHJcbiAgICAvLyBNZW1vcnkgc3RvcmFnZSBmb3IgbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHN0b3JhZ2UgPSBtdWx0ZXIubWVtb3J5U3RvcmFnZSgpO1xyXG5cclxuICAgIC8vIEVuYWJsZSBwYXJzaW5nIG9mIGZvcm0gZGF0YSAoZmlsZXMpIHdpdGggbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHVwbG9hZCA9IG11bHRlcih7XHJcbiAgICAgIHN0b3JhZ2UsXHJcbiAgICAgIGxpbWl0czoge1xyXG4gICAgICAgIGZpZWxkU2l6ZTogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXHJcbiAgICBhcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XHJcblxyXG4gICAgLy8gRW5hYmxlIENPUlMgc3VwcG9ydFxyXG4gICAgYXBwLnVzZShcclxuICAgICAgY29ycyh7XHJcbiAgICAgICAgbWV0aG9kczogWydQT1NUJywgJ0dFVCcsICdPUFRJT05TJ11cclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0dGluZyBhIGxvdCBvZiBgUmFuZ2VOb3RTYXRpc2ZpYWJsZUVycm9yYCBleGNlcHRpb25zIChldmVuIHRob3VnaCB0aGlzXHJcbiAgICAvLyBpcyBhIGRlcHJlY2F0ZWQgb3B0aW9ucywgbGV0J3MgdHJ5IHRvIHNldCBpdCB0byBmYWxzZSlcclxuICAgIGFwcC51c2UoKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgIHJlc3BvbnNlLnNldCgnQWNjZXB0LVJhbmdlcycsICdub25lJyk7XHJcbiAgICAgIG5leHQoKTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgSlNPTiBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLmpzb24oe1xyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgVVJMLWVuY29kZWQgZm9ybSBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLnVybGVuY29kZWQoe1xyXG4gICAgICAgIGV4dGVuZGVkOiB0cnVlLFxyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xyXG4gICAgYXBwLnVzZSh1cGxvYWQubm9uZSgpKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgc3RhdGljIGZvbGRlcidzIHJvdXRlXHJcbiAgICBhcHAudXNlKGV4cHJlc3Muc3RhdGljKGpvaW4oX19kaXJuYW1lLCAncHVibGljJykpKTtcclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5zc2wuZm9yY2UpIHtcclxuICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFApXHJcbiAgICAgIGNvbnN0IGh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApO1xyXG5cclxuICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIC8vIExpc3RlblxyXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnBvcnQsIHNlcnZlck9wdGlvbnMuaG9zdCwgKCkgPT4ge1xyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMucG9ydCwgaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnBvcnR9LmBcclxuICAgICAgICApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgICBpZiAoc2VydmVyT3B0aW9ucy5zc2wuZW5hYmxlKSB7XHJcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cclxuICAgICAgbGV0IGtleSwgY2VydDtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XHJcbiAgICAgICAga2V5ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmtleScpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wgY2VydGlmaWNhdGVcclxuICAgICAgICBjZXJ0ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmNydCcpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFVuYWJsZSB0byBsb2FkIGtleS9jZXJ0aWZpY2F0ZSBmcm9tIHRoZSAnJHtzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChrZXkgJiYgY2VydCkge1xyXG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcclxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xyXG5cclxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgICAgX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAvLyBMaXN0ZW5cclxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyT3B0aW9ucy5zc2wucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUFMgc2VydmVyXHJcbiAgICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAzLFxyXG4gICAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQUyBzZXJ2ZXIgb24gJHtzZXJ2ZXJPcHRpb25zLmhvc3R9OiR7c2VydmVyT3B0aW9ucy5zc2wucG9ydH0uYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgc2VydmVyT3B0aW9ucy5yYXRlTGltaXRpbmcpO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgdmFsaWRhdGlvbiBoYW5kbGVyXHJcbiAgICB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGV4cG9ydFJvdXRlcyhhcHApO1xyXG4gICAgaGVhbHRoUm91dGVzKGFwcCk7XHJcbiAgICB1aVJvdXRlcyhhcHApO1xyXG4gICAgdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgY2VudHJhbGl6ZWQgZXJyb3IgaGFuZGxlclxyXG4gICAgZXJyb3JNaWRkbGV3YXJlKGFwcCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tzZXJ2ZXJdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHN0YXJ0IHRoZSBzZXJ2ZXIuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbG9zZXMgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzKCkge1xyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBzZXJ2ZXJzIHdvcmtpbmdcclxuICBpZiAoYWN0aXZlU2VydmVycy5zaXplID4gMCkge1xyXG4gICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zaW5nIGFsbCBzZXJ2ZXJzLmApO1xyXG5cclxuICAgIC8vIENsb3NlIGVhY2ggb25lIG9mIHNlcnZlcnNcclxuICAgIGZvciAoY29uc3QgW3BvcnQsIHNlcnZlcl0gb2YgYWN0aXZlU2VydmVycykge1xyXG4gICAgICBzZXJ2ZXIuY2xvc2UoKCkgPT4ge1xyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuZGVsZXRlKHBvcnQpO1xyXG4gICAgICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2VkIHNlcnZlciBvbiBwb3J0OiAke3BvcnR9LmApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0U2VydmVyc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPE9iamVjdD59IFNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFNlcnZlcnMoKSB7XHJcbiAgcmV0dXJuIGFjdGl2ZVNlcnZlcnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRFeHByZXNzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRFeHByZXNzKCkge1xyXG4gIHJldHVybiBleHByZXNzO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEFwcFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RXhwcmVzc30gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEFwcCgpIHtcclxuICByZXR1cm4gYXBwO1xyXG59XHJcblxyXG4vKipcclxuICogRW5hYmxlIHJhdGUgbGltaXRpbmcgZm9yIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVSYXRlTGltaXRpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHJhdGVMaW1pdGluZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0XHJcbiAqIG9mIHRoZSBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXNcclxuICogd2lsbCBkZWZhdWx0IHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZyhyYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgIHNlcnZlcjoge1xyXG4gICAgICByYXRlTGltaXRpbmc6IHJhdGVMaW1pdGluZ09wdGlvbnNcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnNcclxuICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgb3B0aW9ucy5zZXJ2ZXIucmF0ZUxpbWl0aW5nT3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBcHBseSBtaWRkbGV3YXJlKHMpIHRvIGEgc3BlY2lmaWMgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIHVzZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLnVzZShwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIEdFVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5nZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBQT1NUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHBvc3RcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAucG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgdG8gdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KGh0dHAuU2VydmVyfGh0dHBzLlNlcnZlcil9IHNlcnZlciAtIFRoZSBIVFRQL0hUVFBTIHNlcnZlciBpbnN0YW5jZS5cclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKHNlcnZlcikge1xyXG4gIHNlcnZlci5vbignY2xpZW50RXJyb3InLCAoZXJyb3IsIHNvY2tldCkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAxLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtzZXJ2ZXJdIENsaWVudCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfSwgZGVzdHJveWluZyBzb2NrZXQuYFxyXG4gICAgKTtcclxuICAgIHNvY2tldC5kZXN0cm95KCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNlcnZlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Nvbm5lY3Rpb24nLCAoc29ja2V0KSA9PiB7XHJcbiAgICBzb2NrZXQub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNvY2tldCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzdGFydFNlcnZlcixcclxuICBjbG9zZVNlcnZlcnMsXHJcbiAgZ2V0U2VydmVycyxcclxuICBnZXRFeHByZXNzLFxyXG4gIGdldEFwcCxcclxuICBlbmFibGVSYXRlTGltaXRpbmcsXHJcbiAgdXNlLFxyXG4gIGdldCxcclxuICBwb3N0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBIYW5kbGVzIGdyYWNlZnVsIHNodXRkb3duIG9mIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIsIGVuc3VyaW5nXHJcbiAqIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlcyBzdWNoIGFzIGJyb3dzZXIsIHBhZ2VzLCBzZXJ2ZXJzLCBhbmQgdGltZXJzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgY2xlYXJBbGxUaW1lcnMgfSBmcm9tICcuL3RpbWVyLmpzJztcclxuXHJcbmltcG9ydCB7IGNsb3NlU2VydmVycyB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogUGVyZm9ybXMgY2xlYW51cCBvcGVyYXRpb25zIHRvIGVuc3VyZSBhIGdyYWNlZnVsIHNodXRkb3duIG9mIHRoZSBwcm9jZXNzLlxyXG4gKiBUaGlzIGluY2x1ZGVzIGNsZWFyaW5nIGFsbCByZWdpc3RlcmVkIHRpbWVvdXRzL2ludGVydmFscywgY2xvc2luZyBhY3RpdmVcclxuICogc2VydmVycywgdGVybWluYXRpbmcgcmVzb3VyY2VzIChwYWdlcykgb2YgdGhlIHBvb2wsIHBvb2wgaXRzZWxmLCBhbmQgY2xvc2luZ1xyXG4gKiB0aGUgYnJvd3Nlci5cclxuICpcclxuICogQGZ1bmN0aW9uIHNodXRkb3duQ2xlYW5VcFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2V4aXRDb2RlPTBdIC0gVGhlIGV4aXQgY29kZSB0byB1c2Ugd2l0aCBgcHJvY2Vzcy5leGl0KClgLlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgMGAuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwKGV4aXRDb2RlID0gMCkge1xyXG4gIC8vIEF3YWl0IGZyZWVpbmcgYWxsIHJlc291cmNlc1xyXG4gIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChbXHJcbiAgICAvLyBDbGVhciBhbGwgb25nb2luZyBpbnRlcnZhbHNcclxuICAgIGNsZWFyQWxsVGltZXJzKCksXHJcblxyXG4gICAgLy8gR2V0IGF2YWlsYWJsZSBzZXJ2ZXIgaW5zdGFuY2VzIChIVFRQL0hUVFBTKSBhbmQgY2xvc2UgdGhlbVxyXG4gICAgY2xvc2VTZXJ2ZXJzKCksXHJcblxyXG4gICAgLy8gQ2xvc2UgYW4gYWN0aXZlIHBvb2wgYWxvbmcgd2l0aCBpdHMgd29ya2VycyBhbmQgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICAgIGtpbGxQb29sKClcclxuICBdKTtcclxuXHJcbiAgLy8gRXhpdCBwcm9jZXNzIHdpdGggYSBjb3JyZWN0IGNvZGVcclxuICBwcm9jZXNzLmV4aXQoZXhpdENvZGUpO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2h1dGRvd25DbGVhblVwXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBDb3JlIG1vZHVsZSBmb3IgaW5pdGlhbGl6aW5nIGFuZCBtYW5hZ2luZyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnRcclxuICogU2VydmVyLiBQcm92aWRlcyBmdW5jdGlvbmFsaXRpZXMgZm9yIGNvbmZpZ3VyaW5nIGV4cG9ydHMsIHNldHRpbmcgdXAgc2VydmVyXHJcbiAqIG9wZXJhdGlvbnMsIGxvZ2dpbmcsIHNjcmlwdHMgY2FjaGluZywgcmVzb3VyY2UgcG9vbGluZywgYW5kIGdyYWNlZnVsIHByb2Nlc3NcclxuICogY2xlYW51cC5cclxuICovXHJcblxyXG5pbXBvcnQgJ2NvbG9ycyc7XHJcblxyXG5pbXBvcnQgeyBjaGVja0FuZFVwZGF0ZUNhY2hlIH0gZnJvbSAnLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7XHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59IGZyb20gJy4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCB1cGRhdGVPcHRpb25zLCBtYXBUb05ld09wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbFxyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2h1dGRvd25DbGVhblVwIH0gZnJvbSAnLi9yZXNvdXJjZVJlbGVhc2UuanMnO1xyXG5cclxuaW1wb3J0IHNlcnZlciBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiB0aGUgY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHJlc291cmNlIHBvb2wgb2NjdXIgZHVyaW5nIHRoaXNcclxuICogc3RhZ2UuXHJcbiAqXHJcbiAqIFRoaXMgZnVuY3Rpb24gbXVzdCBiZSBjYWxsZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXRcclxuICogdXAgYSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW5pdE9wdGlvbnMgLSBUaGUgYGluaXRPcHRpb25zYCBvYmplY3QsIHdoaWNoIG1heVxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nXHJcbiAqIHZhbHVlcyB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdEV4cG9ydChpbml0T3B0aW9ucykge1xyXG4gIC8vIEluaXQgYW5kIHVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyhpbml0T3B0aW9ucyk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcblxyXG4gIC8vIEluaXQgdGhlIHBvb2xcclxuICBhd2FpdCBpbml0UG9vbChvcHRpb25zLnBvb2wsIG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoZXMgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MsIGVuc3VyaW5nIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlc1xyXG4gKiBhbmQgdGVybWluYXRpb24gb24gZXhpdCBzaWduYWxzLiBIYW5kbGVzICdleGl0JywgJ1NJR0lOVCcsICdTSUdURVJNJ1xyXG4gKiBhbmQgJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnNcclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LmApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0lOVCdcclxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHVEVSTSdcclxuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0hVUCdcclxuICBwcm9jZXNzLm9uKCdTSUdIVVAnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAndW5jYXVnaHRFeGNlcHRpb24nXHJcbiAgcHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCBhc3luYyAoZXJyb3IsIG5hbWUpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIC4uLnNlcnZlcixcclxuXHJcbiAgLy8gT3B0aW9uc1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgdXBkYXRlT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcblxyXG4gIC8vIEV4cG9ydGluZ1xyXG4gIGluaXRFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG5cclxuICAvLyBSZWxlYXNlXHJcbiAga2lsbFBvb2wsXHJcbiAgc2h1dGRvd25DbGVhblVwLFxyXG5cclxuICAvLyBMb2dzXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbDogZnVuY3Rpb24gKGxldmVsKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICBsZXZlbFxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgc2V0TG9nTGV2ZWwob3B0aW9ucy5sb2dnaW5nLmxldmVsKTtcclxuICB9LFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nOiBmdW5jdGlvbiAodG9Db25zb2xlKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICB0b0NvbnNvbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUNvbnNvbGVMb2dnaW5nKG9wdGlvbnMubG9nZ2luZy50b0NvbnNvbGUpO1xyXG4gIH0sXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmc6IGZ1bmN0aW9uIChkZXN0LCBmaWxlLCB0b0ZpbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGRlc3QsXHJcbiAgICAgICAgZmlsZSxcclxuICAgICAgICB0b0ZpbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUZpbGVMb2dnaW5nKFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZGVzdCxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLmZpbGUsXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy50b0ZpbGVcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG4iXSwibmFtZXMiOlsiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJiYXNlVVJJIiwiZGVlcENvcHkiLCJvYmpBcnIiLCJvYmpBcnJDb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwiT2JqZWN0IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiZml4Q29uc3RyIiwiY29uc3RyIiwiZml4ZWRDb25zdHIiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJpbmNsdWRlcyIsImZpeE91dGZpbGUiLCJ0eXBlIiwib3V0ZmlsZSIsImdldEFic29sdXRlUGF0aCIsInNwbGl0Iiwic2hpZnQiLCJmaXhUeXBlIiwibWltZVR5cGVzIiwiZm9ybWF0cyIsInZhbHVlcyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsInBhdGgiLCJpc0Fic29sdXRlIiwibm9ybWFsaXplIiwicmVzb2x2ZSIsImdldEJhc2U2NCIsImlucHV0IiwiQnVmZmVyIiwiZnJvbSIsInRvU3RyaW5nIiwiZ2V0TmV3RGF0ZSIsIkRhdGUiLCJ0cmltIiwiZ2V0TmV3RGF0ZVRpbWUiLCJnZXRUaW1lIiwiaXNPYmplY3QiLCJpdGVtIiwiaXNPYmplY3RFbXB0eSIsImtleXMiLCJsZW5ndGgiLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwic29tZSIsInBhdHRlcm4iLCJ0ZXN0IiwibWVhc3VyZVRpbWUiLCJzdGFydCIsInByb2Nlc3MiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJyb3VuZE51bWJlciIsInZhbHVlIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsIk1hdGgiLCJwb3ciLCJyb3VuZCIsIndyYXBBcm91bmQiLCJjdXN0b21Db2RlIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiaXNDYWxsYmFjayIsImVuZHNXaXRoIiwicmVhZEZpbGVTeW5jIiwic3RhcnRzV2l0aCIsImNvbG9ycyIsImxvZ2dpbmciLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsInBhdGhUb0xvZyIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibG9nIiwiYXJncyIsIm5ld0xldmVsIiwidGV4dHMiLCJsZXZlbCIsInByZWZpeCIsIl9sb2dUb0ZpbGUiLCJjb25zb2xlIiwiYXBwbHkiLCJ1bmRlZmluZWQiLCJjb25jYXQiLCJsb2dXaXRoU3RhY2siLCJlcnJvciIsImN1c3RvbU1lc3NhZ2UiLCJtYWluTWVzc2FnZSIsIm1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInB1c2giLCJpbml0TG9nZ2luZyIsImxvZ2dpbmdPcHRpb25zIiwiZGVzdCIsImZpbGUiLCJzZXRMb2dMZXZlbCIsImVuYWJsZUNvbnNvbGVMb2dnaW5nIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJpc0ludGVnZXIiLCJleGlzdHNTeW5jIiwibWtkaXJTeW5jIiwiam9pbiIsImFwcGVuZEZpbGUiLCJkZWZhdWx0Q29uZmlnIiwicHVwcGV0ZWVyIiwidHlwZXMiLCJlbnZMaW5rIiwiY2xpTmFtZSIsImRlc2NyaXB0aW9uIiwicHJvbXB0T3B0aW9ucyIsInNlcGFyYXRvciIsImhpZ2hjaGFydHMiLCJ2ZXJzaW9uIiwiY2RuVXJsIiwiZm9yY2VGZXRjaCIsImNhY2hlUGF0aCIsImNvcmVTY3JpcHRzIiwiaW5zdHJ1Y3Rpb25zIiwibW9kdWxlU2NyaXB0cyIsImluZGljYXRvclNjcmlwdHMiLCJjdXN0b21TY3JpcHRzIiwiZXhwb3J0IiwiaW5maWxlIiwiaW5zdHIiLCJvcHRpb25zIiwic3ZnIiwiYmF0Y2giLCJoaW50IiwiY2hvaWNlcyIsImI2NCIsIm5vRG93bmxvYWQiLCJoZWlnaHQiLCJ3aWR0aCIsInNjYWxlIiwiZGVmYXVsdEhlaWdodCIsImRlZmF1bHRXaWR0aCIsImRlZmF1bHRTY2FsZSIsIm1pbiIsIm1heCIsImdsb2JhbE9wdGlvbnMiLCJ0aGVtZU9wdGlvbnMiLCJyYXN0ZXJpemF0aW9uVGltZW91dCIsImN1c3RvbUxvZ2ljIiwiYWxsb3dDb2RlRXhlY3V0aW9uIiwiY2FsbGJhY2siLCJyZXNvdXJjZXMiLCJsb2FkQ29uZmlnIiwibGVnYWN5TmFtZSIsImNyZWF0ZUNvbmZpZyIsInNlcnZlciIsImVuYWJsZSIsImhvc3QiLCJwb3J0IiwidXBsb2FkTGltaXQiLCJiZW5jaG1hcmtpbmciLCJwcm94eSIsInRpbWVvdXQiLCJyYXRlTGltaXRpbmciLCJtYXhSZXF1ZXN0cyIsIndpbmRvdyIsImRlbGF5IiwidHJ1c3RQcm94eSIsInNraXBLZXkiLCJza2lwVG9rZW4iLCJzc2wiLCJmb3JjZSIsImNlcnRQYXRoIiwicG9vbCIsIm1pbldvcmtlcnMiLCJtYXhXb3JrZXJzIiwid29ya0xpbWl0IiwiYWNxdWlyZVRpbWVvdXQiLCJjcmVhdGVUaW1lb3V0IiwiZGVzdHJveVRpbWVvdXQiLCJpZGxlVGltZW91dCIsImNyZWF0ZVJldHJ5SW50ZXJ2YWwiLCJyZWFwZXJJbnRlcnZhbCIsInVpIiwicm91dGUiLCJvdGhlciIsIm5vZGVFbnYiLCJsaXN0ZW5Ub1Byb2Nlc3NFeGl0cyIsIm5vTG9nbyIsImhhcmRSZXNldFBhZ2UiLCJicm93c2VyU2hlbGxNb2RlIiwiZGVidWciLCJoZWFkbGVzcyIsImRldnRvb2xzIiwibGlzdGVuVG9Db25zb2xlIiwiZHVtcGlvIiwic2xvd01vIiwiZGVidWdnaW5nUG9ydCIsIm5lc3RlZFByb3BzIiwiX2NyZWF0ZU5lc3RlZFByb3BzIiwiYWJzb2x1dGVQcm9wcyIsIl9jcmVhdGVBYnNvbHV0ZVByb3BzIiwiY29uZmlnIiwicHJvcENoYWluIiwiZm9yRWFjaCIsImVudHJ5Iiwic3Vic3RyaW5nIiwiZG90ZW52IiwidiIsImFycmF5IiwiZmlsdGVyQXJyYXkiLCJ6Iiwic3RyaW5nIiwidHJhbnNmb3JtIiwibWFwIiwiZmlsdGVyIiwiYm9vbGVhbiIsImVudW0iLCJyZWZpbmUiLCJwb3NpdGl2ZU51bSIsImlzTmFOIiwicGFyc2VGbG9hdCIsIm5vbk5lZ2F0aXZlTnVtIiwiQ29uZmlnIiwib2JqZWN0IiwiUFVQUEVURUVSX0FSR1MiLCJISUdIQ0hBUlRTX1ZFUlNJT04iLCJISUdIQ0hBUlRTX0NETl9VUkwiLCJISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIIiwiSElHSENIQVJUU19DQUNIRV9QQVRIIiwiSElHSENIQVJUU19BRE1JTl9UT0tFTiIsIkhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTIiwiSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUyIsIkhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFMiLCJISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTIiwiRVhQT1JUX0lORklMRSIsIkVYUE9SVF9JTlNUUiIsIkVYUE9SVF9PUFRJT05TIiwiRVhQT1JUX1NWRyIsIkVYUE9SVF9CQVRDSCIsIkVYUE9SVF9PVVRGSUxFIiwiRVhQT1JUX1RZUEUiLCJFWFBPUlRfQ09OU1RSIiwiRVhQT1JUX0I2NCIsIkVYUE9SVF9OT19ET1dOTE9BRCIsIkVYUE9SVF9IRUlHSFQiLCJFWFBPUlRfV0lEVEgiLCJFWFBPUlRfU0NBTEUiLCJFWFBPUlRfREVGQVVMVF9IRUlHSFQiLCJFWFBPUlRfREVGQVVMVF9XSURUSCIsIkVYUE9SVF9ERUZBVUxUX1NDQUxFIiwiRVhQT1JUX0dMT0JBTF9PUFRJT05TIiwiRVhQT1JUX1RIRU1FX09QVElPTlMiLCJFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0NVU1RPTV9DT0RFIiwiQ1VTVE9NX0xPR0lDX0NBTExCQUNLIiwiQ1VTVE9NX0xPR0lDX1JFU09VUkNFUyIsIkNVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRyIsIkNVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHIiwiU0VSVkVSX0VOQUJMRSIsIlNFUlZFUl9IT1NUIiwiU0VSVkVSX1BPUlQiLCJTRVJWRVJfVVBMT0FEX0xJTUlUIiwiU0VSVkVSX0JFTkNITUFSS0lORyIsIlNFUlZFUl9QUk9YWV9IT1NUIiwiU0VSVkVSX1BST1hZX1BPUlQiLCJTRVJWRVJfUFJPWFlfVElNRU9VVCIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4iLCJTRVJWRVJfU1NMX0VOQUJMRSIsIlNFUlZFUl9TU0xfRk9SQ0UiLCJTRVJWRVJfU1NMX1BPUlQiLCJTRVJWRVJfU1NMX0NFUlRfUEFUSCIsIlBPT0xfTUlOX1dPUktFUlMiLCJQT09MX01BWF9XT1JLRVJTIiwiUE9PTF9XT1JLX0xJTUlUIiwiUE9PTF9BQ1FVSVJFX1RJTUVPVVQiLCJQT09MX0NSRUFURV9USU1FT1VUIiwiUE9PTF9ERVNUUk9ZX1RJTUVPVVQiLCJQT09MX0lETEVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1JFVFJZX0lOVEVSVkFMIiwiUE9PTF9SRUFQRVJfSU5URVJWQUwiLCJQT09MX0JFTkNITUFSS0lORyIsIkxPR0dJTkdfTEVWRUwiLCJMT0dHSU5HX0ZJTEUiLCJMT0dHSU5HX0RFU1QiLCJMT0dHSU5HX1RPX0NPTlNPTEUiLCJMT0dHSU5HX1RPX0ZJTEUiLCJVSV9FTkFCTEUiLCJVSV9ST1VURSIsIk9USEVSX05PREVfRU5WIiwiT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMiLCJPVEhFUl9OT19MT0dPIiwiT1RIRVJfSEFSRF9SRVNFVF9QQUdFIiwiT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFIiwiREVCVUdfRU5BQkxFIiwiREVCVUdfSEVBRExFU1MiLCJERUJVR19ERVZUT09MUyIsIkRFQlVHX0xJU1RFTl9UT19DT05TT0xFIiwiREVCVUdfRFVNUElPIiwiREVCVUdfU0xPV19NTyIsIkRFQlVHX0RFQlVHR0lOR19QT1JUIiwiZW52cyIsInBhcnRpYWwiLCJwYXJzZSIsImVudiIsIl9pbml0T3B0aW9ucyIsImdldE9wdGlvbnMiLCJnZXRDb3B5IiwidXBkYXRlT3B0aW9ucyIsIm5ld09wdGlvbnMiLCJfbWVyZ2VPcHRpb25zIiwibWFwVG9OZXdPcHRpb25zIiwib2xkT3B0aW9ucyIsImVudHJpZXMiLCJwcm9wZXJ0aWVzQ2hhaW4iLCJyZWR1Y2UiLCJvYmoiLCJwcm9wIiwiaW5kZXgiLCJpc0FsbG93ZWRDb25maWciLCJhbGxvd0Z1bmN0aW9ucyIsIm9iamVjdENvbmZpZyIsImV2YWwiLCJKU09OIiwic3RyaW5naWZpZWRPcHRpb25zIiwiX29wdGlvbnNTdHJpbmdpZnkiLCJwYXJzZWRPcHRpb25zIiwiXyIsIm5hbWUiLCJvcmlnaW5hbE9wdGlvbnMiLCJzdHJpbmdpZnlGdW5jdGlvbnMiLCJzdHJpbmdpZnkiLCJyZXBsYWNlQWxsIiwiRXJyb3IiLCJhc3luYyIsImZldGNoIiwidXJsIiwicmVxdWVzdE9wdGlvbnMiLCJQcm9taXNlIiwicmVqZWN0IiwiX2dldFByb3RvY29sTW9kdWxlIiwiZ2V0IiwicmVzcG9uc2UiLCJyZXNwb25zZURhdGEiLCJvbiIsImNodW5rIiwidGV4dCIsImh0dHBzIiwiaHR0cCIsIkV4cG9ydEVycm9yIiwiY29uc3RydWN0b3IiLCJzdGF0dXNDb2RlIiwic3VwZXIiLCJ0aGlzIiwic2V0U3RhdHVzIiwic2V0RXJyb3IiLCJjYWNoZSIsImFjdGl2ZU1hbmlmZXN0Iiwic291cmNlcyIsImhjVmVyc2lvbiIsImNoZWNrQW5kVXBkYXRlQ2FjaGUiLCJoaWdoY2hhcnRzT3B0aW9ucyIsInNlcnZlclByb3h5T3B0aW9ucyIsImZldGNoZWRNb2R1bGVzIiwiZ2V0Q2FjaGVQYXRoIiwibWFuaWZlc3RQYXRoIiwic291cmNlUGF0aCIsInJlY3Vyc2l2ZSIsIl91cGRhdGVDYWNoZSIsInJlcXVlc3RVcGRhdGUiLCJtYW5pZmVzdCIsIm1vZHVsZXMiLCJtb2R1bGVNYXAiLCJtIiwibnVtYmVyT2ZNb2R1bGVzIiwibW9kdWxlTmFtZSIsImV4dHJhY3RWZXJzaW9uIiwiX3NhdmVDb25maWdUb01hbmlmZXN0IiwiZ2V0SGlnaGNoYXJ0c1ZlcnNpb24iLCJ1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiIsIm5ld1ZlcnNpb24iLCJjYWNoZVNvdXJjZXMiLCJpbmRleE9mIiwiZXh0cmFjdE1vZHVsZU5hbWUiLCJzY3JpcHRQYXRoIiwiX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdCIsInNjcmlwdCIsInNob3VsZFRocm93RXJyb3IiLCJuZXdNYW5pZmVzdCIsIndyaXRlRmlsZVN5bmMiLCJfZmV0Y2hTY3JpcHRzIiwicHJveHlBZ2VudCIsInByb3h5SG9zdCIsInByb3h5UG9ydCIsIkh0dHBzUHJveHlBZ2VudCIsImFnZW50IiwiYWxsRmV0Y2hQcm9taXNlcyIsImFsbCIsImMiLCJpIiwic2V0dXBIaWdoY2hhcnRzIiwiSGlnaGNoYXJ0cyIsImFuaW1PYmplY3QiLCJkdXJhdGlvbiIsImNyZWF0ZUNoYXJ0IiwiZXhwb3J0T3B0aW9ucyIsImN1c3RvbUxvZ2ljT3B0aW9ucyIsInNldE9wdGlvbnMiLCJtZXJnZSIsIndyYXAiLCJzZXRPcHRpb25zT2JqIiwiaXNSZW5kZXJDb21wbGV0ZSIsIkNoYXJ0IiwicHJvY2VlZCIsInVzZXJPcHRpb25zIiwiY2IiLCJleHBvcnRpbmciLCJlbmFibGVkIiwicGxvdE9wdGlvbnMiLCJzZXJpZXMiLCJsYWJlbCIsInRvb2x0aXAiLCJhbmltYXRpb24iLCJvbkhpZ2hjaGFydHNSZW5kZXIiLCJhZGRFdmVudCIsIlNlcmllcyIsImNoYXJ0IiwiYWRkaXRpb25hbE9wdGlvbnMiLCJGdW5jdGlvbiIsImZpbmFsT3B0aW9ucyIsImZpbmFsQ2FsbGJhY2siLCJkZWZhdWx0T3B0aW9ucyIsInRlbXBsYXRlIiwiYnJvd3NlciIsImNyZWF0ZUJyb3dzZXIiLCJwdXBwZXRlZXJBcmdzIiwiZW5hYmxlZERlYnVnIiwiZGVidWdPcHRpb25zIiwibGF1bmNoT3B0aW9ucyIsInVzZXJEYXRhRGlyIiwiaGFuZGxlU0lHSU5UIiwiaGFuZGxlU0lHVEVSTSIsImhhbmRsZVNJR0hVUCIsIndhaXRGb3JJbml0aWFsUGFnZSIsImRlZmF1bHRWaWV3cG9ydCIsInRyeUNvdW50Iiwib3BlbiIsImxhdW5jaCIsInNldFRpbWVvdXQiLCJjbG9zZUJyb3dzZXIiLCJjb25uZWN0ZWQiLCJjbG9zZSIsIm5ld1BhZ2UiLCJwb29sUmVzb3VyY2UiLCJwYWdlIiwic2V0Q2FjaGVFbmFibGVkIiwiX3NldFBhZ2VDb250ZW50IiwiX3NldFBhZ2VFdmVudHMiLCJpc0Nsb3NlZCIsImNsZWFyUGFnZSIsImhhcmRSZXNldCIsImdvdG8iLCJ3YWl0VW50aWwiLCJldmFsdWF0ZSIsImJvZHkiLCJpbm5lckhUTUwiLCJpZCIsIndvcmtDb3VudCIsImFkZFBhZ2VSZXNvdXJjZXMiLCJpbmplY3RlZFJlc291cmNlcyIsImluamVjdGVkSnMiLCJqcyIsImNvbnRlbnQiLCJmaWxlcyIsImlzTG9jYWwiLCJqc1Jlc291cmNlIiwiYWRkU2NyaXB0VGFnIiwiaW5qZWN0ZWRDc3MiLCJjc3MiLCJjc3NJbXBvcnRzIiwibWF0Y2giLCJjc3NJbXBvcnRQYXRoIiwiY3NzUmVzb3VyY2UiLCJhZGRTdHlsZVRhZyIsImNsZWFyUGFnZVJlc291cmNlcyIsInJlc291cmNlIiwiZGlzcG9zZSIsIm9sZENoYXJ0cyIsImNoYXJ0cyIsIm9sZENoYXJ0IiwiZGVzdHJveSIsInNjcmlwdHNUb1JlbW92ZSIsImdldEVsZW1lbnRzQnlUYWdOYW1lIiwic3R5bGVzVG9SZW1vdmUiLCJsaW5rc1RvUmVtb3ZlIiwiZWxlbWVudCIsInJlbW92ZSIsInNldENvbnRlbnQiLCJjc3NUZW1wbGF0ZSIsInN2Z1RlbXBsYXRlIiwicHVwcGV0ZWVyRXhwb3J0IiwiaXNTVkciLCJzaXplIiwic3ZnRWxlbWVudCIsInF1ZXJ5U2VsZWN0b3IiLCJjaGFydEhlaWdodCIsImJhc2VWYWwiLCJjaGFydFdpZHRoIiwic3R5bGUiLCJ6b29tIiwibWFyZ2luIiwieCIsInkiLCJfZ2V0Q2xpcFJlZ2lvbiIsInZpZXdwb3J0SGVpZ2h0IiwiYWJzIiwiY2VpbCIsInZpZXdwb3J0V2lkdGgiLCJyZXN1bHQiLCJzZXRWaWV3cG9ydCIsImRldmljZVNjYWxlRmFjdG9yIiwiX2NyZWF0ZVNWRyIsIl9jcmVhdGVJbWFnZSIsIl9jcmVhdGVQREYiLCIkZXZhbCIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsInRydW5jIiwib3V0ZXJIVE1MIiwiY2xpcCIsInJhY2UiLCJzY3JlZW5zaG90IiwiZW5jb2RpbmciLCJmdWxsUGFnZSIsIm9wdGltaXplRm9yU3BlZWQiLCJjYXB0dXJlQmV5b25kVmlld3BvcnQiLCJxdWFsaXR5Iiwib21pdEJhY2tncm91bmQiLCJfcmVzb2x2ZSIsImVtdWxhdGVNZWRpYVR5cGUiLCJwZGYiLCJwb29sU3RhdHMiLCJleHBvcnRzQXR0ZW1wdGVkIiwiZXhwb3J0c1BlcmZvcm1lZCIsImV4cG9ydHNEcm9wcGVkIiwiZXhwb3J0c0Zyb21TdmciLCJleHBvcnRzRnJvbU9wdGlvbnMiLCJleHBvcnRzRnJvbVN2Z0F0dGVtcHRzIiwiZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHMiLCJ0aW1lU3BlbnQiLCJ0aW1lU3BlbnRBdmVyYWdlIiwiaW5pdFBvb2wiLCJwb29sT3B0aW9ucyIsIlBvb2wiLCJfZmFjdG9yeSIsImFjcXVpcmVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlVGltZW91dE1pbGxpcyIsImRlc3Ryb3lUaW1lb3V0TWlsbGlzIiwiaWRsZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzIiwicmVhcEludGVydmFsTWlsbGlzIiwicHJvcGFnYXRlQ3JlYXRlRXJyb3IiLCJjbGVhclN0YXR1cyIsIl9ldmVudElkIiwiaW5pdGlhbFJlc291cmNlcyIsImFjcXVpcmUiLCJwcm9taXNlIiwicmVsZWFzZSIsImtpbGxQb29sIiwid29ya2VyIiwidXNlZCIsImRlc3Ryb3llZCIsInBvc3RXb3JrIiwid29ya2VySGFuZGxlIiwiZ2V0UG9vbEluZm8iLCJhY3F1aXJlQ291bnRlciIsInJlcXVlc3RJZCIsIndvcmtTdGFydCIsImV4cG9ydENvdW50ZXIiLCJleHBvcnRUaW1lIiwiZ2V0UG9vbFN0YXRzIiwiZ2V0UG9vbEluZm9KU09OIiwibnVtVXNlZCIsImF2YWlsYWJsZSIsIm51bUZyZWUiLCJhbGxDcmVhdGVkIiwicGVuZGluZ0FjcXVpcmVzIiwibnVtUGVuZGluZ0FjcXVpcmVzIiwicGVuZGluZ0NyZWF0ZXMiLCJudW1QZW5kaW5nQ3JlYXRlcyIsInBlbmRpbmdWYWxpZGF0aW9ucyIsIm51bVBlbmRpbmdWYWxpZGF0aW9ucyIsInBlbmRpbmdEZXN0cm95cyIsImFic29sdXRlQWxsIiwiY3JlYXRlIiwidXVpZCIsInJhbmRvbSIsInN0YXJ0RGF0ZSIsInZhbGlkYXRlIiwibWFpbkZyYW1lIiwiZGV0YWNoZWQiLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJzYW5pdGl6ZSIsIkpTRE9NIiwiRE9NUHVyaWZ5IiwiQUREX1RBR1MiLCJzaW5nbGVFeHBvcnQiLCJzdGFydEV4cG9ydCIsImRhdGEiLCJiYXRjaEV4cG9ydCIsImJhdGNoRnVuY3Rpb25zIiwicGFpciIsImJhdGNoUmVzdWx0cyIsImFsbFNldHRsZWQiLCJyZWFzb24iLCJpbWFnZU9wdGlvbnMiLCJlbmRDYWxsYmFjayIsImZpbGVDb250ZW50IiwiX2V4cG9ydEZyb21TdmciLCJfZXhwb3J0RnJvbU9wdGlvbnMiLCJnZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJzZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJpbnB1dFRvRXhwb3J0IiwiX3ByZXBhcmVFeHBvcnQiLCJfaGFuZGxlQ3VzdG9tTG9naWMiLCJfaGFuZGxlR2xvYmFsQW5kVGhlbWUiLCJfZmluZENoYXJ0U2l6ZSIsIl9jaGVja0RhdGFTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidG90YWxTaXplIiwiYnl0ZUxlbmd0aCIsInRvRml4ZWQiLCJ0aW1lcklkcyIsImFkZFRpbWVyIiwiY2xlYXJBbGxUaW1lcnMiLCJjbGVhckludGVydmFsIiwiY2xlYXJUaW1lb3V0IiwibG9nRXJyb3JNaWRkbGV3YXJlIiwicmVxdWVzdCIsIm5leHQiLCJyZXR1cm5FcnJvck1pZGRsZXdhcmUiLCJzdGF0dXMiLCJqc29uIiwiZXJyb3JNaWRkbGV3YXJlIiwiYXBwIiwidXNlIiwicmF0ZUxpbWl0aW5nTWlkZGxld2FyZSIsInJhdGVMaW1pdGluZ09wdGlvbnMiLCJyYXRlT3B0aW9ucyIsImxpbWl0ZXIiLCJyYXRlTGltaXQiLCJ3aW5kb3dNcyIsImxpbWl0IiwiZGVsYXlNcyIsImhhbmRsZXIiLCJmb3JtYXQiLCJzZW5kIiwiZGVmYXVsdCIsInNraXAiLCJxdWVyeSIsImFjY2Vzc190b2tlbiIsImNvbnRlbnRUeXBlTWlkZGxld2FyZSIsImNvbnRlbnRUeXBlIiwiaGVhZGVycyIsInJlcXVlc3RCb2R5TWlkZGxld2FyZSIsImNvbm5lY3Rpb24iLCJyZW1vdGVBZGRyZXNzIiwidmFsaWRhdGVkT3B0aW9ucyIsInBhcmFtcyIsImZpbGVuYW1lIiwidmFsaWRhdGlvbk1pZGRsZXdhcmUiLCJwb3N0IiwicmV2ZXJzZWRNaW1lIiwicG5nIiwianBlZyIsImdpZiIsInJlcXVlc3RFeHBvcnQiLCJyZXF1ZXN0Q291bnRlciIsImNvbm5lY3Rpb25BYm9ydGVkIiwic29ja2V0IiwiaGFkRXJyb3JzIiwiaGVhZGVyIiwiYXR0YWNobWVudCIsImV4cG9ydFJvdXRlcyIsInNlcnZlclN0YXJ0VGltZSIsInBhY2thZ2VGaWxlIiwic3VjY2Vzc1JhdGVzIiwicmVjb3JkSW50ZXJ2YWwiLCJ3aW5kb3dTaXplIiwiX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2UiLCJhIiwiYiIsIl9zdGFydFN1Y2Nlc3NSYXRlIiwic2V0SW50ZXJ2YWwiLCJzdGF0cyIsInN1Y2Nlc3NSYXRpbyIsImhlYWx0aFJvdXRlcyIsInBlcmlvZCIsIm1vdmluZ0F2ZXJhZ2UiLCJib290VGltZSIsInVwdGltZSIsImZsb29yIiwic2VydmVyVmVyc2lvbiIsImhpZ2hjaGFydHNWZXJzaW9uIiwiYXZlcmFnZUV4cG9ydFRpbWUiLCJhdHRlbXB0ZWRFeHBvcnRzIiwicGVyZm9ybWVkRXhwb3J0cyIsImZhaWxlZEV4cG9ydHMiLCJzdWNlc3NSYXRpbyIsInN2Z0V4cG9ydHMiLCJqc29uRXhwb3J0cyIsInN2Z0V4cG9ydHNBdHRlbXB0cyIsImpzb25FeHBvcnRzQXR0ZW1wdHMiLCJ1aVJvdXRlcyIsInNlbmRGaWxlIiwiYWNjZXB0UmFuZ2VzIiwidmVyc2lvbkNoYW5nZVJvdXRlcyIsImFkbWluVG9rZW4iLCJ0b2tlbiIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJPcHRpb25zIiwidXBsb2FkTGltaXRCeXRlcyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRTaXplIiwiZGlzYWJsZSIsImNvcnMiLCJtZXRob2RzIiwic2V0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsInN0YXRpYyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyIsImxpc3RlbiIsImNlcnQiLCJodHRwc1NlcnZlciIsImNsb3NlU2VydmVycyIsImRlbGV0ZSIsImdldFNlcnZlcnMiLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwiZW5hYmxlUmF0ZUxpbWl0aW5nIiwibWlkZGxld2FyZXMiLCJzaHV0ZG93bkNsZWFuVXAiLCJleGl0Q29kZSIsImV4aXQiLCJpbml0RXhwb3J0IiwiaW5pdE9wdGlvbnMiLCJfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMiLCJjb2RlIl0sIm1hcHBpbmdzIjoid2xCQTJCTyxNQUFNQSxZQUFZQyxJQUFhQSxjQUFDLElBQUlDLElBQUksT0FBUSxvQkFBQUMsU0FBQUMsUUFBQSxPQUFBQyxjQUFBQyxZQUFBQyxLQUFBQyx3QkFBQSxXQUFBQSx1QkFBQUMsUUFBQUMsZUFBQUYsdUJBQUFHLEtBQUEsSUFBQVQsSUFBQSxZQUFBQyxTQUFBUyxTQUFBTCxPQStCaEQsU0FBU00sU0FBU0MsR0FFdkIsR0FBZSxPQUFYQSxHQUFxQyxpQkFBWEEsRUFDNUIsT0FBT0EsRUFJVCxNQUFNQyxFQUFhQyxNQUFNQyxRQUFRSCxHQUFVLEdBQUssR0FHaEQsSUFBSyxNQUFNSSxLQUFPSixFQUNaSyxPQUFPQyxVQUFVQyxlQUFlQyxLQUFLUixFQUFRSSxLQUMvQ0gsRUFBV0csR0FBT0wsU0FBU0MsRUFBT0ksS0FLdEMsT0FBT0gsQ0FDVCxDQTJETyxTQUFTUSxVQUFVQyxHQUN4QixJQUVFLE1BQU1DLEVBQWMs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 diff --git a/dist/index.esm.js b/dist/index.esm.js index b730bda3..c42623e5 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -1,2 +1,2 @@ -import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,normalize,resolve,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?normalize(e):resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.instr=null,t.export.options=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=e.options||isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`Request [${r}] - [validation] 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`Request [${r}] - [validation] 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);let n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; +import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,normalize,resolve,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?normalize(e):resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.options=null,t.export.instr=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.options=null,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},_checkDataSize({export:t,customLogic:o}),postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}function _checkDataSize(e){const t=Buffer.byteLength(JSON.stringify(e),"utf-8");if(log(3,`[chart] The current total size of the data for the export process is around ${(t/1048576).toFixed(2)}MB.`),t>=104857600)throw new ExportError("[chart] The data for the export process exceeds 100MB limit.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`[validation] Request [${r}] - 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`[validation] Request [${r}] - 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: `HIGHCHARTS_ADMIN_TOKEN` is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; //# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index 53e3cbbe..b8893d8d 100644 --- a/dist/index.esm.js.map +++ b/dist/index.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, normalize, resolve } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? normalize(path) : resolve(path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getCopy=true] - Specifies whether to return a copied\r\n * object of the global options (`true`) or a reference to the global options\r\n * object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} A copy of the global options object, or a reference\r\n * to the global options object.\r\n */\r\nexport function getOptions(getCopy = true) {\r\n return getCopy ? deepCopy(globalOptions) : globalOptions;\r\n}\r\n\r\n/**\r\n * Updates a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(getOptions(getCopy), newOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return reference to the global options\r\n return getOptions(false);\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Get the allow flags for the custom logic check\r\n const { allowCodeExecution, allowFileResources } = getOptions().customLogic;\r\n\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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') ? false : true;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n path: getAbsolutePath(cssImportPath)\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `imageOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} imageOptions - The `imageOptions` object, which should\r\n * include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(imageOptions, endCallback) {\r\n try {\r\n // Check if provided options are in an object\r\n if (!isObject(imageOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: imageOptions.export,\r\n customLogic: imageOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.instr = null;\r\n options.export.options = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n exportOptions.options || isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `Request [${requestId}] - [validation] 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const requestOptions = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = requestOptions.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(requestOptions, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] 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 let newVersion = request.params.newVersion;\r\n if (newVersion) {\r\n try {\r\n // Update version\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 = readFileSync(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = readFileSync(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","normalize","resolve","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","join","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","getCopy","updateOptions","newOptions","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","imageOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","toFixed","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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KA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMmB,cAAgB,CAC3BC,UAAW,CACT/B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFuD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbtG,KAAM,OACNuG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP9D,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbtG,KAAM,SAGV0G,OAAQ,CACN/D,MAAO,8BACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbtG,KAAM,SAGV2G,WAAY,CACVhE,OAAO,EACPuD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbtG,KAAM,WAGV4G,UAAW,CACTjE,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbtG,KAAM,SAGV6G,YAAa,CACXlE,MAAO,CAAC,aAAc,kBAAmB,iBACzCuD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbtG,KAAM,cACN8G,aAAc,0DAGlBC,cAAe,CACbpE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,k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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, normalize, resolve } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? normalize(path) : resolve(path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getCopy=true] - Specifies whether to return a copied\r\n * object of the global options (`true`) or a reference to the global options\r\n * object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} A copy of the global options object, or a reference\r\n * to the global options object.\r\n */\r\nexport function getOptions(getCopy = true) {\r\n return getCopy ? deepCopy(globalOptions) : globalOptions;\r\n}\r\n\r\n/**\r\n * Updates a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(getOptions(getCopy), newOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return reference to the global options\r\n return getOptions(false);\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Get the allow flags for the custom logic check\r\n const { allowCodeExecution, allowFileResources } = getOptions().customLogic;\r\n\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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') ? false : true;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n path: getAbsolutePath(cssImportPath)\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `imageOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} imageOptions - The `imageOptions` object, which should\r\n * include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(imageOptions, endCallback) {\r\n try {\r\n // Check if provided options are in an object\r\n if (!isObject(imageOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: imageOptions.export,\r\n customLogic: imageOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.options = null;\r\n options.export.instr = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.options = null;\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Check if the image options object does not exceed the size limit\r\n _checkDataSize({ export: exportOptions, customLogic: customLogicOptions });\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\n/**\r\n * Validates the size of the data for the export process against a fixed limit\r\n * of 100MB.\r\n *\r\n * @function _checkDataSize\r\n *\r\n * @param {Object} imageOptions - The data object, which includes options from\r\n * the `export` and `customLogic` sections and will be sent to a Puppeteer page.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the size of the data for\r\n * the export process object exceeds the 100MB limit.\r\n */\r\nfunction _checkDataSize(imageOptions) {\r\n // Set the fixed data limit (100MB) for the dev-tools protocol\r\n const dataLimit = 100 * 1024 * 1024;\r\n\r\n // Get the size of the data\r\n const totalSize = Buffer.byteLength(JSON.stringify(imageOptions), 'utf-8');\r\n\r\n // Log the size in MB\r\n log(\r\n 3,\r\n `[chart] The current total size of the data for the export process 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 before passing to a page\r\n if (totalSize >= dataLimit) {\r\n throw new ExportError(\r\n `[chart] The data for the export process exceeds 100MB limit.`\r\n );\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const options = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = options.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(options, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] Invalid or missing token: Set the token in the hc-auth header.',\r\n 401\r\n );\r\n }\r\n\r\n // Get the new version from the params\r\n const newVersion = request.params.newVersion;\r\n\r\n // Update version\r\n if (newVersion) {\r\n try {\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 = readFileSync(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = readFileSync(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","normalize","resolve","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","join","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","getCopy","updateOptions","newOptions","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","imageOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","_checkDataSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","totalSize","byteLength","toFixed","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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KA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMmB,cAAgB,CAC3BC,UAAW,CACT/B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFuD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbtG,KAAM,OACNuG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP9D,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbtG,KAAM,SAGV0G,OAAQ,CACN/D,MAAO,8BACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbtG,KAAM,SAGV2G,WAAY,CACVhE,OAAO,EACPuD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbtG,KAAM,WAGV4G,UAAW,CACTjE,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbtG,KAAM,SAGV6G,YAAa,CACXlE,MAAO,CAAC,aAAc,kBAAmB,iBACzCuD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbtG,KAAM,cACN8G,aAAc,0DAGlBC,cAAe,CACbpE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,k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"} \ No newline at end of file diff --git a/lib/chart.js b/lib/chart.js index 6c0e6937..659a3682 100644 --- a/lib/chart.js +++ b/lib/chart.js @@ -409,8 +409,8 @@ async function _exportFromSvg(inputToExport, options) { options.export.svg = inputToExport; // Reset the rest of the export input options - options.export.instr = null; options.export.options = null; + options.export.instr = null; // Call the function with an SVG string as an export input return _prepareExport(options); @@ -462,6 +462,7 @@ async function _exportFromOptions(inputToExport, options) { options.export.instr = stringifiedOptions; // Reset the rest of the export input options + options.export.options = null; options.export.svg = null; // Call the function with a stringified chart options @@ -514,6 +515,9 @@ async function _prepareExport(options) { ..._findChartSize(exportOptions) }; + // Check if the image options object does not exceed the size limit + _checkDataSize({ export: exportOptions, customLogic: customLogicOptions }); + // Post the work to the pool return postWork(options); } @@ -540,7 +544,7 @@ async function _prepareExport(options) { function _findChartSize(exportOptions) { // Check the `options` and `instr` for chart and exporting sections const { chart: optionsChart, exporting: optionsExporting } = - exportOptions.options || isAllowedConfig(exportOptions.instr) || false; + isAllowedConfig(exportOptions.instr) || false; // Check the `globalOptions` for chart and exporting sections const { chart: globalOptionsChart, exporting: globalOptionsExporting } = @@ -859,6 +863,42 @@ function _handleGlobalAndTheme( } } +/** + * Validates the size of the data for the export process against a fixed limit + * of 100MB. + * + * @function _checkDataSize + * + * @param {Object} imageOptions - The data object, which includes options from + * the `export` and `customLogic` sections and will be sent to a Puppeteer page. + * + * @throws {ExportError} Throws an `ExportError` if the size of the data for + * the export process object exceeds the 100MB limit. + */ +function _checkDataSize(imageOptions) { + // Set the fixed data limit (100MB) for the dev-tools protocol + const dataLimit = 100 * 1024 * 1024; + + // Get the size of the data + const totalSize = Buffer.byteLength(JSON.stringify(imageOptions), 'utf-8'); + + // Log the size in MB + log( + 3, + `[chart] The current total size of the data for the export process is around ${( + totalSize / + (1024 * 1024) + ).toFixed(2)}MB.` + ); + + // Check the size of data before passing to a page + if (totalSize >= dataLimit) { + throw new ExportError( + `[chart] The data for the export process exceeds 100MB limit.` + ); + } +} + export default { singleExport, batchExport, From 1d8dba306b8f559d764f2ef29f3b342eaefb767a Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:21:29 +0100 Subject: [PATCH 087/102] Optimization by moving and modifing the config-related logic. --- README.md | 12 +- lib/config.js | 315 ++++++++++++++++++++++---------------------------- 2 files changed, 147 insertions(+), 180 deletions(-) diff --git a/README.md b/README.md index 9d399698..fee6fc79 100644 --- a/README.md +++ b/README.md @@ -728,7 +728,7 @@ This package supports both CommonJS and ES modules. - `function getServers()`: Get all servers associated with Express app instance. - - `@returns {Array.}` Servers associated with Express app instance. + - `@returns {Array}` Servers associated with Express app instance. - `function getExpress()`: Get the Express instance. @@ -757,24 +757,24 @@ This package supports both CommonJS and ES modules. - `@param {string} path` - The path to which the middleware(s) should be applied. - `@param {...Function} middlewares` - The middleware function(s) to be applied. -- `function getOptions(getCopy = true)`: Retrieves a copy of the global options object or a reference to the global options object, based on the `getCopy` flag. +- `function getOptions(getCopy = true)`: Retrieves a copy of the global options object or an original global options object, based on the `getCopy` flag. - - `@param {boolean} [getCopy=true]` - Specifies whether to return a copied object of the global options (`true`) or a reference to the global options object (`false`). The default value is `false`. + - `@param {boolean} [getCopy=true]` - Specifies whether to return a copied object of the global options (`true`) or a reference to the global options object (`false`). The default value is `true`. - `@returns {Object}` A copy of the global options object, or a reference to the global options object. -- `function updateOptions(newOptions, getCopy = false)`: Updates a copy of the global options object or a reference to the global options object, based on the `getCopy` flag. +- `function updateOptions(newOptions, getCopy = false)`: Updates and returns the global options object or a copy of the global options object, based on the `getCopy` flag. - `@param {Object} newOptions` - An object containing the new options to be merged into the global options. - `@param {boolean} [getCopy=false]` - Determines whether to merge the new options into a copy of the global options object (`true`) or directly into the global options object (`false`). The default value is `false`. - `@returns {Object}` The updated options object, either the modified global options or a modified copy, based on the value of `getCopy`. -- `function mapToNewOptions(oldOptions)`: Maps old-structured configuration options (PhantomJS) to a new format (Puppeteer). This function converts flat, old-structured options into a new, nested configuration format based on a predefined mapping (`nestedProps`). The new format is used for Puppeteer, while the old format was used for PhantomJS. +- `function mapToNewOptions(oldOptions)`: Maps old-structured configuration options (PhantomJS-based) to a new format (Puppeteer-based). This function converts flat, old-structured options into a new, nested configuration format based on a predefined mapping provided in the `nestedProps` object. The new format is used for Puppeteer, while the old format was used for PhantomJS. - `@param {Object} oldOptions` - The old, flat configuration options to be converted. - - `@returns {Object}` A new object containing options structured according to the mapping defined in `nestedProps` or an empty object if the provided `oldOptions` is not a correct object. + - `@returns {Object}` A new object containing options structured according to the mapping defined in the `nestedProps` object or an empty object if the provided `oldOptions` is not a correct object. - `async function initExport(initOptions = {})`: Initializes the export process. Tasks such as configuring logging, checking the cache and sources, and initializing the resource pool occur during this stage. diff --git a/lib/config.js b/lib/config.js index 2260fa0c..a7c1149a 100644 --- a/lib/config.js +++ b/lib/config.js @@ -13,45 +13,51 @@ See LICENSE file in root for details. *******************************************************************************/ /** - * @overview Manages configuration for the Highcharts Export Server by loading - * and merging options from multiple sources, such as default settings, - * environment variables, user-provided options, and command-line arguments. - * Ensures the global options are up-to-date with the highest priority values. - * Provides functions for accessing and updating configuration. + * @overview This module manages configuration for the Highcharts Export Server + * by loading and merging options from multiple sources, such as the default + * settings, environment variables, user-provided options, and command-line + * arguments. Ensures the global options are up-to-date with the highest + * priority values. Provides functions for accessing and updating configuration. */ import { readFileSync } from 'fs'; -import { join } from 'path'; -import { log, logWithStack } from './logger.js'; import { envs } from './envs.js'; -import { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js'; +import { log, logWithStack } from './logger.js'; +import { deepCopy, getAbsolutePath, isObject } from './utils.js'; -import { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js'; +import defaultConfig from './schemas/config.js'; // Sets the global options with initial values from the default config const globalOptions = _initOptions(defaultConfig); +// Properties nesting level of all options +const nestedProps = _createNestedProps(defaultConfig); + +// Properties names that should not be recursively merged +const absoluteProps = _createAbsoluteProps(defaultConfig); + /** - * Retrieves a copy of the global options object or a reference to the global - * options object, based on the `getCopy` flag. + * Retrieves a copy of the global options object or an original global options + * object, based on the `getCopy` flag. * * @function getOptions * * @param {boolean} [getCopy=true] - Specifies whether to return a copied * object of the global options (`true`) or a reference to the global options - * object (`false`). The default value is `false`. + * object (`false`). The default value is `true`. * * @returns {Object} A copy of the global options object, or a reference * to the global options object. */ export function getOptions(getCopy = true) { + // Return a copy or an original global options object return getCopy ? deepCopy(globalOptions) : globalOptions; } /** - * Updates a copy of the global options object or a reference to the global - * options object, based on the `getCopy` flag. + * Updates and returns the global options object or a copy of the global options + * object, based on the `getCopy` flag. * * @function updateOptions * @@ -70,19 +76,19 @@ export function updateOptions(newOptions, getCopy = false) { } /** - * Updates the global options with values provided through the CLI, keeping - * the principle of options load priority. This function accepts a `cliArgs` - * array containing arguments from the CLI, which will be validated and applied - * if provided. + * Updates and returns the global options object with values provided through + * the CLI, keeping the principle of options load priority. The function accepts + * a `cliArgs` array containing arguments from the CLI, which will be validated + * and applied if provided. * - * The priority order for setting values is: + * The function prioritizes values in the following order: * - * 1. Values from a custom JSON file (loaded by the `--loadConfig` option). - * 2. Values from the command line interface (CLI). + * 1. Values from the command line interface (CLI). + * 2. Values from a custom JSON file (loaded by the `--loadConfig` option). * * @function setCliOptions * - * @param {Array.} cliArgs - An array of command line arguments used + * @param {Array} cliArgs - An array of command line arguments used * for additional configuration. * * @returns {Object} The updated global options object, reflecting the merged @@ -94,13 +100,13 @@ export function setCliOptions(cliArgs) { // Get options from the custom JSON loaded via the `--loadConfig` const configOptions = _loadConfigFile(cliArgs); - // Update global options with the values from the `configOptions` + // Update global options with the values from the `configOptions` object updateOptions(configOptions); // Get options from the CLI - const cliOptions = _pairArgumentValue(nestedProps, cliArgs); + const cliOptions = _pairArgumentValue(cliArgs); - // Update global options with the values from the `cliOptions` + // Update global options with the values from the `cliOptions` object updateOptions(cliOptions); } @@ -109,11 +115,11 @@ export function setCliOptions(cliArgs) { } /** - * Maps old-structured configuration options (PhantomJS) to a new format - * (Puppeteer). This function converts flat, old-structured options into - * a new, nested configuration format based on a predefined mapping - * (`nestedProps`). The new format is used for Puppeteer, while the old format - * was used for PhantomJS. + * Maps old-structured configuration options (PhantomJS-based) to a new format + * (Puppeteer-based). This function converts flat, old-structured options into + * a new, nested configuration format based on a predefined mapping provided + * in the `nestedProps` object. The new format is used for Puppeteer, while + * the old format was used for PhantomJS. * * @function mapToNewOptions * @@ -121,8 +127,8 @@ export function setCliOptions(cliArgs) { * to be converted. * * @returns {Object} A new object containing options structured according - * to the mapping defined in `nestedProps` or an empty object if the provided - * `oldOptions` is not a correct object. + * to the mapping defined in the `nestedProps` object or an empty object + * if the provided `oldOptions` is not a correct object. */ export function mapToNewOptions(oldOptions) { // An object for the new structured options @@ -149,7 +155,7 @@ export function mapToNewOptions(oldOptions) { } else { log( 2, - '[config] No correct object with options was provided. Returning an empty array.' + '[config] No correct object with options was provided. Returning an empty object.' ); } @@ -168,11 +174,11 @@ export function mapToNewOptions(oldOptions) { * @param {boolean} [toString=false] - Whether to return a stringified version * of the parsed config. The default value is `false`. * @param {boolean} [allowFunctions=false] - Whether to allow functions - * in the parsed config. If true, functions are preserved. Otherwise, when - * a function is found, null is returned. The default value is `false`. + * in the parsed config. If `true`, functions are preserved. Otherwise, when + * a function is found, `null` is returned. The default value is `false`. * * @returns {(Object|string|null)} Returns a parsed set of options object, - * a stringified set of options object if the `toString` is true, and null + * a stringified set of options object if the `toString` is `true`, and `null` * if the config is not a valid set of options or parsing fails. */ export function isAllowedConfig( @@ -183,7 +189,7 @@ export function isAllowedConfig( try { // Accept only objects and strings if (!isObject(config) && typeof config !== 'string') { - // Return null if any other type + // Return `null` if any other type return null; } @@ -216,92 +222,19 @@ export function isAllowedConfig( // Return stringified or object options based on the `toString` flag return toString ? stringifiedOptions : parsedOptions; } catch (error) { - // Return null if parsing fails + // Return `null` if parsing fails return null; } } -/** - * Prints the Highcharts Export Server logo, version, and license information. - * - * @function printLicense - */ -export function printLicense() { - // Print the logo and version information - printVersion(); - - // Print the license information - console.log( - 'This software requires a valid Highcharts license for commercial use.\n' - .yellow, - '\nFor a full list of CLI options, type:', - '\nhighcharts-export-server --help\n'.green, - '\nIf you do not have a license, one can be obtained here:', - '\nhttps://shop.highsoft.com/\n'.green, - '\nTo customize your installation, please refer to the README file at:', - '\nhttps://github.com/highcharts/node-export-server#readme\n'.green - ); -} - -/** - * Prints usage information for CLI arguments, displaying available options - * and their descriptions. It can list properties recursively if categories - * contain nested options. - * - * @function printUsage - */ -export function printUsage() { - // Display README and general usage information - console.log( - '\nUsage of CLI arguments:'.bold, - '\n-----------------------', - `\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\n` - ); - - // Iterate through each category in the `defaultConfig` and display usage info - Object.keys(defaultConfig).forEach((category) => { - console.log(`${category.toUpperCase()}`.bold.red); - _cycleCategories(defaultConfig[category]); - console.log(''); - }); -} - -/** - * Prints the Highcharts Export Server logo or text with the version - * information. - * - * @function printVersion - * - * @param {boolean} [noLogo=false] - If true, only prints text with the version - * information, without the logo. The default value is `false`. - */ -export function printVersion(noLogo = false) { - // Get package version either from `.env` or from `package.json` - const packageVersion = JSON.parse( - readFileSync(join(__dirname, 'package.json'), 'utf8') - ).version; - - // Print text only - if (noLogo) { - console.log(`Highcharts Export Server v${packageVersion}`); - } else { - // Print the logo - console.log( - readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString() - .bold.yellow, - `v${packageVersion}\n`.bold - ); - } -} - /** * Initializes and returns the global options object based on the provided * configuration, setting values from nested properties recursively. * - * The priority order for setting values is: + * The function prioritizes values in the following order: * - * 1. Values from the `./lib/schemas/config.js` file (defaults). - * 2. Values from environment variables (specified in the `.env` file). + * 1. Values from environment variables (specified in the `.env` file). + * 2. Values from the `./lib/schemas/config.js` file (defaults). * * @function _initOptions * @@ -329,7 +262,7 @@ function _initOptions(config) { options[name] = item.value; } } else { - // Create a section in the options + // Create a category of options in the `options` object options[name] = _initOptions(item); } } @@ -339,16 +272,20 @@ function _initOptions(config) { } /** - * Merges two sets of configuration options, considering absolute properties. + * Recursively merges two sets of configuration options, taking into account + * properties specified in the `absoluteProps` array that require absolute + * merging. The `originalOptions` object will be extended with options from + * the `newOptions` object. * * @function _mergeOptions * - * @param {Object} originalOptions - Original configuration options. - * @param {Object} newOptions - New configuration options to be merged. + * @param {Object} originalOptions - The original configuration options object + * to be extended. + * @param {Object} newOptions - The new configuration options object to merge. * - * @returns {Object} Merged configuration options. + * @returns {Object} The extended `originalOptions` object. */ -export function _mergeOptions(originalOptions, newOptions) { +function _mergeOptions(originalOptions, newOptions) { // Check if the `originalOptions` and `newOptions` are correct objects if (isObject(originalOptions) && isObject(newOptions)) { for (const [key, value] of Object.entries(newOptions)) { @@ -368,40 +305,40 @@ export function _mergeOptions(originalOptions, newOptions) { } /** - * Converts the provided options object to a JSON-formatted string - * with the option to preserve functions. In order for a function - * to be preserved, it needs to follow the format `function (...) {...}`. - * Such a function can also be stringified. + * Converts the provided options object to a JSON string with the option + * to preserve functions. In order for a function to be preserved, it needs + * to follow the format `function (...) {...}`. Such a function can also + * be stringified. * * @function _optionsStringify * * @param {Object} options - The options object to be converted to a string. - * @param {boolean} allowFunctions - If set to true, functions are preserved + * @param {boolean} allowFunctions - If set to `true`, functions are preserved * in the output. Otherwise an error is thrown. - * @param {boolean} stringifyFunctions - If set to true, functions are saved - * as strings. The `allowFunctions` must be set to true as well for this to take - * an effect. + * @param {boolean} stringifyFunctions - If set to `true`, functions are saved + * as strings. The `allowFunctions` must be set to `true` as well for this + * to take an effect. * * @returns {string} The JSON-formatted string representing the options. * * @throws {Error} Throws an `Error` when functions are not allowed but are * found in provided options object. */ -export function _optionsStringify(options, allowFunctions, stringifyFunctions) { +function _optionsStringify(options, allowFunctions, stringifyFunctions) { const replacerCallback = (_, value) => { // Trim string values if (typeof value === 'string') { value = value.trim(); } - // If value is a function or stringified function + // If `value` is a function or stringified function if ( typeof value === 'function' || (typeof value === 'string' && value.startsWith('function') && value.endsWith('}')) ) { - // If allowFunctions is set to true, preserve functions + // If the `allowFunctions` is set to `true`, preserve functions if (allowFunctions) { // Based on the `stringifyFunctions` options, set function values return stringifyFunctions @@ -432,7 +369,7 @@ export function _optionsStringify(options, allowFunctions, stringifyFunctions) { * * @function _loadConfigFile * - * @param {Array.} cliArgs - Command-line arguments to search + * @param {Array} cliArgs - Command-line arguments to search * for the `--loadConfig` option and the corresponding file path. * * @returns {Object} The additional configuration loaded from the specified @@ -476,20 +413,18 @@ function _loadConfigFile(cliArgs) { /** * Parses command-line arguments and pairs each argument with its corresponding * option in the configuration. The values are structured into a nested options - * object, based on predefined mappings. + * object, based on predefined mappings in the `nestedProps` object. * * @function _pairArgumentValue * - * @param {Array.} nestedProps - An array of nesting level for all - * options. - * @param {Array.} cliArgs - An array of command-line arguments + * @param {Array} cliArgs - An array of command-line arguments * containing options and their associated values. * * @returns {Object} An updated options object where each option from * the command-line is paired with its value, structured into nested objects * as defined. */ -function _pairArgumentValue(nestedProps, cliArgs) { +function _pairArgumentValue(cliArgs) { // An empty object to collect and structurize data from the args const cliOptions = {}; @@ -525,46 +460,81 @@ function _pairArgumentValue(nestedProps, cliArgs) { } /** - * Recursively traverses the options object to print the usage information - * for each option category and individual option. - * - * @function _cycleCategories - * - * @param {Object} options - The options object containing CLI options. It may - * include nested categories and individual options. + * Recursively generates a mapping of nested argument chains from a nested + * config object. This function traverses a nested object and creates a mapping + * where each key is an argument name (either from `cliName`, `legacyName`, + * or the original key) and each value is a string representing the chain + * of nested properties leading to that argument. + * + * @function _createNestedProps + * + * @param {Object} config - The configuration object. + * @param {Object} [nestedProps={}] - The accumulator object for storing + * the resulting arguments chains. The default value is an empty object. + * @param {string} [propChain=''] - The current chain of nested properties, + * used internally during recursion. The default value is an empty string. + * + * @returns {Object} An object mapping argument names to their corresponding + * nested property chains. */ -function _cycleCategories(options) { - for (const [name, option] of Object.entries(options)) { - // If the current entry is a category and not a leaf option, recurse into it - if (!Object.prototype.hasOwnProperty.call(option, 'value')) { - _cycleCategories(option); +function _createNestedProps(config, nestedProps = {}, propChain = '') { + Object.keys(config).forEach((key) => { + // Get the specific section + const entry = config[key]; + + // Check if there is still more depth to traverse + if (typeof entry.value === 'undefined') { + // Recurse into deeper levels of nested arguments + _createNestedProps(entry, nestedProps, `${propChain}.${key}`); } else { - // Prepare description - const descName = ` --${option.cliName || name}`; + // Create the chain of nested arguments + nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1); - // Get the value - let optionValue = option.value; - - // Prepare value for option that is not null and is array of strings - if (optionValue !== null && option.types.includes('string[]')) { - optionValue = - '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']'; + // Support for the legacy, PhantomJS properties names + if (entry.legacyName !== undefined) { + nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1); } + } + }); - // Prepare value for option that is not null and is a string - if (optionValue !== null && option.types.includes('string')) { - optionValue = `'${optionValue}'`; - } + // Return the object with nested argument chains + return nestedProps; +} - // Display correctly aligned messages - console.log( - descName.green, - `${('<' + option.types.join('|') + '>').yellow}`, - `${String(optionValue).bold}`.blue, - `- ${option.description}.` - ); +/** + * Recursively gathers the names of properties from a configuration object that + * should be treated as absolute properties. These properties have values that + * are objects and do not contain further nested depth when merging an object + * containing these options. + * + * @function _createAbsoluteProps + * + * @param {Object} config - The configuration object. + * @param {Array} [absoluteProps=[]] - An array to collect the names + * of absolute properties. The default value is an empty array. + * + * @returns {Array} An array containing the names of absolute + * properties. + */ +function _createAbsoluteProps(config, absoluteProps = []) { + Object.keys(config).forEach((key) => { + // Get the specific section + const entry = config[key]; + + // Check if there is still more depth to traverse + if (typeof entry.types === 'undefined') { + // Recurse into deeper levels + _createAbsoluteProps(entry, absoluteProps); + } else { + // If the option can be an object, save its type in the array + if (entry.types.includes('Object')) { + absoluteProps.push(key); + } } - } + }); + + // Return the array with the names of absolute properties + return absoluteProps; } export default { @@ -572,8 +542,5 @@ export default { updateOptions, setCliOptions, mapToNewOptions, - isAllowedConfig, - printLicense, - printUsage, - printVersion + isAllowedConfig }; From c67c3228cb830376ac9a8c850d0295cc72c00e9c Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:21:52 +0100 Subject: [PATCH 088/102] The schemas/config.js file contains the default configuration object only as a default export now. --- lib/schemas/config.js | 114 +++++------------------------------------- 1 file changed, 12 insertions(+), 102 deletions(-) diff --git a/lib/schemas/config.js b/lib/schemas/config.js index 1c44f1b7..fa318f10 100644 --- a/lib/schemas/config.js +++ b/lib/schemas/config.js @@ -14,26 +14,24 @@ See LICENSE file in root for details. /** * @overview Configuration management module for the Highcharts Export Server. - * Provides default configurations that support environment variables, CLI - * arguments, and interactive prompts for customization of options and features. - * Additionally, it maps legacy options to modern structures, generates nested - * argument mappings, and displays CLI usage information. + * It provides a default configuration object with predefined default values, + * descriptions, and characteristics for each option used in the Export Server. */ /** - * The configuration object containing all available options, organized + * The default configuration object containing all available options, organized * by sections. * * This object includes: - * - Default values for each option - * - Data types for validation - * - Names of corresponding environment variables - * - Descriptions of each property - * - Information used for prompts in interactive configuration - * - [Optional] Corresponding CLI argument names for CLI usage - * - [Optional] Legacy names from the previous PhantomJS-based server + * - Default values for each option. + * - Data types for validation. + * - Names of corresponding environment variables. + * - Descriptions of each property. + * - Information used for prompts in interactive configuration. + * - [Optional] Corresponding CLI argument names for CLI usage. + * - [Optional] Legacy names from the previous PhantomJS-based server. */ -export const defaultConfig = { +const defaultConfig = { puppeteer: { args: { value: [ @@ -992,92 +990,4 @@ export const defaultConfig = { } }; -// Properties nesting level of all options -export const nestedProps = _createNestedProps(defaultConfig); - -// Properties names that should not be recursively merged -export const absoluteProps = _createAbsoluteProps(defaultConfig); - -/** - * Recursively generates a mapping of nested argument chains from a nested - * config object. This function traverses a nested object and creates a mapping - * where each key is an argument name (either from `cliName`, `legacyName`, - * or the original key) and each value is a string representing the chain - * of nested properties leading to that argument. - * - * @function _createNestedProps - * - * @param {Object} config - The configuration object. - * @param {Object} [nestedProps={}] - The accumulator object for storing - * the resulting arguments chains. The default value is an empty object. - * @param {string} [propChain=''] - The current chain of nested properties, - * used internally during recursion. The default value is an empty string. - * - * @returns {Object} An object mapping argument names to their corresponding - * nested property chains. - */ -function _createNestedProps(config, nestedProps = {}, propChain = '') { - Object.keys(config).forEach((key) => { - // Get the specific section - const entry = config[key]; - - // Check if there is still more depth to traverse - if (typeof entry.value === 'undefined') { - // Recurse into deeper levels of nested arguments - _createNestedProps(entry, nestedProps, `${propChain}.${key}`); - } else { - // Create the chain of nested arguments - nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1); - - // Support for the legacy, PhantomJS properties names - if (entry.legacyName !== undefined) { - nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1); - } - } - }); - - // Return the object with nested argument chains - return nestedProps; -} - -/** - * Recursively gathers the names of properties from a configuration object that - * can be treated as absolute properties. These properties have values that - * are objects and do not contain further nested depth when merging an object - * containing these options. - * - * @function _createAbsoluteProps - * - * @param {Object} config - The configuration object. - * @param {Array.} [absoluteProps=[]] - An array to collect the names - * of absolute properties. The default value is an empty array. - * - * @returns {Array.} An array containing the names of absolute - * properties. - */ -function _createAbsoluteProps(config, absoluteProps = []) { - Object.keys(config).forEach((key) => { - // Get the specific section - const entry = config[key]; - - // Check if there is still more depth to traverse - if (typeof entry.types === 'undefined') { - // Recurse into deeper levels - _createAbsoluteProps(entry, absoluteProps); - } else { - // If the option can be an object, save its type in the array - if (entry.types.includes('Object')) { - absoluteProps.push(key); - } - } - }); - - // Return the array with the names of absolute properties - return absoluteProps; -} - -export default { - defaultConfig, - nestedProps, - absoluteProps -}; +export default defaultConfig; From 018a102b2f6650ce205ec061649aa14e6f328e6d Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:22:16 +0100 Subject: [PATCH 089/102] Optimized and simplified the cache.js module logic. --- lib/cache.js | 422 +++++++++++++++++++++++++-------------------------- 1 file changed, 208 insertions(+), 214 deletions(-) diff --git a/lib/cache.js b/lib/cache.js index 8971dd08..8afb54b6 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -27,7 +27,7 @@ import { join } from 'path'; import { HttpsProxyAgent } from 'https-proxy-agent'; import { getOptions, updateOptions } from './config.js'; -import { fetch } from './fetch.js'; +import { get } from './fetch.js'; import { log } from './logger.js'; import { getAbsolutePath } from './utils.js'; @@ -46,17 +46,14 @@ const cache = { * and loads the sources. * * @async - * @function checkAndUpdateCache + * @function checkCache * * @param {Object} highchartsOptions - The configuration object containing * `highcharts` options. * @param {Object} serverProxyOptions- The configuration object containing * `server.proxy` options. */ -export async function checkAndUpdateCache( - highchartsOptions, - serverProxyOptions -) { +export async function checkCache(highchartsOptions, serverProxyOptions) { try { let fetchedModules; @@ -74,6 +71,8 @@ export async function checkAndUpdateCache( // or if the `forceFetch` option is enabled if (!existsSync(manifestPath) || highchartsOptions.forceFetch) { log(3, '[cache] Fetching and caching Highcharts dependencies.'); + + // The initial cache update fetchedModules = await _updateCache( highchartsOptions, serverProxyOptions, @@ -86,7 +85,7 @@ export async function checkAndUpdateCache( const manifest = JSON.parse(readFileSync(manifestPath), 'utf8'); // Check if the modules is an array, if so, we rewrite it to a map to make - // it easier to resolve modules. + // it easier to resolve modules if (manifest.modules && Array.isArray(manifest.modules)) { const moduleMap = {}; manifest.modules.forEach((m) => (moduleMap[m] = 1)); @@ -103,6 +102,7 @@ export async function checkAndUpdateCache( // If there are changes, fetch requested modules and products, // and bake them into a giant blob. Save the blob. if (manifest.version !== highchartsOptions.version) { + // Check the Highcharts version log( 2, '[cache] A Highcharts version mismatch in the cache, need to re-fetch.' @@ -111,6 +111,7 @@ export async function checkAndUpdateCache( } else if ( Object.keys(manifest.modules || {}).length !== numberOfModules ) { + // Check the number of modules log( 2, '[cache] The cache and the requested modules do not match, need to re-fetch.' @@ -146,13 +147,13 @@ export async function checkAndUpdateCache( fetchedModules = manifest.modules; // Extract and save version of currently used Highcharts - cache.hcVersion = extractVersion(cache.sources); + cache.hcVersion = _extractHcVersion(cache.sources); } } // Finally, save the new manifest, which is basically our current config // in a slightly different format - await _saveConfigToManifest(highchartsOptions, fetchedModules); + await _saveConfigToManifest(highchartsOptions.version, fetchedModules); } catch (error) { throw new ExportError( '[cache] Could not configure cache and create or update the config manifest.', @@ -164,24 +165,24 @@ export async function checkAndUpdateCache( /** * Gets the version of Highcharts from the cache. * - * @function getHighchartsVersion + * @function getHcVersion * * @returns {string} The cached Highcharts version. */ -export function getHighchartsVersion() { +export function getHcVersion() { return cache.hcVersion; } /** * Updates the Highcharts version in the applied configuration and checks - * the cache for the new version. + * the cache for the scripts of a new version. * * @async - * @function updateHighchartsVersion + * @function updateHcVersion * * @param {string} newVersion - The new Highcharts version to be applied. */ -export async function updateHighchartsVersion(newVersion) { +export async function updateHcVersion(newVersion) { // Update to the new version const options = updateOptions({ highcharts: { @@ -190,75 +191,175 @@ export async function updateHighchartsVersion(newVersion) { }); // Check if cache needs to be updated - await checkAndUpdateCache(options.highcharts, options.server.proxy); + await checkCache(options.highcharts, options.server.proxy); } /** - * Extracts Highcharts version from the cache's sources string. - * - * @function extractVersion + * Retrieves the current cache object. * - * @param {Object} cacheSources - The cache sources object. + * @function getCache * - * @returns {string} The extracted Highcharts version. + * @returns {Object} The cache object containing various cached data. */ -export function extractVersion(cacheSources) { - return cacheSources - .substring(0, cacheSources.indexOf('*/')) - .replace('/*', '') - .replace('*/', '') - .replace(/\n/g, '') - .trim(); +export function getCache() { + return cache; } /** - * Extracts the Highcharts module name based on the scriptPath. - * - * @function extractModuleName + * Gets the cache path for Highcharts. * - * @param {string} scriptPath - The path of the script from which the module - * name will be extracted. + * @function getCachePath * - * @returns {string} The extracted module name. + * @returns {string} The absolute path to the cache directory for Highcharts. */ -export function extractModuleName(scriptPath) { - return scriptPath.replace( - /(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi, - '' - ); +export function getCachePath() { + return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562 } /** - * Retrieves the current cache object. + * Saves the provided configuration and fetched modules to the cache manifest + * file. * - * @function getCache + * @async + * @function _saveConfigToManifest * - * @returns {Object} The cache object containing various cached data. + * @param {number} version - The currently used Highcharts version. + * @param {Object} [fetchedModules={}] - An object which tracks which modules + * have been fetched. The default value is an empty object. + * + * @throws {ExportError} Throws an `ExportError` if an error occurs while + * writing the cache manifest. */ -export function getCache() { - return cache; +async function _saveConfigToManifest(version, fetchedModules = {}) { + // Update cache object with the current modules + cache.activeManifest = { + version, + modules: fetchedModules + }; + + log(3, '[cache] Writing a new manifest.'); + try { + writeFileSync( + join(getCachePath(), 'manifest.json'), + JSON.stringify(cache.activeManifest), + 'utf8' + ); + } catch (error) { + throw new ExportError( + '[cache] Error writing the cache manifest.', + 500 + ).setError(error); + } } /** - * Gets the cache path for Highcharts. + * Updates the local cache with Highcharts scripts content and information, + * and used Highcharts version. * - * @function getCachePath + * @async + * @function _updateCache * - * @returns {string} The absolute path to the cache directory for Highcharts. + * @param {Object} highchartsOptions - The configuration object containing + * `highcharts` options. + * @param {Object} serverProxyOptions - The configuration object containing + * `server.proxy` options. + * @param {string} sourcePath - The path to the source file in the cache. + * + * @returns {Promise} A Promise that resolves to an object representing + * the fetched modules. + * + * @throws {ExportError} Throws an `ExportError` if there is an issue updating + * the local Highcharts cache. */ -export function getCachePath() { - return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562 +async function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) { + try { + // Get Highcharts version for scripts + const hcVersion = + highchartsOptions.version === 'latest' + ? null + : `${highchartsOptions.version}`; + + log( + 3, + `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.` + ); + + // Get the CDN url for scripts + const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl; + + // Prepare options for a request + const requestOptions = _configureRequest(serverProxyOptions); + + // An object to record which scripts are fetched + const fetchedModules = {}; + + // Join all fetched scripts and save in the manifest's sources + cache.sources = ( + await Promise.all([ + // Highcharts core scripts fetch + ...highchartsOptions.coreScripts.map((cs) => + _fetchScript( + hcVersion ? `${cdnUrl}/${hcVersion}/${cs}` : `${cdnUrl}/${cs}`, + requestOptions, + fetchedModules, + true + ) + ), + // Highcharts module scripts fetch + ...highchartsOptions.moduleScripts.map((ms) => + _fetchScript( + ms === 'map' + ? hcVersion + ? `${cdnUrl}/maps/${hcVersion}/modules/${ms}` + : `${cdnUrl}/maps/modules/${ms}` + : hcVersion + ? `${cdnUrl}/${hcVersion}/modules/${ms}` + : `${cdnUrl}/modules/${ms}`, + requestOptions, + fetchedModules + ) + ), + // Highcharts indicator scripts fetch + ...highchartsOptions.indicatorScripts.map((is) => + _fetchScript( + hcVersion + ? `${cdnUrl}/stock/${hcVersion}/indicators/${is}` + : `${cdnUrl}/stock/indicators/${is}`, + requestOptions, + fetchedModules + ) + ), + // Custom scripts fetch + ...highchartsOptions.customScripts.map((cs) => + _fetchScript(`${cs}`, requestOptions) + ) + ]) + ).join(';\n'); + + // Extract and save version of currently used Highcharts + cache.hcVersion = _extractHcVersion(cache.sources); + + // Save the fetched modules into caches' source JSON + writeFileSync(sourcePath, cache.sources); + + // Return the fetched modules + return fetchedModules; + } catch (error) { + throw new ExportError( + '[cache] Unable to update the local Highcharts cache.', + 500 + ).setError(error); + } } /** * Fetches a single script and updates the `fetchedModules` accordingly. * * @async - * @function _fetchAndProcessScript + * @function _fetchScript * * @param {string} script - A path to script to get. - * @param {Object} requestOptions - Additional options for the proxy agent - * to use for a request. + * @param {Object} requestOptions - Additional requests options. * @param {Object} fetchedModules - An object which tracks which Highcharts * modules have been fetched. * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error @@ -271,7 +372,7 @@ export function getCachePath() { * @throws {ExportError} Throws an `ExportError` if there is a problem * with fetching the script. */ -async function _fetchAndProcessScript( +async function _fetchScript( script, requestOptions, fetchedModules, @@ -284,12 +385,12 @@ async function _fetchAndProcessScript( log(4, `[cache] Fetching script - ${script}.js`); // Fetch the script - const response = await fetch(`${script}.js`, requestOptions); + const response = await get(`${script}.js`, requestOptions); // If OK, return its text representation if (response.statusCode === 200 && typeof response.text == 'string') { if (fetchedModules) { - const moduleName = extractModuleName(script); + const moduleName = _extractModuleName(script); fetchedModules[moduleName] = 1; } return response.text; @@ -310,84 +411,41 @@ async function _fetchAndProcessScript( } /** - * Saves the provided configuration and fetched modules to the cache manifest - * file. + * Configures a proxy agent for outgoing HTTP requests based on the provided + * `server.proxy` options. If a valid `host` and `port` are specified, it tries + * to create an `HttpsProxyAgent`. If the creation fails, an `ExportError` + * is thrown. If no proxy is configured, an empty object is returned. * - * @async - * @function _saveConfigToManifest - * - * @param {Object} highchartsOptions - The configuration object containing - * `highcharts` options. - * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts - * modules have been fetched. The default value is an empty object. + * @function _configureRequest * - * @throws {ExportError} Throws an `ExportError` if an error occurs while - * writing the cache manifest. - */ -async function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) { - const newManifest = { - version: highchartsOptions.version, - modules: fetchedModules - }; - - // Update cache object with the current modules - cache.activeManifest = newManifest; - - log(3, '[cache] Writing a new manifest.'); - try { - writeFileSync( - join(getCachePath(), 'manifest.json'), - JSON.stringify(newManifest), - 'utf8' - ); - } catch (error) { - throw new ExportError( - '[cache] Error writing the cache manifest.', - 500 - ).setError(error); - } -} - -/** - * Fetches Highcharts `scripts` and `customScripts` from the given CDNs. - * - * @async - * @function _fetchScripts - * - * @param {Array.} coreScripts - Highcharts core scripts to fetch. - * @param {Array.} moduleScripts - Highcharts modules to fetch. - * @param {Array.} customScripts - Custom script paths to fetch (full - * URLs). - * @param {Object} serverProxyOptions - The configuration object containing + * @param {Object} serverProxyOptions- The configuration object containing * `server.proxy` options. - * @param {Object} fetchedModules - An object which tracks which Highcharts - * modules have been fetched. * - * @returns {Promise} A Promise that resolves to the fetched scripts - * content joined. + * @returns {Object} The request options, including the proxy agent if created, + * or an empty object if no proxy configuration is provided. * - * @throws {ExportError} Throws an `ExportError` if an error occurs while - * setting an HTTP Agent for proxy. + * @throws {ExportError} Throws an `ExportError` if the proxy agent creation + * fails. */ -async function _fetchScripts( - coreScripts, - moduleScripts, - customScripts, - serverProxyOptions, - fetchedModules -) { - // Configure proxy if exists - let proxyAgent; +function _configureRequest(serverProxyOptions) { + // Get the `host` and `port` of the proxy const proxyHost = serverProxyOptions.host; const proxyPort = serverProxyOptions.port; - // Try to create a Proxy Agent + // Try to create a proxy agent if (proxyHost && proxyPort) { try { - proxyAgent = new HttpsProxyAgent({ + // Create the agent + const proxyAgent = new HttpsProxyAgent({ host: proxyHost, port: proxyPort }); + + // Add the agent to the request's options + return { + agent: proxyAgent, + timeout: serverProxyOptions.timeout + }; } catch (error) { throw new ExportError( '[cache] Could not create a Proxy Agent.', @@ -396,113 +454,49 @@ async function _fetchScripts( } } - // If exists, add proxy agent to request options - const requestOptions = proxyAgent - ? { - agent: proxyAgent, - timeout: serverProxyOptions.timeout - } - : {}; - - const allFetchPromises = [ - ...coreScripts.map((script) => - _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true) - ), - ...moduleScripts.map((script) => - _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules) - ), - ...customScripts.map((script) => - _fetchAndProcessScript(`${script}`, requestOptions) - ) - ]; - - const fetchedScripts = await Promise.all(allFetchPromises); - return fetchedScripts.join(';\n'); + // Return an empty object when no proxy agent is created + return {}; } /** - * Updates the local cache with Highcharts scripts and their versions. + * Extracts Highcharts version from the cache's sources string. * - * @async - * @function _updateCache + * @function _extractHcVersion * - * @param {Object} highchartsOptions - The configuration object containing - * `highcharts` options. - * @param {Object} serverProxyOptions - The configuration object containing - * `server.proxy` options. - * @param {string} sourcePath - The path to the source file in the cache. - * - * @returns {Promise} A Promise that resolves to an object representing - * the fetched modules. + * @param {Object} cacheSources - The cache sources object. * - * @throws {ExportError} Throws an `ExportError` if there is an issue updating - * the local Highcharts cache. + * @returns {string} The extracted Highcharts version. */ -async function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) { - // Get Highcharts version for scripts - const hcVersion = - highchartsOptions.version === 'latest' - ? null - : `${highchartsOptions.version}`; - - // Get the CDN url for scripts - const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl; - - try { - const fetchedModules = {}; - - log( - 3, - `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.` - ); - - cache.sources = await _fetchScripts( - [ - ...highchartsOptions.coreScripts.map((c) => - hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}` - ) - ], - [ - ...highchartsOptions.moduleScripts.map((m) => - m === 'map' - ? hcVersion - ? `${cdnUrl}/maps/${hcVersion}/modules/${m}` - : `${cdnUrl}/maps/modules/${m}` - : hcVersion - ? `${cdnUrl}/${hcVersion}/modules/${m}` - : `${cdnUrl}/modules/${m}` - ), - ...highchartsOptions.indicatorScripts.map((i) => - hcVersion - ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}` - : `${cdnUrl}/stock/indicators/${i}` - ) - ], - highchartsOptions.customScripts, - serverProxyOptions, - fetchedModules - ); - - // Extract and save version of currently used Highcharts - cache.hcVersion = extractVersion(cache.sources); +function _extractHcVersion(cacheSources) { + return cacheSources + .substring(0, cacheSources.indexOf('*/')) + .replace('/*', '') + .replace('*/', '') + .replace(/\n/g, '') + .trim(); +} - // Save the fetched modules into caches' source JSON - writeFileSync(sourcePath, cache.sources); - return fetchedModules; - } catch (error) { - throw new ExportError( - '[cache] Unable to update the local Highcharts cache.', - 500 - ).setError(error); - } +/** + * Extracts the Highcharts module name based on the `scriptPath` property. + * + * @function _extractModuleName + * + * @param {string} scriptPath - The path of the script from which the module + * name will be extracted. + * + * @returns {string} The extracted module name. + */ +function _extractModuleName(scriptPath) { + return scriptPath.replace( + /(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi, + '' + ); } export default { - checkAndUpdateCache, - getHighchartsVersion, - updateHighchartsVersion, - extractVersion, - extractModuleName, + checkCache, + getHcVersion, + updateHcVersion, getCache, getCachePath }; From f3f79e28f1f3509baf056c0515e36f860dbee1be Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:22:27 +0100 Subject: [PATCH 090/102] Optimized and simplified the chart.js module logic. --- lib/chart.js | 325 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 221 insertions(+), 104 deletions(-) diff --git a/lib/chart.js b/lib/chart.js index 659a3682..e3665637 100644 --- a/lib/chart.js +++ b/lib/chart.js @@ -13,8 +13,8 @@ See LICENSE file in root for details. *******************************************************************************/ /** - * @overview This module provides functions to prepare for the exporting charts - * into various image output formats such as JPEG, PNG, PDF, and SVGs. + * @overview This module provides functions that prepare for the exporting + * charts into various image output formats such as JPEG, PNG, PDF, and SVGs. * It supports single and batch export operations and allows customization * through options passed from configurations or APIs. */ @@ -25,16 +25,7 @@ import { isAllowedConfig, updateOptions } from './config.js'; import { log, logWithStack } from './logger.js'; import { getPoolStats, killPool, postWork } from './pool.js'; import { sanitize } from './sanitize.js'; -import { - fixConstr, - fixOutfile, - fixType, - getAbsolutePath, - getBase64, - isObject, - roundNumber, - wrapAround -} from './utils.js'; +import { getAbsolutePath, getBase64, isObject, roundNumber } from './utils.js'; import ExportError from './errors/ExportError.js'; @@ -482,16 +473,20 @@ async function _exportFromOptions(inputToExport, options) { * process. */ async function _prepareExport(options) { + // Get the `export` and `customLogic` options const { export: exportOptions, customLogic: customLogicOptions } = options; + // Prepare the `constr` option + exportOptions.constr = _fixConstr(exportOptions.constr); + // Prepare the `type` option - exportOptions.type = fixType(exportOptions.type, exportOptions.outfile); + exportOptions.type = _fixType(exportOptions.type, exportOptions.outfile); // Prepare the `outfile` option - exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile); - - // Prepare the `constr` option - exportOptions.constr = fixConstr(exportOptions.constr); + exportOptions.outfile = _fixOutfile( + exportOptions.type, + exportOptions.outfile + ); // Notify about the custom logic usage status log( @@ -499,21 +494,14 @@ async function _prepareExport(options) { `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.` ); - // Prepare the custom logic options (`customCode`, `callback`, `resources`) - _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution); + // Prepare the `customCode`, `callback`, and `resources` options + _handleCustomLogic(customLogicOptions); // Prepare the `globalOptions` and `themeOptions` options - _handleGlobalAndTheme( - exportOptions, - customLogicOptions.allowFileResources, - customLogicOptions.allowCodeExecution - ); + _handleGlobalAndTheme(exportOptions, customLogicOptions); // Prepare the `height`, `width`, and `scale` options - options.export = { - ...exportOptions, - ..._findChartSize(exportOptions) - }; + _handleSize(exportOptions); // Check if the image options object does not exceed the size limit _checkDataSize({ export: exportOptions, customLogic: customLogicOptions }); @@ -523,25 +511,117 @@ async function _prepareExport(options) { } /** - * Calculates the `height`, `width` and `scale` for chart exports based + * Handles adjusting the constructor name by transforming and normalizing + * it based on common chart types. + * + * @function _fixConstr + * + * @param {string} constr - The original constructor name to be adjusted. + * + * @returns {string} The corrected constructor name, or 'chart' if the input + * is not recognized. + */ +function _fixConstr(constr) { + try { + // Fix the constructor by lowering casing + const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`; + + // Handle the case where the result is just 'Chart' + if (fixedConstr === 'Chart') { + fixedConstr.toLowerCase(); + } + + // Return the corrected constructor, otherwise default to 'chart' + return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes( + fixedConstr + ) + ? fixedConstr + : 'chart'; + } catch { + // Default to 'chart' in case of any error + return 'chart'; + } +} + +/** + * Handles fixing the outfile based on provided type. + * + * @function _fixOutfile + * + * @param {string} type - The original export type. + * @param {string} outfile - The file path or name. + * + * @returns {string} The corrected outfile, or 'chart.png' if the input + * is not recognized. + */ +function _fixOutfile(type, outfile) { + // Get the file name from the `outfile` option + const fileName = getAbsolutePath(outfile || 'chart') + .split('.') + .shift(); + + // Return a correct outfile + return `${fileName}.${type || 'png'}`; +} + +/** + * Handles fixing the export type based on MIME types and file extensions. + * + * @function _fixType + * + * @param {string} type - The original export type. + * @param {string} [outfile=null] - The file path or name. The default value + * is `null`. + * + * @returns {string} The corrected export type, or 'png' if the input + * is not recognized. + */ +function _fixType(type, outfile = null) { + // MIME types + const mimeTypes = { + 'image/png': 'png', + 'image/jpeg': 'jpeg', + 'application/pdf': 'pdf', + 'image/svg+xml': 'svg' + }; + + // Get formats + const formats = Object.values(mimeTypes); + + // Check if type and outfile's extensions are the same + if (outfile) { + const outType = outfile.split('.').pop(); + + // Support the JPG type + if (outType === 'jpg') { + type = 'jpeg'; + } else if (formats.includes(outType) && type !== outType) { + type = outType; + } + } + + // Return a correct type + return mimeTypes[type] || formats.find((t) => t === type) || 'png'; +} + +/** + * Handle calculating the `height`, `width` and `scale` for chart exports based * on the provided export options. * * The function prioritizes values in the following order: + * * 1. The `height`, `width`, `scale` from the `exportOptions`. * 2. Options from the chart configuration (from `exporting` and `chart`). * 3. Options from the global options (from `exporting` and `chart`). * 4. Options from the theme options (from `exporting` and `chart` sections). * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`). * - * @function _findChartSize + * @function _handleSize * * @param {Object} exportOptions - The configuration object containing `export` * options. - * - * @returns {Object} The object containing calculated `height`, `width` - * and `scale` values for the chart export. */ -function _findChartSize(exportOptions) { +function _handleSize(exportOptions) { // Check the `options` and `instr` for chart and exporting sections const { chart: optionsChart, exporting: optionsExporting } = isAllowedConfig(exportOptions.instr) || false; @@ -554,26 +634,6 @@ function _findChartSize(exportOptions) { const { chart: themeOptionsChart, exporting: themeOptionsExporting } = isAllowedConfig(exportOptions.themeOptions) || false; - // Find the `scale` value: - // - It cannot be lower than 0.1 - // - It cannot be higher than 5.0 - // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23) - const scale = roundNumber( - Math.max( - 0.1, - Math.min( - exportOptions.scale || - optionsExporting?.scale || - globalOptionsExporting?.scale || - themeOptionsExporting?.scale || - exportOptions.defaultScale || - 1, - 5.0 - ) - ), - 2 - ); - // Find the `height` value const height = exportOptions.height || @@ -598,17 +658,37 @@ function _findChartSize(exportOptions) { exportOptions.defaultWidth || 600; - // Gather `height`, `width` and `scale` information in one object - const size = { height, width, scale }; + // Find the `scale` value: + // - Cannot be lower than 0.1 + // - Cannot be higher than 5.0 + // - Must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23) + const scale = roundNumber( + Math.max( + 0.1, + Math.min( + exportOptions.scale || + optionsExporting?.scale || + globalOptionsExporting?.scale || + themeOptionsExporting?.scale || + exportOptions.defaultScale || + 1, + 5.0 + ) + ), + 2 + ); + + // Update `height`, `width`, and `scale` information in the `export` options + exportOptions.height = height; + exportOptions.width = width; + exportOptions.scale = scale; // Get rid of potential `px` and `%` - for (let [param, value] of Object.entries(size)) { - size[param] = - typeof value === 'string' ? +value.replace(/px|%/gi, '') : value; + for (let param of ['height', 'width', 'scale']) { + if (typeof exportOptions[param] === 'string') { + exportOptions[param] = +exportOptions[param].replace(/px|%/gi, ''); + } } - - // Return the size object - return size; } /** @@ -621,40 +701,32 @@ function _findChartSize(exportOptions) { * * @param {Object} customLogicOptions - The configuration object containing * `customLogic` options. - * @param {boolean} allowCodeExecution - A flag indicating whether code - * execution is allowed. * * @throws {ExportError} Throws an `ExportError` if code execution * is not allowed but custom logic options are still provided. */ -function _handleCustomLogic(customLogicOptions, allowCodeExecution) { +function _handleCustomLogic(customLogicOptions) { // In case of allowing code execution - if (allowCodeExecution) { + if (customLogicOptions.allowCodeExecution) { // Process the `resources` option - if (typeof customLogicOptions.resources === 'string') { - // Custom stringified resources + try { + // Try to handle resources customLogicOptions.resources = _handleResources( customLogicOptions.resources, customLogicOptions.allowFileResources, true ); - } else if (!customLogicOptions.resources) { - try { - // Load the default one - customLogicOptions.resources = _handleResources( - readFileSync(getAbsolutePath('resources.json'), 'utf8'), - customLogicOptions.allowFileResources, - true - ); - } catch (error) { - log(2, '[chart] Unable to load the default `resources.json` file.'); - } + } catch (error) { + log(2, '[chart] The `resources` cannot be loaded.'); + + // In case of an error, set the option with null + customLogicOptions.resources = null; } // Process the `customCode` option try { // Try to load custom code and wrap around it in a self invoking function - customLogicOptions.customCode = wrapAround( + customLogicOptions.customCode = _handleCustomCode( customLogicOptions.customCode, customLogicOptions.allowFileResources ); @@ -668,7 +740,7 @@ function _handleCustomLogic(customLogicOptions, allowCodeExecution) { // Process the `callback` option try { // Try to load callback function - customLogicOptions.callback = wrapAround( + customLogicOptions.callback = _handleCustomCode( customLogicOptions.callback, customLogicOptions.allowFileResources, true @@ -738,23 +810,30 @@ function _handleResources( allowFileResources, allowCodeExecution ) { + let handledResources = resources; + + // If no resources found, try to load the default resources + if (!handledResources) { + resources = 'resources.json'; + } + // List of allowed sections in the resources JSON const allowedProps = ['js', 'css', 'files']; - let handledResources = resources; + // A flag that decides based to return resources or `null` let correctResources = false; // Try to load resources from a file - if (allowFileResources && resources.endsWith('.json')) { - try { - handledResources = isAllowedConfig( - readFileSync(getAbsolutePath(resources), 'utf8'), - false, - allowCodeExecution - ); - } catch { - return null; - } + if ( + allowFileResources && + typeof resources === 'string' && + resources.endsWith('.json') + ) { + handledResources = isAllowedConfig( + readFileSync(getAbsolutePath(resources), 'utf8'), + false, + allowCodeExecution + ); } else { // Try to get JSON handledResources = isAllowedConfig(resources, false, allowCodeExecution); @@ -791,29 +870,67 @@ function _handleResources( return handledResources; } +/** + * Handles custom code to execute it safely. + * + * @function _handleCustomCode + * + * @param {string} customCode - The custom code to be wrapped. + * @param {boolean} allowFileResources - Flag to allow loading code from a file. + * @param {boolean} [isCallback=false] - Flag that indicates the returned code + * must be in a callback format. + * + * @returns {(string|null)} The wrapped custom code or null if wrapping fails. + */ +function _handleCustomCode(customCode, allowFileResources, isCallback = false) { + if (customCode && typeof customCode === 'string') { + customCode = customCode.trim(); + + if (customCode.endsWith('.js')) { + // Load a file if the file resources are allowed + return allowFileResources + ? _handleCustomCode( + readFileSync(getAbsolutePath(customCode), 'utf8'), + allowFileResources, + isCallback + ) + : null; + } else if ( + !isCallback && + (customCode.startsWith('function()') || + customCode.startsWith('function ()') || + customCode.startsWith('()=>') || + customCode.startsWith('() =>')) + ) { + // Treat a function as a self-invoking expression + return `(${customCode})()`; + } + + // Or return as a stringified code + return customCode.replace(/;$/, ''); + } +} + /** * Handles the loading and validation of the `globalOptions` and `themeOptions` * in the export options. If the option is a string and references a JSON file - * (when the `allowFileResources` is true), it reads and parses the file. + * (when the `allowFileResources` is `true`), it reads and parses the file. * Otherwise, it attempts to parse the string or object as JSON. If any errors - * occur during this process, the option is set to null. If there is an error + * occur during this process, the option is set to `null`. If there is an error * loading or parsing the `globalOptions` or `themeOptions`, the error is logged - * and the option is set to null. + * and the option is set to `null`. * * @function _handleGlobalAndTheme * * @param {Object} exportOptions - The configuration object containing `export` * options. - * @param {boolean} allowFileResources - A flag indicating whether loading - * resources from files is allowed. - * @param {boolean} allowCodeExecution - A flag indicating whether code - * execution is allowed. + * @param {Object} customLogicOptions - The configuration object containing + * `customLogic` options. */ -function _handleGlobalAndTheme( - exportOptions, - allowFileResources, - allowCodeExecution -) { +function _handleGlobalAndTheme(exportOptions, customLogicOptions) { + // Get the `allowFileResources` and `allowCodeExecution` flags + const { allowFileResources, allowCodeExecution } = customLogicOptions; + // Check the `globalOptions` and `themeOptions` options ['globalOptions', 'themeOptions'].forEach((optionsName) => { try { From 906d56460585288f89eb96ad6a896c2d7328f9f6 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:22:44 +0100 Subject: [PATCH 091/102] Corrections and moved some function to the chart.js module. --- lib/utils.js | 151 +++------------------------------------------------ 1 file changed, 8 insertions(+), 143 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 6309835b..9c0bbf6d 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -18,7 +18,6 @@ See LICENSE file in root for details. * and enhance various operations required for Highcharts export tasks. */ -import { readFileSync } from 'fs'; import { isAbsolute, normalize, resolve } from 'path'; import { fileURLToPath } from 'url'; @@ -122,103 +121,11 @@ export async function expBackoff(fn, attempt = 0, ...args) { } } -/** - * Adjusts the constructor name by transforming and normalizing it based - * on common chart types. - * - * @function fixConstr - * - * @param {string} constr - The original constructor name to be fixed. - * - * @returns {string} The corrected constructor name, or 'chart' if the input - * is not recognized. - */ -export function fixConstr(constr) { - try { - // Fix the constructor by lowering casing - const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`; - - // Handle the case where the result is just 'Chart' - if (fixedConstr === 'Chart') { - fixedConstr.toLowerCase(); - } - - // Return the corrected constructor, otherwise default to 'chart' - return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes( - fixedConstr - ) - ? fixedConstr - : 'chart'; - } catch { - // Default to 'chart' in case of any error - return 'chart'; - } -} - -/** - * Fixes the outfile based on provided type. - * - * @function fixOutfile - * - * @param {string} type - The original export type. - * @param {string} outfile - The file path or name. - * - * @returns {string} The corrected outfile. - */ -export function fixOutfile(type, outfile) { - // Get the file name from the `outfile` option - const fileName = getAbsolutePath(outfile || 'chart') - .split('.') - .shift(); - - // Return a correct outfile - return `${fileName}.${type}`; -} - -/** - * Fixes the export type based on MIME types and file extensions. - * - * @function fixType - * - * @param {string} type - The original export type. - * @param {string} [outfile=null] - The file path or name. The default value - * is `null`. - * - * @returns {string} The corrected export type. - */ -export function fixType(type, outfile = null) { - // MIME types - const mimeTypes = { - 'image/png': 'png', - 'image/jpeg': 'jpeg', - 'application/pdf': 'pdf', - 'image/svg+xml': 'svg' - }; - - // Get formats - const formats = Object.values(mimeTypes); - - // Check if type and outfile's extensions are the same - if (outfile) { - const outType = outfile.split('.').pop(); - - // Support the JPG type - if (outType === 'jpg') { - type = 'jpeg'; - } else if (formats.includes(outType) && type !== outType) { - type = outType; - } - } - - // Return a correct type - return mimeTypes[type] || formats.find((t) => t === type) || 'png'; -} - /** * Checks if the given path is relative or absolute and returns the corrected, * absolute path. * - * @function isAbsolutePath + * @function getAbsolutePath * * @param {string} path - The path to be checked on. * @@ -274,7 +181,8 @@ export function getNewDateTime() { * * @param {unknown} item - The item to be checked. * - * @returns {boolean} True if the item is an object, false otherwise. + * @returns {boolean} Returns `true` if the item is an object, `false` + * otherwise. */ export function isObject(item) { return Object.prototype.toString.call(item) === '[object Object]'; @@ -287,7 +195,8 @@ export function isObject(item) { * * @param {Object} item - The object to be checked. * - * @returns {boolean} True if the object is empty, false otherwise. + * @returns {boolean} Returns `true` if the item is an empty object, `false` + * otherwise. */ export function isObjectEmpty(item) { return ( @@ -305,7 +214,8 @@ export function isObjectEmpty(item) { * * @param {string} item - The string to be checked for a private IP range URL. * - * @returns {boolean} True if a private IP range URL is found, false otherwise. + * @returns {boolean} Returns `true` if a private IP range URL is found, `false` + * otherwise. */ export function isPrivateRangeUrlFound(item) { const regexPatterns = [ @@ -361,55 +271,11 @@ export function toBoolean(item) { : !!item; } -/** - * Wraps custom code to execute it safely. - * - * @function wrapAround - * - * @param {string} customCode - The custom code to be wrapped. - * @param {boolean} allowFileResources - Flag to allow loading code from a file. - * @param {boolean} [isCallback=false] - Flag that indicates the returned code - * must be in a callback format. - * - * @returns {(string|null)} The wrapped custom code or null if wrapping fails. - */ -export function wrapAround(customCode, allowFileResources, isCallback = false) { - if (customCode && typeof customCode === 'string') { - customCode = customCode.trim(); - - if (customCode.endsWith('.js')) { - // Load a file if the file resources are allowed - return allowFileResources - ? wrapAround( - readFileSync(getAbsolutePath(customCode), 'utf8'), - allowFileResources, - isCallback - ) - : null; - } else if ( - !isCallback && - (customCode.startsWith('function()') || - customCode.startsWith('function ()') || - customCode.startsWith('()=>') || - customCode.startsWith('() =>')) - ) { - // Treat a function as a self-invoking expression - return `(${customCode})()`; - } - - // Or return as a stringified code - return customCode.replace(/;$/, ''); - } -} - export default { __dirname, clearText, deepCopy, expBackoff, - fixConstr, - fixOutfile, - fixType, getAbsolutePath, getBase64, getNewDate, @@ -419,6 +285,5 @@ export default { isPrivateRangeUrlFound, measureTime, roundNumber, - toBoolean, - wrapAround + toBoolean }; From f09dd551997f3170daf564e58599b4e69386341e Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:23:21 +0100 Subject: [PATCH 092/102] Moving some logic from the puppeteerExport into a new, _getChartSize function. --- lib/export.js | 96 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 37 deletions(-) diff --git a/lib/export.js b/lib/export.js index cb43417e..e07be658 100644 --- a/lib/export.js +++ b/lib/export.js @@ -84,43 +84,7 @@ export async function puppeteerExport(page, exportOptions, customLogicOptions) { ); // Get the real chart size and set the zoom accordingly - const size = isSVG - ? await page.evaluate((scale) => { - const svgElement = document.querySelector( - '#chart-container svg:first-of-type' - ); - - // Get the values correctly scaled - const chartHeight = svgElement.height.baseVal.value * scale; - const chartWidth = svgElement.width.baseVal.value * scale; - - // In case of SVG the zoom must be set directly for body 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 { - chartHeight, - chartWidth - }; - }, 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 { - chartHeight, - chartWidth - }; - }); + const size = await _getChartSize(page, isSVG, exportOptions.scale); // Get the clip region for the page const { x, y } = await _getClipRegion(page); @@ -210,6 +174,64 @@ async function _getClipRegion(page) { }); } +/** + * Retrieves the real chart dimensions from a Puppeteer page. The function + * behaves differently based on whether the export type is SVG or another + * format. + * + * @async + * @function _getChartSize + * + * @param {Object} page - Puppeteer page object. + * @param {boolean} isSVG - Determines whether the chart being processed + * is an SVG or another format. + * @param {number} scale - The scale factor to be applied to the chart + * dimensions. + * + * @returns {Promise} A Promise that resolves to an object containing + * the actual height and width of the chart after scaling. + */ +async function _getChartSize(page, isSVG, scale) { + // Trigger appropriate function based on the `isSvg` flag to get chart size + return isSVG + ? await page.evaluate((scale) => { + const svgElement = document.querySelector( + '#chart-container svg:first-of-type' + ); + + // Get the values correctly scaled + const chartHeight = svgElement.height.baseVal.value * scale; + const chartWidth = svgElement.width.baseVal.value * scale; + + // In case of SVG the zoom must be set directly for body 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 { + chartHeight, + chartWidth + }; + }, parseFloat(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 { + chartHeight, + chartWidth + }; + }); +} + /** * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element * inside an element with the id 'container'. From 9955adfea4dabf35261d685b2acd68ee252812b6 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:23:39 +0100 Subject: [PATCH 093/102] Fixed the problem with not loading images from URLs. --- lib/highcharts.js | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/highcharts.js b/lib/highcharts.js index c3d62a2a..812617f0 100644 --- a/lib/highcharts.js +++ b/lib/highcharts.js @@ -113,9 +113,6 @@ export async function createChart(exportOptions, customLogicOptions) { // Get the `themeOptions` option const themeOptions = new Function(`return ${exportOptions.themeOptions}`)(); - // Get the `globalOptions` option - const globalOptions = new Function(`return ${exportOptions.globalOptions}`)(); - // Merge the following options objects to create final options const finalOptions = merge( false, @@ -135,6 +132,9 @@ export async function createChart(exportOptions, customLogicOptions) { new Function('options', customLogicOptions.customCode)(userOptions); } + // Get the `globalOptions` option + const globalOptions = new Function(`return ${exportOptions.globalOptions}`)(); + // Set the global options if exist if (globalOptions) { setOptions(globalOptions); @@ -143,6 +143,26 @@ export async function createChart(exportOptions, customLogicOptions) { // Call the chart creation Highcharts[exportOptions.constr]('container', finalOptions, finalCallback); + // Get all images from within the chart + const images = Array.from( + document.querySelectorAll('.highcharts-container image') + ); + + // Wait for all images for 2 seconds + await Promise.race([ + Promise.all( + images.map((image) => + image.complete && image.naturalHeight !== 0 + ? Promise.resolve() + : new Promise((resolve) => + image.addEventListener('load', resolve, { once: true }) + ) + ) + ), + // Proceed further even if images did not load + new Promise((resolve) => setTimeout(resolve, 2000)) + ]); + // Get the current global options const defaultOptions = getOptions(); From 2da70b8ab893bd33f2737c327555f8df09614a7e Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:23:53 +0100 Subject: [PATCH 094/102] Fixed the ui.enabled options usage. --- lib/server/routes/ui.js | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/server/routes/ui.js b/lib/server/routes/ui.js index 7b4ef89a..f4d45eff 100644 --- a/lib/server/routes/ui.js +++ b/lib/server/routes/ui.js @@ -31,18 +31,21 @@ import { __dirname } from '../../utils.js'; * @param {Express} app - The Express app instance. */ export default function uiRoutes(app) { - /** - * Adds the GET '/' - A route for a UI when enabled on the export server. - */ - app.get(getOptions().ui.route || '/', (request, response, next) => { - try { - log(4, '[ui] Returning UI for the export.'); - - response.sendFile(join(__dirname, 'public', 'index.html'), { - acceptRanges: false - }); - } catch (error) { - return next(error); - } - }); + // Add the UI endpoint only if required + if (getOptions().ui.enable) { + /** + * Adds the GET '/' - A route for a UI when enabled on the export server. + */ + app.get(getOptions().ui.route || '/', (request, response, next) => { + try { + log(4, '[ui] Returning UI for the export.'); + + response.sendFile(join(__dirname, 'public', 'index.html'), { + acceptRanges: false + }); + } catch (error) { + return next(error); + } + }); + } } From 81f133dc36a7e3aaa1469cd1580f9b8170d3b903 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:24:04 +0100 Subject: [PATCH 095/102] Removed the setStatus function. --- lib/errors/ExportError.js | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/errors/ExportError.js b/lib/errors/ExportError.js index a580e886..c024e42e 100644 --- a/lib/errors/ExportError.js +++ b/lib/errors/ExportError.js @@ -28,27 +28,16 @@ class ExportError extends Error { constructor(message, statusCode) { super(); + // Set the `message` and `stackMessage` with provided message this.message = message; this.stackMessage = message; + // Set the `statusCode` if provided if (statusCode) { this.statusCode = statusCode; } } - /** - * Sets or updates the HTTP status code for the error. - * - * @param {number} statusCode - The HTTP status code to assign to the error. - * - * @returns {ExportError} The updated instance of the `ExportError` class. - */ - setStatus(statusCode) { - this.statusCode = statusCode; - - return this; - } - /** * Sets additional error details based on an existing error object. * @@ -58,21 +47,26 @@ class ExportError extends Error { * @returns {ExportError} The updated instance of the `ExportError` class. */ setError(error) { + // Save the provided error this.error = error; + // Set the error's name if present if (error.name) { this.name = error.name; } + // Set the error's status code if present if (error.statusCode) { this.statusCode = error.statusCode; } + // Set the error's stack and stack's message if present if (error.stack) { this.stackMessage = error.message; this.stack = error.stack; } + // Return updated `ExportError` instance return this; } } From 809f6a2d2d6e239e1014b403cdcff340ff87c964 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:24:19 +0100 Subject: [PATCH 096/102] Optimized, reduced and moved info-related functions into a separate module. --- bin/cli.js | 55 ++++++++++---------- lib/info.js | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 28 deletions(-) create mode 100644 lib/info.js diff --git a/bin/cli.js b/bin/cli.js index cabdf6b0..f73c1f5b 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -16,19 +16,16 @@ See LICENSE file in root for details. /** * @overview This module serves as the entry point for the Highcharts Export * Server. It provides functionality for starting the server, performing batch - * or single chart exports, and managing configuration options. It supports both - * CLI and server-based usage, allowing flexible integration for generating - * Highcharts exports in various environments. + * or single chart exports, managing manual configuration options through + * the prompts functionality, and updating options with values from the passed + * CLI arguments. It supports both CLI and server-based usage, enabling + * integration for generating Highcharts exports in various environments. */ -import { singleExport, batchExport } from '../lib/chart.js'; -import { - printLicense, - printUsage, - printVersion, - setCliOptions -} from '../lib/config.js'; +import { batchExport, singleExport } from '../lib/chart.js'; +import { setCliOptions } from '../lib/config.js'; import { initExport } from '../lib/index.js'; +import { printInfo, printUsage } from '../lib/info.js'; import { log, logWithStack } from '../lib/logger.js'; import { manualConfig } from '../lib/prompt.js'; import { shutdownCleanUp } from '../lib/resourceRelease.js'; @@ -38,16 +35,17 @@ import { startServer } from '../lib/server/server.js'; import ExportError from '../lib/errors/ExportError.js'; /** - * The primary function to initiate the server or perform the direct export. + * The primary function to initiate the server or perform the direct CLI export. * Logs an error if it occurs during the execution and gracefully shuts down * the process. * * @async * @function start * - * @returns {Promise} A Promise that resolves to ending the function - * execution when displaying license, version, or usage information, or when - * triggering manual configuration using the prompts functionality. + * @returns {Promise} A Promise that resolves when the function execution + * ends after displaying the logo with license and version information, showing + * the CLI usage details, or triggering manual configuration using the prompts + * functionality. * * @throws {ExportError} Throws an `ExportError` if no valid options * are provided. @@ -57,10 +55,21 @@ async function start() { // Get the CLI arguments const args = process.argv; + // If no arguments are supplied + if (args.length <= 2) { + // Print logo with version and license information + printInfo(); + log( + 2, + '[cli] The number of provided arguments is too small. Please refer to the help section (use --h or --help command).' + ); + return; + } + // Display version and license information if requested if (['-v', '--v'].some((a) => args.includes(a))) { // Print logo with version and license information - printLicense(); + printInfo(); return; } @@ -71,23 +80,13 @@ async function start() { return; } - // Print CLI usage information if no arguments are supplied - if (args.length <= 2) { - printVersion(); - log( - 2, - '[cli] The number of provided arguments is too small. Please refer to the help section (use --h or --help command).' - ); - return; - } - // Set the options from CLI, keeping the priority order of setting values const options = setCliOptions(args); // If all options are correctly parsed if (options) { - // Print initial logo or text with the version - printVersion(options.other.noLogo); + // Print initial logo or text with the version information + printInfo(options.other.noLogo, true); // In this case we want to prepare config manually if (options.customLogic.createConfig) { @@ -133,7 +132,7 @@ async function start() { } } else { throw new ExportError( - '[cli] No valid options provided. Please check your input and try again.', + '[cli] No valid options found. Please check your input and try again.', 400 ); } diff --git a/lib/info.js b/lib/info.js new file mode 100644 index 00000000..992897c4 --- /dev/null +++ b/lib/info.js @@ -0,0 +1,141 @@ +/******************************************************************************* + +Highcharts Export Server + +Copyright (c) 2016-2025, Highsoft + +Licenced under the MIT licence. + +Additionally a valid Highcharts license is required for use. + +See LICENSE file in root for details. + +*******************************************************************************/ + +/** + * @overview This module provides simple functions for displaying information + * about the Highcharts Export Server, including version details, license + * information, and CLI usage instructions. + */ + +import { readFileSync } from 'fs'; +import { join } from 'path'; + +import { __dirname } from './utils.js'; + +import defaultConfig from './schemas/config.js'; + +/** + * Prints the Highcharts Export Server logo or text with the version and license + * information. + * + * @function printInfo + * + * @param {boolean} [noLogo=false] - If `true`, the text with the version number + * is only printed, without the logo. The default value is `false`. + * @param {boolean} [noLicense=false] - If `true`, the license information will + * not be displayed. The default value is `false`. + */ +export function printInfo(noLogo = false, noLicense = false) { + // Get package version either from `.env` or from `package.json` + const packageVersion = JSON.parse( + readFileSync(join(__dirname, 'package.json'), 'utf8') + ).version; + + // Print text only + if (noLogo) { + console.log(`Highcharts Export Server v${packageVersion}`); + } else { + // Print the logo + console.log( + readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString() + .bold.yellow, + `v${packageVersion}\n`.bold + ); + } + + // Print the license information, if needed + if (!noLicense) { + console.log( + 'This software requires a valid Highcharts license for commercial use.\n' + .yellow, + '\nFor a full list of CLI options, type:', + '\nhighcharts-export-server --help\n'.green, + '\nIf you do not have a license, one can be obtained here:', + '\nhttps://shop.highsoft.com/\n'.green, + '\nTo customize your installation, please refer to the README file at:', + '\nhttps://github.com/highcharts/node-export-server#readme\n'.green + ); + } +} + +/** + * Prints usage information for CLI arguments, displaying available options + * and their descriptions. It can list properties recursively if categories + * contain nested options. + * + * @function printUsage + */ +export function printUsage() { + // Display README and general usage information + console.log( + '\nUsage of CLI arguments:'.bold, + '\n-----------------------', + `\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\n` + ); + + // Iterate through each category in the `defaultConfig` and display usage info + Object.keys(defaultConfig).forEach((category) => { + console.log(`${category.toUpperCase()}`.bold.red); + _cycleCategories(defaultConfig[category]); + console.log(''); + }); +} + +/** + * Recursively traverses the options object to print the usage information + * for each option category and individual option. + * + * @function _cycleCategories + * + * @param {Object} options - The options object containing CLI options. It may + * include nested categories and individual options. + */ +function _cycleCategories(options) { + for (const [name, option] of Object.entries(options)) { + // If the current entry is a category and not a leaf option, recurse into it + if (!Object.prototype.hasOwnProperty.call(option, 'value')) { + _cycleCategories(option); + } else { + // Prepare description + const descName = ` --${option.cliName || name}`; + + // Get the value + let optionValue = option.value; + + // Prepare value for option that is not null and is array of strings + if (optionValue !== null && option.types.includes('string[]')) { + optionValue = + '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']'; + } + + // Prepare value for option that is not null and is a string + if (optionValue !== null && option.types.includes('string')) { + optionValue = `'${optionValue}'`; + } + + // Display correctly aligned messages + console.log( + descName.green, + `${('<' + option.types.join('|') + '>').yellow}`, + `${String(optionValue).bold}`.blue, + `- ${option.description}.` + ); + } + } +} + +export default { + printInfo, + printUsage +}; From 071525aafb0bfe158d56578700e14d0b714746e6 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:25:53 +0100 Subject: [PATCH 097/102] Smaller optimizations, corrections and descriptions updates. --- lib/browser.js | 51 ++++++++++++------------ lib/envs.js | 2 +- lib/fetch.js | 19 +++++---- lib/index.js | 22 ++++++----- lib/logger.js | 10 ++--- lib/pool.js | 54 +++++++++++--------------- lib/prompt.js | 28 +++++++------ lib/sanitize.js | 2 +- lib/server/middlewares/rateLimiting.js | 1 - lib/server/routes/health.js | 4 +- lib/server/routes/versionChange.js | 6 +-- lib/server/server.js | 4 +- lib/timer.js | 5 +-- tests/http/httpTestRunner.js | 4 +- tests/http/httpTestRunnerSingle.js | 4 +- tests/other/sideBySide.js | 4 +- tests/other/stressTest.js | 4 +- tests/unit/utils.test.js | 11 ------ 18 files changed, 111 insertions(+), 124 deletions(-) diff --git a/lib/browser.js b/lib/browser.js index eafb1076..1c0dd88d 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -14,10 +14,10 @@ See LICENSE file in root for details. /** * @overview This module provides functions for managing Puppeteer browser - * instance, creating and clearing pages, injecting custom resources, + * instance, creating and clearing pages, injecting custom JS and CSS resources, * and setting up Highcharts for server-side rendering. The module ensures - * that resources are correctly managed and can handle failures during - * operations like launching the browser or creating new pages. + * that the browser and pages are correctly managed and can handle failures + * during operations like launching the browser or creating new pages. */ import { readFileSync } from 'fs'; @@ -34,7 +34,7 @@ import { __dirname, getAbsolutePath } from './utils.js'; import ExportError from './errors/ExportError.js'; // Get the template for pages -const template = readFileSync( +const pageTemplate = readFileSync( join(__dirname, 'templates', 'template.html'), 'utf8' ); @@ -65,8 +65,8 @@ export function getBrowser() { * @async * @function createBrowser * - * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer - * launch. + * @param {Array} puppeteerArgs - Additional arguments for Puppeteer + * browser's launch. * * @returns {Promise} A Promise that resolves to the created Puppeteer * browser instance. @@ -99,12 +99,11 @@ export async function createBrowser(puppeteerArgs) { if (!browser) { // A counter for the browser's launch retries let tryCount = 0; - - const open = async () => { + const openBrowser = async () => { try { log( 3, - `[browser] Attempting to get a browser instance (try ${++tryCount}).` + `[browser] Attempting to launch and get a browser instance (try ${++tryCount}).` ); // Launch the browser @@ -122,7 +121,7 @@ export async function createBrowser(puppeteerArgs) { // Wait for a 4 seconds before trying again await new Promise((response) => setTimeout(response, 4000)); - await open(); + await openBrowser(); } else { throw error; } @@ -130,7 +129,8 @@ export async function createBrowser(puppeteerArgs) { }; try { - await open(); + // Try to open a browser + await openBrowser(); // Shell mode inform if (launchOptions.headless === 'shell') { @@ -148,6 +148,7 @@ export async function createBrowser(puppeteerArgs) { ).setError(error); } + // No correct browser if (!browser) { throw new ExportError('[browser] Cannot find a browser to open.', 500); } @@ -211,7 +212,7 @@ export async function newPage(poolResource) { } /** - * Clears the content of a Puppeteer Page based on the specified mode. Logs + * Clears the content of a Puppeteer page based on the specified mode. Logs * thrown error if clearing of a page's content fails. * * @async @@ -219,12 +220,12 @@ export async function newPage(poolResource) { * * @param {Object} poolResource - The pool resource that contains page and id. * @param {boolean} [hardReset=false] - A flag indicating the type of clearing - * to be performed. If true, navigates to `about:blank` and resets content - * and scripts. If false, clears the body content by setting a predefined HTML + * to be performed. If `true`, navigates to `about:blank` and resets content + * and scripts. If `false`, clears the body content by setting a predefined HTML * structure. The default value is `false`. * - * @returns {Promise} A Promise that resolves to true when page - * is correctly cleared and false when it is not. + * @returns {Promise} A Promise that resolves to `true` when page + * is correctly cleared and `false` when it is not. */ export async function clearPage(poolResource, hardReset = false) { try { @@ -260,7 +261,7 @@ export async function clearPage(poolResource, hardReset = false) { } /** - * Adds custom JS and CSS resources to a Puppeteer Page based on the specified + * Adds custom JS and CSS resources to a Puppeteer page based on the specified * options. * * @async @@ -271,14 +272,14 @@ export async function clearPage(poolResource, hardReset = false) { * @param {Object} customLogicOptions - The configuration object containing * `customLogic` options. * - * @returns {Promise>} A Promise that resolves to an array + * @returns {Promise>} A Promise that resolves to an array * of injected resources. */ export async function addPageResources(page, customLogicOptions) { // Injected resources array const injectedResources = []; - // Use resources + // Use the content of the `resources` const resources = customLogicOptions.resources; if (resources) { const injectedJs = []; @@ -308,6 +309,7 @@ export async function addPageResources(page, customLogicOptions) { } } + // The actual injection of collected scripts for (const jsResource of injectedJs) { try { injectedResources.push(await page.addScriptTag(jsResource)); @@ -320,7 +322,7 @@ export async function addPageResources(page, customLogicOptions) { // Load CSS const injectedCss = []; if (resources.css) { - let cssImports = resources.css.match(/@import\s*([^;]*);/g); + const cssImports = resources.css.match(/@import\s*([^;]*);/g); if (cssImports) { // Handle css section for (let cssImportPath of cssImports) { @@ -353,6 +355,7 @@ export async function addPageResources(page, customLogicOptions) { content: resources.css.replace(/@import\s*([^;]*);/g, '') || ' ' }); + // The actual injection of collected CSS for (const cssResource of injectedCss) { try { injectedResources.push(await page.addStyleTag(cssResource)); @@ -380,7 +383,7 @@ export async function addPageResources(page, customLogicOptions) { * * @param {Object} page - The Puppeteer page object from which resources will * be cleared. - * @param {Array.} injectedResources - Array of injected resources + * @param {Array} injectedResources - Array of injected resources * to be cleared. */ export async function clearPageResources(page, injectedResources) { @@ -429,7 +432,7 @@ export async function clearPageResources(page, injectedResources) { } /** - * Sets the content for a Puppeteer Page using a predefined template + * Sets the content for a Puppeteer page using a predefined template * and additional scripts. * * @async @@ -440,7 +443,7 @@ export async function clearPageResources(page, injectedResources) { */ async function _setPageContent(page) { // Set the initial page content - await page.setContent(template, { waitUntil: 'domcontentloaded' }); + await page.setContent(pageTemplate, { waitUntil: 'domcontentloaded' }); // Add all registered Higcharts scripts, quite demanding await page.addScriptTag({ path: join(getCachePath(), 'sources.js') }); @@ -450,7 +453,7 @@ async function _setPageContent(page) { } /** - * Set events (like `pageerror` and `console`) for a Puppeteer Page in order + * Set events (like `pageerror` and `console`) for a Puppeteer page in order * to catch and display errors and console logs from the window context. * * @function _setPageEvents diff --git a/lib/envs.js b/lib/envs.js index 1939e9f2..3393aa2a 100644 --- a/lib/envs.js +++ b/lib/envs.js @@ -26,7 +26,7 @@ See LICENSE file in root for details. import dotenv from 'dotenv'; import { z } from 'zod'; -import { defaultConfig } from './schemas/config.js'; +import defaultConfig from './schemas/config.js'; // Load .env into environment variables dotenv.config(); diff --git a/lib/fetch.js b/lib/fetch.js index 6d249b92..db87f8c1 100644 --- a/lib/fetch.js +++ b/lib/fetch.js @@ -23,20 +23,21 @@ import http from 'http'; import https from 'https'; /** - * Fetches data from the specified URL using either HTTP or HTTPS protocol. + * Sends a GET request to the specified URL using either HTTP or HTTPS protocol. * * @async - * @function fetch + * @function get * - * @param {string} url - The URL to fetch data from. + * @param {string} url - The URL to get data from. * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request. * The default value is an empty object. * * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response * object with added 'text' property or rejecting with an error. */ -export async function fetch(url, requestOptions = {}) { +export async function get(url, requestOptions = {}) { return new Promise((resolve, reject) => { + // Decide on the protocol _getProtocolModule(url) .get(url, requestOptions, (response) => { let responseData = ''; @@ -51,6 +52,8 @@ export async function fetch(url, requestOptions = {}) { if (!responseData) { reject('Nothing was fetched from the URL.'); } + + // Get the full result and resolve the request response.text = responseData; resolve(response); }); @@ -81,7 +84,7 @@ export async function post(url, body = {}, requestOptions = {}) { return new Promise((resolve, reject) => { const data = JSON.stringify(body); - // Set default headers and merge with requestOptions + // Set default headers and merge with `requestOptions` const options = Object.assign( { method: 'POST', @@ -93,6 +96,7 @@ export async function post(url, body = {}, requestOptions = {}) { requestOptions ); + // Decide on the protocol const request = _getProtocolModule(url) .request(url, options, (response) => { let responseData = ''; @@ -105,6 +109,7 @@ export async function post(url, body = {}, requestOptions = {}) { // The whole response has been received response.on('end', () => { try { + // Get the full result and resolve the request response.text = responseData; resolve(response); } catch (error) { @@ -129,13 +134,13 @@ export async function post(url, body = {}, requestOptions = {}) { * * @param {string} url - The URL to determine the protocol. * - * @returns {Object} The HTTP or HTTPS protocol module (http or https). + * @returns {Object} The HTTP or HTTPS protocol module (`http` or `https`). */ function _getProtocolModule(url) { return url.startsWith('https') ? https : http; } export default { - fetch, + get, post }; diff --git a/lib/index.js b/lib/index.js index bf4cffca..07f7efa2 100644 --- a/lib/index.js +++ b/lib/index.js @@ -13,18 +13,20 @@ See LICENSE file in root for details. *******************************************************************************/ /** - * @overview Core module for initializing and managing the Highcharts Export - * Server. Provides functionalities for configuring exports, setting up server - * operations, logging, scripts caching, resource pooling, and graceful process - * cleanup. + * @overview This core module initializes and manages the Highcharts Export + * Server. The main `initExport` function handles logging, script caching, + * resource pooling, browser startup, and ensures graceful process cleanup + * on exit. Additionally, it provides API functions for using it as a Node.js + * module, offering functionalities for processing options, configuring + * and performing exports, and setting up server. */ import 'colors'; -import { checkAndUpdateCache } from './cache.js'; +import { checkCache } from './cache.js'; import { - singleExport, batchExport, + singleExport, startExport, setAllowCodeExecution } from './chart.js'; @@ -72,8 +74,8 @@ export async function initExport(initOptions) { _attachProcessExitListeners(); } - // Check if cache needs to be updated - await checkAndUpdateCache(options.highcharts, options.server.proxy); + // Check the current status of cache + await checkCache(options.highcharts, options.server.proxy); // Init the pool await initPool(options.pool, options.puppeteer.args); @@ -81,7 +83,7 @@ export async function initExport(initOptions) { /** * Attaches exit listeners to the process, ensuring proper cleanup of resources - * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM' + * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM', * and 'uncaughtException' events. * * @function _attachProcessExitListeners @@ -91,7 +93,7 @@ function _attachProcessExitListeners() { // Handler for the 'exit' process.on('exit', (code) => { - log(4, `[process] Process exited with code ${code}.`); + log(4, `[process] Process exited with code: ${code}.`); }); // Handler for the 'SIGINT' diff --git a/lib/logger.js b/lib/logger.js index 0f7e9ef6..5972777c 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -105,8 +105,8 @@ export function log(...args) { } /** - * Logs an error message along with its stack trace. Optionally, a custom message - * can be provided. + * Logs an error message along with its stack trace. Optionally, a custom + * message can be provided. * * @function logWithStack * @@ -233,8 +233,8 @@ export function enableFileLogging(dest, file, toFile) { // Set the `dest` and `file` options only if the file logging is enabled if (logging.toFile) { - logging.dest = dest || ''; - logging.file = file || ''; + logging.dest = dest || 'log'; + logging.file = file || 'highcharts-export-server.log'; } } @@ -245,7 +245,7 @@ export function enableFileLogging(dest, file, toFile) { * * @function _logToFile * - * @param {Array.} texts - An array of texts to be logged. + * @param {Array} texts - An array of texts to be logged. * @param {string} prefix - An optional prefix to be added to each log entry. */ function _logToFile(texts, prefix) { diff --git a/lib/pool.js b/lib/pool.js index a67766b2..d3847748 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -55,7 +55,7 @@ const poolStats = { * * @param {Object} poolOptions - The configuration object containing `pool` * options. - * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer + * @param {Array} puppeteerArgs - Additional arguments for Puppeteer * launch. * * @returns {Promise} A Promise that resolves to ending the function @@ -208,7 +208,7 @@ export async function postWork(options) { // Display the pool information if needed if (options.pool.benchmarking) { - getPoolInfo(); + _getPoolInfo(); } // Throw an error in case of lacking the pool instance @@ -256,9 +256,6 @@ export async function postWork(options) { ); } - // Save the start time - const workStart = getNewDateTime(); - log( 4, `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.` @@ -268,14 +265,14 @@ export async function postWork(options) { const exportCounter = measureTime(); // Perform an export on a puppeteer level - const result = await puppeteerExport( + const exportResult = await puppeteerExport( workerHandle.page, options.export, options.customLogic ); // Check if it's an error - if (result instanceof Error) { + if (exportResult instanceof Error) { // NOTE: // If there's a rasterization timeout, we want need to flush the page. // This is because the page may be in a state where it's waiting for @@ -283,32 +280,31 @@ export async function postWork(options) { // Which of course causes a lot of issues with the event system, // and page consistency. // - // NOTE: - // Only page.screenshot will throw this, timeouts for PDF's are - // handled by the page.pdf function itself. + // Only `page.screenshot` will throw this, timeouts for PDF's are + // handled by the `page.pdf` function itself. // // ...yes, this is ugly. - if (result.message === 'Rasterization timeout') { + if (exportResult.message === 'Rasterization timeout') { // Set the `workLimit` to exceeded in order to recreate the resource workerHandle.workCount = options.pool.workLimit + 1; workerHandle.page = null; } if ( - result.name === 'TimeoutError' || - result.message === 'Rasterization timeout' + exportResult.name === 'TimeoutError' || + exportResult.message === 'Rasterization timeout' ) { throw new ExportError( `[pool] ${ options.requestId ? `Request [${options.requestId}] - ` : '' }Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.` - ).setError(result); + ).setError(exportResult); } else { throw new ExportError( `[pool] ${ options.requestId ? `Request [${options.requestId}] - ` : '' }Error encountered during export: ${exportCounter()}ms.` - ).setError(result); + ).setError(exportResult); } } @@ -324,25 +320,22 @@ export async function postWork(options) { // Release the resource back to the pool pool.release(workerHandle); - // Used for statistics in averageTime and processedWorkCount, which - // in turn is used by the /health route. - const workEnd = getNewDateTime(); - const exportTime = workEnd - workStart; - - poolStats.timeSpent += exportTime; + // Update statistics + poolStats.timeSpent += exportCounter(); poolStats.timeSpentAverage = poolStats.timeSpent / ++poolStats.exportsPerformed; - log(4, `[pool] Work completed in ${exportTime}ms.`); + log(4, `[pool] Work completed in ${exportCounter()}ms.`); - // Otherwise return the result + // Otherwise return an object with the result and options return { - result, + result: exportResult, options }; } catch (error) { ++poolStats.exportsDropped; + // Try to release the worker, if it exists if (workerHandle) { pool.release(workerHandle); } @@ -356,7 +349,7 @@ export async function postWork(options) { * * @function getPool * - * @returns {(Object|null)} The current pool instance if initialized, or null + * @returns {(Object|null)} The current pool instance if initialized, or `null` * if the pool has not been created. */ export function getPool() { @@ -408,9 +401,9 @@ export function getPoolInfoJSON() { * and maximum workers, available workers, workers in use, and pending acquire * requests. * - * @function getPoolInfo + * @function _getPoolInfo */ -export function getPoolInfo() { +function _getPoolInfo() { const { min, max, @@ -452,8 +445,8 @@ export function getPoolInfo() { } /** - * Factory function that returns an object with `create`, `validate`, - * and `destroy` functions for the pool instance. + * Factory function that returns an object with `create`, `validate`, `destroy` + * functions for the pool instance. * * @function _factory * @@ -524,7 +517,7 @@ function _factory(poolOptions) { */ validate: async (poolResource) => { // NOTE: - // In certain cases acquiring throws a TargetCloseError, which may + // In certain cases acquiring throws a `TargetCloseError`, which may // be caused by two things: // - The page is closed and attempted to be reused. // - Lost contact with the browser. @@ -623,6 +616,5 @@ export default { postWork, getPool, getPoolStats, - getPoolInfo, getPoolInfoJSON }; diff --git a/lib/prompt.js b/lib/prompt.js index 794d74b2..3d018b8b 100644 --- a/lib/prompt.js +++ b/lib/prompt.js @@ -13,9 +13,8 @@ See LICENSE file in root for details. *******************************************************************************/ /** - * @overview This module provides a streamlined manual configuration feature - * that prompts users to customize and save their desired settings into - * a configuration file. + * @overview This module provides a manual configuration feature that prompts + * users to customize and save their desired settings into a configuration file. */ import { existsSync, readFileSync, writeFileSync } from 'fs'; @@ -26,7 +25,7 @@ import { isAllowedConfig } from './config.js'; import { log, logWithStack } from './logger.js'; import { getAbsolutePath } from './utils.js'; -import { defaultConfig } from './schemas/config.js'; +import defaultConfig from './schemas/config.js'; /** * Initiates a manual configuration process, prompting the user to configure @@ -36,24 +35,23 @@ import { defaultConfig } from './schemas/config.js'; * @async * @function manualConfig * - * @param {string} configFileName - The name of the configuration file to save - * to. + * @param {string} fileName - The name of the configuration file to save to. * @param {boolean} allowCodeExecution - A flag indicating whether code * execution is allowed. * * @returns {Promise} A Promise that resolves to true once the manual * configuration process is completed and the updated configuration is saved. */ -export async function manualConfig(configFileName, allowCodeExecution) { +export async function manualConfig(fileName, allowCodeExecution) { // Initialize an empty object to hold the config data let configFile = {}; // Check if the specified configuration file already exists - if (existsSync(getAbsolutePath(configFileName))) { + if (existsSync(getAbsolutePath(fileName))) { try { // If the file exists, read and parse its contents configFile = isAllowedConfig( - readFileSync(getAbsolutePath(configFileName), 'utf8'), + readFileSync(getAbsolutePath(fileName), 'utf8'), false, allowCodeExecution ); @@ -73,9 +71,9 @@ export async function manualConfig(configFileName, allowCodeExecution) { * @function onSubmit * * @param {unknown} _ - Unused, automatically passed argument. - * @param {Array.} categories - The selected categories to configure. + * @param {Array} categories - The selected categories to configure. * - * @returns {Promise} A Promise that resolves to true once + * @returns {Promise} A Promise that resolves to `true` once * the configuration process is completed and saved. */ const onSubmit = async (_, categories) => { @@ -167,7 +165,7 @@ export async function manualConfig(configFileName, allowCodeExecution) { try { // Save the prompt result writeFileSync( - getAbsolutePath(configFileName), + getAbsolutePath(fileName), isAllowedConfig(configFile, true, allowCodeExecution), 'utf8' ); @@ -175,7 +173,7 @@ export async function manualConfig(configFileName, allowCodeExecution) { logWithStack( 1, error, - `[prompt] An error occurred while creating the ${configFileName} file.` + `[prompt] An error occurred while creating the ${fileName} file.` ); } return true; @@ -213,7 +211,7 @@ export async function manualConfig(configFileName, allowCodeExecution) { * * @function _preparePrompt * - * @param {Array.[string, Object]} entry - An array where the first element + * @param {Array[string, Object]} entry - An array where the first element * is the name of the option, and the second element is an object containing * the option details. * @param {string} section - The section name for the prompt, used to categorize @@ -275,7 +273,7 @@ function _preparePrompt(entry, section) { * * @param {Object} objectToUpdate - The object in which nested properties * will be updated or created. - * @param {Array.} nestedNames - An array of property names representing + * @param {Array} nestedNames - An array of property names representing * the nesting hierarchy. * @param {unknown} value - The final value to be assigned to the deepest nested * property. diff --git a/lib/sanitize.js b/lib/sanitize.js index e1e445e1..64710c33 100644 --- a/lib/sanitize.js +++ b/lib/sanitize.js @@ -38,7 +38,7 @@ export function sanitize(input) { // Create a purifying instance const purify = DOMPurify(window); - // Return sanitized input + // Return sanitized input, allowing for the `foreignObject` elements return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] }); } diff --git a/lib/server/middlewares/rateLimiting.js b/lib/server/middlewares/rateLimiting.js index d64278f2..b7e23694 100644 --- a/lib/server/middlewares/rateLimiting.js +++ b/lib/server/middlewares/rateLimiting.js @@ -27,7 +27,6 @@ import ExportError from '../../errors/ExportError.js'; * Middleware for enabling rate limiting on the specified Express app. * * @param {Express} app - The Express app instance. - * * @param {Object} rateLimitingOptions - The configuration object containing * `rateLimiting` options. * diff --git a/lib/server/routes/health.js b/lib/server/routes/health.js index 1e50ab42..dd9345fa 100644 --- a/lib/server/routes/health.js +++ b/lib/server/routes/health.js @@ -20,7 +20,7 @@ See LICENSE file in root for details. import { readFileSync } from 'fs'; import { join } from 'path'; -import { getHighchartsVersion } from '../../cache.js'; +import { getHcVersion } from '../../cache.js'; import { log } from '../../logger.js'; import { getPoolStats, getPoolInfoJSON } from '../../pool.js'; import { addTimer } from '../../timer.js'; @@ -110,7 +110,7 @@ export default function healthRoutes(app) { // Versions serverVersion: packageFile.version, - highchartsVersion: getHighchartsVersion(), + highchartsVersion: getHcVersion(), // Exports averageExportTime: stats.timeSpentAverage, diff --git a/lib/server/routes/versionChange.js b/lib/server/routes/versionChange.js index 4e20e4c9..976a1ccb 100644 --- a/lib/server/routes/versionChange.js +++ b/lib/server/routes/versionChange.js @@ -17,7 +17,7 @@ See LICENSE file in root for details. * on the server, with authentication and validation. */ -import { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js'; +import { getHcVersion, updateHcVersion } from '../../cache.js'; import { envs } from '../../envs.js'; import { log } from '../../logger.js'; @@ -67,7 +67,7 @@ export default function versionChangeRoutes(app) { // Update version if (newVersion) { try { - await updateHighchartsVersion(newVersion); + await updateHcVersion(newVersion); } catch (error) { throw new ExportError( `[version] Version change: ${error.message}`, @@ -78,7 +78,7 @@ export default function versionChangeRoutes(app) { // Success response.status(200).send({ statusCode: 200, - highchartsVersion: getHighchartsVersion(), + highchartsVersion: getHcVersion(), message: `Successfully updated Highcharts to version: ${newVersion}.` }); } else { diff --git a/lib/server/server.js b/lib/server/server.js index 3a07e0b4..d1120384 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -15,7 +15,7 @@ See LICENSE file in root for details. /** * @overview A module that sets up and manages HTTP and HTTPS servers * for the Highcharts Export Server. It handles server initialization, - * configuration, error handling, middleware setup, route definition, and rate + * configuration, error handling, middlewares setup, route definition, and rate * limiting. The module exports functions to start, stop, and manage server * instances, as well as utility functions for defining routes and attaching * middlewares. @@ -253,7 +253,7 @@ export function closeServers() { * * @function getServers * - * @returns {Array.} Servers associated with Express app instance. + * @returns {Array} Servers associated with Express app instance. */ export function getServers() { return activeServers; diff --git a/lib/timer.js b/lib/timer.js index 8b74c825..2b777996 100644 --- a/lib/timer.js +++ b/lib/timer.js @@ -15,9 +15,8 @@ See LICENSE file in root for details. /** * @overview This module provides utility functions for managing intervals * and timeouts in a centralized manner. It maintains a registry of all active - * timers and allows for their efficient cleanup when needed. This can be useful - * in applications where proper resource management and clean shutdown of timers - * are critical to avoid memory leaks or unintended behavior. + * timers and allows for their efficient cleanup when needed to avoid potential + * memory leaks, unintended behavior or a process from being stopped. */ import { log } from './logger.js'; diff --git a/tests/http/httpTestRunner.js b/tests/http/httpTestRunner.js index 37508214..c7414ee2 100644 --- a/tests/http/httpTestRunner.js +++ b/tests/http/httpTestRunner.js @@ -24,7 +24,7 @@ import { join } from 'path'; import 'colors'; -import { fetch } from '../../lib/fetch.js'; +import { get } from '../../lib/fetch.js'; import { __dirname, clearText, getNewDateTime } from '../../lib/utils.js'; // Test runner message @@ -40,7 +40,7 @@ console.log( const url = 'http://127.0.0.1:7801'; // Perform a health check before continuing -fetch(`${url}/health`) +get(`${url}/health`) .then(() => { // Results and scenarios paths const resultsPath = join(__dirname, 'tests', 'http', '_results'); diff --git a/tests/http/httpTestRunnerSingle.js b/tests/http/httpTestRunnerSingle.js index 2453df4f..bc563ee8 100644 --- a/tests/http/httpTestRunnerSingle.js +++ b/tests/http/httpTestRunnerSingle.js @@ -18,7 +18,7 @@ import { basename, join } from 'path'; import 'colors'; -import { fetch } from '../../lib/fetch.js'; +import { get } from '../../lib/fetch.js'; import { __dirname, clearText, getNewDateTime } from '../../lib/utils.js'; // Test runner message @@ -34,7 +34,7 @@ console.log( const url = 'http://127.0.0.1:7801'; // Perform a health check before continuing -fetch(`${url}/health`) +get(`${url}/health`) .then(() => { // Results path const resultsPath = join(__dirname, 'tests', 'http', '_results'); diff --git a/tests/other/sideBySide.js b/tests/other/sideBySide.js index 84a172c0..3a2d3679 100644 --- a/tests/other/sideBySide.js +++ b/tests/other/sideBySide.js @@ -12,7 +12,7 @@ See LICENSE file in root for details. *******************************************************************************/ -import { fetch } from '../../lib/fetch.js'; +import { get } from '../../lib/fetch.js'; import { exec as spawn } from 'child_process'; import { existsSync, mkdirSync } from 'fs'; import { join } from 'path'; @@ -41,7 +41,7 @@ try { // Run for both servers for (const [index, url] of urls.entries()) { // Perform a health check before continuing - fetch(`${url}/health`) + get(`${url}/health`) .then(() => { // And all types for (const type of ['png', 'jpeg', 'svg', 'pdf']) { diff --git a/tests/other/stressTest.js b/tests/other/stressTest.js index 7f548bdf..07542be4 100644 --- a/tests/other/stressTest.js +++ b/tests/other/stressTest.js @@ -14,7 +14,7 @@ See LICENSE file in root for details. import 'colors'; -import { fetch, post } from '../../lib/fetch.js'; +import { get, post } from '../../lib/fetch.js'; import { getNewDateTime } from '../../lib/utils.js'; // Test message @@ -63,7 +63,7 @@ const stressTest = () => { }; // Perform a health check before continuing -fetch(`${url}/health`) +get(`${url}/health`) .then(() => { stressTest(); setInterval(stressTest, interval); diff --git a/tests/unit/utils.test.js b/tests/unit/utils.test.js index c8ecb954..64c4ca00 100644 --- a/tests/unit/utils.test.js +++ b/tests/unit/utils.test.js @@ -16,7 +16,6 @@ import { describe, expect, it } from '@jest/globals'; import { clearText, - fixType, roundNumber, toBoolean, isObject, @@ -32,16 +31,6 @@ describe('clearText', () => { }); }); -describe('fixType', () => { - it('corrects the export type based on file extension', () => { - expect(fixType('image/jpeg', 'output.png')).toBe('png'); - }); - - it('returns the original type if no outfile is provided', () => { - expect(fixType('pdf')).toBe('pdf'); - }); -}); - describe('roundNumber', () => { it('rounds a number to the specified precision', () => { expect(roundNumber(3.14159, 2)).toBe(3.14); From 9ea61e3ba7b57a14813b10a599f5ae14726debca Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:26:08 +0100 Subject: [PATCH 098/102] Removed unnecessary tests. --- tests/unit/cache.test.js | 39 --------------------------------------- tests/unit/index.test.js | 24 ------------------------ 2 files changed, 63 deletions(-) delete mode 100644 tests/unit/cache.test.js delete mode 100644 tests/unit/index.test.js diff --git a/tests/unit/cache.test.js b/tests/unit/cache.test.js deleted file mode 100644 index 105274e6..00000000 --- a/tests/unit/cache.test.js +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - -Highcharts Export Server - -Copyright (c) 2016-2025, Highsoft - -Licenced under the MIT licence. - -Additionally a valid Highcharts license is required for use. - -See LICENSE file in root for details. - -*******************************************************************************/ - -import { describe, expect, it } from '@jest/globals'; - -import { extractVersion, extractModuleName } from '../../lib/cache'; - -describe('extractVersion', () => { - it('should extract the Highcharts version correctly', () => { - const cache = { sources: '/* Highcharts 9.3.2 */' }; - - const version = extractVersion(cache.sources); - expect(version).toBe('Highcharts 9.3.2'); - }); -}); - -describe('extractModuleName', () => { - it('should extract the module name from a given script path', () => { - const paths = [ - { input: 'modules/exporting', expected: 'exporting' }, - { input: 'maps/modules/map', expected: 'map' } - ]; - - paths.forEach(({ input, expected }) => { - expect(extractModuleName(input)).toBe(expected); - }); - }); -}); diff --git a/tests/unit/index.test.js b/tests/unit/index.test.js deleted file mode 100644 index b92c2dd3..00000000 --- a/tests/unit/index.test.js +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* - -Highcharts Export Server - -Copyright (c) 2016-2025, Highsoft - -Licenced under the MIT licence. - -Additionally a valid Highcharts license is required for use. - -See LICENSE file in root for details. - -*******************************************************************************/ - -import { describe, expect, it } from '@jest/globals'; - -describe('Simple Variable Comparison', () => { - it('should compare two variables for equality', () => { - const variable1 = 42; - const variable2 = 42; - - expect(variable1).toBe(variable2); - }); -}); From bac641b90abd5a1ae8363f8bf7d2acfa667a4227 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:26:27 +0100 Subject: [PATCH 099/102] Updated package.json. --- package-lock.json | 442 ++++++++++++++++++++++------------------------ package.json | 6 +- 2 files changed, 218 insertions(+), 230 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7940663c..ae550f9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "jsdom": "^26.0.0", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^24.1.0", + "puppeteer": "^24.1.1", "tarn": "^3.0.2", "uuid": "^11.0.5", "zod": "^3.24.1" @@ -36,10 +36,10 @@ "eslint-plugin-prettier": "^5.2.3", "husky": "^9.1.7", "jest": "^29.7.0", - "lint-staged": "^15.4.1", + "lint-staged": "^15.4.3", "nodemon": "^3.1.9", "prettier": "^3.4.2", - "rollup": "^4.31.0" + "rollup": "^4.34.0" }, "engines": { "node": ">=18.12.0" @@ -103,22 +103,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -239,27 +239,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", - "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.5" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -523,17 +523,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", - "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.5", + "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", - "@babel/types": "^7.26.5", + "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -552,9 +552,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", - "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", "dev": true, "license": "MIT", "dependencies": { @@ -1337,9 +1337,9 @@ } }, "node_modules/@puppeteer/browsers/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1372,9 +1372,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.31.0.tgz", - "integrity": "sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.0.tgz", + "integrity": "sha512-Eeao7ewDq79jVEsrtWIj5RNqB8p2knlm9fhR6uJ2gqP7UfbLrTrxevudVrEPDM7Wkpn/HpRC2QfazH7MXLz3vQ==", "cpu": [ "arm" ], @@ -1386,9 +1386,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.31.0.tgz", - "integrity": "sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.0.tgz", + "integrity": "sha512-yVh0Kf1f0Fq4tWNf6mWcbQBCLDpDrDEl88lzPgKhrgTcDrTtlmun92ywEF9dCjmYO3EFiSuJeeo9cYRxl2FswA==", "cpu": [ "arm64" ], @@ -1400,9 +1400,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.31.0.tgz", - "integrity": "sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.0.tgz", + "integrity": "sha512-gCs0ErAZ9s0Osejpc3qahTsqIPUDjSKIyxK/0BGKvL+Tn0n3Kwvj8BrCv7Y5sR1Ypz1K2qz9Ny0VvkVyoXBVUQ==", "cpu": [ "arm64" ], @@ -1414,9 +1414,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.31.0.tgz", - "integrity": "sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.0.tgz", + "integrity": "sha512-aIB5Anc8hngk15t3GUkiO4pv42ykXHfmpXGS+CzM9CTyiWyT8HIS5ygRAy7KcFb/wiw4Br+vh1byqcHRTfq2tQ==", "cpu": [ "x64" ], @@ -1428,9 +1428,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.31.0.tgz", - "integrity": "sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.0.tgz", + "integrity": "sha512-kpdsUdMlVJMRMaOf/tIvxk8TQdzHhY47imwmASOuMajg/GXpw8GKNd8LNwIHE5Yd1onehNpcUB9jHY6wgw9nHQ==", "cpu": [ "arm64" ], @@ -1442,9 +1442,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.31.0.tgz", - "integrity": "sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.0.tgz", + "integrity": "sha512-D0RDyHygOBCQiqookcPevrvgEarN0CttBecG4chOeIYCNtlKHmf5oi5kAVpXV7qs0Xh/WO2RnxeicZPtT50V0g==", "cpu": [ "x64" ], @@ -1456,9 +1456,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.31.0.tgz", - "integrity": "sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.0.tgz", + "integrity": "sha512-mCIw8j5LPDXmCOW8mfMZwT6F/Kza03EnSr4wGYEswrEfjTfVsFOxvgYfuRMxTuUF/XmRb9WSMD5GhCWDe2iNrg==", "cpu": [ "arm" ], @@ -1470,9 +1470,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.31.0.tgz", - "integrity": "sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.0.tgz", + "integrity": "sha512-AwwldAu4aCJPob7zmjuDUMvvuatgs8B/QiVB0KwkUarAcPB3W+ToOT+18TQwY4z09Al7G0BvCcmLRop5zBLTag==", "cpu": [ "arm" ], @@ -1484,9 +1484,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.31.0.tgz", - "integrity": "sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.0.tgz", + "integrity": "sha512-e7kDUGVP+xw05pV65ZKb0zulRploU3gTu6qH1qL58PrULDGxULIS0OSDQJLH7WiFnpd3ZKUU4VM3u/Z7Zw+e7Q==", "cpu": [ "arm64" ], @@ -1498,9 +1498,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.31.0.tgz", - "integrity": "sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.0.tgz", + "integrity": "sha512-SXYJw3zpwHgaBqTXeAZ31qfW/v50wq4HhNVvKFhRr5MnptRX2Af4KebLWR1wpxGJtLgfS2hEPuALRIY3LPAAcA==", "cpu": [ "arm64" ], @@ -1512,9 +1512,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.31.0.tgz", - "integrity": "sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.0.tgz", + "integrity": "sha512-e5XiCinINCI4RdyU3sFyBH4zzz7LiQRvHqDtRe9Dt8o/8hTBaYpdPimayF00eY2qy5j4PaaWK0azRgUench6WQ==", "cpu": [ "loong64" ], @@ -1526,9 +1526,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.31.0.tgz", - "integrity": "sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.0.tgz", + "integrity": "sha512-3SWN3e0bAsm9ToprLFBSro8nJe6YN+5xmB11N4FfNf92wvLye/+Rh5JGQtKOpwLKt6e61R1RBc9g+luLJsc23A==", "cpu": [ "ppc64" ], @@ -1540,9 +1540,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.31.0.tgz", - "integrity": "sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.0.tgz", + "integrity": "sha512-B1Oqt3GLh7qmhvfnc2WQla4NuHlcxAD5LyueUi5WtMc76ZWY+6qDtQYqnxARx9r+7mDGfamD+8kTJO0pKUJeJA==", "cpu": [ "riscv64" ], @@ -1554,9 +1554,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.31.0.tgz", - "integrity": "sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.0.tgz", + "integrity": "sha512-UfUCo0h/uj48Jq2lnhX0AOhZPSTAq3Eostas+XZ+GGk22pI+Op1Y6cxQ1JkUuKYu2iU+mXj1QjPrZm9nNWV9rg==", "cpu": [ "s390x" ], @@ -1568,9 +1568,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.31.0.tgz", - "integrity": "sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.0.tgz", + "integrity": "sha512-chZLTUIPbgcpm+Z7ALmomXW8Zh+wE2icrG+K6nt/HenPLmtwCajhQC5flNSk1Xy5EDMt/QAOz2MhzfOfJOLSiA==", "cpu": [ "x64" ], @@ -1582,9 +1582,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.31.0.tgz", - "integrity": "sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.0.tgz", + "integrity": "sha512-jo0UolK70O28BifvEsFD/8r25shFezl0aUk2t0VJzREWHkq19e+pcLu4kX5HiVXNz5qqkD+aAq04Ct8rkxgbyQ==", "cpu": [ "x64" ], @@ -1596,9 +1596,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.31.0.tgz", - "integrity": "sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.0.tgz", + "integrity": "sha512-Vmg0NhAap2S54JojJchiu5An54qa6t/oKT7LmDaWggpIcaiL8WcWHEN6OQrfTdL6mQ2GFyH7j2T5/3YPEDOOGA==", "cpu": [ "arm64" ], @@ -1610,9 +1610,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.31.0.tgz", - "integrity": "sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.0.tgz", + "integrity": "sha512-CV2aqhDDOsABKHKhNcs1SZFryffQf8vK2XrxP6lxC99ELZAdvsDgPklIBfd65R8R+qvOm1SmLaZ/Fdq961+m7A==", "cpu": [ "ia32" ], @@ -1624,9 +1624,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.31.0.tgz", - "integrity": "sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.0.tgz", + "integrity": "sha512-g2ASy1QwHP88y5KWvblUolJz9rN+i4ZOsYzkEwcNfaNooxNUXG+ON6F5xFo0NIItpHqxcdAyls05VXpBnludGw==", "cpu": [ "x64" ], @@ -1774,9 +1774,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", - "integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", + "version": "22.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", + "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1818,9 +1818,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", - "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, "license": "ISC" }, @@ -2102,6 +2102,16 @@ "node": ">=4" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2592,9 +2602,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001695", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", - "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", + "version": "1.0.30001696", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001696.tgz", + "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==", "dev": true, "funding": [ { @@ -2678,9 +2688,9 @@ } }, "node_modules/chromium-bidi": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.12.0.tgz", - "integrity": "sha512-xzXveJmX826GGq1MeE5okD8XxaDT8172CXByhFJ687eY65rbjOIebdbUuQh+jXKaNyGKI14Veb3KjLLmSueaxA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-1.1.0.tgz", + "integrity": "sha512-HislCEczCuamWm3+55Lig9XKmMF13K+BGKum9rwtDAzgUAHT4h5jNwhDmD4U20VoVUG8ujnv9UZ89qiIf5uF8w==", "license": "Apache-2.0", "dependencies": { "mitt": "3.0.1", @@ -2707,9 +2717,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true, "license": "MIT" }, @@ -2871,9 +2881,9 @@ } }, "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "license": "MIT", "engines": { @@ -3134,9 +3144,9 @@ } }, "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "license": "MIT" }, "node_modules/dedent": { @@ -3327,9 +3337,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.85", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.85.tgz", - "integrity": "sha512-UgTI7ZHxtSjOUwV0vZLpqT604U1Z8L3bq8mAtAKtuRPlMZ/6dLFMYgYnLdXSi/urbVTP2ykDb9EDDUrdIzw4Qg==", + "version": "1.5.90", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.90.tgz", + "integrity": "sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug==", "dev": true, "license": "ISC" }, @@ -4128,9 +4138,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "dev": true, "license": "ISC", "dependencies": { @@ -4255,13 +4265,19 @@ "license": "ISC" }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", + "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/form-data": { @@ -4805,9 +4821,9 @@ "license": "ISC" }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -4930,12 +4946,13 @@ "license": "MIT" }, "node_modules/is-async-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", - "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { + "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", @@ -5394,9 +5411,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -5933,9 +5950,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -6226,22 +6243,22 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.4.1.tgz", - "integrity": "sha512-P8yJuVRyLrm5KxCtFx+gjI5Bil+wO7wnTl7C3bXhvtTaAFGirzeB24++D0wGoUwxrUKecNiehemgCob9YL39NA==", + "version": "15.4.3", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.4.3.tgz", + "integrity": "sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "~5.4.1", - "commander": "~12.1.0", - "debug": "~4.4.0", - "execa": "~8.0.1", - "lilconfig": "~3.1.3", - "listr2": "~8.2.5", - "micromatch": "~4.0.8", - "pidtree": "~0.6.0", - "string-argv": "~0.3.2", - "yaml": "~2.6.1" + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -6589,9 +6606,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -6867,9 +6884,9 @@ } }, "node_modules/nodemon/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -7563,17 +7580,17 @@ } }, "node_modules/puppeteer": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.1.0.tgz", - "integrity": "sha512-F+3yKILaosLToT7amR7LIkTKkKMR0EGQPjFBch+MtgS8vRPS+4cPnLJuXDVTfCj2NqfrCnShtOr7yD+9dEgHRQ==", + "version": "24.1.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.1.1.tgz", + "integrity": "sha512-fuhceZ5HZuDXVuaMIRxUuDHfCJLmK0pXh8FlzVQ0/+OApStevxZhU5kAVeYFOEqeCF5OoAyZjcWbdQK27xW/9A==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.7.0", - "chromium-bidi": "0.12.0", + "chromium-bidi": "1.1.0", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1380148", - "puppeteer-core": "24.1.0", + "puppeteer-core": "24.1.1", "typed-query-selector": "^2.12.0" }, "bin": { @@ -7584,13 +7601,13 @@ } }, "node_modules/puppeteer-core": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.1.0.tgz", - "integrity": "sha512-ReefWoQgqdyl67uWEBy/TMZ4mAB7hP0JB5HIxSE8B1ot/4ningX1gmzHCOSNfMbTiS/VJHCvaZAe3oJTXph7yw==", + "version": "24.1.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.1.1.tgz", + "integrity": "sha512-7FF3gq6bpIsbq3I8mfbodXh3DCzXagoz3l2eGv1cXooYU4g0P4mcHQVHuBD4iSZPXNg8WjzlP5kmRwK9UvwF0A==", "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "2.7.0", - "chromium-bidi": "0.11.0", + "chromium-bidi": "1.1.0", "debug": "^4.4.0", "devtools-protocol": "0.0.1380148", "typed-query-selector": "^2.12.0", @@ -7600,28 +7617,6 @@ "node": ">=18" } }, - "node_modules/puppeteer-core/node_modules/chromium-bidi": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz", - "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==", - "license": "Apache-2.0", - "dependencies": { - "mitt": "3.0.1", - "zod": "3.23.8" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, - "node_modules/puppeteer-core/node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -7675,12 +7670,6 @@ ], "license": "MIT" }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "license": "MIT" - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -7954,9 +7943,9 @@ } }, "node_modules/rollup": { - "version": "4.31.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.31.0.tgz", - "integrity": "sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==", + "version": "4.34.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.0.tgz", + "integrity": "sha512-+4C/cgJ9w6sudisA0nZz0+O7lTP9a3CzNLsoDwaRumM8QHwghUsu6tqHXiTmNUp/rqNiM14++7dkzHDyCRs0Jg==", "dev": true, "license": "MIT", "dependencies": { @@ -7970,25 +7959,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.31.0", - "@rollup/rollup-android-arm64": "4.31.0", - "@rollup/rollup-darwin-arm64": "4.31.0", - "@rollup/rollup-darwin-x64": "4.31.0", - "@rollup/rollup-freebsd-arm64": "4.31.0", - "@rollup/rollup-freebsd-x64": "4.31.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.31.0", - "@rollup/rollup-linux-arm-musleabihf": "4.31.0", - "@rollup/rollup-linux-arm64-gnu": "4.31.0", - "@rollup/rollup-linux-arm64-musl": "4.31.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.31.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.31.0", - "@rollup/rollup-linux-riscv64-gnu": "4.31.0", - "@rollup/rollup-linux-s390x-gnu": "4.31.0", - "@rollup/rollup-linux-x64-gnu": "4.31.0", - "@rollup/rollup-linux-x64-musl": "4.31.0", - "@rollup/rollup-win32-arm64-msvc": "4.31.0", - "@rollup/rollup-win32-ia32-msvc": "4.31.0", - "@rollup/rollup-win32-x64-msvc": "4.31.0", + "@rollup/rollup-android-arm-eabi": "4.34.0", + "@rollup/rollup-android-arm64": "4.34.0", + "@rollup/rollup-darwin-arm64": "4.34.0", + "@rollup/rollup-darwin-x64": "4.34.0", + "@rollup/rollup-freebsd-arm64": "4.34.0", + "@rollup/rollup-freebsd-x64": "4.34.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.0", + "@rollup/rollup-linux-arm-musleabihf": "4.34.0", + "@rollup/rollup-linux-arm64-gnu": "4.34.0", + "@rollup/rollup-linux-arm64-musl": "4.34.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.0", + "@rollup/rollup-linux-riscv64-gnu": "4.34.0", + "@rollup/rollup-linux-s390x-gnu": "4.34.0", + "@rollup/rollup-linux-x64-gnu": "4.34.0", + "@rollup/rollup-linux-x64-musl": "4.34.0", + "@rollup/rollup-win32-arm64-msvc": "4.34.0", + "@rollup/rollup-win32-ia32-msvc": "4.34.0", + "@rollup/rollup-win32-x64-msvc": "4.34.0", "fsevents": "~2.3.2" } }, @@ -8383,9 +8372,9 @@ } }, "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", "dev": true, "license": "ISC", "bin": { @@ -8554,13 +8543,12 @@ } }, "node_modules/streamx": { - "version": "2.21.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", - "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz", + "integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==", "license": "MIT", "dependencies": { "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", "text-decoder": "^1.1.0" }, "optionalDependencies": { @@ -8915,21 +8903,21 @@ "license": "MIT" }, "node_modules/tldts": { - "version": "6.1.74", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.74.tgz", - "integrity": "sha512-O5vTZ1UmmEmrLl/59U9igitnSMlprALLaLgbv//dEvjobPT9vyURhHXKMCDLEhn3qxZFIkb9PwAfNYV0Ol7RPQ==", + "version": "6.1.76", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.76.tgz", + "integrity": "sha512-6U2ti64/nppsDxQs9hw8ephA3nO6nSQvVVfxwRw8wLQPFtLI1cFI1a1eP22g+LUP+1TA2pKKjUTwWB+K2coqmQ==", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.74" + "tldts-core": "^6.1.76" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.74", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.74.tgz", - "integrity": "sha512-gTwtY6L2GfuxiL4CWpLknv9JDYYqBvKCk/BT5uAaAvCA0s6pzX7lr2IrkQZSUlnSjRHIjTl8ZwKCVXJ7XNRWYw==", + "version": "6.1.76", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.76.tgz", + "integrity": "sha512-uzhJ02RaMzgQR3yPoeE65DrcHI6LoM4saUqXOt/b5hmb3+mc4YWpdSeAQqVqRUlQ14q8ZuLRWyBR1ictK1dzzg==", "license": "MIT" }, "node_modules/tmpl": { @@ -9661,9 +9649,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", - "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true, "license": "ISC", "bin": { diff --git a/package.json b/package.json index 518930a1..ad8893cf 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "jsdom": "^26.0.0", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^24.1.0", + "puppeteer": "^24.1.1", "tarn": "^3.0.2", "uuid": "^11.0.5", "zod": "^3.24.1" @@ -67,9 +67,9 @@ "eslint-plugin-prettier": "^5.2.3", "husky": "^9.1.7", "jest": "^29.7.0", - "lint-staged": "^15.4.1", + "lint-staged": "^15.4.3", "nodemon": "^3.1.9", "prettier": "^3.4.2", - "rollup": "^4.31.0" + "rollup": "^4.34.0" } } From cce01a66b05f2c7eed58868996201b3e3ee5d638 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 3 Feb 2025 00:27:07 +0100 Subject: [PATCH 100/102] Built scripts. --- dist/index.cjs | 4 ++-- dist/index.esm.js | 2 +- dist/index.esm.js.map | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/index.cjs b/dist/index.cjs index 02abf87a..f85346b5 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),url=require("url"),dotenv=require("dotenv"),zod=require("zod"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const __dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return path.isAbsolute(e)?path.normalize(e):path.resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new httpsProxyAgent.HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),fs.writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.options=null,t.export.instr=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.options=null,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},_checkDataSize({export:t,customLogic:o}),postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(fs.readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}function _checkDataSize(e){const t=Buffer.byteLength(JSON.stringify(e),"utf-8");if(log(3,`[chart] The current total size of the data for the export process is around ${(t/1048576).toFixed(2)}MB.`),t>=104857600)throw new ExportError("[chart] The data for the export process exceeds 100MB limit.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`[validation] Request [${r}] - 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`[validation] Request [${r}] - 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: `HIGHCHARTS_ADMIN_TOKEN` is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHV0aWxpdHkgbW9kdWxlIHByb3ZpZGVzXHJcbiAqIGEgY29tcHJlaGVuc2l2ZSBzZXQgb2YgaGVscGVyIGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGRlc2lnbmVkIHRvIHN0cmVhbWxpbmVcclxuICogYW5kIGVuaGFuY2UgdmFyaW91cyBvcGVyYXRpb25zIHJlcXVpcmVkIGZvciBIaWdoY2hhcnRzIGV4cG9ydCB0YXNrcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIG5vcm1hbGl6ZSwgcmVzb2x2ZSB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBkaXJlY3RvcnkgcGF0aFxyXG5leHBvcnQgY29uc3QgX19kaXJuYW1lID0gZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuLi8uJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFuZCBzdGFuZGFyZGl6ZXMgdGV4dCBieSByZXBsYWNpbmcgbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZVxyXG4gKiBjaGFyYWN0ZXJzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIHRyaW1taW5nIGFueSBsZWFkaW5nIG9yIHRyYWlsaW5nXHJcbiAqIHdoaXRlc3BhY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhclRleHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyB0aGUgJy9cXHNcXHMrL2cnIFJlZ0V4cC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtyZXBsYWNlcj0nICddIC0gVGhlIHN0cmluZyB1c2VkIHRvIHJlcGxhY2UgbXVsdGlwbGVcclxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgJyAnIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyVGV4dCh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpIHtcclxuICByZXR1cm4gdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBkZWVwQ29weVxyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8QXJyYXkpfSBvYmpBcnIgLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fEFycmF5KX0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDb3B5KG9iakFycikge1xyXG4gIC8vIElmIHRoZSBgb2JqQXJyYCBpcyBudWxsIG9yIG5vdCBvZiB0aGUgYG9iamVjdGAgdHlwZSwgcmV0dXJuIGl0XHJcbiAgaWYgKG9iakFyciA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqQXJyICE9PSAnb2JqZWN0Jykge1xyXG4gICAgcmV0dXJuIG9iakFycjtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZWl0aGVyIGEgbmV3IGFycmF5IG9yIGEgbmV3IG9iamVjdFxyXG4gIGNvbnN0IG9iakFyckNvcHkgPSBBcnJheS5pc0FycmF5KG9iakFycikgPyBbXSA6IHt9O1xyXG5cclxuICAvLyBSZWN1cnNpdmVseSBjb3B5IGVhY2ggcHJvcGVydHlcclxuICBmb3IgKGNvbnN0IGtleSBpbiBvYmpBcnIpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqQXJyLCBrZXkpKSB7XHJcbiAgICAgIG9iakFyckNvcHlba2V5XSA9IGRlZXBDb3B5KG9iakFycltrZXldKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY29waWVkIG9iamVjdFxyXG4gIHJldHVybiBvYmpBcnJDb3B5O1xyXG59XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZXhwQmFja29mZlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBmdW5jdGlvbiB0byBiZSByZXRyaWVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYDBgLlxyXG4gKiBAcGFyYW0gey4uLnVua25vd259IGFyZ3MgLSBBcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSByZXN1bHRcclxuICogb2YgdGhlIGZ1bmN0aW9uIGlmIHN1Y2Nlc3NmdWwuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCBpZiB0aGUgbWF4aW11bSBudW1iZXIgb2YgYXR0ZW1wdHNcclxuICogaXMgcmVhY2hlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleHBCYWNrb2ZmKGZuLCBhdHRlbXB0ID0gMCwgLi4uYXJncykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBUcnkgdG8gY2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIHJldHVybiBhd2FpdCBmbiguLi5hcmdzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gQ2FsY3VsYXRlIGRlbGF5IGluIG1zXHJcbiAgICBjb25zdCBkZWxheUluTXMgPSAyICoqIGF0dGVtcHQgKiAxMDAwO1xyXG5cclxuICAgIC8vIElmIHRoZSBhdHRlbXB0IGV4Y2VlZHMgdGhlIG1heGltdW0gYXR0ZW1wdHMgb2YgcmVwZWF0LCB0aHJvdyBhbiBlcnJvclxyXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXYWl0IGdpdmVuIGFtb3VudCBvZiB0aW1lXHJcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIGRlbGF5SW5NcykpO1xyXG5cclxuICAgIC8vLyBUTyBETzogQ29ycmVjdFxyXG4gICAgLy8gLy8gSW5mb3JtYXRpb24gYWJvdXQgdGhlIHJlc291cmNlIHRpbWVvdXRcclxuICAgIC8vIGxvZyhcclxuICAgIC8vICAgMyxcclxuICAgIC8vICAgYFt1dGlsc10gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIG9mIElEOiAke2FyZ3NbMF19LmBcclxuICAgIC8vICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRqdXN0cyB0aGUgY29uc3RydWN0b3IgbmFtZSBieSB0cmFuc2Zvcm1pbmcgYW5kIG5vcm1hbGl6aW5nIGl0IGJhc2VkXHJcbiAqIG9uIGNvbW1vbiBjaGFydCB0eXBlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeENvbnN0clxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY29uc3RyIC0gVGhlIG9yaWdpbmFsIGNvbnN0cnVjdG9yIG5hbWUgdG8gYmUgZml4ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IgbmFtZSwgb3IgJ2NoYXJ0JyBpZiB0aGUgaW5wdXRcclxuICogaXMgbm90IHJlY29nbml6ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZml4Q29uc3RyKGNvbnN0cikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBGaXggdGhlIGNvbnN0cnVjdG9yIGJ5IGxvd2VyaW5nIGNhc2luZ1xyXG4gICAgY29uc3QgZml4ZWRDb25zdHIgPSBgJHtjb25zdHIudG9Mb3dlckNhc2UoKS5yZXBsYWNlKCdjaGFydCcsICcnKX1DaGFydGA7XHJcblxyXG4gICAgLy8gSGFuZGxlIHRoZSBjYXNlIHdoZXJlIHRoZSByZXN1bHQgaXMganVzdCAnQ2hhcnQnXHJcbiAgICBpZiAoZml4ZWRDb25zdHIgPT09ICdDaGFydCcpIHtcclxuICAgICAgZml4ZWRDb25zdHIudG9Mb3dlckNhc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZXR1cm4gdGhlIGNvcnJlY3RlZCBjb25zdHJ1Y3Rvciwgb3RoZXJ3aXNlIGRlZmF1bHQgdG8gJ2NoYXJ0J1xyXG4gICAgcmV0dXJuIFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10uaW5jbHVkZXMoXHJcbiAgICAgIGZpeGVkQ29uc3RyXHJcbiAgICApXHJcbiAgICAgID8gZml4ZWRDb25zdHJcclxuICAgICAgOiAnY2hhcnQnO1xyXG4gIH0gY2F0Y2gge1xyXG4gICAgLy8gRGVmYXVsdCB0byAnY2hhcnQnIGluIGNhc2Ugb2YgYW55IGVycm9yXHJcbiAgICByZXR1cm4gJ2NoYXJ0JztcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGaXhlcyB0aGUgb3V0ZmlsZSBiYXNlZCBvbiBwcm92aWRlZCB0eXBlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZml4T3V0ZmlsZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSBvcmlnaW5hbCBleHBvcnQgdHlwZS5cclxuICogQHBhcmFtIHtzdHJpbmd9IG91dGZpbGUgLSBUaGUgZmlsZSBwYXRoIG9yIG5hbWUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgb3V0ZmlsZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmaXhPdXRmaWxlKHR5cGUsIG91dGZpbGUpIHtcclxuICAvLyBHZXQgdGhlIGZpbGUgbmFtZSBmcm9tIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgY29uc3QgZmlsZU5hbWUgPSBnZXRBYnNvbHV0ZVBhdGgob3V0ZmlsZSB8fCAnY2hhcnQnKVxyXG4gICAgLnNwbGl0KCcuJylcclxuICAgIC5zaGlmdCgpO1xyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IG91dGZpbGVcclxuICByZXR1cm4gYCR7ZmlsZU5hbWV9LiR7dHlwZX1gO1xyXG59XHJcblxyXG4vKipcclxuICogRml4ZXMgdGhlIGV4cG9ydCB0eXBlIGJhc2VkIG9uIE1JTUUgdHlwZXMgYW5kIGZpbGUgZXh0ZW5zaW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIGZpeFR5cGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBbb3V0ZmlsZT1udWxsXSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYG51bGxgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGZpeFR5cGUodHlwZSwgb3V0ZmlsZSA9IG51bGwpIHtcclxuICAvLyBNSU1FIHR5cGVzXHJcbiAgY29uc3QgbWltZVR5cGVzID0ge1xyXG4gICAgJ2ltYWdlL3BuZyc6ICdwbmcnLFxyXG4gICAgJ2ltYWdlL2pwZWcnOiAnanBlZycsXHJcbiAgICAnYXBwbGljYXRpb24vcGRmJzogJ3BkZicsXHJcbiAgICAnaW1hZ2Uvc3ZnK3htbCc6ICdzdmcnXHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IGZvcm1hdHNcclxuICBjb25zdCBmb3JtYXRzID0gT2JqZWN0LnZhbHVlcyhtaW1lVHlwZXMpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICAvLyBTdXBwb3J0IHRoZSBKUEcgdHlwZVxyXG4gICAgaWYgKG91dFR5cGUgPT09ICdqcGcnKSB7XHJcbiAgICAgIHR5cGUgPSAnanBlZyc7XHJcbiAgICB9IGVsc2UgaWYgKGZvcm1hdHMuaW5jbHVkZXMob3V0VHlwZSkgJiYgdHlwZSAhPT0gb3V0VHlwZSkge1xyXG4gICAgICB0eXBlID0gb3V0VHlwZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGNvcnJlY3QgdHlwZVxyXG4gIHJldHVybiBtaW1lVHlwZXNbdHlwZV0gfHwgZm9ybWF0cy5maW5kKCh0KSA9PiB0ID09PSB0eXBlKSB8fCAncG5nJztcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gcGF0aCBpcyByZWxhdGl2ZSBvciBhYnNvbHV0ZSBhbmQgcmV0dXJucyB0aGUgY29ycmVjdGVkLFxyXG4gKiBhYnNvbHV0ZSBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaXNBYnNvbHV0ZVBhdGhcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byBiZSBjaGVja2VkIG9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgYWJzb2x1dGUgcGF0aC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBYnNvbHV0ZVBhdGgocGF0aCkge1xyXG4gIHJldHVybiBpc0Fic29sdXRlKHBhdGgpID8gbm9ybWFsaXplKHBhdGgpIDogcmVzb2x2ZShwYXRoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGlucHV0IGRhdGEgdG8gYSBCYXNlNjQgc3RyaW5nIGJhc2VkIG9uIHRoZSBleHBvcnQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJhc2U2NFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgaW5wdXQgdG8gYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZvcm1hdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBCYXNlNjQgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRCYXNlNjQoaW5wdXQsIHR5cGUpIHtcclxuICAvLyBGb3IgcGRmIGFuZCBzdmcgdHlwZXMgdGhlIGlucHV0IG11c3QgYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZyb20gYSBidWZmZXJcclxuICBpZiAodHlwZSA9PT0gJ3BkZicgfHwgdHlwZSA9PSAnc3ZnJykge1xyXG4gICAgcmV0dXJuIEJ1ZmZlci5mcm9tKGlucHV0LCAndXRmOCcpLnRvU3RyaW5nKCdiYXNlNjQnKTtcclxuICB9XHJcblxyXG4gIC8vIEZvciBwbmcgYW5kIGpwZWcgaW5wdXQgaXMgYWxyZWFkeSBhIEJhc2U2NCBzdHJpbmdcclxuICByZXR1cm4gaW5wdXQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHN0cmluZ2lmaWVkIGRhdGUgd2l0aG91dCB0aGUgR01UIHRleHQgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3RGF0ZSgpIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIHJldHVybiBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBzdG9yZWQgdGltZSB2YWx1ZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlVGltZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGVUaW1lKCkge1xyXG4gIHJldHVybiBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gaXRlbSBpcyBhbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiB0aGUgaXRlbSBpcyBhbiBvYmplY3QsIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdChpdGVtKSB7XHJcbiAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChpdGVtKSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIG9iamVjdCBpcyBlbXB0eS5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW0gLSBUaGUgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdEVtcHR5KGl0ZW0pIHtcclxuICByZXR1cm4gKFxyXG4gICAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgICAhQXJyYXkuaXNBcnJheShpdGVtKSAmJlxyXG4gICAgaXRlbSAhPT0gbnVsbCAmJlxyXG4gICAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kLCBmYWxzZSBvdGhlcndpc2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChpdGVtKSB7XHJcbiAgY29uc3QgcmVnZXhQYXR0ZXJucyA9IFtcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT9sb2NhbGhvc3RcXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEwXFwuXFxkezEsM31cXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMjdcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE3MlxcLigxWzYtOV18MlswLTldfDNbMC0xXSlcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcclxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xOTJcXC4xNjhcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiL1xyXG4gIF07XHJcblxyXG4gIHJldHVybiByZWdleFBhdHRlcm5zLnNvbWUoKHBhdHRlcm4pID0+IHBhdHRlcm4udGVzdChpdGVtKSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVdGlsaXR5IHRvIG1lYXN1cmUgZWxhcHNlZCB0aW1lIHVzaW5nIHRoZSBOb2RlLmpzIGBwcm9jZXNzLmhydGltZSgpYCBtZXRob2QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBtZWFzdXJlVGltZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RnVuY3Rpb259IEEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBlbGFwc2VkIHRpbWUgaW4gbWlsbGlzZWNvbmRzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1lYXN1cmVUaW1lKCkge1xyXG4gIGNvbnN0IHN0YXJ0ID0gcHJvY2Vzcy5ocnRpbWUuYmlnaW50KCk7XHJcbiAgcmV0dXJuICgpID0+IE51bWJlcihwcm9jZXNzLmhydGltZS5iaWdpbnQoKSAtIHN0YXJ0KSAvIDEwMDAwMDA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSb3VuZHMgYSBudW1iZXIgdG8gdGhlIHNwZWNpZmllZCBwcmVjaXNpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByb3VuZE51bWJlclxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBwcmVjaXNpb24gLSBUaGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIHRvIHJvdW5kIHRvLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgcm91bmRlZCBudW1iZXIuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcm91bmROdW1iZXIodmFsdWUsIHByZWNpc2lvbiA9IDEpIHtcclxuICBjb25zdCBtdWx0aXBsaWVyID0gTWF0aC5wb3coMTAsIHByZWNpc2lvbiB8fCAwKTtcclxuICByZXR1cm4gTWF0aC5yb3VuZCgrdmFsdWUgKiBtdWx0aXBsaWVyKSAvIG11bHRpcGxpZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBhIHZhbHVlIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHRvQm9vbGVhblxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSBib29sZWFuIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dCB2YWx1ZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB0b0Jvb2xlYW4oaXRlbSkge1xyXG4gIHJldHVybiBbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTicsICcwJywgJyddLmluY2x1ZGVzKGl0ZW0pXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6ICEhaXRlbTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFdyYXBzIGN1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgaXQgc2FmZWx5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gd3JhcEFyb3VuZFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tQ29kZSAtIFRoZSBjdXN0b20gY29kZSB0byBiZSB3cmFwcGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEZsYWcgdG8gYWxsb3cgbG9hZGluZyBjb2RlIGZyb20gYSBmaWxlLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtpc0NhbGxiYWNrPWZhbHNlXSAtIEZsYWcgdGhhdCBpbmRpY2F0ZXMgdGhlIHJldHVybmVkIGNvZGVcclxuICogbXVzdCBiZSBpbiBhIGNhbGxiYWNrIGZvcm1hdC5cclxuICpcclxuICogQHJldHVybnMgeyhzdHJpbmd8bnVsbCl9IFRoZSB3cmFwcGVkIGN1c3RvbSBjb2RlIG9yIG51bGwgaWYgd3JhcHBpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gd3JhcEFyb3VuZChjdXN0b21Db2RlLCBhbGxvd0ZpbGVSZXNvdXJjZXMsIGlzQ2FsbGJhY2sgPSBmYWxzZSkge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICAvLyBMb2FkIGEgZmlsZSBpZiB0aGUgZmlsZSByZXNvdXJjZXMgYXJlIGFsbG93ZWRcclxuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICAgID8gd3JhcEFyb3VuZChcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChjdXN0b21Db2RlKSwgJ3V0ZjgnKSxcclxuICAgICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgICAgICBpc0NhbGxiYWNrXHJcbiAgICAgICAgICApXHJcbiAgICAgICAgOiBudWxsO1xyXG4gICAgfSBlbHNlIGlmIChcclxuICAgICAgIWlzQ2FsbGJhY2sgJiZcclxuICAgICAgKGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24oKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbiAoKScpIHx8XHJcbiAgICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpID0+JykpXHJcbiAgICApIHtcclxuICAgICAgLy8gVHJlYXQgYSBmdW5jdGlvbiBhcyBhIHNlbGYtaW52b2tpbmcgZXhwcmVzc2lvblxyXG4gICAgICByZXR1cm4gYCgke2N1c3RvbUNvZGV9KSgpYDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBPciByZXR1cm4gYXMgYSBzdHJpbmdpZmllZCBjb2RlXHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGRlZXBDb3B5LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4Q29uc3RyLFxyXG4gIGZpeE91dGZpbGUsXHJcbiAgZml4VHlwZSxcclxuICBnZXRBYnNvbHV0ZVBhdGgsXHJcbiAgZ2V0QmFzZTY0LFxyXG4gIGdldE5ld0RhdGUsXHJcbiAgZ2V0TmV3RGF0ZVRpbWUsXHJcbiAgaXNPYmplY3QsXHJcbiAgaXNPYmplY3RFbXB0eSxcclxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxyXG4gIG1lYXN1cmVUaW1lLFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHRvQm9vbGVhbixcclxuICB3cmFwQXJvdW5kXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSBmb3IgbWFuYWdpbmcgbG9nZ2luZyBmdW5jdGlvbmFsaXR5IHdpdGggY3VzdG9taXphYmxlXHJcbiAqIGxvZyBsZXZlbHMsIGNvbnNvbGUgYW5kIGZpbGUgbG9nZ2luZyBvcHRpb25zLCBhbmQgZXJyb3IgaGFuZGxpbmcgc3VwcG9ydC5cclxuICogVGhlIG1vZHVsZSBhbHNvIGVuc3VyZXMgdGhhdCBmaWxlLWJhc2VkIGxvZ3MgYXJlIHN0b3JlZCBpbiBhIHN0cnVjdHVyZWRcclxuICogZGlyZWN0b3J5LCBjcmVhdGluZyB0aGUgbmVjZXNzYXJ5IHBhdGhzIGF1dG9tYXRpY2FsbHkgaWYgdGhleSBkbyBub3QgZXhpc3QuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYXBwZW5kRmlsZSwgZXhpc3RzU3luYywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGgsIGdldE5ld0RhdGUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmNvbnN0IGxvZ2dpbmcgPSB7XHJcbiAgLy8gRmxhZ3MgZm9yIGxvZ2dpbmcgc3RhdHVzXHJcbiAgdG9Db25zb2xlOiB0cnVlLFxyXG4gIHRvRmlsZTogZmFsc2UsXHJcbiAgcGF0aENyZWF0ZWQ6IGZhbHNlLFxyXG4gIC8vIEZ1bGwgcGF0aCB0byB0aGUgbG9nIGZpbGVcclxuICBwYXRoVG9Mb2c6ICcnLFxyXG4gIC8vIExvZyBsZXZlbHNcclxuICBsZXZlbHNEZXNjOiBbXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnZXJyb3InLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzBdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3dhcm5pbmcnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzFdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ25vdGljZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMl1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAndmVyYm9zZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbM11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnYmVuY2htYXJrJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1s0XVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGEgbWVzc2FnZSB3aXRoIGEgc3BlY2lmaWVkIGxvZyBsZXZlbC4gQWNjZXB0cyBhIHZhcmlhYmxlIG51bWJlclxyXG4gKiBvZiBhcmd1bWVudHMuIFRoZSBhcmd1bWVudHMgYWZ0ZXIgdGhlIGBsZXZlbGAgYXJlIHBhc3NlZCB0byBgY29uc29sZS5sb2dgXHJcbiAqIGFuZC9vciB1c2VkIHRvIGNvbnN0cnVjdCBhbmQgYXBwZW5kIG1lc3NhZ2VzIHRvIGEgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dcclxuICpcclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQW4gYXJyYXkgb2YgYXJndW1lbnRzIHdoZXJlIHRoZSBmaXJzdCBpcyB0aGUgbG9nXHJcbiAqIGxldmVsIGFuZCB0aGUgcmVtYWluaW5nIGFyZSBzdHJpbmdzIHVzZWQgdG8gYnVpbGQgdGhlIGxvZyBtZXNzYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nKC4uLmFyZ3MpIHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbHNEZXNjLCBsZXZlbCB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlIG9yIGlzIGl0IGEgYmVuY2htYXJrIGxvZ1xyXG4gIGlmIChcclxuICAgIG5ld0xldmVsICE9PSA1ICYmXHJcbiAgICAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKVxyXG4gICkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdCh0ZXh0cylcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIGFsb25nIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ1dpdGhTdGFja1xyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbG9nIGxldmVsLlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QgY29udGFpbmluZyB0aGUgc3RhY2sgdHJhY2UuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21NZXNzYWdlIC0gQW4gb3B0aW9uYWwgY3VzdG9tIG1lc3NhZ2UgdG8gYmUgaW5jbHVkZWRcclxuICogaW4gdGhlIGxvZyBhbG9uZ3NpZGUgdGhlIGVycm9yLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nV2l0aFN0YWNrKG5ld0xldmVsLCBlcnJvciwgY3VzdG9tTWVzc2FnZSkge1xyXG4gIC8vIEdldCB0aGUgbWFpbiBtZXNzYWdlXHJcbiAgY29uc3QgbWFpbk1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlIHx8IChlcnJvciAmJiBlcnJvci5tZXNzYWdlKSB8fCAnJztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQWRkIHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXHJcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID0gZXJyb3IgJiYgZXJyb3Iuc3RhY2s7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2UsIGlmIGV4aXN0c1xyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlXTtcclxuICBpZiAoc3RhY2tNZXNzYWdlKSB7XHJcbiAgICB0ZXh0cy5wdXNoKCdcXG4nLCBzdGFja01lc3NhZ2UpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcclxuICAgICAgICB0ZXh0cy5zaGlmdCgpW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAuLi50ZXh0c1xyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpbml0TG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgbG9nZ2luZ2Agb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpbml0TG9nZ2luZyhsb2dnaW5nT3B0aW9ucykge1xyXG4gIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGBsb2dnaW5nT3B0aW9uc2Agb2JqZWN0XHJcbiAgY29uc3QgeyBsZXZlbCwgZGVzdCwgZmlsZSwgdG9Db25zb2xlLCB0b0ZpbGUgfSA9IGxvZ2dpbmdPcHRpb25zO1xyXG5cclxuICAvLyBSZXNldCBmbGFncyB0byB0aGUgZGVmYXVsdCB2YWx1ZXNcclxuICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgbG9nZ2luZy5wYXRoVG9Mb2cgPSAnJztcclxuXHJcbiAgLy8gU2V0IHRoZSBsb2dnaW5nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobGV2ZWwpO1xyXG5cclxuICAvLyBTZXQgdGhlIGNvbnNvbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nKHRvQ29uc29sZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgZmlsZSBsb2dnaW5nXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGxvZyBsZXZlbCB0byB0aGUgc3BlY2lmaWVkIHZhbHVlLiBMb2cgbGV2ZWxzIGFyZSAoYDBgID0gbm8gbG9nZ2luZyxcclxuICogYDFgID0gZXJyb3IsIGAyYCA9IHdhcm5pbmcsIGAzYCA9IG5vdGljZSwgYDRgID0gdmVyYm9zZSwgb3IgYDVgID0gYmVuY2htYXJrKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldExvZ0xldmVsXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsb2cgbGV2ZWwgdG8gYmUgc2V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldExvZ0xldmVsKGxldmVsKSB7XHJcbiAgaWYgKFxyXG4gICAgTnVtYmVyLmlzSW50ZWdlcihsZXZlbCkgJiZcclxuICAgIGxldmVsID49IDAgJiZcclxuICAgIGxldmVsIDw9IGxvZ2dpbmcubGV2ZWxzRGVzYy5sZW5ndGhcclxuICApIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgbGV2ZWxgIG9wdGlvblxyXG4gICAgbG9nZ2luZy5sZXZlbCA9IGxldmVsO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgY29uc29sZSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB0b0NvbnNvbGUgLSBUaGUgZmxhZyBmb3Igc2V0dGluZyB0aGUgbG9nZ2luZyB0byB0aGUgY29uc29sZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpIHtcclxuICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYHRvQ29uc29sZWAgb3B0aW9uXHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSAhIXRvQ29uc29sZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUgbmFtZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBkZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggd2hlcmUgdGhlIGxvZyBmaWxlIHNob3VsZFxyXG4gKiBiZSBzYXZlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGZpbGUgLSBUaGUgbmFtZSBvZiB0aGUgbG9nIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9GaWxlIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2dnaW5nIHNob3VsZFxyXG4gKiBiZSBkaXJlY3RlZCB0byBhIGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0ZpbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9GaWxlID0gISF0b0ZpbGU7XHJcblxyXG4gIC8vIFNldCB0aGUgYGRlc3RgIGFuZCBgZmlsZWAgb3B0aW9ucyBvbmx5IGlmIHRoZSBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZFxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ID0gZGVzdCB8fCAnJztcclxuICAgIGxvZ2dpbmcuZmlsZSA9IGZpbGUgfHwgJyc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyB0aGUgcHJvdmlkZWQgdGV4dHMgdG8gYSBmaWxlLCBpZiBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZC4gSXQgY3JlYXRlc1xyXG4gKiB0aGUgbmVjZXNzYXJ5IGRpcmVjdG9yeSBzdHJ1Y3R1cmUgaWYgbm90IGFscmVhZHkgY3JlYXRlZCBhbmQgYXBwZW5kc1xyXG4gKiB0aGUgY29udGVudCwgaW5jbHVkaW5nIGFuIG9wdGlvbmFsIHByZWZpeCwgdG8gdGhlIHNwZWNpZmllZCBsb2cgZmlsZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9sb2dUb0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBBbiBvcHRpb25hbCBwcmVmaXggdG8gYmUgYWRkZWQgdG8gZWFjaCBsb2cgZW50cnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpIHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpICYmXHJcbiAgICAgIG1rZGlyU3luYyhnZXRBYnNvbHV0ZVBhdGgobG9nZ2luZy5kZXN0KSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBmdWxsIHBhdGhcclxuICAgIGxvZ2dpbmcucGF0aFRvTG9nID0gZ2V0QWJzb2x1dGVQYXRoKGpvaW4obG9nZ2luZy5kZXN0LCBsb2dnaW5nLmZpbGUpKTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yICYmIGxvZ2dpbmcudG9GaWxlICYmIGxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgQ29uZmlndXJhdGlvbiBtYW5hZ2VtZW50IG1vZHVsZSBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci5cclxuICogUHJvdmlkZXMgZGVmYXVsdCBjb25maWd1cmF0aW9ucyB0aGF0IHN1cHBvcnQgZW52aXJvbm1lbnQgdmFyaWFibGVzLCBDTElcclxuICogYXJndW1lbnRzLCBhbmQgaW50ZXJhY3RpdmUgcHJvbXB0cyBmb3IgY3VzdG9taXphdGlvbiBvZiBvcHRpb25zIGFuZCBmZWF0dXJlcy5cclxuICogQWRkaXRpb25hbGx5LCBpdCBtYXBzIGxlZ2FjeSBvcHRpb25zIHRvIG1vZGVybiBzdHJ1Y3R1cmVzLCBnZW5lcmF0ZXMgbmVzdGVkXHJcbiAqIGFyZ3VtZW50IG1hcHBpbmdzLCBhbmQgZGlzcGxheXMgQ0xJIHVzYWdlIGluZm9ybWF0aW9uLlxyXG4gKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBhbGwgYXZhaWxhYmxlIG9wdGlvbnMsIG9yZ2FuaXplZFxyXG4gKiBieSBzZWN0aW9ucy5cclxuICpcclxuICogVGhpcyBvYmplY3QgaW5jbHVkZXM6XHJcbiAqIC0gRGVmYXVsdCB2YWx1ZXMgZm9yIGVhY2ggb3B0aW9uXHJcbiAqIC0gRGF0YSB0eXBlcyBmb3IgdmFsaWRhdGlvblxyXG4gKiAtIE5hbWVzIG9mIGNvcnJlc3BvbmRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIC0gRGVzY3JpcHRpb25zIG9mIGVhY2ggcHJvcGVydHlcclxuICogLSBJbmZvcm1hdGlvbiB1c2VkIGZvciBwcm9tcHRzIGluIGludGVyYWN0aXZlIGNvbmZpZ3VyYXRpb25cclxuICogLSBbT3B0aW9uYWxdIENvcnJlc3BvbmRpbmcgQ0xJIGFyZ3VtZW50IG5hbWVzIGZvciBDTEkgdXNhZ2VcclxuICogLSBbT3B0aW9uYWxdIExlZ2FjeSBuYW1lcyBmcm9tIHRoZSBwcmV2aW91cyBQaGFudG9tSlMtYmFzZWQgc2VydmVyXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IHtcclxuICAgIGFyZ3M6IHtcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnLS1hbGxvdy1ydW5uaW5nLWluc2VjdXJlLWNvbnRlbnQnLFxyXG4gICAgICAgICctLWFzaC1uby1udWRnZXMnLFxyXG4gICAgICAgICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICAgICAgICctLWJsb2NrLW5ldy13ZWItY29udGVudHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYWNjZWxlcmF0ZWQtMmQtY2FudmFzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtbmV0d29ya2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLXRpbWVyLXRocm90dGxpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZGluZy1vY2NsdWRlZC13aW5kb3dzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJyZWFrcGFkJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNoZWNrZXItaW1hZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jbGllbnQtc2lkZS1waGlzaGluZy1kZXRlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LWV4dGVuc2lvbnMtd2l0aC1iYWNrZ3JvdW5kLXBhZ2VzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICAgICAgICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAgICAgICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZmVhdHVyZXM9Q2FsY3VsYXRlTmF0aXZlV2luT2NjbHVzaW9uLEludGVyZXN0RmVlZENvbnRlbnRTdWdnZXN0aW9ucyxXZWJPVFAnLFxyXG4gICAgICAgICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWlwYy1mbG9vZGluZy1wcm90ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWxvZ2dpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtbm90aWZpY2F0aW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1vZmZlci1zdG9yZS11bm1hc2tlZC13YWxsZXQtY2FyZHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtcG9wdXAtYmxvY2tpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJpbnQtcHJldmlldycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcm9tcHQtb24tcmVwb3N0JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXJlbmRlcmVyLWJhY2tncm91bmRpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2VhcmNoLWVuZ2luZS1jaG9pY2Utc2NyZWVuJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlc3Npb24tY3Jhc2hlZC1idWJibGUnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxyXG4gICAgICAgICctLWRpc2FibGUtc2l0ZS1pc29sYXRpb24tdHJpYWxzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICAgICAgICctLWRpc2FibGUtc3luYycsXHJcbiAgICAgICAgJy0tZW5hYmxlLXVuc2FmZS13ZWJncHUnLFxyXG4gICAgICAgICctLWhpZGUtY3Jhc2gtcmVzdG9yZS1idWJibGUnLFxyXG4gICAgICAgICctLWhpZGUtc2Nyb2xsYmFycycsXHJcbiAgICAgICAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgICAgICAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgICAgICAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAgICAgICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICAgICAgICctLW5vLXBpbmdzJyxcclxuICAgICAgICAnLS1uby1zYW5kYm94JyxcclxuICAgICAgICAnLS1uby1zdGFydHVwLXdpbmRvdycsXHJcbiAgICAgICAgJy0tbm8tenlnb3RlJyxcclxuICAgICAgICAnLS1wYXNzd29yZC1zdG9yZT1iYXNpYycsXHJcbiAgICAgICAgJy0tcHJvY2Vzcy1wZXItdGFiJyxcclxuICAgICAgICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuICAgICAgXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ1BVUFBFVEVFUl9BUkdTJyxcclxuICAgICAgY2xpTmFtZTogJ3B1cHBldGVlckFyZ3MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FycmF5IG9mIFB1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfVkVSU0lPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2RuVXJsOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NETl9VUkwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0NETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGbGFnIHRvIHJlZmV0Y2ggc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RpcmVjdG9yeSBwYXRoIGZvciBjYWNoZWQgSGlnaGNoYXJ0cyBzY3JpcHRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DT1JFX1NDUklQVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtb2R1bGVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ3N0b2NrJyxcclxuICAgICAgICAnbWFwJyxcclxuICAgICAgICAnZ2FudHQnLFxyXG4gICAgICAgICdleHBvcnRpbmcnLFxyXG4gICAgICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAgICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgICAgIC8vICdhbm5vdGF0aW9ucy1hZHZhbmNlZCcsXHJcbiAgICAgICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAgICAgJ2Jvb3N0JyxcclxuICAgICAgICAnZGF0YScsXHJcbiAgICAgICAgJ2RhdGEtdG9vbHMnLFxyXG4gICAgICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICAgICAnc3RhdGljLXNjYWxlJyxcclxuICAgICAgICAnYnJva2VuLWF4aXMnLFxyXG4gICAgICAgICdoZWF0bWFwJyxcclxuICAgICAgICAndGlsZW1hcCcsXHJcbiAgICAgICAgJ3RpbGVkd2VibWFwJyxcclxuICAgICAgICAndGltZWxpbmUnLFxyXG4gICAgICAgICd0cmVlbWFwJyxcclxuICAgICAgICAndHJlZWdyYXBoJyxcclxuICAgICAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgICAgICdkcmlsbGRvd24nLFxyXG4gICAgICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICAgICAnYnVsbGV0JyxcclxuICAgICAgICAnZnVubmVsJyxcclxuICAgICAgICAnZnVubmVsM2QnLFxyXG4gICAgICAgICdnZW9oZWF0bWFwJyxcclxuICAgICAgICAncHlyYW1pZDNkJyxcclxuICAgICAgICAnbmV0d29ya2dyYXBoJyxcclxuICAgICAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAgICAgJ3BhcmV0bycsXHJcbiAgICAgICAgJ3BhdHRlcm4tZmlsbCcsXHJcbiAgICAgICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAgICAgJ3ByaWNlLWluZGljYXRvcicsXHJcbiAgICAgICAgJ3NhbmtleScsXHJcbiAgICAgICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICAgICAnZGVwZW5kZW5jeS13aGVlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1sYWJlbCcsXHJcbiAgICAgICAgJ3Nlcmllcy1vbi1wb2ludCcsXHJcbiAgICAgICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICAgICAnc29uaWZpY2F0aW9uJyxcclxuICAgICAgICAvLyAnc3RvY2stdG9vbHMnLFxyXG4gICAgICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAgICAgJ3N1bmJ1cnN0JyxcclxuICAgICAgICAndmFyaWFibGUtcGllJyxcclxuICAgICAgICAndmFyaXdpZGUnLFxyXG4gICAgICAgICd2ZWN0b3InLFxyXG4gICAgICAgICd2ZW5uJyxcclxuICAgICAgICAnd2luZGJhcmInLFxyXG4gICAgICAgICd3b3JkY2xvdWQnLFxyXG4gICAgICAgICd4cmFuZ2UnLFxyXG4gICAgICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgICAgICdkcmFnLXBhbmVzJyxcclxuICAgICAgICAnZGVidWdnZXInLFxyXG4gICAgICAgICdkdW1iYmVsbCcsXHJcbiAgICAgICAgJ2xvbGxpcG9wJyxcclxuICAgICAgICAnY3lsaW5kZXInLFxyXG4gICAgICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgICAgICdkb3RwbG90JyxcclxuICAgICAgICAnbWFya2VyLWNsdXN0ZXJzJyxcclxuICAgICAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgICAgICdoZWlraW5hc2hpJyxcclxuICAgICAgICAnZmxvd21hcCcsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAnbmF2aWdhdG9yJyxcclxuICAgICAgICAndGV4dHBhdGgnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIG1vZHVsZSBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbmRpY2F0b3JTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2luZGljYXRvcnMtYWxsJ10sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGluZGljYXRvciBzY3JpcHRzIHRvIGZldGNoJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjMwLjEvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuNDUvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBZGRpdGlvbmFsIGN1c3RvbSBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgICAgc2VwYXJhdG9yOiAnOydcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5GSUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0IGZpbGVuYW1lIHdpdGggdHlwZSwgZm9ybWF0dGVkIGNvcnJlY3RseSBhcyBKU09OIG9yIFNWRycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9JTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdmVycmlkZXMgdGhlIGBpbmZpbGVgIHdpdGggSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgU1ZHIGlucHV0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgb3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWxpYXMgZm9yIHRoZSBgaW5zdHJgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHN2Zzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TVkcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1NWRyBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IHRvIHJlbmRlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJhdGNoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0JBVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0JhdGNoIGpvYiBzdHJpbmcgd2l0aCBpbnB1dC9vdXRwdXQgcGFpcnM6IFwiaW49b3V0O2luPW91dDsuLi5cIicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG91dGZpbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfT1VURklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdPdXRwdXQgZmlsZW5hbWUgd2l0aCB0eXBlLiBDYW4gYmUganBlZywgcG5nLCBwZGYsIG9yIHN2ZyBhbmQgaWdub3JlcyBgdHlwZWAgb3B0aW9uJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdHlwZToge1xyXG4gICAgICB2YWx1ZTogJ3BuZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1RZUEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0ZpbGUgZXhwb3J0IGZvcm1hdC4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3NlbGVjdCcsXHJcbiAgICAgICAgaGludDogJ0RlZmF1bHQ6IHBuZycsXHJcbiAgICAgICAgY2hvaWNlczogWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvbnN0cjoge1xyXG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfQ09OU1RSJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NoYXJ0IGNvbnN0cnVjdG9yLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBjaGFydCcsXHJcbiAgICAgICAgY2hvaWNlczogWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYjY0OiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0I2NCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byB0aGUgY2hhcnQgc2hvdWxkIGJlIHJlY2VpdmVkIGluIEJhc2U2NCBmb3JtYXQgaW5zdGVhZCBvZiBiaW5hcnknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vRG93bmxvYWQ6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfTk9fRE9XTkxPQUQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gaW5jbHVkZSBvciBleGNsdWRlIGF0dGFjaG1lbnQgaGVhZGVycyBpbiB0aGUgcmVzcG9uc2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgd2lkdGg6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfV0lEVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1dpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGVzIGNoYXJ0IHNldHRpbmdzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzY2FsZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9TQ0FMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdTY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncy4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdEhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX0hFSUdIVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRXaWR0aDoge1xyXG4gICAgICB2YWx1ZTogNjAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWZhdWx0IHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0U2NhbGU6IHtcclxuICAgICAgdmFsdWU6IDEsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGVmYXVsdCBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldC4gUmFuZ2VzIGZyb20gMC4xIHRvIDUuMCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBtaW46IDAuMSxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGdsb2JhbE9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0dMT0JBTF9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCBnbG9iYWwgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdGhlbWVPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9USEVNRV9PUFRJT05TJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0pTT04sIHN0cmluZ2lmaWVkIEpTT04gb3IgZmlsZW5hbWUgd2l0aCB0aGVtZSBvcHRpb25zIGZvciBIaWdoY2hhcnRzLnNldE9wdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXN0ZXJpemF0aW9uVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMTUwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaWxsaXNlY29uZHMgdG8gd2FpdCBmb3Igd2VicGFnZSByZW5kZXJpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgY3VzdG9tTG9naWM6IHtcclxuICAgIGFsbG93Q29kZUV4ZWN1dGlvbjoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbGxvd3Mgb3IgZGlzYWxsb3dzIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBkdXJpbmcgZXhwb3J0aW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBpbmplY3Rpb24gb2YgZmlsZXN5c3RlbSByZXNvdXJjZXMgKGRpc2FibGVkIGluIHNlcnZlciBtb2RlKScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tQ29kZToge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DVVNUT01fQ09ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDdXN0b20gY29kZSB0byBleGVjdXRlIGJlZm9yZSBjaGFydCBpbml0aWFsaXphdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24sIGNvZGUgd3JhcHBlZCBpbiBhIGZ1bmN0aW9uLCBvciBhIC5qcyBmaWxlbmFtZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhbGxiYWNrOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0NBTExCQUNLJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0phdmFTY3JpcHQgY29kZSB0byBydW4gZHVyaW5nIGNvbnN0cnVjdGlvbi4gQ2FuIGJlIGEgZnVuY3Rpb24gb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX1JFU09VUkNFUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBZGRpdGlvbmFsIHJlc291cmNlcyBhcyBKU09OLCBzdHJpbmdpZmllZCBKU09OLCBvciBmaWxlbmFtZSwgY29udGFpbmluZyBmaWxlcywganMsIGFuZCBjc3Mgc2VjdGlvbnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsb2FkQ29uZmlnOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHJyxcclxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIHdpdGggYSBwcmUtZGVmaW5lZCBjb25maWd1cmF0aW9uIHRvIHVzZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZUNvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1Byb21wdC1iYXNlZCBvcHRpb24gc2V0dGluZywgc2F2ZWQgdG8gYSBwcm92aWRlZCBjb25maWcgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU3RhcnRzIHRoZSBzZXJ2ZXIgd2hlbiB0cnVlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIHZhbHVlOiAnMC4wLjAuMCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hvc3RuYW1lIG9mIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA3ODAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG51bWJlciBmb3IgdGhlIHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdXBsb2FkTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDMsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX1VQTE9BRF9MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSByZXF1ZXN0IGJvZHkgc2l6ZSBpbiBNQicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRGlzcGxheXMgb3Igbm90IGFjdGlvbiBkdXJhdGlvbnMgaW4gbWlsbGlzZWNvbmRzIGR1cmluZyBzZXJ2ZXIgcmVxdWVzdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHByb3h5OiB7XHJcbiAgICAgIGhvc3Q6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfSE9TVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5SG9zdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdIb3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcG9ydDoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgb2YgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRpbWVvdXQ6IHtcclxuICAgICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX1RJTUVPVVQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVRpbWVvdXQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJhdGVMaW1pdGluZzoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdlbmFibGVSYXRlTGltaXRpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzZXJ2ZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBtYXhSZXF1ZXN0czoge1xyXG4gICAgICAgIHZhbHVlOiAxMCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAncmF0ZUxpbWl0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgcGVyIG1pbnV0ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIHZhbHVlOiAxLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1cnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGltZSB3aW5kb3cgaW4gbWludXRlcyBmb3IgcmF0ZSBsaW1pdGluZycsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGRlbGF5OiB7XHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdEZWxheSBkdXJhdGlvbiBiZXR3ZWVuIHN1Y2Nlc3NpdmUgcmVxdWVzdHMgYmVmb3JlIHJlYWNoaW5nIHRoZSBsaW1pdCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHRydXN0UHJveHk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTZXQgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXInLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBza2lwS2V5OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnS2V5IHRvIGJ5cGFzcyB0aGUgcmF0ZSBsaW1pdGVyLCB1c2VkIHdpdGggYHNraXBUb2tlbmAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTicsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUb2tlbiB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwS2V5YCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgU1NMIHByb3RvY29sJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgZm9yY2U6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0ZPUkNFJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsRm9yY2UnLFxyXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdzc2xPbmx5JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0ZvcmNlcyB0aGUgc2VydmVyIHRvIHVzZSBIVFRQUyBvbmx5IHdoZW4gdHJ1ZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogNDQzLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX1BPUlQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgZm9yIHRoZSBTU0wgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgY2VydFBhdGg6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0NFUlRfUEFUSCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbENlcnRQYXRoJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsUGF0aCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHRoZSBTU0wgY2VydGlmaWNhdGUva2V5IGZpbGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX01JTl9XT1JLRVJTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNaW5pbXVtIGFuZCBpbml0aWFsIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG1heFdvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDgsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NQVhfV09SS0VSUycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICd3b3JrZXJzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiBwb29sIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdvcmtMaW1pdDoge1xyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9XT1JLX0xJTUlUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdOdW1iZXIgb2YgdGFza3MgYSB3b3JrZXIgY2FuIGhhbmRsZSBiZWZvcmUgcmVzdGFydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWNxdWlyZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBhY3F1aXJpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3JlYXRlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGlkbGVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiAzMDAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0lETEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGRlc3Ryb3lpbmcgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWw6IHtcclxuICAgICAgdmFsdWU6IDIwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYmVmb3JlIHJldHJ5aW5nIHJlc291cmNlIGNyZWF0aW9uIG9uIGZhaWx1cmUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0ludGVydmFsIGluIG1pbGxpc2Vjb25kcyB0byBjaGVjayBhbmQgZGVzdHJveSBpZGxlIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAncG9vbEJlbmNobWFya2luZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnU2hvd3Mgc3RhdGlzdGljcyBmb3IgdGhlIHBvb2wgb2YgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfTEVWRUwnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0xvZ2dpbmcgdmVyYm9zaXR5IGxldmVsJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIHJvdW5kOiAwLFxyXG4gICAgICAgIG1pbjogMCxcclxuICAgICAgICBtYXg6IDVcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgdmFsdWU6ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdMb2cgZmlsZSBuYW1lLiBSZXF1aXJlcyBgbG9nVG9GaWxlYCBhbmQgYGxvZ0Rlc3RgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Q6IHtcclxuICAgICAgdmFsdWU6ICdsb2cnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfREVTVCcsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgdG8gYmUgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9Db25zb2xlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0NPTlNPTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9Db25zb2xlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGNvbnNvbGUgbG9nZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgdG9GaWxlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGxvZ2dpbmcgdG8gYSBmaWxlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcm91dGU6IHtcclxuICAgICAgdmFsdWU6ICcvJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdVSV9ST1VURScsXHJcbiAgICAgIGNsaU5hbWU6ICd1aVJvdXRlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgZW5kcG9pbnQgcm91dGUgZm9yIHRoZSBVSScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgb3RoZXI6IHtcclxuICAgIG5vZGVFbnY6IHtcclxuICAgICAgdmFsdWU6ICdwcm9kdWN0aW9uJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT0RFX0VOVicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIE5vZGUuanMgZW52aXJvbm1lbnQgdHlwZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvUHJvY2Vzc0V4aXRzOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gYXR0YWNoIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbm9Mb2dvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9fTE9HTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGlzcGxheSBvciBza2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIHN0YXJ0dXAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhhcmRSZXNldFBhZ2U6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9IQVJEX1JFU0VUX1BBR0UnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHJlc2V0IHRoZSBwYWdlIGNvbnRlbnQgZW50aXJlbHknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJyb3dzZXJTaGVsbE1vZGU6IHtcclxuICAgICAgdmFsdWU6IHRydWUsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX0JST1dTRVJfU0hFTExfTU9ERScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBzaGVsbCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGRlYnVnOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlRGVidWcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIHVuZGVybHlpbmcgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaGVhZGxlc3M6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19IRUFETEVTUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGV0aGVyIG9yIG5vdCB0byBzZXQgdGhlIGJyb3dzZXIgdG8gcnVuIGluIGhlYWRsZXNzIG1vZGUgZHVyaW5nIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGV2dG9vbHM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19ERVZUT09MUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBEZXZUb29scyBpbiBoZWFkZnVsIG1vZGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGxpc3RlblRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0xJU1RFTl9UT19DT05TT0xFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VuYWJsZXMgb3IgZGlzYWJsZXMgbGlzdGVuaW5nIHRvIGNvbnNvbGUgbWVzc2FnZXMgZnJvbSB0aGUgYnJvd3NlcicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZHVtcGlvOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRFVNUElPJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1JlZGlyZWN0cyBvciBub3QgYnJvd3NlciBzdGRvdXQgYW5kIHN0ZGVyciB0byBwcm9jZXNzLnN0ZG91dCBhbmQgcHJvY2Vzcy5zdGRlcnInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNsb3dNbzoge1xyXG4gICAgICB2YWx1ZTogMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19TTE9XX01PJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWxheXMgUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBtaWxsaXNlY29uZHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlYnVnZ2luZ1BvcnQ6IHtcclxuICAgICAgdmFsdWU6IDkyMjIsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVCVUdHSU5HX1BPUlQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1BvcnQgdXNlZCBmb3IgZGVidWdnaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vLyBQcm9wZXJ0aWVzIG5lc3RpbmcgbGV2ZWwgb2YgYWxsIG9wdGlvbnNcclxuZXhwb3J0IGNvbnN0IG5lc3RlZFByb3BzID0gX2NyZWF0ZU5lc3RlZFByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLy8gUHJvcGVydGllcyBuYW1lcyB0aGF0IHNob3VsZCBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkXHJcbmV4cG9ydCBjb25zdCBhYnNvbHV0ZVByb3BzID0gX2NyZWF0ZUFic29sdXRlUHJvcHMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgZ2VuZXJhdGVzIGEgbWFwcGluZyBvZiBuZXN0ZWQgYXJndW1lbnQgY2hhaW5zIGZyb20gYSBuZXN0ZWRcclxuICogY29uZmlnIG9iamVjdC4gVGhpcyBmdW5jdGlvbiB0cmF2ZXJzZXMgYSBuZXN0ZWQgb2JqZWN0IGFuZCBjcmVhdGVzIGEgbWFwcGluZ1xyXG4gKiB3aGVyZSBlYWNoIGtleSBpcyBhbiBhcmd1bWVudCBuYW1lIChlaXRoZXIgZnJvbSBgY2xpTmFtZWAsIGBsZWdhY3lOYW1lYCxcclxuICogb3IgdGhlIG9yaWdpbmFsIGtleSkgYW5kIGVhY2ggdmFsdWUgaXMgYSBzdHJpbmcgcmVwcmVzZW50aW5nIHRoZSBjaGFpblxyXG4gKiBvZiBuZXN0ZWQgcHJvcGVydGllcyBsZWFkaW5nIHRvIHRoYXQgYXJndW1lbnQuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtuZXN0ZWRQcm9wcz17fV0gLSBUaGUgYWNjdW11bGF0b3Igb2JqZWN0IGZvciBzdG9yaW5nXHJcbiAqIHRoZSByZXN1bHRpbmcgYXJndW1lbnRzIGNoYWlucy4gIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtwcm9wQ2hhaW49JyddIC0gVGhlIGN1cnJlbnQgY2hhaW4gb2YgbmVzdGVkIHByb3BlcnRpZXMsXHJcbiAqIHVzZWQgaW50ZXJuYWxseSBkdXJpbmcgcmVjdXJzaW9uLiAgVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgc3RyaW5nLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgbWFwcGluZyBhcmd1bWVudCBuYW1lcyB0byB0aGVpciBjb3JyZXNwb25kaW5nXHJcbiAqIG5lc3RlZCBwcm9wZXJ0eSBjaGFpbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlTmVzdGVkUHJvcHMoY29uZmlnLCBuZXN0ZWRQcm9wcyA9IHt9LCBwcm9wQ2hhaW4gPSAnJykge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHMgb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICBfY3JlYXRlTmVzdGVkUHJvcHMoZW50cnksIG5lc3RlZFByb3BzLCBgJHtwcm9wQ2hhaW59LiR7a2V5fWApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIHRoZSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzXHJcbiAgICAgIG5lc3RlZFByb3BzW2VudHJ5LmNsaU5hbWUgfHwga2V5XSA9IGAke3Byb3BDaGFpbn0uJHtrZXl9YC5zdWJzdHJpbmcoMSk7XHJcblxyXG4gICAgICAvLyBTdXBwb3J0IGZvciB0aGUgbGVnYWN5LCBQaGFudG9tSlMgcHJvcGVydGllcyBuYW1lc1xyXG4gICAgICBpZiAoZW50cnkubGVnYWN5TmFtZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgbmVzdGVkUHJvcHNbZW50cnkubGVnYWN5TmFtZV0gPSBgJHtwcm9wQ2hhaW59LiR7a2V5fWAuc3Vic3RyaW5nKDEpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb2JqZWN0IHdpdGggbmVzdGVkIGFyZ3VtZW50IGNoYWluc1xyXG4gIHJldHVybiBuZXN0ZWRQcm9wcztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdhdGhlcnMgdGhlIG5hbWVzIG9mIHByb3BlcnRpZXMgZnJvbSBhIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRoYXRcclxuICogY2FuIGJlIHRyZWF0ZWQgYXMgYWJzb2x1dGUgcHJvcGVydGllcy4gVGhlc2UgcHJvcGVydGllcyBoYXZlIHZhbHVlcyB0aGF0XHJcbiAqIGFyZSBvYmplY3RzIGFuZCBkbyBub3QgY29udGFpbiBmdXJ0aGVyIG5lc3RlZCBkZXB0aCB3aGVuIG1lcmdpbmcgYW4gb2JqZWN0XHJcbiAqIGNvbnRhaW5pbmcgdGhlc2Ugb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVBYnNvbHV0ZVByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IFthYnNvbHV0ZVByb3BzPVtdXSAtIEFuIGFycmF5IHRvIGNvbGxlY3QgdGhlIG5hbWVzXHJcbiAqIG9mIGFic29sdXRlIHByb3BlcnRpZXMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPHN0cmluZz59IEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG5hbWVzIG9mIGFic29sdXRlXHJcbiAqIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wcyhjb25maWcsIGFic29sdXRlUHJvcHMgPSBbXSkge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS50eXBlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHNcclxuICAgICAgX2NyZWF0ZUFic29sdXRlUHJvcHMoZW50cnksIGFic29sdXRlUHJvcHMpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSWYgdGhlIG9wdGlvbiBjYW4gYmUgYW4gb2JqZWN0LCBzYXZlIGl0cyB0eXBlIGluIHRoZSBhcnJheVxyXG4gICAgICBpZiAoZW50cnkudHlwZXMuaW5jbHVkZXMoJ09iamVjdCcpKSB7XHJcbiAgICAgICAgYWJzb2x1dGVQcm9wcy5wdXNoKGtleSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBhcnJheSB3aXRoIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzXHJcbiAgcmV0dXJuIGFic29sdXRlUHJvcHM7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBkZWZhdWx0Q29uZmlnLFxyXG4gIG5lc3RlZFByb3BzLFxyXG4gIGFic29sdXRlUHJvcHNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgZmlsZSBpcyByZXNwb25zaWJsZSBmb3IgcGFyc2luZyB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIHdpdGggdGhlICd6b2QnIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkXHJcbiAqIHRvIGJlIHVzZWQgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIGBlbnZzYC4gV2Ugc2hvdWxkIG5vdCB1c2UgdGhlIGBwcm9jZXNzLmVudmBcclxuICogZGlyZWN0bHkgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuXHJcbiAqIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lclxyXG4gKiBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICovXHJcblxyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5cclxuaW1wb3J0IHsgZGVmYXVsdENvbmZpZyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuLy8gTG9hZCAuZW52IGludG8gZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbi8vIE9iamVjdCB3aXRoIGN1c3RvbSB2YWxpZGF0b3JzIGFuZCB0cmFuc2Zvcm1lcnMsIHRvIGF2b2lkIHJlcGV0aXRpb25cclxuLy8gaW4gdGhlIENvbmZpZyBvYmplY3RcclxuY29uc3QgdiA9IHtcclxuICAvLyBTcGxpdHMgc3RyaW5nIHZhbHVlIGludG8gZWxlbWVudHMgaW4gYW4gYXJyYXksIHRyaW1zIGV2ZXJ5IGVsZW1lbnQsIGNoZWNrc1xyXG4gIC8vIGlmIGFuIGFycmF5IGlzIGNvcnJlY3QsIGlmIGl0IGlzIGVtcHR5LCBhbmQgaWYgaXQgaXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgYXJyYXk6IChmaWx0ZXJBcnJheSkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlXHJcbiAgICAgICAgICAuc3BsaXQoJywnKVxyXG4gICAgICAgICAgLm1hcCgodmFsdWUpID0+IHZhbHVlLnRyaW0oKSlcclxuICAgICAgICAgIC5maWx0ZXIoKHZhbHVlKSA9PiBmaWx0ZXJBcnJheS5pbmNsdWRlcyh2YWx1ZSkpXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZS5sZW5ndGggPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3Mgb25seSB0cnVlLCBmYWxzZSBhbmQgY29ycmVjdGx5IHBhcnNlIHRoZSB2YWx1ZSB0byBib29sZWFuXHJcbiAgLy8gb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSB1bmRlZmluZWRcclxuICBib29sZWFuOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbJ3RydWUnLCAnZmFsc2UnLCAnJ10pXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgPT09ICd0cnVlJyA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3MgcGFzc2VkIHZhbHVlcyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsXHJcbiAgLy8gYmUgdW5kZWZpbmVkXHJcbiAgZW51bTogKHZhbHVlcykgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWy4uLnZhbHVlcywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIFRyaW1zIHRoZSBzdHJpbmcgdmFsdWUgYW5kIGNoZWNrcyBpZiBpdCBpcyBlbXB0eSBvciBjb250YWlucyBzdHJpbmdpZmllZFxyXG4gIC8vIHZhbHVlcyBzdWNoIGFzIGZhbHNlLCB1bmRlZmluZWQsIG51bGwsIE5hTiwgaWYgaXQgZG9lcywgcmV0dXJucyB1bmRlZmluZWRcclxuICBzdHJpbmc6ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgIVsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJ10uaW5jbHVkZXModmFsdWUpIHx8XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSBzdHJpbmcgY29udGFpbnMgZm9yYmlkZGVuIHZhbHVlcywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBvc2l0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIHBvc2l0aXZlTnVtOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBwb3NpdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG5vbi1uZWdhdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlXHJcbiAgLy8gd2lsbCBiZSB1bmRlZmluZWRcclxuICBub25OZWdhdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCksXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSB2YWx1ZSBtdXN0IGJlIG51bWVyaWMgYW5kIG5vbi1uZWdhdGl2ZSwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKVxyXG59O1xyXG5cclxuZXhwb3J0IGNvbnN0IENvbmZpZyA9IHoub2JqZWN0KHtcclxuICAvLyBwdXBwZXRlZXJcclxuICBQVVBQRVRFRVJfQVJHUzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEhJR0hDSEFSVFNfVkVSU0lPTiBtdXN0IGJlICdsYXRlc3QnLCBhIG1ham9yIHZlcnNpb24sIG9yIGluIHRoZSBmb3JtIFhYLllZLlpaLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfQ0ROX1VSTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHA6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcbiAgSElHSENIQVJUU19GT1JDRV9GRVRDSDogdi5ib29sZWFuKCksXHJcbiAgSElHSENIQVJUU19DQUNIRV9QQVRIOiB2LnN0cmluZygpLFxyXG4gIEhJR0hDSEFSVFNfQURNSU5fVE9LRU46IHYuc3RyaW5nKCksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNvcmVTY3JpcHRzLnZhbHVlKSxcclxuICBISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLm1vZHVsZVNjcmlwdHMudmFsdWVcclxuICApLFxyXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuaW5kaWNhdG9yU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcbiAgSElHSENIQVJUU19DVVNUT01fU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jdXN0b21TY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuXHJcbiAgLy8gZXhwb3J0XHJcbiAgRVhQT1JUX0lORklMRTogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfSU5TVFI6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1NWRzogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfQkFUQ0g6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX09VVEZJTEU6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RZUEU6IHYuZW51bShbJ2pwZWcnLCAncG5nJywgJ3BkZicsICdzdmcnXSksXHJcbiAgRVhQT1JUX0NPTlNUUjogdi5lbnVtKFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10pLFxyXG4gIEVYUE9SVF9CNjQ6IHYuYm9vbGVhbigpLFxyXG4gIEVYUE9SVF9OT19ET1dOTE9BRDogdi5ib29sZWFuKCksXHJcbiAgRVhQT1JUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1dJRFRIOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfU0NBTEU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfR0xPQkFMX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1RIRU1FX09QVElPTlM6IHYuc3RyaW5nKCksXHJcbiAgRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG5cclxuICAvLyBjdXN0b21cclxuICBDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT046IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUzogdi5ib29sZWFuKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NVU1RPTV9DT0RFOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19DQUxMQkFDSzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfUkVTT1VSQ0VTOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuICBDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRzogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gc2VydmVyXHJcbiAgU0VSVkVSX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX0hPU1Q6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfVVBMT0FEX0xJTUlUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX0JFTkNITUFSS0lORzogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIHNlcnZlciBwcm94eVxyXG4gIFNFUlZFUl9QUk9YWV9IT1NUOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QUk9YWV9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1BST1hZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gc2VydmVyIHJhdGUgbGltaXRpbmdcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFk6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU46IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHNlcnZlciBzc2xcclxuICBTRVJWRVJfU1NMX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9GT1JDRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1NTTF9DRVJUX1BBVEg6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIHBvb2xcclxuICBQT09MX01JTl9XT1JLRVJTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9NQVhfV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfV09SS19MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfREVTVFJPWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfUkVBUEVSX0lOVEVSVkFMOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBsb2dnZXJcclxuICBMT0dHSU5HX0xFVkVMOiB6XHJcbiAgICAuc3RyaW5nKClcclxuICAgIC50cmltKClcclxuICAgIC5yZWZpbmUoXHJcbiAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZSA9PT0gJycgfHxcclxuICAgICAgICAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPj0gMCAmJlxyXG4gICAgICAgICAgcGFyc2VGbG9hdCh2YWx1ZSkgPD0gNSksXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgTE9HR0lOR19MRVZFTC4gV2Ugb25seSBhY2NlcHQgdmFsdWVzIGZyb20gMCB0byA1IGFzIGxvZ2dpbmcgbGV2ZWxzLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpLFxyXG4gIExPR0dJTkdfRklMRTogdi5zdHJpbmcoKSxcclxuICBMT0dHSU5HX0RFU1Q6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19UT19DT05TT0xFOiB2LmJvb2xlYW4oKSxcclxuICBMT0dHSU5HX1RPX0ZJTEU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyB1aVxyXG4gIFVJX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgVUlfUk9VVEU6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIG90aGVyXHJcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcclxuICBPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfTk9fTE9HTzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcclxuICBPVEhFUl9CUk9XU0VSX1NIRUxMX01PREU6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBkZWJ1Z2dlclxyXG4gIERFQlVHX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfSEVBRExFU1M6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RFVlRPT0xTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19MSVNURU5fVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfRFVNUElPOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19TTE9XX01POiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgREVCVUdfREVCVUdHSU5HX1BPUlQ6IHYucG9zaXRpdmVOdW0oKVxyXG59KTtcclxuXHJcbmV4cG9ydCBjb25zdCBlbnZzID0gQ29uZmlnLnBhcnRpYWwoKS5wYXJzZShwcm9jZXNzLmVudik7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBNYW5hZ2VzIGNvbmZpZ3VyYXRpb24gZm9yIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgYnkgbG9hZGluZ1xyXG4gKiBhbmQgbWVyZ2luZyBvcHRpb25zIGZyb20gbXVsdGlwbGUgc291cmNlcywgc3VjaCBhcyBkZWZhdWx0IHNldHRpbmdzLFxyXG4gKiBlbnZpcm9ubWVudCB2YXJpYWJsZXMsIHVzZXItcHJvdmlkZWQgb3B0aW9ucywgYW5kIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqIEVuc3VyZXMgdGhlIGdsb2JhbCBvcHRpb25zIGFyZSB1cC10by1kYXRlIHdpdGggdGhlIGhpZ2hlc3QgcHJpb3JpdHkgdmFsdWVzLlxyXG4gKiBQcm92aWRlcyBmdW5jdGlvbnMgZm9yIGFjY2Vzc2luZyBhbmQgdXBkYXRpbmcgY29uZmlndXJhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBkZWVwQ29weSwgZ2V0QWJzb2x1dGVQYXRoLCBpc09iamVjdCB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IHsgYWJzb2x1dGVQcm9wcywgbmVzdGVkUHJvcHMsIGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFNldHMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggaW5pdGlhbCB2YWx1ZXMgZnJvbSB0aGUgZGVmYXVsdCBjb25maWdcclxuY29uc3QgZ2xvYmFsT3B0aW9ucyA9IF9pbml0T3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtnZXRDb3B5PXRydWVdIC0gU3BlY2lmaWVzIHdoZXRoZXIgdG8gcmV0dXJuIGEgY29waWVkXHJcbiAqIG9iamVjdCBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgKGB0cnVlYCkgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCBvciBhIHJlZmVyZW5jZVxyXG4gKiB0byB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE9wdGlvbnMoZ2V0Q29weSA9IHRydWUpIHtcclxuICByZXR1cm4gZ2V0Q29weSA/IGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpIDogZ2xvYmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXBkYXRlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBuZXcgb3B0aW9ucyB0byBiZVxyXG4gKiBtZXJnZWQgaW50byB0aGUgZ2xvYmFsIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldENvcHk9ZmFsc2VdIC0gRGV0ZXJtaW5lcyB3aGV0aGVyIHRvIG1lcmdlIHRoZSBuZXdcclxuICogb3B0aW9ucyBpbnRvIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IChgdHJ1ZWApIG9yIGRpcmVjdGx5IGludG9cclxuICogdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0LCBlaXRoZXIgdGhlIG1vZGlmaWVkIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9yIGEgbW9kaWZpZWQgY29weSwgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGBnZXRDb3B5YC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVPcHRpb25zKG5ld09wdGlvbnMsIGdldENvcHkgPSBmYWxzZSkge1xyXG4gIC8vIE1lcmdlIG5ldyBvcHRpb25zIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyBvciBpdHMgY29weSBhbmQgcmV0dXJuIHRoZSByZXN1bHRcclxuICByZXR1cm4gX21lcmdlT3B0aW9ucyhnZXRPcHRpb25zKGdldENvcHkpLCBuZXdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGdsb2JhbCBvcHRpb25zIHdpdGggdmFsdWVzIHByb3ZpZGVkIHRocm91Z2ggdGhlIENMSSwga2VlcGluZ1xyXG4gKiB0aGUgcHJpbmNpcGxlIG9mIG9wdGlvbnMgbG9hZCBwcmlvcml0eS4gVGhpcyBmdW5jdGlvbiBhY2NlcHRzIGEgYGNsaUFyZ3NgXHJcbiAqIGFycmF5IGNvbnRhaW5pbmcgYXJndW1lbnRzIGZyb20gdGhlIENMSSwgd2hpY2ggd2lsbCBiZSB2YWxpZGF0ZWQgYW5kIGFwcGxpZWRcclxuICogaWYgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIFRoZSBwcmlvcml0eSBvcmRlciBmb3Igc2V0dGluZyB2YWx1ZXMgaXM6XHJcbiAqXHJcbiAqIDEuIFZhbHVlcyBmcm9tIGEgY3VzdG9tIEpTT04gZmlsZSAobG9hZGVkIGJ5IHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24pLlxyXG4gKiAyLiBWYWx1ZXMgZnJvbSB0aGUgY29tbWFuZCBsaW5lIGludGVyZmFjZSAoQ0xJKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldENsaU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQgbGluZSBhcmd1bWVudHMgdXNlZFxyXG4gKiBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHJlZmxlY3RpbmcgdGhlIG1lcmdlZFxyXG4gKiBjb25maWd1cmF0aW9uIGZyb20gc291cmNlcyBwcm92aWRlZCB0aHJvdWdoIHRoZSBDTEkuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0Q2xpT3B0aW9ucyhjbGlBcmdzKSB7XHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChjbGlBcmdzICYmIEFycmF5LmlzQXJyYXkoY2xpQXJncykgJiYgY2xpQXJncy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGxvYWRlZCB2aWEgdGhlIGAtLWxvYWRDb25maWdgXHJcbiAgICBjb25zdCBjb25maWdPcHRpb25zID0gX2xvYWRDb25maWdGaWxlKGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNvbmZpZ09wdGlvbnNgXHJcbiAgICB1cGRhdGVPcHRpb25zKGNvbmZpZ09wdGlvbnMpO1xyXG5cclxuICAgIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIENMSVxyXG4gICAgY29uc3QgY2xpT3B0aW9ucyA9IF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncyk7XHJcblxyXG4gICAgLy8gVXBkYXRlIGdsb2JhbCBvcHRpb25zIHdpdGggdGhlIHZhbHVlcyBmcm9tIHRoZSBgY2xpT3B0aW9uc2BcclxuICAgIHVwZGF0ZU9wdGlvbnMoY2xpT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcmVmZXJlbmNlIHRvIHRoZSBnbG9iYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnZXRPcHRpb25zKGZhbHNlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgY29uZmlndXJhdGlvbiBvcHRpb25zIChQaGFudG9tSlMpIHRvIGEgbmV3IGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS4gVGhpcyBmdW5jdGlvbiBjb252ZXJ0cyBmbGF0LCBvbGQtc3RydWN0dXJlZCBvcHRpb25zIGludG9cclxuICogYSBuZXcsIG5lc3RlZCBjb25maWd1cmF0aW9uIGZvcm1hdCBiYXNlZCBvbiBhIHByZWRlZmluZWQgbWFwcGluZ1xyXG4gKiAoYG5lc3RlZFByb3BzYCkuIFRoZSBuZXcgZm9ybWF0IGlzIHVzZWQgZm9yIFB1cHBldGVlciwgd2hpbGUgdGhlIG9sZCBmb3JtYXRcclxuICogd2FzIHVzZWQgZm9yIFBoYW50b21KUy5cclxuICpcclxuICogQGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2xkT3B0aW9ucyAtIFRoZSBvbGQsIGZsYXQgY29uZmlndXJhdGlvbiBvcHRpb25zXHJcbiAqIHRvIGJlIGNvbnZlcnRlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQSBuZXcgb2JqZWN0IGNvbnRhaW5pbmcgb3B0aW9ucyBzdHJ1Y3R1cmVkIGFjY29yZGluZ1xyXG4gKiB0byB0aGUgbWFwcGluZyBkZWZpbmVkIGluIGBuZXN0ZWRQcm9wc2Agb3IgYW4gZW1wdHkgb2JqZWN0IGlmIHRoZSBwcm92aWRlZFxyXG4gKiBgb2xkT3B0aW9uc2AgaXMgbm90IGEgY29ycmVjdCBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zKG9sZE9wdGlvbnMpIHtcclxuICAvLyBBbiBvYmplY3QgZm9yIHRoZSBuZXcgc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCB2YWx1ZSBpcyBhIGNvcnJlY3Qgb2JqZWN0XHJcbiAgaWYgKGlzT2JqZWN0KG9sZE9wdGlvbnMpKSB7XHJcbiAgICAvLyBJdGVyYXRlIG92ZXIgZWFjaCBrZXktdmFsdWUgcGFpciBpbiB0aGUgb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgdGhlcmUgaXMgYSBuZXN0ZWQgbWFwcGluZywgc3BsaXQgaXQgaW50byBhIHByb3BlcnRpZXMgY2hhaW5cclxuICAgICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNba2V5XVxyXG4gICAgICAgID8gbmVzdGVkUHJvcHNba2V5XS5zcGxpdCgnLicpXHJcbiAgICAgICAgOiBbXTtcclxuXHJcbiAgICAgIC8vIElmIGl0IGlzIHRoZSBsYXN0IHByb3BlcnR5IGluIHRoZSBjaGFpbiwgYXNzaWduIHRoZSB2YWx1ZSwgb3RoZXJ3aXNlLFxyXG4gICAgICAvLyBjcmVhdGUgb3IgcmV1c2UgdGhlIG5lc3RlZCBvYmplY3RcclxuICAgICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cclxuICAgICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgICAgbmV3T3B0aW9uc1xyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgICdbY29uZmlnXSBObyBjb3JyZWN0IG9iamVjdCB3aXRoIG9wdGlvbnMgd2FzIHByb3ZpZGVkLiBSZXR1cm5pbmcgYW4gZW1wdHkgYXJyYXkuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgbmV3LCBzdHJ1Y3R1cmVkIG9wdGlvbnMgb2JqZWN0XHJcbiAgcmV0dXJuIG5ld09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBWYWxpZGF0ZXMsIHBhcnNlcywgYW5kIGNoZWNrcyBpZiB0aGUgcHJvdmlkZWQgY29uZmlnIGlzIGFsbG93ZWQgc2V0XHJcbiAqIG9mIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc0FsbG93ZWRDb25maWdcclxuICpcclxuICogQHBhcmFtIHt1bmtub3dufSBjb25maWcgLSBUaGUgY29uZmlnIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkIGFzIGEgc2V0XHJcbiAqIG9mIG9wdGlvbnMuIE11c3QgYmUgZWl0aGVyIGFuIG9iamVjdCBvciBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBbdG9TdHJpbmc9ZmFsc2VdIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCB2ZXJzaW9uXHJcbiAqIG9mIHRoZSBwYXJzZWQgY29uZmlnLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFthbGxvd0Z1bmN0aW9ucz1mYWxzZV0gLSBXaGV0aGVyIHRvIGFsbG93IGZ1bmN0aW9uc1xyXG4gKiBpbiB0aGUgcGFyc2VkIGNvbmZpZy4gSWYgdHJ1ZSwgZnVuY3Rpb25zIGFyZSBwcmVzZXJ2ZWQuIE90aGVyd2lzZSwgd2hlblxyXG4gKiBhIGZ1bmN0aW9uIGlzIGZvdW5kLCBudWxsIGlzIHJldHVybmVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyB0cnVlLCBhbmQgbnVsbFxyXG4gKiBpZiB0aGUgY29uZmlnIGlzIG5vdCBhIHZhbGlkIHNldCBvZiBvcHRpb25zIG9yIHBhcnNpbmcgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaXNBbGxvd2VkQ29uZmlnKFxyXG4gIGNvbmZpZyxcclxuICB0b1N0cmluZyA9IGZhbHNlLFxyXG4gIGFsbG93RnVuY3Rpb25zID0gZmFsc2VcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEFjY2VwdCBvbmx5IG9iamVjdHMgYW5kIHN0cmluZ3NcclxuICAgIGlmICghaXNPYmplY3QoY29uZmlnKSAmJiB0eXBlb2YgY29uZmlnICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBSZXR1cm4gbnVsbCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gbnVsbCBpZiBwYXJzaW5nIGZhaWxzXHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciBsb2dvLCB2ZXJzaW9uLCBhbmQgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHByaW50TGljZW5zZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50TGljZW5zZSgpIHtcclxuICAvLyBQcmludCB0aGUgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvblxyXG4gIHByaW50VmVyc2lvbigpO1xyXG5cclxuICAvLyBQcmludCB0aGUgbGljZW5zZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1RoaXMgc29mdHdhcmUgcmVxdWlyZXMgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgZm9yIGNvbW1lcmNpYWwgdXNlLlxcbidcclxuICAgICAgLnllbGxvdyxcclxuICAgICdcXG5Gb3IgYSBmdWxsIGxpc3Qgb2YgQ0xJIG9wdGlvbnMsIHR5cGU6JyxcclxuICAgICdcXG5oaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIgLS1oZWxwXFxuJy5ncmVlbixcclxuICAgICdcXG5JZiB5b3UgZG8gbm90IGhhdmUgYSBsaWNlbnNlLCBvbmUgY2FuIGJlIG9idGFpbmVkIGhlcmU6JyxcclxuICAgICdcXG5odHRwczovL3Nob3AuaGlnaHNvZnQuY29tL1xcbicuZ3JlZW4sXHJcbiAgICAnXFxuVG8gY3VzdG9taXplIHlvdXIgaW5zdGFsbGF0aW9uLCBwbGVhc2UgcmVmZXIgdG8gdGhlIFJFQURNRSBmaWxlIGF0OicsXHJcbiAgICAnXFxuaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZVxcbicuZ3JlZW5cclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLCBkaXNwbGF5aW5nIGF2YWlsYWJsZSBvcHRpb25zXHJcbiAqIGFuZCB0aGVpciBkZXNjcmlwdGlvbnMuIEl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHkgaWYgY2F0ZWdvcmllc1xyXG4gKiBjb250YWluIG5lc3RlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcHJpbnRVc2FnZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgLy8gRGlzcGxheSBSRUFETUUgYW5kIGdlbmVyYWwgdXNhZ2UgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSBSRUFETUUgZmlsZSBhdDogJHsnaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvbm9kZS1leHBvcnQtc2VydmVyI3JlYWRtZScuZ3JlZW59LlxcbmBcclxuICApO1xyXG5cclxuICAvLyBJdGVyYXRlIHRocm91Z2ggZWFjaCBjYXRlZ29yeSBpbiB0aGUgYGRlZmF1bHRDb25maWdgIGFuZCBkaXNwbGF5IHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYCR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLmJvbGQucmVkKTtcclxuICAgIF9jeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgY29uc29sZS5sb2coJycpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBvciB0ZXh0IHdpdGggdGhlIHZlcnNpb25cclxuICogaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBwcmludFZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBbbm9Mb2dvPWZhbHNlXSAtIElmIHRydWUsIG9ubHkgcHJpbnRzIHRleHQgd2l0aCB0aGUgdmVyc2lvblxyXG4gKiBpbmZvcm1hdGlvbiwgd2l0aG91dCB0aGUgbG9nby4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFZlcnNpb24obm9Mb2dvID0gZmFsc2UpIHtcclxuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGAuZW52YCBvciBmcm9tIGBwYWNrYWdlLmpzb25gXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJyksICd1dGY4JylcclxuICApLnZlcnNpb247XHJcblxyXG4gIC8vIFByaW50IHRleHQgb25seVxyXG4gIGlmIChub0xvZ28pIHtcclxuICAgIGNvbnNvbGUubG9nKGBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259YCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIFByaW50IHRoZSBsb2dvXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAnbXNnJywgJ3N0YXJ0dXAubXNnJyksICd1dGY4JykudG9TdHJpbmcoKVxyXG4gICAgICAgIC5ib2xkLnllbGxvdyxcclxuICAgICAgYHYke3BhY2thZ2VWZXJzaW9ufVxcbmAuYm9sZFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgcmV0dXJucyB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IGJhc2VkIG9uIHRoZSBwcm92aWRlZFxyXG4gKiBjb25maWd1cmF0aW9uLCBzZXR0aW5nIHZhbHVlcyBmcm9tIG5lc3RlZCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5LlxyXG4gKlxyXG4gKiBUaGUgcHJpb3JpdHkgb3JkZXIgZm9yIHNldHRpbmcgdmFsdWVzIGlzOlxyXG4gKlxyXG4gKiAxLiBWYWx1ZXMgZnJvbSB0aGUgYC4vbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIChkZWZhdWx0cykuXHJcbiAqIDIuIFZhbHVlcyBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcyAoc3BlY2lmaWVkIGluIHRoZSBgLmVudmAgZmlsZSkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaW5pdE9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogdGhlIGdsb2JhbCBvcHRpb25zLiBJdCBzaG91bGQgaW5jbHVkZSBuZXN0ZWQgcHJvcGVydGllcyB3aXRoIGEgYHZhbHVlYFxyXG4gKiBhbmQgYW4gYGVudkxpbmtgIGZvciBsaW5raW5nIHRvIGVudmlyb25tZW50IHZhcmlhYmxlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGluaXRpYWxpemVkIGdsb2JhbCBvcHRpb25zIG9iamVjdCwgcG9wdWxhdGVkIHdpdGhcclxuICogdmFsdWVzIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHlcclxuICogb3JkZXIuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaW5pdE9wdGlvbnMoY29uZmlnKSB7XHJcbiAgLy8gSW5pdCB0aGUgb2JqZWN0IGZvciBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBTdGFydCBpbml0aWFsaXppbmcgdGhlIGBvcHRpb25zYCBvYmplY3QgcmVjdXJzaXZlbHlcclxuICBmb3IgKGNvbnN0IFtuYW1lLCBpdGVtXSBvZiBPYmplY3QuZW50cmllcyhjb25maWcpKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY29ycmVjdCB2YWx1ZSBiYXNlZCBvbiB0aGUgZXN0YWJsaXNoZWQgcHJpb3JpdHkgb3JkZXJcclxuICAgICAgaWYgKGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gdW5kZWZpbmVkICYmIGVudnNbaXRlbS5lbnZMaW5rXSAhPT0gbnVsbCkge1xyXG4gICAgICAgIC8vIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgdmFsdWVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gZW52c1tpdGVtLmVudkxpbmtdO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRoZSB2YWx1ZSBmcm9tIHRoZSBjb25maWcgZmlsZVxyXG4gICAgICAgIG9wdGlvbnNbbmFtZV0gPSBpdGVtLnZhbHVlO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDcmVhdGUgYSBzZWN0aW9uIGluIHRoZSBvcHRpb25zXHJcbiAgICAgIG9wdGlvbnNbbmFtZV0gPSBfaW5pdE9wdGlvbnMoaXRlbSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIGNyZWF0ZWQgYG9wdGlvbnNgIG9iamVjdFxyXG4gIHJldHVybiBvcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogTWVyZ2VzIHR3byBzZXRzIG9mIGNvbmZpZ3VyYXRpb24gb3B0aW9ucywgY29uc2lkZXJpbmcgYWJzb2x1dGUgcHJvcGVydGllcy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9tZXJnZU9wdGlvbnNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9yaWdpbmFsT3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTWVyZ2VkIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9ucywgbmV3T3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSBgb3JpZ2luYWxPcHRpb25zYCBhbmQgYG5ld09wdGlvbnNgIGFyZSBjb3JyZWN0IG9iamVjdHNcclxuICBpZiAoaXNPYmplY3Qob3JpZ2luYWxPcHRpb25zKSAmJiBpc09iamVjdChuZXdPcHRpb25zKSkge1xyXG4gICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobmV3T3B0aW9ucykpIHtcclxuICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gPVxyXG4gICAgICAgIGlzT2JqZWN0KHZhbHVlKSAmJlxyXG4gICAgICAgICFhYnNvbHV0ZVByb3BzLmluY2x1ZGVzKGtleSkgJiZcclxuICAgICAgICBvcmlnaW5hbE9wdGlvbnNba2V5XSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICA/IF9tZXJnZU9wdGlvbnMob3JpZ2luYWxPcHRpb25zW2tleV0sIHZhbHVlKVxyXG4gICAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgICAgOiBvcmlnaW5hbE9wdGlvbnNba2V5XSB8fCBudWxsO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBvcmlnaW5hbCAobW9kaWZpZWQgb3Igbm90KSBvcHRpb25zXHJcbiAgcmV0dXJuIG9yaWdpbmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIHRoZSBwcm92aWRlZCBvcHRpb25zIG9iamVjdCB0byBhIEpTT04tZm9ybWF0dGVkIHN0cmluZ1xyXG4gKiB3aXRoIHRoZSBvcHRpb24gdG8gcHJlc2VydmUgZnVuY3Rpb25zLiBJbiBvcmRlciBmb3IgYSBmdW5jdGlvblxyXG4gKiB0byBiZSBwcmVzZXJ2ZWQsIGl0IG5lZWRzIHRvIGZvbGxvdyB0aGUgZm9ybWF0IGBmdW5jdGlvbiAoLi4uKSB7Li4ufWAuXHJcbiAqIFN1Y2ggYSBmdW5jdGlvbiBjYW4gYWxzbyBiZSBzdHJpbmdpZmllZC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9vcHRpb25zU3RyaW5naWZ5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IHRvIGJlIGNvbnZlcnRlZCB0byBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0Z1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHByZXNlcnZlZFxyXG4gKiBpbiB0aGUgb3V0cHV0LiBPdGhlcndpc2UgYW4gZXJyb3IgaXMgdGhyb3duLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHN0cmluZ2lmeUZ1bmN0aW9ucyAtIElmIHNldCB0byB0cnVlLCBmdW5jdGlvbnMgYXJlIHNhdmVkXHJcbiAqIGFzIHN0cmluZ3MuIFRoZSBgYWxsb3dGdW5jdGlvbnNgIG11c3QgYmUgc2V0IHRvIHRydWUgYXMgd2VsbCBmb3IgdGhpcyB0byB0YWtlXHJcbiAqIGFuIGVmZmVjdC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCB3aGVuIGZ1bmN0aW9ucyBhcmUgbm90IGFsbG93ZWQgYnV0IGFyZVxyXG4gKiBmb3VuZCBpbiBwcm92aWRlZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBfb3B0aW9uc1N0cmluZ2lmeShvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucywgc3RyaW5naWZ5RnVuY3Rpb25zKSB7XHJcbiAgY29uc3QgcmVwbGFjZXJDYWxsYmFjayA9IChfLCB2YWx1ZSkgPT4ge1xyXG4gICAgLy8gVHJpbSBzdHJpbmcgdmFsdWVzXHJcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xyXG4gICAgICB2YWx1ZSA9IHZhbHVlLnRyaW0oKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBJZiB2YWx1ZSBpcyBhIGZ1bmN0aW9uIG9yIHN0cmluZ2lmaWVkIGZ1bmN0aW9uXHJcbiAgICBpZiAoXHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJyB8fFxyXG4gICAgICAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJykgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIElmIGFsbG93RnVuY3Rpb25zIGlzIHNldCB0byB0cnVlLCBwcmVzZXJ2ZSBmdW5jdGlvbnNcclxuICAgICAgaWYgKGFsbG93RnVuY3Rpb25zKSB7XHJcbiAgICAgICAgLy8gQmFzZWQgb24gdGhlIGBzdHJpbmdpZnlGdW5jdGlvbnNgIG9wdGlvbnMsIHNldCBmdW5jdGlvbiB2YWx1ZXNcclxuICAgICAgICByZXR1cm4gc3RyaW5naWZ5RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IC8vIEFzIHN0cmluZ2lmaWVkIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgXCJFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5cImBcclxuICAgICAgICAgIDogLy8gQXMgZnVuY3Rpb25zXHJcbiAgICAgICAgICAgIGBFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5gO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRocm93IGFuIGVycm9yIG90aGVyd2lzZVxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcigpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSW4gYWxsIG90aGVyIGNhc2VzLCBzaW1wbHkgcmV0dXJuIHRoZSB2YWx1ZVxyXG4gICAgcmV0dXJuIHZhbHVlO1xyXG4gIH07XHJcblxyXG4gIC8vIFN0cmluZ2lmeSBvcHRpb25zIGFuZCBpZiByZXF1aXJlZCwgcmVwbGFjZSBzcGVjaWFsIGZ1bmN0aW9ucyBtYXJrc1xyXG4gIHJldHVybiBKU09OLnN0cmluZ2lmeShvcHRpb25zLCByZXBsYWNlckNhbGxiYWNrKS5yZXBsYWNlQWxsKFxyXG4gICAgc3RyaW5naWZ5RnVuY3Rpb25zID8gL1xcXFxcIkVYUF9GVU58RVhQX0ZVTlxcXFxcIi9nIDogL1wiRVhQX0ZVTnxFWFBfRlVOXCIvZyxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgcHJvdmlkZWQgdmlhXHJcbiAqIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gaW4gdGhlIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgdG8gc2VhcmNoXHJcbiAqIGZvciB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIGFuZCB0aGUgY29ycmVzcG9uZGluZyBmaWxlIHBhdGguXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZFxyXG4gKiBmaWxlLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgdGhlIGZpbGUgaXMgbm90IGZvdW5kLCBpbnZhbGlkLCBvciBhbiBlcnJvclxyXG4gKiBvY2N1cnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGUoY2xpQXJncykge1xyXG4gIC8vIEdldCB0aGUgYWxsb3cgZmxhZ3MgZm9yIHRoZSBjdXN0b20gbG9naWMgY2hlY2tcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiwgYWxsb3dGaWxlUmVzb3VyY2VzIH0gPSBnZXRPcHRpb25zKCkuY3VzdG9tTG9naWM7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gd2FzIHVzZWRcclxuICBjb25zdCBjb25maWdJbmRleCA9IGNsaUFyZ3MuZmluZEluZGV4KFxyXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiB2YWx1ZVxyXG4gIGNvbnN0IGNvbmZpZ0ZpbGVOYW1lID0gY29uZmlnSW5kZXggPiAtMSAmJiBjbGlBcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBgLS1sb2FkQ29uZmlnYCBpcyBwcmVzZW50IGFuZCBoYXMgYSBjb3JyZWN0IHZhbHVlXHJcbiAgaWYgKGNvbmZpZ0ZpbGVOYW1lICYmIGFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICByZXR1cm4gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoY29uZmlnRmlsZU5hbWUpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7Y29uZmlnRmlsZU5hbWV9IGZpbGUuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gYWRkaXRpb25hbCBvcHRpb25zIHRvIHJldHVyblxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhcnNlcyBjb21tYW5kLWxpbmUgYXJndW1lbnRzIGFuZCBwYWlycyBlYWNoIGFyZ3VtZW50IHdpdGggaXRzIGNvcnJlc3BvbmRpbmdcclxuICogb3B0aW9uIGluIHRoZSBjb25maWd1cmF0aW9uLiBUaGUgdmFsdWVzIGFyZSBzdHJ1Y3R1cmVkIGludG8gYSBuZXN0ZWQgb3B0aW9uc1xyXG4gKiBvYmplY3QsIGJhc2VkIG9uIHByZWRlZmluZWQgbWFwcGluZ3MuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWVcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gbmVzdGVkUHJvcHMgLSBBbiBhcnJheSBvZiBuZXN0aW5nIGxldmVsIGZvciBhbGxcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gY2xpQXJncyAtIEFuIGFycmF5IG9mIGNvbW1hbmQtbGluZSBhcmd1bWVudHNcclxuICogY29udGFpbmluZyBvcHRpb25zIGFuZCB0aGVpciBhc3NvY2lhdGVkIHZhbHVlcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQW4gdXBkYXRlZCBvcHRpb25zIG9iamVjdCB3aGVyZSBlYWNoIG9wdGlvbiBmcm9tXHJcbiAqIHRoZSBjb21tYW5kLWxpbmUgaXMgcGFpcmVkIHdpdGggaXRzIHZhbHVlLCBzdHJ1Y3R1cmVkIGludG8gbmVzdGVkIG9iamVjdHNcclxuICogYXMgZGVmaW5lZC5cclxuICovXHJcbmZ1bmN0aW9uIF9wYWlyQXJndW1lbnRWYWx1ZShuZXN0ZWRQcm9wcywgY2xpQXJncykge1xyXG4gIC8vIEFuIGVtcHR5IG9iamVjdCB0byBjb2xsZWN0IGFuZCBzdHJ1Y3R1cml6ZSBkYXRhIGZyb20gdGhlIGFyZ3NcclxuICBjb25zdCBjbGlPcHRpb25zID0ge307XHJcblxyXG4gIC8vIEN5Y2xlIHRocm91Z2ggYWxsIENMSSBhcmdzIGFuZCBmaWx0ZXIgdGhlbVxyXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgY2xpQXJncy5sZW5ndGg7IGkrKykge1xyXG4gICAgY29uc3Qgb3B0aW9uID0gY2xpQXJnc1tpXS5yZXBsYWNlKC8tL2csICcnKTtcclxuXHJcbiAgICAvLyBGaW5kIHRoZSByaWdodCBwbGFjZSBmb3IgcHJvcGVydHkncyB2YWx1ZVxyXG4gICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkUHJvcHNbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZFByb3BzW29wdGlvbl0uc3BsaXQoJy4nKVxyXG4gICAgICA6IFtdO1xyXG5cclxuICAgIC8vIENyZWF0ZSBvcHRpb25zIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIENMSSBmb3IgbGF0ZXIgcGFyc2luZyBhbmQgbWVyZ2luZ1xyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZSgob2JqLCBwcm9wLCBpbmRleCkgPT4ge1xyXG4gICAgICBpZiAocHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4KSB7XHJcbiAgICAgICAgY29uc3QgdmFsdWUgPSBjbGlBcmdzWysraV07XHJcbiAgICAgICAgaWYgKCF2YWx1ZSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NvbmZpZ10gTWlzc2luZyB2YWx1ZSBmb3IgdGhlIENMSSAnLS0ke29wdGlvbn0nIGFyZ3VtZW50LiBVc2luZyB0aGUgZGVmYXVsdCB2YWx1ZS5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgICBvYmpbcHJvcF0gPSB2YWx1ZSB8fCBudWxsO1xyXG4gICAgICB9IGVsc2UgaWYgKG9ialtwcm9wXSA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgb2JqW3Byb3BdID0ge307XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcclxuICAgIH0sIGNsaU9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHBhcnNlZCBDTEkgb3B0aW9uc1xyXG4gIHJldHVybiBjbGlPcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgdHJhdmVyc2VzIHRoZSBvcHRpb25zIG9iamVjdCB0byBwcmludCB0aGUgdXNhZ2UgaW5mb3JtYXRpb25cclxuICogZm9yIGVhY2ggb3B0aW9uIGNhdGVnb3J5IGFuZCBpbmRpdmlkdWFsIG9wdGlvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBDTEkgb3B0aW9ucy4gSXQgbWF5XHJcbiAqIGluY2x1ZGUgbmVzdGVkIGNhdGVnb3JpZXMgYW5kIGluZGl2aWR1YWwgb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9jeWNsZUNhdGVnb3JpZXMob3B0aW9ucykge1xyXG4gIGZvciAoY29uc3QgW25hbWUsIG9wdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcclxuICAgIC8vIElmIHRoZSBjdXJyZW50IGVudHJ5IGlzIGEgY2F0ZWdvcnkgYW5kIG5vdCBhIGxlYWYgb3B0aW9uLCByZWN1cnNlIGludG8gaXRcclxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9wdGlvbiwgJ3ZhbHVlJykpIHtcclxuICAgICAgX2N5Y2xlQ2F0ZWdvcmllcyhvcHRpb24pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gUHJlcGFyZSBkZXNjcmlwdGlvblxyXG4gICAgICBjb25zdCBkZXNjTmFtZSA9IGAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9YDtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdmFsdWVcclxuICAgICAgbGV0IG9wdGlvblZhbHVlID0gb3B0aW9uLnZhbHVlO1xyXG5cclxuICAgICAgLy8gUHJlcGFyZSB2YWx1ZSBmb3Igb3B0aW9uIHRoYXQgaXMgbm90IG51bGwgYW5kIGlzIGFycmF5IG9mIHN0cmluZ3NcclxuICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSBudWxsICYmIG9wdGlvbi50eXBlcy5pbmNsdWRlcygnc3RyaW5nW10nKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID1cclxuICAgICAgICAgICdbJyArIG9wdGlvblZhbHVlLm1hcCgoaXRlbSkgPT4gYCcke2l0ZW19J2ApLmpvaW4oJywgJykgKyAnXSc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFByZXBhcmUgdmFsdWUgZm9yIG9wdGlvbiB0aGF0IGlzIG5vdCBudWxsIGFuZCBpcyBhIHN0cmluZ1xyXG4gICAgICBpZiAob3B0aW9uVmFsdWUgIT09IG51bGwgJiYgb3B0aW9uLnR5cGVzLmluY2x1ZGVzKCdzdHJpbmcnKSkge1xyXG4gICAgICAgIG9wdGlvblZhbHVlID0gYCcke29wdGlvblZhbHVlfSdgO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgIGRlc2NOYW1lLmdyZWVuLFxyXG4gICAgICAgIGAkeygnPCcgKyBvcHRpb24udHlwZXMuam9pbignfCcpICsgJz4nKS55ZWxsb3d9YCxcclxuICAgICAgICBgJHtTdHJpbmcob3B0aW9uVmFsdWUpLmJvbGR9YC5ibHVlLFxyXG4gICAgICAgIGAtICR7b3B0aW9uLmRlc2NyaXB0aW9ufS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0T3B0aW9ucyxcclxuICB1cGRhdGVPcHRpb25zLFxyXG4gIHNldENsaU9wdGlvbnMsXHJcbiAgbWFwVG9OZXdPcHRpb25zLFxyXG4gIGlzQWxsb3dlZENvbmZpZyxcclxuICBwcmludExpY2Vuc2UsXHJcbiAgcHJpbnRVc2FnZSxcclxuICBwcmludFZlcnNpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhUVFAgdXRpbGl0eSBtb2R1bGUgZm9yIGZldGNoaW5nIGFuZCBwb3N0aW5nIGRhdGEuIFN1cHBvcnRzIGJvdGhcclxuICogSFRUUCBhbmQgSFRUUFMgcHJvdG9jb2xzLCBwcm92aWRpbmcgbWV0aG9kcyB0byBtYWtlIEdFVCBhbmQgUE9TVCByZXF1ZXN0c1xyXG4gKiB3aXRoIGN1c3RvbWl6YWJsZSBvcHRpb25zLiBJbmNsdWRlcyBwcm90b2NvbCBkZXRlcm1pbmF0aW9uIGJhc2VkIG9uIFVSTFxyXG4gKiBhbmQgYXVnbWVudHMgcmVzcG9uc2Ugb2JqZWN0cyB3aXRoIGEgJ3RleHQnIHByb3BlcnR5IGZvciBlYXNpZXIgZGF0YSBhY2Nlc3MuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBkYXRhIGZyb20gdGhlIHNwZWNpZmllZCBVUkwgdXNpbmcgZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZmV0Y2hcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZmV0Y2ggZGF0YSBmcm9tLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZldGNoKHVybCwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAuZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIGlmICghcmVzcG9uc2VEYXRhKSB7XHJcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXNwb25zZS50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgUE9TVCByZXF1ZXN0IHRvIHRoZSBzcGVjaWZpZWQgVVJMIHdpdGggdGhlIHByb3ZpZGVkIEpTT04gYm9keSB1c2luZ1xyXG4gKiBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIHNlbmQgdGhlIFBPU1QgcmVxdWVzdCB0by5cclxuICogQHBhcmFtIHtPYmplY3R9IFtib2R5PXt9XSAtIFRoZSBKU09OIGJvZHkgdG8gaW5jbHVkZSBpbiB0aGUgUE9TVCByZXF1ZXN0LlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcG9zdCh1cmwsIGJvZHkgPSB7fSwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoYm9keSk7XHJcblxyXG4gICAgLy8gU2V0IGRlZmF1bHQgaGVhZGVycyBhbmQgbWVyZ2Ugd2l0aCByZXF1ZXN0T3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oXHJcbiAgICAgIHtcclxuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcclxuICAgICAgICBoZWFkZXJzOiB7XHJcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxyXG4gICAgICAgICAgJ0NvbnRlbnQtTGVuZ3RoJzogZGF0YS5sZW5ndGhcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zXHJcbiAgICApO1xyXG5cclxuICAgIGNvbnN0IHJlcXVlc3QgPSBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAucmVxdWVzdCh1cmwsIG9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgIC8vIFdyaXRlIHRoZSByZXF1ZXN0IGJvZHkgYW5kIGVuZCB0aGUgcmVxdWVzdFxyXG4gICAgcmVxdWVzdC53cml0ZShkYXRhKTtcclxuICAgIHJlcXVlc3QuZW5kKCk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgVVJMLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2dldFByb3RvY29sTW9kdWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGRldGVybWluZSB0aGUgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSAoaHR0cCBvciBodHRwcykuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKSB7XHJcbiAgcmV0dXJuIHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZmV0Y2gsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBBIGN1c3RvbSBlcnJvciBjbGFzcyBmb3IgaGFuZGxpbmcgZXhwb3J0LXJlbGF0ZWQgZXJyb3JzLiBFeHRlbmRzIHRoZSBuYXRpdmVcclxuICogYEVycm9yYCBjbGFzcyB0byBpbmNsdWRlIGFkZGl0aW9uYWwgcHJvcGVydGllcyBsaWtlIHN0YXR1cyBjb2RlIGFuZCBzdGFja1xyXG4gKiB0cmFjZSBkZXRhaWxzLlxyXG4gKi9cclxuY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlIC0gVGhlIGVycm9yIG1lc3NhZ2UgdG8gYmUgZGlzcGxheWVkLlxyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBzdGF0dXNDb2RlIC0gT3B0aW9uYWwgSFRUUCBzdGF0dXMgY29kZSBhc3NvY2lhdGVkXHJcbiAgICogd2l0aCB0aGUgZXJyb3IgKGUuZy4sIDQwMCwgNTAwKS5cclxuICAgKi9cclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXNDb2RlKSB7XHJcbiAgICBzdXBlcigpO1xyXG5cclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcblxyXG4gICAgaWYgKHN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgb3IgdXBkYXRlcyB0aGUgSFRUUCBzdGF0dXMgY29kZSBmb3IgdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0YXR1c0NvZGUgLSBUaGUgSFRUUCBzdGF0dXMgY29kZSB0byBhc3NpZ24gdG8gdGhlIGVycm9yLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRTdGF0dXMoc3RhdHVzQ29kZSkge1xyXG4gICAgdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzQ29kZTtcclxuXHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNldHMgYWRkaXRpb25hbCBlcnJvciBkZXRhaWxzIGJhc2VkIG9uIGFuIGV4aXN0aW5nIGVycm9yIG9iamVjdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gQW4gZXJyb3Igb2JqZWN0IGNvbnRhaW5pbmcgZGV0YWlscyB0byBwb3B1bGF0ZVxyXG4gICAqIHRoZSBgRXhwb3J0RXJyb3JgIGluc3RhbmNlLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge0V4cG9ydEVycm9yfSBUaGUgdXBkYXRlZCBpbnN0YW5jZSBvZiB0aGUgYEV4cG9ydEVycm9yYCBjbGFzcy5cclxuICAgKi9cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG5cclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKGVycm9yLnN0YXR1c0NvZGUpIHtcclxuICAgICAgdGhpcy5zdGF0dXNDb2RlID0gZXJyb3Iuc3RhdHVzQ29kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoZSBjYWNoZSBtYW5hZ2VyIGlzIHJlc3BvbnNpYmxlIGZvciBoYW5kbGluZyBhbmQgbWFuYWdpbmdcclxuICogdGhlIEhpZ2hjaGFydHMgbGlicmFyeSBhbG9uZyB3aXRoIGl0cyBkZXBlbmRlbmNpZXMuIEl0IGVuc3VyZXMgdGhhdCB0aGVzZVxyXG4gKiByZXNvdXJjZXMgYXJlIHN0b3JlZCBhbmQgcmV0cmlldmVkIGVmZmljaWVudGx5IHRvIG9wdGltaXplIHBlcmZvcm1hbmNlXHJcbiAqIGFuZCByZWR1Y2UgcmVkdW5kYW50IG5ldHdvcmsgcmVxdWVzdHMuIFRoZSBjYWNoZSBpcyBzdG9yZWQgaW4gdGhlIGAuY2FjaGVgXHJcbiAqIGRpcmVjdG9yeSBieSBkZWZhdWx0LCB3aGljaCBzZXJ2ZXMgYXMgYSBkZWRpY2F0ZWQgZm9sZGVyIGZvciBrZWVwaW5nIGNhY2hlZFxyXG4gKiBmaWxlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgdXBkYXRlT3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBUaGUgaW5pdGlhbCBjYWNoZSB0ZW1wbGF0ZVxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5Vcmw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNoZWNrQW5kVXBkYXRlQ2FjaGVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBzZXJ2ZXJQcm94eU9wdGlvbnNcclxuKSB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBmZXRjaGVkTW9kdWxlcztcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNhY2hlIHBhdGhcclxuICAgIGNvbnN0IGNhY2hlUGF0aCA9IGdldENhY2hlUGF0aCgpO1xyXG5cclxuICAgIC8vIFByZXBhcmUgcGF0aHMgdG8gbWFuaWZlc3QgYW5kIHNvdXJjZXMgZnJvbSB0aGUgY2FjaGUgZm9sZGVyXHJcbiAgICBjb25zdCBtYW5pZmVzdFBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKTtcclxuICAgIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ3NvdXJjZXMuanMnKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcclxuXHJcbiAgICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIHRoZSBgbWFuaWZlc3QuanNvbmAgZG9lcyBub3QgZXhpc3RcclxuICAgIC8vIG9yIGlmIHRoZSBgZm9yY2VGZXRjaGAgb3B0aW9uIGlzIGVuYWJsZWRcclxuICAgIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGhpZ2hjaGFydHNPcHRpb25zLmZvcmNlRmV0Y2gpIHtcclxuICAgICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAgIC8vIFJlYWQgdGhlIG1hbmlmZXN0IEpTT05cclxuICAgICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpLCAndXRmOCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG1vZHVsZXMgaXMgYW4gYXJyYXksIGlmIHNvLCB3ZSByZXdyaXRlIGl0IHRvIGEgbWFwIHRvIG1ha2VcclxuICAgICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIGFjdHVhbCBudW1iZXIgb2Ygc2NyaXB0cyB0byBiZSBmZXRjaGVkXHJcbiAgICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9XHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnM7XHJcbiAgICAgIGNvbnN0IG51bWJlck9mTW9kdWxlcyA9XHJcbiAgICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcclxuICAgICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24pIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAgIE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzXHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICAnW2NhY2hlXSBUaGUgY2FjaGUgYW5kIHRoZSByZXF1ZXN0ZWQgbW9kdWxlcyBkbyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIW1hbmlmZXN0Lm1vZHVsZXNbbW9kdWxlTmFtZV0pIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjYWNoZV0gVGhlICR7bW9kdWxlTmFtZX0gaXMgbWlzc2luZyBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVXBkYXRlIGNhY2hlIGlmIG5lZWRlZFxyXG4gICAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgX3VwZGF0ZUNhY2hlKFxyXG4gICAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgICAgICBzb3VyY2VQYXRoXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xyXG5cclxuICAgICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgICAgLy8gRXh0cmFjdCBhbmQgc2F2ZSB2ZXJzaW9uIG9mIGN1cnJlbnRseSB1c2VkIEhpZ2hjaGFydHNcclxuICAgICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEZpbmFsbHksIHNhdmUgdGhlIG5ldyBtYW5pZmVzdCwgd2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjdXJyZW50IGNvbmZpZ1xyXG4gICAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgICBhd2FpdCBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoaGlnaGNoYXJ0c09wdGlvbnMsIGZldGNoZWRNb2R1bGVzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY29uZmlndXJlIGNhY2hlIGFuZCBjcmVhdGUgb3IgdXBkYXRlIHRoZSBjb25maWcgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY2FjaGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRIaWdoY2hhcnRzVmVyc2lvbigpIHtcclxuICByZXR1cm4gY2FjaGUuaGNWZXJzaW9uO1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIaWdoY2hhcnRzVmVyc2lvblxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbikge1xyXG4gIC8vIFVwZGF0ZSB0byB0aGUgbmV3IHZlcnNpb25cclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICBoaWdoY2hhcnRzOiB7XHJcbiAgICAgIHZlcnNpb246IG5ld1ZlcnNpb25cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGUncyBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjYWNoZVNvdXJjZXMgLSBUaGUgY2FjaGUgc291cmNlcyBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGV4dHJhY3RWZXJzaW9uKGNhY2hlU291cmNlcykge1xyXG4gIHJldHVybiBjYWNoZVNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGVTb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIGV4dHJhY3RNb2R1bGVOYW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHRQYXRoIC0gVGhlIHBhdGggb2YgdGhlIHNjcmlwdCBmcm9tIHdoaWNoIHRoZSBtb2R1bGVcclxuICogbmFtZSB3aWxsIGJlIGV4dHJhY3RlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBtb2R1bGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHRQYXRoKSB7XHJcbiAgcmV0dXJuIHNjcmlwdFBhdGgucmVwbGFjZShcclxuICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgJydcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IGNhY2hlIG9iamVjdC5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldENhY2hlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBjYWNoZSBvYmplY3QgY29udGFpbmluZyB2YXJpb3VzIGNhY2hlZCBkYXRhLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldENhY2hlKCkge1xyXG4gIHJldHVybiBjYWNoZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNhY2hlIHBhdGggZm9yIEhpZ2hjaGFydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVBhdGhcclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeSBmb3IgSGlnaGNoYXJ0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZVBhdGgoKSB7XHJcbiAgcmV0dXJuIGdldEFic29sdXRlUGF0aChnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgsICd1dGY4Jyk7IC8vICM1NjJcclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBgZmV0Y2hlZE1vZHVsZXNgIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxyXG4gKiB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxyXG4gKiBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuIFRoZSBkZWZhdWx0XHJcbiAqIHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbVxyXG4gKiB3aXRoIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxyXG4gIHNjcmlwdCxcclxuICByZXF1ZXN0T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlcyxcclxuICBzaG91bGRUaHJvd0Vycm9yID0gZmFsc2VcclxuKSB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgLmpzIGZyb20gdGhlIGN1c3RvbSBzdHJpbmdzXHJcbiAgaWYgKHNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgIHNjcmlwdCA9IHNjcmlwdC5zdWJzdHJpbmcoMCwgc2NyaXB0Lmxlbmd0aCAtIDMpO1xyXG4gIH1cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gIH1cclxuXHJcbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYCxcclxuICAgICAgNDA0XHJcbiAgICApLnNldEVycm9yKHJlc3BvbnNlKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uLmBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3RcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGhpZ2hjaGFydHNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbZmV0Y2hlZE1vZHVsZXM9e31dIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZVxyXG4gKiB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzT3B0aW9ucywgZmV0Y2hlZE1vZHVsZXMgPSB7fSkge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzXHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihnZXRDYWNoZVBhdGgoKSwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgYHNjcmlwdHNgIGFuZCBgY3VzdG9tU2NyaXB0c2AgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfZmV0Y2hTY3JpcHRzXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGNvcmVTY3JpcHRzIC0gSGlnaGNoYXJ0cyBjb3JlIHNjcmlwdHMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IG1vZHVsZVNjcmlwdHMgLSBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7QXJyYXkuPHN0cmluZz59IGN1c3RvbVNjcmlwdHMgLSBDdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsXHJcbiAqIFVSTHMpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBmZXRjaGVkIHNjcmlwdHNcclxuICogY29udGVudCBqb2luZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogc2V0dGluZyBhbiBIVFRQIEFnZW50IGZvciBwcm94eS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9mZXRjaFNjcmlwdHMoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHNlcnZlclByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pIHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gc2VydmVyUHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gc2VydmVyUHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiBleGlzdHMsIGFkZCBwcm94eSBhZ2VudCB0byByZXF1ZXN0IG9wdGlvbnNcclxuICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHByb3h5QWdlbnRcclxuICAgID8ge1xyXG4gICAgICAgIGFnZW50OiBwcm94eUFnZW50LFxyXG4gICAgICAgIHRpbWVvdXQ6IHNlcnZlclByb3h5T3B0aW9ucy50aW1lb3V0XHJcbiAgICAgIH1cclxuICAgIDoge307XHJcblxyXG4gIGNvbnN0IGFsbEZldGNoUHJvbWlzZXMgPSBbXHJcbiAgICAuLi5jb3JlU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIF9mZXRjaEFuZFByb2Nlc3NTY3JpcHQoYCR7c2NyaXB0fWAsIHJlcXVlc3RPcHRpb25zLCBmZXRjaGVkTW9kdWxlcylcclxuICAgICksXHJcbiAgICAuLi5jdXN0b21TY3JpcHRzLm1hcCgoc2NyaXB0KSA9PlxyXG4gICAgICBfZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCByZXF1ZXN0T3B0aW9ucylcclxuICAgIClcclxuICBdO1xyXG5cclxuICBjb25zdCBmZXRjaGVkU2NyaXB0cyA9IGF3YWl0IFByb21pc2UuYWxsKGFsbEZldGNoUHJvbWlzZXMpO1xyXG4gIHJldHVybiBmZXRjaGVkU2NyaXB0cy5qb2luKCc7XFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX3VwZGF0ZUNhY2hlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBoaWdoY2hhcnRzYCBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyUHJveHlPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgcmVwcmVzZW50aW5nXHJcbiAqIHRoZSBmZXRjaGVkIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF91cGRhdGVDYWNoZShoaWdoY2hhcnRzT3B0aW9ucywgc2VydmVyUHJveHlPcHRpb25zLCBzb3VyY2VQYXRoKSB7XHJcbiAgLy8gR2V0IEhpZ2hjaGFydHMgdmVyc2lvbiBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGhjVmVyc2lvbiA9XHJcbiAgICBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uID09PSAnbGF0ZXN0J1xyXG4gICAgICA/IG51bGxcclxuICAgICAgOiBgJHtoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9ufWA7XHJcblxyXG4gIC8vIEdldCB0aGUgQ0ROIHVybCBmb3Igc2NyaXB0c1xyXG4gIGNvbnN0IGNkblVybCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVybCB8fCBjYWNoZS5jZG5Vcmw7XHJcblxyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5zb3VyY2VzID0gYXdhaXQgX2ZldGNoU2NyaXB0cyhcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoYykgPT5cclxuICAgICAgICAgIGhjVmVyc2lvbiA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259LyR7Y31gIDogYCR7Y2RuVXJsfS8ke2N9YFxyXG4gICAgICAgIClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vbWFwcy8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tYXBzL21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgOiBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259L21vZHVsZXMvJHttfWBcclxuICAgICAgICAgICAgICA6IGAke2NkblVybH0vbW9kdWxlcy8ke219YFxyXG4gICAgICAgICksXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGkpID0+XHJcbiAgICAgICAgICBoY1ZlcnNpb25cclxuICAgICAgICAgICAgPyBgJHtjZG5Vcmx9L3N0b2NrLyR7aGNWZXJzaW9ufS9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9zdG9jay9pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBzZXJ2ZXJQcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldEhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uLFxyXG4gIGV4dHJhY3RWZXJzaW9uLFxyXG4gIGV4dHJhY3RNb2R1bGVOYW1lLFxyXG4gIGdldENhY2hlLFxyXG4gIGdldENhY2hlUGF0aFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWV0aG9kcyBmb3IgaW5pdGlhbGl6aW5nIEhpZ2hjaGFydHMgd2l0aCBjdXN0b21pemVkXHJcbiAqIGFuaW1hdGlvbiBzZXR0aW5ncyBhbmQgdHJpZ2dlcmluZyB0aGUgY3JlYXRpb24gb2YgSGlnaGNoYXJ0cyBjaGFydHMgd2l0aFxyXG4gKiBleHBvcnQtc3BlY2lmaWMgY29uZmlndXJhdGlvbnMgaW4gdGhlIHBhZ2UgY29udGV4dC4gU3VwcG9ydHMgZHluYW1pYyBvcHRpb25cclxuICogbWVyZ2luZywgY3VzdG9tIGxvZ2ljIGluamVjdGlvbiwgYW5kIGNvbnRyb2wgb3ZlciByZW5kZXJpbmcgYmVoYXZpb3JzLiBVc2VkXHJcbiAqIGJ5IHRoZSBQdXBwZXRlZXIgcGFnZS5cclxuICovXHJcblxyXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xyXG5cclxuLyoqXHJcbiAqIFNldHRpbmcgdGhlIGBIaWdoY2hhcnRzLmFuaW1PYmplY3RgIGZ1bmN0aW9uLiBDYWxsZWQgd2hlbiBpbml0aW5nIHRoZSBwYWdlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xyXG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcclxuICAgIHJldHVybiB7IGR1cmF0aW9uOiAwIH07XHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBIaWdoY2hhcnRzIGNoYXJ0IG9uIGEgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBjcmVhdGVDaGFydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQ2hhcnQoZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgc2V0T3B0aW9ucywgbWVyZ2UsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgYHNldE9wdGlvbnNgIHVzYWdlcyBpbiBvcmRlclxyXG4gIC8vIHRvIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gTk9URTogSXMgdGhpcyB1c2VkIGZvciBhbnl0aGluZyB1c2VmdWw/XHJcbiAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSBmYWxzZTtcclxuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcclxuICAgIC8vIE92ZXJyaWRlIHRoZSBgdXNlck9wdGlvbnNgIHdpdGggaW1hZ2UgZnJpZW5kbHkgb3B0aW9uc1xyXG4gICAgdXNlck9wdGlvbnMgPSBtZXJnZSh1c2VyT3B0aW9ucywge1xyXG4gICAgICBleHBvcnRpbmc6IHtcclxuICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICB9LFxyXG4gICAgICBwbG90T3B0aW9uczoge1xyXG4gICAgICAgIHNlcmllczoge1xyXG4gICAgICAgICAgbGFiZWw6IHtcclxuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIC8qIEV4cGVjdHMgdG9vbHRpcCBpbiB0aGUgYHVzZXJPcHRpb25zYCB3aGVuIGBmb3JFeHBvcnRgIGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBTb21lIG1hbmRhdG9yeSBhZGRpdGlvbmFsIGBjaGFydGAgYW5kIGBleHBvcnRpbmdgIG9wdGlvbnNcclxuICBjb25zdCBhZGRpdGlvbmFsT3B0aW9ucyA9IHtcclxuICAgIGNoYXJ0OiB7XHJcbiAgICAgIC8vIEJ5IGRlZmF1bHQgYW5pbWF0aW9uIGlzIGRpc2FibGVkXHJcbiAgICAgIGFuaW1hdGlvbjogZmFsc2UsXHJcbiAgICAgIC8vIEdldCB0aGUgcmlnaHQgc2l6ZSB2YWx1ZXNcclxuICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcclxuICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgIH0sXHJcbiAgICBleHBvcnRpbmc6IHtcclxuICAgICAgLy8gTm8gbmVlZCBmb3IgdGhlIGV4cG9ydGluZyBidXR0b25cclxuICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBHZXQgdGhlIGlucHV0IHRvIGV4cG9ydCBmcm9tIHRoZSBgaW5zdHJgIG9wdGlvblxyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmluc3RyfWApKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYHRoZW1lT3B0aW9uc2Agb3B0aW9uXHJcbiAgY29uc3QgdGhlbWVPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9uc31gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb25cclxuICBjb25zdCBnbG9iYWxPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gTWVyZ2UgdGhlIGZvbGxvd2luZyBvcHRpb25zIG9iamVjdHMgdG8gY3JlYXRlIGZpbmFsIG9wdGlvbnNcclxuICBjb25zdCBmaW5hbE9wdGlvbnMgPSBtZXJnZShcclxuICAgIGZhbHNlLFxyXG4gICAgdGhlbWVPcHRpb25zLFxyXG4gICAgdXNlck9wdGlvbnMsXHJcbiAgICAvLyBQbGFjZWQgaXQgaGVyZSBpbnN0ZWFkIGluIHRoZSBpbml0IGJlY2F1c2Ugb2YgdGhlIHNpemUgaXNzdWVzXHJcbiAgICBhZGRpdGlvbmFsT3B0aW9uc1xyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjYWxsYmFja2Agb3B0aW9uXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke2N1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja31gKSgpXHJcbiAgICA6IG51bGw7XHJcblxyXG4gIC8vIFRyaWdnZXIgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbignb3B0aW9ucycsIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSBjaGFydCBjcmVhdGlvblxyXG4gIEhpZ2hjaGFydHNbZXhwb3J0T3B0aW9ucy5jb25zdHJdKCdjb250YWluZXInLCBmaW5hbE9wdGlvbnMsIGZpbmFsQ2FsbGJhY2spO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBgc2V0T3B0aW9uc2Agd2FzIHVzZWQgaW4gdGhlIGBjdXN0b21Db2RlYClcclxuICBmb3IgKGNvbnN0IHByb3AgaW4gZGVmYXVsdE9wdGlvbnMpIHtcclxuICAgIGlmICh0eXBlb2YgZGVmYXVsdE9wdGlvbnNbcHJvcF0gIT09ICdmdW5jdGlvbicpIHtcclxuICAgICAgZGVsZXRlIGRlZmF1bHRPcHRpb25zW3Byb3BdO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBkZWZhdWx0IG9wdGlvbnMgYmFja1xyXG4gIHNldE9wdGlvbnMoSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqKTtcclxuXHJcbiAgLy8gRW1wdHkgdGhlIGN1c3RvbSBnbG9iYWwgb3B0aW9ucyBvYmplY3RcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSB7fTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNldHVwSGlnaGNoYXJ0cyxcclxuICBjcmVhdGVDaGFydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZSwgY3JlYXRpbmcgYW5kIGNsZWFyaW5nIHBhZ2VzLCBpbmplY3RpbmcgY3VzdG9tIHJlc291cmNlcyxcclxuICogYW5kIHNldHRpbmcgdXAgSGlnaGNoYXJ0cyBmb3Igc2VydmVyLXNpZGUgcmVuZGVyaW5nLiBUaGUgbW9kdWxlIGVuc3VyZXNcclxuICogdGhhdCByZXNvdXJjZXMgYXJlIGNvcnJlY3RseSBtYW5hZ2VkIGFuZCBjYW4gaGFuZGxlIGZhaWx1cmVzIGR1cmluZ1xyXG4gKiBvcGVyYXRpb25zIGxpa2UgbGF1bmNoaW5nIHRoZSBicm93c2VyIG9yIGNyZWF0aW5nIG5ldyBwYWdlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXRBYnNvbHV0ZVBhdGggfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciBwYWdlc1xyXG5jb25zdCB0ZW1wbGF0ZSA9IHJlYWRGaWxlU3luYyhcclxuICBqb2luKF9fZGlybmFtZSwgJ3RlbXBsYXRlcycsICd0ZW1wbGF0ZS5odG1sJyksXHJcbiAgJ3V0ZjgnXHJcbik7XHJcblxyXG4vLyBUbyBzYXZlIHRoZSBicm93c2VyXHJcbmxldCBicm93c2VyID0gbnVsbDtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QnJvd3NlclxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBubyB2YWxpZCBicm93c2VyXHJcbiAqIGhhcyBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QnJvd3NlcigpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nLCA1MDApO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNyZWF0ZUJyb3dzZXJcclxuICpcclxuICogQHBhcmFtIHtBcnJheS48c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgY3JlYXRlZCBQdXBwZXRlZXJcclxuICogYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG1heCByZXRyaWVzIHRvIG9wZW5cclxuICogYSBicm93c2VyIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyXHJcbiAqIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gR2V0IGBkZWJ1Z2AgYW5kIGBvdGhlcmAgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWdPcHRpb25zIH0gPSBkZWJ1ZztcclxuXHJcbiAgLy8gTGF1bmNoIG9wdGlvbnMgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgY29uc3QgbGF1bmNoT3B0aW9ucyA9IHtcclxuICAgIGhlYWRsZXNzOiBvdGhlci5icm93c2VyU2hlbGxNb2RlID8gJ3NoZWxsJyA6IHRydWUsXHJcbiAgICB1c2VyRGF0YURpcjogJ3RtcCcsXHJcbiAgICBhcmdzOiBwdXBwZXRlZXJBcmdzIHx8IFtdLFxyXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHSFVQOiBmYWxzZSxcclxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXHJcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXHJcbiAgICAuLi4oZW5hYmxlZERlYnVnICYmIGRlYnVnT3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICAvLyBBIGNvdW50ZXIgZm9yIHRoZSBicm93c2VyJ3MgbGF1bmNoIHJldHJpZXNcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFticm93c2VyXSBBdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeSAkeysrdHJ5Q291bnR9KS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gTGF1bmNoIHRoZSBicm93c2VyXHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgJ1ticm93c2VyXSBGYWlsZWQgdG8gbGF1bmNoIGEgYnJvd3NlciBpbnN0YW5jZS4nXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0cnkgdG8gbGF1bmNoIGJyb3dzZXIgdW50aWwgcmVhY2hpbmcgbWF4IGF0dGVtcHRzXHJcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcclxuICAgICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIFJldHJ5IHRvIG9wZW4gYSBicm93c2VyICgke3RyeUNvdW50fSBvdXQgb2YgMjUpLmApO1xyXG5cclxuICAgICAgICAgIC8vIFdhaXQgZm9yIGEgNCBzZWNvbmRzIGJlZm9yZSB0cnlpbmcgYWdhaW5cclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG5cclxuICAgICAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGxhdW5jaE9wdGlvbnMuaGVhZGxlc3MgPT09ICdzaGVsbCcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIHNoZWxsIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIERlYnVnIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChlbmFibGVkRGVidWcpIHtcclxuICAgICAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIGRlYnVnIG1vZGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2Jyb3dzZXJdIE1heGltdW0gcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlciBpbnN0YW5jZSByZWFjaGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nLCA1MDApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xvc2VCcm93c2VyXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2VCcm93c2VyKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIgJiYgYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgYnJvd3NlciA9IG51bGw7XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgcGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHRoZSBgX3NldFBhZ2VDb250ZW50KClgLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBuZXdQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIGBpZGAsXHJcbiAqIGB3b3JrQ291bnRgLCBhbmQgYHBhZ2VgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjb25uZWN0ZWQgb3IgaWYgYSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG5ld1BhZ2UocG9vbFJlc291cmNlKSB7XHJcbiAgLy8gRXJyb3IgaW4gY2FzZSBvZiBubyBjb25uZWN0ZWQgYnJvd3NlclxyXG4gIGlmICghYnJvd3NlciB8fCAhYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW2Jyb3dzZXJdIEJyb3dzZXIgaXMgbm90IHlldCBjb25uZWN0ZWQuYCwgNTAwKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBwb29sUmVzb3VyY2UucGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBEaXNhYmxlIGNhY2hlXHJcbiAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXHJcbiAgX3NldFBhZ2VFdmVudHMocG9vbFJlc291cmNlLnBhZ2UpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgcGFnZSBpcyBjb3JyZWN0bHkgY3JlYXRlZFxyXG4gIGlmICghcG9vbFJlc291cmNlLnBhZ2UgfHwgcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgdGhlIGNvbnRlbnQgb2YgYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG1vZGUuIExvZ3NcclxuICogdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIG9mIGEgcGFnZSdzIGNvbnRlbnQgZmFpbHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIHBhZ2UgYW5kIGlkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtoYXJkUmVzZXQ9ZmFsc2VdIC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gYGFib3V0OmJsYW5rYCBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgd2hlbiBwYWdlXHJcbiAqIGlzIGNvcnJlY3RseSBjbGVhcmVkIGFuZCBmYWxzZSB3aGVuIGl0IGlzIG5vdC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2UocG9vbFJlc291cmNlLCBoYXJkUmVzZXQgPSBmYWxzZSkge1xyXG4gIHRyeSB7XHJcbiAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGBhYm91dDpibGFua2BcclxuICAgICAgICBhd2FpdCBwb29sUmVzb3VyY2UucGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHtcclxuICAgICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBfc2V0UGFnZUNvbnRlbnQocG9vbFJlc291cmNlLnBhZ2UpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gQ29udGVudCBvZiB0aGUgcGFnZSBjb3VsZCBub3QgYmUgY2xlYXJlZC5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICBwb29sUmVzb3VyY2Uud29ya0NvdW50ID0gZ2V0T3B0aW9ucygpLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICB9XHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyBjdXN0b20gSlMgYW5kIENTUyByZXNvdXJjZXMgdG8gYSBQdXBwZXRlZXIgUGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gYWRkUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5LjxPYmplY3Q+Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gYXJyYXlcclxuICogb2YgaW5qZWN0ZWQgcmVzb3VyY2VzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5XHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLy8gVXNlIHJlc291cmNlc1xyXG4gIGNvbnN0IHJlc291cmNlcyA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9IGZpbGUuc3RhcnRzV2l0aCgnaHR0cCcpID8gZmFsc2UgOiB0cnVlO1xyXG5cclxuICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gc2NyaXB0IGZyb20gcmVzb3VyY2VzJyBmaWxlc1xyXG4gICAgICAgIGluamVjdGVkSnMucHVzaChcclxuICAgICAgICAgIGlzTG9jYWxcclxuICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICBjb250ZW50OiByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGZpbGUpLCAndXRmOCcpXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICA6IHtcclxuICAgICAgICAgICAgICAgIHVybDogZmlsZVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgZm9yIChjb25zdCBqc1Jlc291cmNlIG9mIGluamVjdGVkSnMpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU2NyaXB0VGFnKGpzUmVzb3VyY2UpKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gVGhlIEpTIHJlc291cmNlIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICAgIGluamVjdGVkSnMubGVuZ3RoID0gMDtcclxuXHJcbiAgICAvLyBMb2FkIENTU1xyXG4gICAgY29uc3QgaW5qZWN0ZWRDc3MgPSBbXTtcclxuICAgIGlmIChyZXNvdXJjZXMuY3NzKSB7XHJcbiAgICAgIGxldCBjc3NJbXBvcnRzID0gcmVzb3VyY2VzLmNzcy5tYXRjaCgvQGltcG9ydFxccyooW147XSopOy9nKTtcclxuICAgICAgaWYgKGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAvLyBIYW5kbGUgY3NzIHNlY3Rpb25cclxuICAgICAgICBmb3IgKGxldCBjc3NJbXBvcnRQYXRoIG9mIGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoKSB7XHJcbiAgICAgICAgICAgIGNzc0ltcG9ydFBhdGggPSBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ3VybCgnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgnQGltcG9ydCcsICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvJy9nLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvOy8sICcnKVxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cXCkvZywgJycpXHJcbiAgICAgICAgICAgICAgLnRyaW0oKTtcclxuXHJcbiAgICAgICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBjc3MgZnJvbSByZXNvdXJjZXNcclxuICAgICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGguc3RhcnRzV2l0aCgnaHR0cCcpKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgICAgICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgICAgICAgICBwYXRoOiBnZXRBYnNvbHV0ZVBhdGgoY3NzSW1wb3J0UGF0aClcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gVGhlIHJlc3Qgb2YgdGhlIENTUyBzZWN0aW9uIHdpbGwgYmUgY29udGVudCBieSBub3dcclxuICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmNzcy5yZXBsYWNlKC9AaW1wb3J0XFxzKihbXjtdKik7L2csICcnKSB8fCAnICdcclxuICAgICAgfSk7XHJcblxyXG4gICAgICBmb3IgKGNvbnN0IGNzc1Jlc291cmNlIG9mIGluamVjdGVkQ3NzKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyhjc3NSZXNvdXJjZSkpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICBgW2Jyb3dzZXJdIFRoZSBDU1MgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBpbmplY3RlZENzcy5sZW5ndGggPSAwO1xyXG4gICAgfVxyXG4gIH1cclxuICByZXR1cm4gaW5qZWN0ZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgb3V0IGFsbCBzdGF0ZSBzZXQgb24gdGhlIHBhZ2Ugd2l0aCBgYWRkU2NyaXB0VGFnYCBhbmQgYGFkZFN0eWxlVGFnYC5cclxuICogUmVtb3ZlcyBpbmplY3RlZCByZXNvdXJjZXMgYW5kIHJlc2V0cyBDU1MgYW5kIHNjcmlwdCB0YWdzIG9uIHRoZSBwYWdlLlxyXG4gKiBBZGRpdGlvbmFsbHksIGl0IGRlc3Ryb3lzIHByZXZpb3VzbHkgZXhpc3RpbmcgY2hhcnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgZnJvbSB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxPYmplY3Q+fSBpbmplY3RlZFJlc291cmNlcyAtIEFycmF5IG9mIGluamVjdGVkIHJlc291cmNlc1xyXG4gKiB0byBiZSBjbGVhcmVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcykge1xyXG4gIHRyeSB7XHJcbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlc291cmNlLmRpc3Bvc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IG9sZCBjaGFydHMgYWZ0ZXIgZXhwb3J0IGlzIGRvbmUgYW5kIHJlc2V0IGFsbCBDU1MgYW5kIHNjcmlwdCB0YWdzXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgLy8gV2UgYXJlIG5vdCBndWFyYW50ZWVkIHRoYXQgSGlnaGNoYXJ0cyBpcyBsb2FkZWQsIHdoZW4gZG9pbmcgU1ZHIGV4cG9ydHNcclxuICAgICAgaWYgKHR5cGVvZiBIaWdoY2hhcnRzICE9PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgIGNvbnN0IG9sZENoYXJ0cyA9IEhpZ2hjaGFydHMuY2hhcnRzO1xyXG5cclxuICAgICAgICAvLyBDaGVjayBpbiBhbnkgYWxyZWFkeSBleGlzdGluZyBjaGFydHNcclxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShvbGRDaGFydHMpICYmIG9sZENoYXJ0cy5sZW5ndGgpIHtcclxuICAgICAgICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0c1xyXG4gICAgICAgICAgZm9yIChjb25zdCBvbGRDaGFydCBvZiBvbGRDaGFydHMpIHtcclxuICAgICAgICAgICAgb2xkQ2hhcnQgJiYgb2xkQ2hhcnQuZGVzdHJveSgpO1xyXG4gICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgICAgSGlnaGNoYXJ0cy5jaGFydHMuc2hpZnQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbYnJvd3Nlcl0gQ291bGQgbm90IGNsZWFyIHBhZ2UncyByZXNvdXJjZXMuYCk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgY29udGVudCBmb3IgYSBQdXBwZXRlZXIgUGFnZSB1c2luZyBhIHByZWRlZmluZWQgdGVtcGxhdGVcclxuICogYW5kIGFkZGl0aW9uYWwgc2NyaXB0cy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfc2V0UGFnZUNvbnRlbnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBjb250ZW50XHJcbiAqIGlzIGJlaW5nIHNldC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9zZXRQYWdlQ29udGVudChwYWdlKSB7XHJcbiAgLy8gU2V0IHRoZSBpbml0aWFsIHBhZ2UgY29udGVudFxyXG4gIGF3YWl0IHBhZ2Uuc2V0Q29udGVudCh0ZW1wbGF0ZSwgeyB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJyB9KTtcclxuXHJcbiAgLy8gQWRkIGFsbCByZWdpc3RlcmVkIEhpZ2NoYXJ0cyBzY3JpcHRzLCBxdWl0ZSBkZW1hbmRpbmdcclxuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdzb3VyY2VzLmpzJykgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBgYW5pbU9iamVjdGAgZm9yIEhpZ2hjaGFydHNcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKHNldHVwSGlnaGNoYXJ0cyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgZXZlbnRzIChsaWtlIGBwYWdlZXJyb3JgIGFuZCBgY29uc29sZWApIGZvciBhIFB1cHBldGVlciBQYWdlIGluIG9yZGVyXHJcbiAqIHRvIGNhdGNoIGFuZCBkaXNwbGF5IGVycm9ycyBhbmQgY29uc29sZSBsb2dzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VFdmVudHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBsaXN0ZW5lcnNcclxuICogYXJlIGJlaW5nIHNldC5cclxuICovXHJcbmZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzKHBhZ2UpIHtcclxuICAvLyBHZXQgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBkZWJ1ZyB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBTZXQgdGhlIGBwYWdlZXJyb3JgIGxpc3RlbmVyXHJcbiAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKCkgPT4ge1xyXG4gICAgLy8gSXQgd291bGQgc2VlbSBsaWtlIHRoaXMgbWF5IGZpcmUgYXQgdGhlIHNhbWUgdGltZSBvciBzaG9ydGx5IGJlZm9yZVxyXG4gICAgLy8gYSBwYWdlIGlzIGNsb3NlZC5cclxuICAgIGlmIChwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGBjb25zb2xlYCBsaXN0ZW5lciwgaWYgbmVlZGVkXHJcbiAgaWYgKGRlYnVnLmVuYWJsZSAmJiBkZWJ1Zy5saXN0ZW5Ub0NvbnNvbGUpIHtcclxuICAgIHBhZ2Uub24oJ2NvbnNvbGUnLCAobWVzc2FnZSkgPT4ge1xyXG4gICAgICBjb25zb2xlLmxvZyhgW2RlYnVnXSAke21lc3NhZ2UudGV4dCgpfWApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0QnJvd3NlcixcclxuICBjcmVhdGVCcm93c2VyLFxyXG4gIGNsb3NlQnJvd3NlcixcclxuICBuZXdQYWdlLFxyXG4gIGNsZWFyUGFnZSxcclxuICBhZGRQYWdlUmVzb3VyY2VzLFxyXG4gIGNsZWFyUGFnZVJlc291cmNlc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgQ1NTIHRvIGJlIHVzZWQgb24gdGhlIGV4cG9ydGVkIHBhZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBDU1MgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0ICgpID0+IGBcclxuXHJcbmh0bWwsIGJvZHkge1xyXG4gIG1hcmdpbjogMDtcclxuICBwYWRkaW5nOiAwO1xyXG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XHJcbn1cclxuXHJcbiN0YWJsZS1kaXYsICNzbGlkZXJzLCAjZGF0YXRhYmxlLCAjY29udHJvbHMsIC5sZC1yb3cge1xyXG4gIGRpc3BsYXk6IG5vbmU7XHJcbiAgaGVpZ2h0OiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyIHtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG4gIG1hcmdpbjogMDtcclxuICBvdmVyZmxvdzogYXV0bztcclxuICBmb250LXNpemU6IDA7XHJcbn1cclxuXHJcbiNjaGFydC1jb250YWluZXIgPiBmaWd1cmUsIGRpdiB7XHJcbiAgbWFyZ2luLXRvcDogMCAhaW1wb3J0YW50O1xyXG4gIG1hcmdpbi1ib3R0b206IDAgIWltcG9ydGFudDtcclxufVxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY3NzVGVtcGxhdGUgZnJvbSAnLi9jc3MuanMnO1xyXG5cclxuLyoqXHJcbiAqIFRoZSBTVkcgdGVtcGxhdGUgdG8gdXNlIHdoZW4gbG9hZGluZyBTVkcgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHN2ZyAtIFRoZSBTVkcgaW5wdXQgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIFNWRyB0ZW1wbGF0ZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChzdmcpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNoYXJ0cyBFeHBvcnQ8L3RpdGxlPlxyXG4gIDwvaGVhZD5cclxuICA8c3R5bGU+XHJcbiAgICAke2Nzc1RlbXBsYXRlKCl9XHJcbiAgPC9zdHlsZT5cclxuICA8Ym9keT5cclxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cclxuICAgICAgJHtzdmd9XHJcbiAgICA8L2Rpdj5cclxuICA8L2JvZHk+XHJcbjwvaHRtbD5cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBoYW5kbGVzIGNoYXJ0IGV4cG9ydCBmdW5jdGlvbmFsaXR5IHVzaW5nIFB1cHBldGVlci5cclxuICogSXQgc3VwcG9ydHMgZXhwb3J0aW5nIGNoYXJ0cyBhcyBTVkcsIFBORywgSlBFRywgYW5kIFBERiBmb3JtYXRzLiBUaGUgbW9kdWxlXHJcbiAqIG1hbmFnZXMgcGFnZSByZXNvdXJjZXMsIHNldHMgdXAgdGhlIGV4cG9ydCBlbnZpcm9ubWVudCwgYW5kIHByb2Nlc3NlcyBjaGFydFxyXG4gKiBjb25maWd1cmF0aW9ucyBvciBTVkcgaW5wdXRzIGZvciByZW5kZXJpbmcuIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZVxyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYWRkUGFnZVJlc291cmNlcywgY2xlYXJQYWdlUmVzb3VyY2VzIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgY3JlYXRlQ2hhcnQgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgc3ZnVGVtcGxhdGUgZnJvbSAnLi4vdGVtcGxhdGVzL3N2Z0V4cG9ydC9zdmdFeHBvcnQuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2UgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTwoc3RyaW5nfEJ1ZmZlcnxFeHBvcnRFcnJvcik+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlc1xyXG4gKiB0byB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgZXhwb3J0IHRvIGFuIHVuc3VwcG9ydGVkXHJcbiAqIG91dHB1dCBmb3JtYXQgb2NjdXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydChwYWdlLCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXkgKGFkZGl0aW9uYWwgSlMgYW5kIENTUylcclxuICBjb25zdCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICB0cnkge1xyXG4gICAgbGV0IGlzU1ZHID0gZmFsc2U7XHJcblxyXG4gICAgLy8gRGVjaWRlIG9uIHRoZSBleHBvcnQgbWV0aG9kXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy5zdmcpIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgYHR5cGVgIGlzIGFsc28gU1ZHLCByZXR1cm4gdGhlIGlucHV0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGV4cG9ydE9wdGlvbnMuc3ZnO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBNYXJrIGFzIFNWRyBleHBvcnQgZm9yIHRoZSBsYXRlciBzaXplIGNvcnJlY3Rpb25zXHJcbiAgICAgIGlzU1ZHID0gdHJ1ZTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRcclxuICAgICAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKGV4cG9ydE9wdGlvbnMuc3ZnKSwge1xyXG4gICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgIH0pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBKU09OIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoY3JlYXRlQ2hhcnQsIGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXHJcbiAgICAvLyBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAuLi4oYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBjdXN0b21Mb2dpY09wdGlvbnMpKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZSBhbmQgc2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XHJcbiAgICBjb25zdCBzaXplID0gaXNTVkdcclxuICAgICAgPyBhd2FpdCBwYWdlLmV2YWx1YXRlKChzY2FsZSkgPT4ge1xyXG4gICAgICAgICAgY29uc3Qgc3ZnRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXHJcbiAgICAgICAgICAgICcjY2hhcnQtY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJ1xyXG4gICAgICAgICAgKTtcclxuXHJcbiAgICAgICAgICAvLyBHZXQgdGhlIHZhbHVlcyBjb3JyZWN0bHkgc2NhbGVkXHJcbiAgICAgICAgICBjb25zdCBjaGFydEhlaWdodCA9IHN2Z0VsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0V2lkdGggPSBzdmdFbGVtZW50LndpZHRoLmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBJbiBjYXNlIG9mIFNWRyB0aGUgem9vbSBtdXN0IGJlIHNldCBkaXJlY3RseSBmb3IgYm9keSBhcyBzY2FsZVxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBTZXQgdGhlIG1hcmdpbiB0byAwcHhcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcclxuXHJcbiAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICBjaGFydEhlaWdodCxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aFxyXG4gICAgICAgICAgfTtcclxuICAgICAgICB9LCBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpKVxyXG4gICAgICA6IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBjb25zdCB7IGNoYXJ0SGVpZ2h0LCBjaGFydFdpZHRoIH0gPSB3aW5kb3cuSGlnaGNoYXJ0cy5jaGFydHNbMF07XHJcblxyXG4gICAgICAgICAgLy8gTm8gbmVlZCBmb3Igc3VjaCBzY2FsZSBtYW5pcHVsYXRpb24gaW4gY2FzZSBvZiBvdGhlciB0eXBlc1xyXG4gICAgICAgICAgLy8gb2YgZXhwb3J0cy4gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjbGlwIHJlZ2lvbiBmb3IgdGhlIHBhZ2VcclxuICAgIGNvbnN0IHsgeCwgeSB9ID0gYXdhaXQgX2dldENsaXBSZWdpb24ocGFnZSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGBoZWlnaHRgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRIZWlnaHQgfHwgZXhwb3J0T3B0aW9ucy5oZWlnaHQpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgd2lkdGhgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRXaWR0aCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcclxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgIH0pO1xyXG5cclxuICAgIGxldCByZXN1bHQ7XHJcbiAgICAvLyBSYXN0ZXJpemF0aW9uIHByb2Nlc3NcclxuICAgIHN3aXRjaCAoZXhwb3J0T3B0aW9ucy50eXBlKSB7XHJcbiAgICAgIGNhc2UgJ3N2Zyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVNWRyhwYWdlKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncG5nJzpcclxuICAgICAgY2FzZSAnanBlZyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZUltYWdlKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMudHlwZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIHgsXHJcbiAgICAgICAgICAgIHlcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncGRmJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlUERGKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBkZWZhdWx0OlxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBVbnN1cHBvcnRlZCBvdXRwdXQgZm9ybWF0OiAke2V4cG9ydE9wdGlvbnMudHlwZX0uYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2xlYXIgcHJldmlvdXNseSBpbmplY3RlZCBKUyBhbmQgQ1NTIHJlc291cmNlc1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMgb2YgdGhlIHNwZWNpZmllZCBwYWdlIGVsZW1lbnRcclxuICogd2l0aCB0aGUgJ2NoYXJ0LWNvbnRhaW5lcicgaWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2dldENsaXBSZWdpb25cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGB4YCwgYHlgLCBgd2lkdGhgLCBhbmQgYGhlaWdodGAgcHJvcGVydGllcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XHJcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB4LFxyXG4gICAgICB5LFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXHJcbiAgICB9O1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBTVkcgYnkgZXZhbHVhdGluZyB0aGUgYG91dGVySFRNTGAgb2YgdGhlIGZpcnN0ICdzdmcnIGVsZW1lbnRcclxuICogaW5zaWRlIGFuIGVsZW1lbnQgd2l0aCB0aGUgaWQgJ2NvbnRhaW5lcicuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZVNWR1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFNWRyBzdHJpbmcuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlU1ZHKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbChcclxuICAgICcjY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJyxcclxuICAgIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTFxyXG4gICk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIGltYWdlIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgYHNjcmVlbnNob3RgIGZ1bmN0aW9uYWxpdHkgd2l0aFxyXG4gKiBzcGVjaWZpZWQgb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlSW1hZ2VcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gSW1hZ2UgdHlwZS5cclxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgaW1hZ2UgYnVmZmVyXHJcbiAqIG9yIHJlamVjdGluZyB3aXRoIGFuIGBFeHBvcnRFcnJvcmAgZm9yIHRpbWVvdXQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlSW1hZ2UocGFnZSwgdHlwZSwgY2xpcCwgcmFzdGVyaXphdGlvblRpbWVvdXQpIHtcclxuICByZXR1cm4gUHJvbWlzZS5yYWNlKFtcclxuICAgIHBhZ2Uuc2NyZWVuc2hvdCh7XHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGNsaXAsXHJcbiAgICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgICAgZnVsbFBhZ2U6IGZhbHNlLFxyXG4gICAgICBvcHRpbWl6ZUZvclNwZWVkOiB0cnVlLFxyXG4gICAgICBjYXB0dXJlQmV5b25kVmlld3BvcnQ6IHRydWUsXHJcbiAgICAgIC4uLih0eXBlICE9PSAncG5nJyA/IHsgcXVhbGl0eTogODAgfSA6IHt9KSxcclxuICAgICAgLy8gQWx3YXlzIHJlbmRlciBvbiBhIHRyYW5zcGFyZW50IHBhZ2UgaWYgdGhlIGV4cGVjdGVkIHR5cGUgZm9ybWF0IGlzIFBOR1xyXG4gICAgICBvbWl0QmFja2dyb3VuZDogdHlwZSA9PSAncG5nJyAvLyAjNDQ3LCAjNDYzXHJcbiAgICB9KSxcclxuICAgIG5ldyBQcm9taXNlKChfcmVzb2x2ZSwgcmVqZWN0KSA9PlxyXG4gICAgICBzZXRUaW1lb3V0KFxyXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcsIDQwOCkpLFxyXG4gICAgICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICAgICAgKVxyXG4gICAgKVxyXG4gIF0pO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIFBERiB1c2luZyBQdXBwZXRlZXIncyBwYWdlIGBwZGZgIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlUERGXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0IC0gUERGIGhlaWdodC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoIC0gUERGIHdpZHRoLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIFBERiBidWZmZXIuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfY3JlYXRlUERGKHBhZ2UsIGhlaWdodCwgd2lkdGgsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSB7XHJcbiAgYXdhaXQgcGFnZS5lbXVsYXRlTWVkaWFUeXBlKCdzY3JlZW4nKTtcclxuICByZXR1cm4gcGFnZS5wZGYoe1xyXG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXHJcbiAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICB3aWR0aCxcclxuICAgIGVuY29kaW5nOiAnYmFzZTY0JyxcclxuICAgIHRpbWVvdXQ6IHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHB1cHBldGVlckV4cG9ydFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgYSB3b3JrZXIgcG9vbCBpbXBsZW1lbnRhdGlvbiBmb3IgbWFuYWdpbmdcclxuICogdGhlIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHBhZ2VzLCBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIHVzZSB3aXRoXHJcbiAqIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIuIEl0IG9wdGltaXplcyByZXNvdXJjZXMgdXNhZ2UgYW5kIHBlcmZvcm1hbmNlXHJcbiAqIGJ5IG1haW50YWluaW5nIGEgcG9vbCBvZiB3b3JrZXJzIHRoYXQgY2FuIGhhbmRsZSBjb25jdXJyZW50IGV4cG9ydCB0YXNrc1xyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgUG9vbCB9IGZyb20gJ3Rhcm4nO1xyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBjcmVhdGVCcm93c2VyLCBjbG9zZUJyb3dzZXIsIG5ld1BhZ2UsIGNsZWFyUGFnZSB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IHB1cHBldGVlckV4cG9ydCB9IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldE5ld0RhdGVUaW1lLCBtZWFzdXJlVGltZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBwb29sIGluc3RhbmNlXHJcbmxldCBwb29sID0gbnVsbDtcclxuXHJcbi8vIFBvb2wgc3RhdGlzdGljc1xyXG5jb25zdCBwb29sU3RhdHMgPSB7XHJcbiAgZXhwb3J0c0F0dGVtcHRlZDogMCxcclxuICBleHBvcnRzUGVyZm9ybWVkOiAwLFxyXG4gIGV4cG9ydHNEcm9wcGVkOiAwLFxyXG4gIGV4cG9ydHNGcm9tU3ZnOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uczogMCxcclxuICBleHBvcnRzRnJvbVN2Z0F0dGVtcHRzOiAwLFxyXG4gIGV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzOiAwLFxyXG4gIHRpbWVTcGVudDogMCxcclxuICB0aW1lU3BlbnRBdmVyYWdlOiAwXHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwb29sIHdpdGggdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24sIGNyZWF0aW5nXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhbmQgc2V0dGluZyB1cCB3b3JrZXIgcmVzb3VyY2VzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGluaXRQb29sXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBwb29sYFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge0FycmF5LjxzdHJpbmc+fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlclxyXG4gKiBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBlbmRpbmcgdGhlIGZ1bmN0aW9uXHJcbiAqIGV4ZWN1dGlvbiB3aGVuIGFuIGFscmVhZHkgaW5pdGlhbGl6ZWQgcG9vbCBvZiByZXNvdXJjZXMgaXMgZm91bmQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb3VsZCBub3QgY3JlYXRlIHRoZSBwb29sXHJcbiAqIG9mIHdvcmtlcnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdFBvb2wocG9vbE9wdGlvbnMsIHB1cHBldGVlckFyZ3MpIHtcclxuICAvLyBDcmVhdGUgYSBicm93c2VyIGluc3RhbmNlIHdpdGggdGhlIHB1cHBldGVlciBhcmd1bWVudHNcclxuICBhd2FpdCBjcmVhdGVCcm93c2VyKHB1cHBldGVlckFyZ3MpO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIEluaXRpYWxpemluZyBwb29sIHdpdGggd29ya2VyczogbWluICR7cG9vbE9wdGlvbnMubWluV29ya2Vyc30sIG1heCAke3Bvb2xPcHRpb25zLm1heFdvcmtlcnN9LmBcclxuICAgICk7XHJcblxyXG4gICAgaWYgKHBvb2wpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXAgYW4gZXllIG9uIGEgY29ycmVjdCBtaW4gYW5kIG1heCB3b3JrZXJzIG51bWJlclxyXG4gICAgaWYgKHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPiBwb29sT3B0aW9ucy5tYXhXb3JrZXJzKSB7XHJcbiAgICAgIHBvb2xPcHRpb25zLm1pbldvcmtlcnMgPSBwb29sT3B0aW9ucy5tYXhXb3JrZXJzO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENyZWF0ZSBhIHBvb2wgYWxvbmcgd2l0aCBhIG1pbmltYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgcG9vbCA9IG5ldyBQb29sKHtcclxuICAgICAgLy8gR2V0IHRoZSBgY3JlYXRlYCwgYHZhbGlkYXRlYCwgYW5kIGBkZXN0cm95YCBmdW5jdGlvbnNcclxuICAgICAgLi4uX2ZhY3RvcnkocG9vbE9wdGlvbnMpLFxyXG4gICAgICBtaW46IHBvb2xPcHRpb25zLm1pbldvcmtlcnMsXHJcbiAgICAgIG1heDogcG9vbE9wdGlvbnMubWF4V29ya2VycyxcclxuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmFjcXVpcmVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5jcmVhdGVUaW1lb3V0LFxyXG4gICAgICBkZXN0cm95VGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuZGVzdHJveVRpbWVvdXQsXHJcbiAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5pZGxlVGltZW91dCxcclxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlUmV0cnlJbnRlcnZhbCxcclxuICAgICAgcmVhcEludGVydmFsTWlsbGlzOiBwb29sT3B0aW9ucy5yZWFwZXJJbnRlcnZhbCxcclxuICAgICAgcHJvcGFnYXRlQ3JlYXRlRXJyb3I6IGZhbHNlXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBTZXQgZXZlbnRzXHJcbiAgICBwb29sLm9uKCdyZWxlYXNlJywgYXN5bmMgKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIENsZWFyIHBhZ2VcclxuICAgICAgY29uc3QgY2xlYXJTdGF0dXMgPSBhd2FpdCBjbGVhclBhZ2UocmVzb3VyY2UsIGZhbHNlKTtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBSZWxlYXNpbmcgYSB3b3JrZXIuIENsZWFyIHBhZ2Ugc3RhdHVzOiAke2NsZWFyU3RhdHVzfS5gXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChfZXZlbnRJZCwgcmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDQsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Jlc291cmNlLmlkfV0gLSBEZXN0cm95ZWQgYSB3b3JrZXIgc3VjY2Vzc2Z1bGx5LmBcclxuICAgICAgKTtcclxuICAgICAgcmVzb3VyY2UucGFnZSA9IG51bGw7XHJcbiAgICB9KTtcclxuXHJcbiAgICBjb25zdCBpbml0aWFsUmVzb3VyY2VzID0gW107XHJcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBvb2xPcHRpb25zLm1pbldvcmtlcnM7IGkrKykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuICAgICAgICBpbml0aWFsUmVzb3VyY2VzLnB1c2gocmVzb3VyY2UpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIGFuIGluaXRpYWwgcmVzb3VyY2UuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXMgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgaW5pdGlhbFJlc291cmNlcy5mb3JFYWNoKChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICBwb29sLnJlbGVhc2UocmVzb3VyY2UpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIFRoZSBwb29sIGlzIHJlYWR5JHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aCA/IGAgd2l0aCAke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RofSBpbml0aWFsIHJlc291cmNlcyB3YWl0aW5nLmAgOiAnLid9YFxyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3Bvb2xdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLicsXHJcbiAgICAgIDUwMFxyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogVGVybWluYXRlcyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24ga2lsbFBvb2xcclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgYWxsIHdvcmtlcnMgYXJlXHJcbiAqIHRlcm1pbmF0ZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIHN1Y2Nlc3NmdWxseSBjbG9zZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XHJcbiAgbG9nKDMsICdbcG9vbF0gS2lsbGluZyBwb29sIHdpdGggYWxsIHdvcmtlcnMgYW5kIGNsb3NpbmcgYnJvd3Nlci4nKTtcclxuXHJcbiAgLy8gSWYgc3RpbGwgYWxpdmUsIGRlc3Ryb3kgdGhlIHBvb2wgb2YgcGFnZXMgYmVmb3JlIGNsb3NpbmcgYSBicm93c2VyXHJcbiAgaWYgKHBvb2wpIHtcclxuICAgIC8vIEZyZWUgdXAgbm90IHJlbGVhc2VkIHdvcmtlcnNcclxuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHBvb2wudXNlZCkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VyLnJlc291cmNlKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IHRoZSBwb29sIGlmIGl0IGlzIHN0aWxsIGF2YWlsYWJsZVxyXG4gICAgaWYgKCFwb29sLmRlc3Ryb3llZCkge1xyXG4gICAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gRGVzdHJveWVkIHRoZSBwb29sIG9mIHJlc291cmNlcy4nKTtcclxuICAgIH1cclxuICAgIHBvb2wgPSBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICBhd2FpdCBjbG9zZUJyb3dzZXIoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByb2Nlc3NlcyB0aGUgZXhwb3J0IHdvcmsgdXNpbmcgYSB3b3JrZXIgZnJvbSB0aGUgcG9vbC4gQWNxdWlyZXMgYSB3b3JrZXJcclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcclxuICogdGhlIHdvcmtlciBoYW5kbGUgYmFjayB0byB0aGUgcG9vbC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBwb3N0V29ya1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgZXhwb3J0IHJlc3VsdFxyXG4gKiBhbmQgb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3RXb3JrKG9wdGlvbnMpIHtcclxuICBsZXQgd29ya2VySGFuZGxlO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgICAvLyBBbiBleHBvcnQgYXR0ZW1wdCBjb3VudGVkXHJcbiAgICArK3Bvb2xTdGF0cy5leHBvcnRzQXR0ZW1wdGVkO1xyXG5cclxuICAgIC8vIERpc3BsYXkgdGhlIHBvb2wgaW5mb3JtYXRpb24gaWYgbmVlZGVkXHJcbiAgICBpZiAob3B0aW9ucy5wb29sLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGluIGNhc2Ugb2YgbGFja2luZyB0aGUgcG9vbCBpbnN0YW5jZVxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhlIGFjcXVpcmUgY291bnRlclxyXG4gICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIFRyeSB0byBhY3F1aXJlIHRoZSB3b3JrZXIgYWxvbmcgd2l0aCB0aGUgaWQsIHdvcmtzIGNvdW50IGFuZCBwYWdlXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgICAgLy8gQWNxdWlyZSBhIHBvb2wgcmVzb3VyY2VcclxuICAgICAgd29ya2VySGFuZGxlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxyXG4gICAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgICAgYEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUgdG9vayAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUud29ya0NvdW50ID0gb3B0aW9ucy5wb29sLndvcmtMaW1pdCArIDE7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW3Bvb2xdIFJlc29sdmVkIHdvcmtlciBwYWdlIGlzIGludmFsaWQ6IHRoZSBwb29sIHNldHVwIGlzIHdvbmt5LicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgY29uc3Qgd29ya1N0YXJ0ID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHt3b3JrZXJIYW5kbGUuaWR9XSAtIFN0YXJ0aW5nIHdvcmsgb24gdGhpcyBwb29sIGVudHJ5LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU3RhcnQgbWVhc3VyaW5nIGV4cG9ydCB0aW1lXHJcbiAgICBjb25zdCBleHBvcnRDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KFxyXG4gICAgICB3b3JrZXJIYW5kbGUucGFnZSxcclxuICAgICAgb3B0aW9ucy5leHBvcnQsXHJcbiAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKHJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIElmIHRoZXJlJ3MgYSByYXN0ZXJpemF0aW9uIHRpbWVvdXQsIHdlIHdhbnQgbmVlZCB0byBmbHVzaCB0aGUgcGFnZS5cclxuICAgICAgLy8gVGhpcyBpcyBiZWNhdXNlIHRoZSBwYWdlIG1heSBiZSBpbiBhIHN0YXRlIHdoZXJlIGl0J3Mgd2FpdGluZyBmb3JcclxuICAgICAgLy8gdGhlIHNjcmVlbnNob3QgdG8gZmluaXNoIGV2ZW4gdGhvdWdoIHRoZSB0aW1lb3V0IGhhcyBvY2N1cmVkLlxyXG4gICAgICAvLyBXaGljaCBvZiBjb3Vyc2UgY2F1c2VzIGEgbG90IG9mIGlzc3VlcyB3aXRoIHRoZSBldmVudCBzeXN0ZW0sXHJcbiAgICAgIC8vIGFuZCBwYWdlIGNvbnNpc3RlbmN5LlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBPbmx5IHBhZ2Uuc2NyZWVuc2hvdCB3aWxsIHRocm93IHRoaXMsIHRpbWVvdXRzIGZvciBQREYncyBhcmVcclxuICAgICAgLy8gaGFuZGxlZCBieSB0aGUgcGFnZS5wZGYgZnVuY3Rpb24gaXRzZWxmLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyAuLi55ZXMsIHRoaXMgaXMgdWdseS5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IG9wdGlvbnMucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlID0gbnVsbDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJlc3VsdC5uYW1lID09PSAnVGltZW91dEVycm9yJyB8fFxyXG4gICAgICAgIHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0J1xyXG4gICAgICApIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfVJhc3Rlcml6YXRpb24gdGltZW91dDogeW91ciBjaGFydCBtYXkgYmUgdG9vIGNvbXBsZXggb3IgbGFyZ2UsIGFuZCBmYWlsZWQgdG8gcmVuZGVyIHdpdGhpbiB0aGUgYWxsb3R0ZWQgdGltZS5gXHJcbiAgICAgICAgKS5zZXRFcnJvcihyZXN1bHQpO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgICAgb3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnXHJcbiAgICAgICAgICB9RXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGV4cG9ydDogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIHRoZSBQdXBwZXRlZXIgZXhwb3J0IHRpbWVcclxuICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDUsXHJcbiAgICAgICAgYFtiZW5jaG1hcmtdICR7b3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnfWAsXHJcbiAgICAgICAgYEV4cG9ydGluZyBhIGNoYXJ0IHN1Y2Vzc2Z1bGx5IHRvb2sgJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIHJlc291cmNlIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG5cclxuICAgIC8vIFVzZWQgZm9yIHN0YXRpc3RpY3MgaW4gYXZlcmFnZVRpbWUgYW5kIHByb2Nlc3NlZFdvcmtDb3VudCwgd2hpY2hcclxuICAgIC8vIGluIHR1cm4gaXMgdXNlZCBieSB0aGUgL2hlYWx0aCByb3V0ZS5cclxuICAgIGNvbnN0IHdvcmtFbmQgPSBnZXROZXdEYXRlVGltZSgpO1xyXG4gICAgY29uc3QgZXhwb3J0VGltZSA9IHdvcmtFbmQgLSB3b3JrU3RhcnQ7XHJcblxyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UgPVxyXG4gICAgICBwb29sU3RhdHMudGltZVNwZW50IC8gKytwb29sU3RhdHMuZXhwb3J0c1BlcmZvcm1lZDtcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBXb3JrIGNvbXBsZXRlZCBpbiAke2V4cG9ydFRpbWV9bXMuYCk7XHJcblxyXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICByZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICsrcG9vbFN0YXRzLmV4cG9ydHNEcm9wcGVkO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhyb3cgZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fG51bGwpfSBUaGUgY3VycmVudCBwb29sIGluc3RhbmNlIGlmIGluaXRpYWxpemVkLCBvciBudWxsXHJcbiAqIGlmIHRoZSBwb29sIGhhcyBub3QgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2woKSB7XHJcbiAgcmV0dXJuIHBvb2w7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBzdGF0aXN0aWMgb2YgYSBwb29sIGluc3RhY2UgYWJvdXQgZXhwb3J0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xTdGF0c1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY3VycmVudCBwb29sIHN0YXRpc3RpY3MuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbFN0YXRzKCkge1xyXG4gIHJldHVybiBwb29sU3RhdHM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgcG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdCwgaW5jbHVkaW5nIG1pbmltdW0gYW5kIG1heGltdW1cclxuICogd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm9KU09OKCkge1xyXG4gIHJldHVybiB7XHJcbiAgICBtaW46IHBvb2wubWluLFxyXG4gICAgbWF4OiBwb29sLm1heCxcclxuICAgIHVzZWQ6IHBvb2wubnVtVXNlZCgpLFxyXG4gICAgYXZhaWxhYmxlOiBwb29sLm51bUZyZWUoKSxcclxuICAgIGFsbENyZWF0ZWQ6IHBvb2wubnVtVXNlZCgpICsgcG9vbC5udW1GcmVlKCksXHJcbiAgICBwZW5kaW5nQWNxdWlyZXM6IHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCksXHJcbiAgICBwZW5kaW5nQ3JlYXRlczogcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpLFxyXG4gICAgcGVuZGluZ1ZhbGlkYXRpb25zOiBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpLFxyXG4gICAgcGVuZGluZ0Rlc3Ryb3lzOiBwb29sLnBlbmRpbmdEZXN0cm95cy5sZW5ndGgsXHJcbiAgICBhYnNvbHV0ZUFsbDpcclxuICAgICAgcG9vbC5udW1Vc2VkKCkgK1xyXG4gICAgICBwb29sLm51bUZyZWUoKSArXHJcbiAgICAgIHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdDcmVhdGVzKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdWYWxpZGF0aW9ucygpICtcclxuICAgICAgcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoXHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHBvb2wsIGluY2x1ZGluZyB0aGUgbWluaW11bVxyXG4gKiBhbmQgbWF4aW11bSB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmVcclxuICogcmVxdWVzdHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sSW5mb1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvKCkge1xyXG4gIGNvbnN0IHtcclxuICAgIG1pbixcclxuICAgIG1heCxcclxuICAgIHVzZWQsXHJcbiAgICBhdmFpbGFibGUsXHJcbiAgICBhbGxDcmVhdGVkLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXMsXHJcbiAgICBwZW5kaW5nVmFsaWRhdGlvbnMsXHJcbiAgICBwZW5kaW5nRGVzdHJveXMsXHJcbiAgICBhYnNvbHV0ZUFsbFxyXG4gIH0gPSBnZXRQb29sSW5mb0pTT04oKTtcclxuXHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1pbmltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWlufS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttYXh9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgdXNlZCByZXNvdXJjZXM6ICR7dXNlZH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBmcmVlIHJlc291cmNlczogJHthdmFpbGFibGV9LmApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIGNyZWF0ZWQgKHVzZWQgYW5kIGZyZWUpIHJlc291cmNlczogJHthbGxDcmVhdGVkfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGFjcXVpcmVkOiAke3BlbmRpbmdBY3F1aXJlc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBjcmVhdGVkOiAke3BlbmRpbmdDcmVhdGVzfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIHZhbGlkYXRlZDogJHtwZW5kaW5nVmFsaWRhdGlvbnN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgZGVzdHJveWVkOiAke3BlbmRpbmdEZXN0cm95c30uYFxyXG4gICk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgcmVzb3VyY2VzOiAke2Fic29sdXRlQWxsfS5gKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEZhY3RvcnkgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGFuIG9iamVjdCB3aXRoIGBjcmVhdGVgLCBgdmFsaWRhdGVgLFxyXG4gKiBhbmQgYGRlc3Ryb3lgIGZ1bmN0aW9ucyBmb3IgdGhlIHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmFjdG9yeVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcG9vbE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgcG9vbGBcclxuICogb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9mYWN0b3J5KHBvb2xPcHRpb25zKSB7XHJcbiAgcmV0dXJuIHtcclxuICAgIC8qKlxyXG4gICAgICogQ3JlYXRlcyBhIG5ldyB3b3JrZXIgcGFnZSBmb3IgdGhlIGV4cG9ydCBwb29sLlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIGNyZWF0ZVxyXG4gICAgICpcclxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdFxyXG4gICAgICogY29udGFpbmluZyB0aGUgd29ya2VyIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgaW5pdGlhbFxyXG4gICAgICogd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gZXJyb3IgZHVyaW5nXHJcbiAgICAgKiB0aGUgY3JlYXRpb24gb2YgdGhlIG5ldyBwYWdlLlxyXG4gICAgICovXHJcbiAgICBjcmVhdGU6IGFzeW5jICgpID0+IHtcclxuICAgICAgLy8gSW5pdCB0aGUgcmVzb3VyY2Ugd2l0aCB1bmlxdWUgaWQgYW5kIHdvcmsgY291bnRcclxuICAgICAgY29uc3QgcG9vbFJlc291cmNlID0ge1xyXG4gICAgICAgIGlkOiB1dWlkKCksXHJcbiAgICAgICAgLy8gVHJ5IHRvIGRpc3RyaWJ1dGUgdGhlIGluaXRpYWwgd29yayBjb3VudFxyXG4gICAgICAgIHdvcmtDb3VudDogTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogKHBvb2xPcHRpb25zLndvcmtMaW1pdCAvIDIpKVxyXG4gICAgICB9O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBTdGFydCBtZWFzdXJpbmcgYSBwYWdlIGNyZWF0aW9uIHRpbWVcclxuICAgICAgICBjb25zdCBzdGFydERhdGUgPSBnZXROZXdEYXRlVGltZSgpO1xyXG5cclxuICAgICAgICAvLyBDcmVhdGUgYSBuZXcgcGFnZVxyXG4gICAgICAgIGF3YWl0IG5ld1BhZ2UocG9vbFJlc291cmNlKTtcclxuXHJcbiAgICAgICAgLy8gTWVhc3VyZSB0aGUgdGltZSBvZiBmdWxsIGNyZWF0aW9uIGFuZCBjb25maWd1cmF0aW9uIG9mIGEgcGFnZVxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBhIHdvcmtlciwgdG9vayAke1xyXG4gICAgICAgICAgICBnZXROZXdEYXRlVGltZSgpIC0gc3RhcnREYXRlXHJcbiAgICAgICAgICB9bXMuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHVybiByZWFkeSBwb29sIHJlc291cmNlXHJcbiAgICAgICAgcmV0dXJuIHBvb2xSZXNvdXJjZTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBjcmVhdGluZyBhIG5ldyBwYWdlLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcclxuICAgICAqIHRoZSB3b3JrIGxpbWl0LlxyXG4gICAgICpcclxuICAgICAqIEBhc3luY1xyXG4gICAgICogQGZ1bmN0aW9uIHZhbGlkYXRlXHJcbiAgICAgKlxyXG4gICAgICogQHBhcmFtIHtPYmplY3R9IHBvb2xSZXNvdXJjZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAgICogdGhlIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBpZiB0aGUgd29ya2VyXHJcbiAgICAgKiBpcyB2YWxpZCBhbmQgd2l0aGluIHRoZSB3b3JrIGxpbWl0OyBvdGhlcndpc2UsIHRvIGZhbHNlLlxyXG4gICAgICovXHJcbiAgICB2YWxpZGF0ZTogYXN5bmMgKHBvb2xSZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBOT1RFOlxyXG4gICAgICAvLyBJbiBjZXJ0YWluIGNhc2VzIGFjcXVpcmluZyB0aHJvd3MgYSBUYXJnZXRDbG9zZUVycm9yLCB3aGljaCBtYXlcclxuICAgICAgLy8gYmUgY2F1c2VkIGJ5IHR3byB0aGluZ3M6XHJcbiAgICAgIC8vIC0gVGhlIHBhZ2UgaXMgY2xvc2VkIGFuZCBhdHRlbXB0ZWQgdG8gYmUgcmV1c2VkLlxyXG4gICAgICAvLyAtIExvc3QgY29udGFjdCB3aXRoIHRoZSBicm93c2VyLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBXaGF0IHdlJ3JlIHNlZWluZyBpbiBsb2dzIGlzIHRoYXQgc3VjY2Vzc2l2ZSBleHBvcnRzIHR5cGljYWxseVxyXG4gICAgICAvLyBzdWNjZWVkcywgYW5kIHRoZSBzZXJ2ZXIgcmVjb3ZlcnMsIGluZGljYXRpbmcgdGhhdCBpdCdzIGxpa2VseVxyXG4gICAgICAvLyB0aGUgZmlyc3QgY2FzZS4gVGhpcyBpcyBhbiBhdHRlbXB0IGF0IGFsbGlldmF0aW5nIHRoZSBpc3N1ZSBieVxyXG4gICAgICAvLyBzaW1wbHkgbm90IHZhbGlkYXRpbmcgdGhlIHdvcmtlciBpZiB0aGUgcGFnZSBpcyBudWxsIG9yIGNsb3NlZC5cclxuICAgICAgLy9cclxuICAgICAgLy8gVGhlIGFjdHVhbCByZXN1bHQgZnJvbSB3aGVuIHRoaXMgaGFwcGVuZWQsIHdhcyB0aGF0IGEgd29ya2VyIHdvdWxkXHJcbiAgICAgIC8vIGJlIGNvbXBsZXRlbHkgbG9ja2VkLCBzdG9wcGluZyBpdCBmcm9tIGJlaW5nIGFjcXVpcmVkIHVudGlsXHJcbiAgICAgIC8vIGl0cyB3b3JrIGNvdW50IHJlYWNoZWQgdGhlIGxpbWl0LlxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBwYWdlYCBpcyB2YWxpZFxyXG4gICAgICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChubyB2YWxpZCBwYWdlIGlzIGZvdW5kKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgY2xvc2VkXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlIGlzIGNsb3NlZCBvciBpbnZhbGlkKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgbWFpbkZyYW1lYCBpcyBkZXRhY2hlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UubWFpbkZyYW1lKCkuZGV0YWNoZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKHBhZ2UncyBmcmFtZSBpcyBkZXRhY2hlZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHdvcmtMaW1pdGAgaXMgZXhjZWVkZWRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHBvb2xPcHRpb25zLndvcmtMaW1pdCAmJlxyXG4gICAgICAgICsrcG9vbFJlc291cmNlLndvcmtDb3VudCA+IHBvb2xPcHRpb25zLndvcmtMaW1pdFxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKGV4Y2VlZGVkIHRoZSAke3Bvb2xPcHRpb25zLndvcmtMaW1pdH0gd29ya3MgcGVyIHJlc291cmNlIGxpbWl0KS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSBgcG9vbFJlc291cmNlYCBpcyB2YWxpZGF0ZWRcclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gZGVzdHJveVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKi9cclxuICAgIGRlc3Ryb3k6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRGVzdHJveWluZyBhIHdvcmtlci5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgLy8gUmVtb3ZlIGFsbCBhdHRhY2hlZCBldmVudCBsaXN0ZW5lcnMgZnJvbSB0aGUgcmVzb3VyY2VcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygncGFnZWVycm9yJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2NvbnNvbGUnKTtcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygnZnJhbWVkZXRhY2hlZCcpO1xyXG5cclxuICAgICAgICAgIC8vIFdlIG5lZWQgdG8gd2FpdCBhcm91bmQgZm9yIHRoaXNcclxuICAgICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gUGFnZSBjb3VsZCBub3QgYmUgY2xvc2VkIHVwb24gZGVzdHJveWluZy5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGluaXRQb29sLFxyXG4gIGtpbGxQb29sLFxyXG4gIHBvc3RXb3JrLFxyXG4gIGdldFBvb2wsXHJcbiAgZ2V0UG9vbFN0YXRzLFxyXG4gIGdldFBvb2xJbmZvLFxyXG4gIGdldFBvb2xJbmZvSlNPTlxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVXNlZCB0byBzYW5pdGl6ZSB0aGUgc3RyaW5ncyBjb21pbmcgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZVxyXG4gKiB0byBwcmV2ZW50IFhTUyBhdHRhY2tzICh3aXRoIHRoZSBET01QdXJpZnkgbGlicmFyeSkuXHJcbiAqL1xyXG5cclxuaW1wb3J0IERPTVB1cmlmeSBmcm9tICdkb21wdXJpZnknO1xyXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcclxuXHJcbi8qKlxyXG4gKiBTYW5pdGl6ZXMgYSBnaXZlbiBIVE1MIHN0cmluZyBieSByZW1vdmluZyA8c2NyaXB0PiB0YWdzLiBUaGlzIGZ1bmN0aW9uIHVzZXNcclxuICogYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gZmluZCBhbmQgcmVtb3ZlIGFsbCBvY2N1cnJlbmNlcyBvZiA8c2NyaXB0Pjwvc2NyaXB0PlxyXG4gKiB0YWdzIGFuZCBhbnkgY29udGVudCB3aXRoaW4gdGhlbS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNhbml0aXplXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCAtIFRoZSBIVE1MIHN0cmluZyB0byBiZSBzYW5pdGl6ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTCBzdHJpbmcuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXQpIHtcclxuICAvLyBHZXQgdGhlIHZpcnR1YWwgRE9NXHJcbiAgY29uc3Qgd2luZG93ID0gbmV3IEpTRE9NKCcnKS53aW5kb3c7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHB1cmlmeWluZyBpbnN0YW5jZVxyXG4gIGNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cpO1xyXG5cclxuICAvLyBSZXR1cm4gc2FuaXRpemVkIGlucHV0XHJcbiAgcmV0dXJuIHB1cmlmeS5zYW5pdGl6ZShpbnB1dCwgeyBBRERfVEFHUzogWydmb3JlaWduT2JqZWN0J10gfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzYW5pdGl6ZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIHRvIHByZXBhcmUgZm9yIHRoZSBleHBvcnRpbmcgY2hhcnRzXHJcbiAqIGludG8gdmFyaW91cyBpbWFnZSBvdXRwdXQgZm9ybWF0cyBzdWNoIGFzIEpQRUcsIFBORywgUERGLCBhbmQgU1ZHcy5cclxuICogSXQgc3VwcG9ydHMgc2luZ2xlIGFuZCBiYXRjaCBleHBvcnQgb3BlcmF0aW9ucyBhbmQgYWxsb3dzIGN1c3RvbWl6YXRpb25cclxuICogdGhyb3VnaCBvcHRpb25zIHBhc3NlZCBmcm9tIGNvbmZpZ3VyYXRpb25zIG9yIEFQSXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnLCB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeENvbnN0cixcclxuICBmaXhPdXRmaWxlLFxyXG4gIGZpeFR5cGUsXHJcbiAgZ2V0QWJzb2x1dGVQYXRoLFxyXG4gIGdldEJhc2U2NCxcclxuICBpc09iamVjdCxcclxuICByb3VuZE51bWJlcixcclxuICB3cmFwQXJvdW5kXHJcbn0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGdsb2JhbCBmbGFnIGZvciB0aGUgY29kZSBleGVjdXRpb24gcGVybWlzc2lvblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucyBhbmQgc2F2ZXNcclxuICogdGhlIHJlc3VsdGluZyBpbWFnZSB0byB0aGUgcHJvdmlkZWQgb3V0cHV0IGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIFRoZSBvYmplY3QgbXVzdCBjb250YWluIGF0IGxlYXN0IG9uZVxyXG4gKiBvZiB0aGUgZm9sbG93aW5nIGBleHBvcnRgIHByb3BlcnRpZXM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgXHJcbiAqIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpbmdsZUV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0KSB7XHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQoXHJcbiAgICAgIHsgZXhwb3J0OiBvcHRpb25zLmV4cG9ydCwgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWMgfSxcclxuICAgICAgYXN5bmMgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSBvbmUgb2YgdGhlIGZvbGxvd2luZyBvcHRpb25zOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIGluZm9ybWF0aW9uXHJcbiAqIHByb3ZpZGVkIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZ1xyXG4gKiBmb3JtYXQ6IFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzXHJcbiAqIGFyZSBzYXZlZCB0byB0aGUgc3BlY2lmaWVkIG91dHB1dCBmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIGZyb21cclxuICogdGhlIGBleHBvcnRgIHNlY3Rpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3NlcyBhcmUgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJhdGNoRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQgJiYgb3B0aW9ucy5leHBvcnQuYmF0Y2gpIHtcclxuICAgIC8vIEFuIGFycmF5IGZvciBjb2xsZWN0aW5nIGJhdGNoIGV4cG9ydHNcclxuICAgIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIGBiYXRjaGAgYXJndW1lbnRzXHJcbiAgICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykgfHwgW10pIHtcclxuICAgICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgaW1hZ2VPcHRpb25zYCBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXRcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgcGFydGlhbFxyXG4gKiBvcHRpb25zIGFyZSBwcm92aWRlZCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICpcclxuICogVGhlIGBlbmRDYWxsYmFja2AgZnVuY3Rpb24gaXMgaW52b2tlZCB1cG9uIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBleHBvcnQsXHJcbiAqIGVpdGhlciBzdWNjZXNzZnVsbHkgb3Igd2l0aCBhbiBlcnJvci4gVGhlIGBlcnJvcmAgb2JqZWN0IGlzIHByb3ZpZGVkXHJcbiAqIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIGNvbnRhaW5pbmdcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5XHJcbiAqIGFuZCB0aGUgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW1hZ2VPcHRpb25zIC0gVGhlIGBpbWFnZU9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkXHJcbiAqIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgdGhlIHByb3ZpZGVkXHJcbiAqIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHRoZSBleHBvcnQgcHJvY2VzcyBvciB1cG9uIGVuY291bnRlcmluZyBhbiBlcnJvci4gVGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGlzIHRoZSBgZXJyb3JgIG9iamVjdCwgYW5kIHRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIGBkYXRhYCBvYmplY3QsXHJcbiAqIHdoaWNoIGluY2x1ZGVzIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YFxyXG4gKiBwcm9wZXJ0eSBhbmQgdGhlIGZ1bGwgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGltYWdlT3B0aW9ucywgZW5kQ2FsbGJhY2spIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgb3B0aW9ucyBhcmUgaW4gYW4gb2JqZWN0XHJcbiAgICBpZiAoIWlzT2JqZWN0KGltYWdlT3B0aW9ucykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgcHJvdmlkZWQgYGltYWdlT3B0aW9uc2AuIE5lZWRzIHRvIGJlIGFuIG9iamVjdC4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE1lcmdlIGFkZGl0aW9uYWwgb3B0aW9ucyB0byB0aGUgY29weSBvZiB0aGUgaW5zdGFuY2Ugb3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoXHJcbiAgICAgIHtcclxuICAgICAgICBleHBvcnQ6IGltYWdlT3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgY3VzdG9tTG9naWM6IGltYWdlT3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcbiAgICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnMgPSBudWxsO1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBvdXRmaWxlYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLm91dGZpbGUgPSBmaXhPdXRmaWxlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGNvbnN0cmAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5jb25zdHIgPSBmaXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b20gbG9naWMgb3B0aW9ucyAoYGN1c3RvbUNvZGVgLCBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYClcclxuICBfaGFuZGxlQ3VzdG9tTG9naWMoY3VzdG9tTG9naWNPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGdsb2JhbE9wdGlvbnNgIGFuZCBgdGhlbWVPcHRpb25zYCBvcHRpb25zXHJcbiAgX2hhbmRsZUdsb2JhbEFuZFRoZW1lKFxyXG4gICAgZXhwb3J0T3B0aW9ucyxcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgYGhlaWdodGAsIGB3aWR0aGAsIGFuZCBgc2NhbGVgIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLmV4cG9ydE9wdGlvbnMsXHJcbiAgICAuLi5fZmluZENoYXJ0U2l6ZShleHBvcnRPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSBpbWFnZSBvcHRpb25zIG9iamVjdCBkb2VzIG5vdCBleGNlZWQgdGhlIHNpemUgbGltaXRcclxuICBfY2hlY2tEYXRhU2l6ZSh7IGV4cG9ydDogZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWM6IGN1c3RvbUxvZ2ljT3B0aW9ucyB9KTtcclxuXHJcbiAgLy8gUG9zdCB0aGUgd29yayB0byB0aGUgcG9vbFxyXG4gIHJldHVybiBwb3N0V29yayhvcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgdGhlIGBoZWlnaHRgLCBgd2lkdGhgIGFuZCBgc2NhbGVgIGZvciBjaGFydCBleHBvcnRzIGJhc2VkXHJcbiAqIG9uIHRoZSBwcm92aWRlZCBleHBvcnQgb3B0aW9ucy5cclxuICpcclxuICogVGhlIGZ1bmN0aW9uIHByaW9yaXRpemVzIHZhbHVlcyBpbiB0aGUgZm9sbG93aW5nIG9yZGVyOlxyXG4gKiAxLiBUaGUgYGhlaWdodGAsIGB3aWR0aGAsIGBzY2FsZWAgZnJvbSB0aGUgYGV4cG9ydE9wdGlvbnNgLlxyXG4gKiAyLiBPcHRpb25zIGZyb20gdGhlIGNoYXJ0IGNvbmZpZ3VyYXRpb24gKGZyb20gYGV4cG9ydGluZ2AgYW5kIGBjaGFydGApLlxyXG4gKiAzLiBPcHRpb25zIGZyb20gdGhlIGdsb2JhbCBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogNC4gT3B0aW9ucyBmcm9tIHRoZSB0aGVtZSBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgIHNlY3Rpb25zKS5cclxuICogNS4gRmFsbGJhY2sgZGVmYXVsdCB2YWx1ZXMgKGBoZWlnaHQgPSA0MDBgLCBgd2lkdGggPSA2MDBgLCBgc2NhbGUgPSAxYCkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZmluZENoYXJ0U2l6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBvYmplY3QgY29udGFpbmluZyBjYWxjdWxhdGVkIGBoZWlnaHRgLCBgd2lkdGhgXHJcbiAqIGFuZCBgc2NhbGVgIHZhbHVlcyBmb3IgdGhlIGNoYXJ0IGV4cG9ydC5cclxuICovXHJcbmZ1bmN0aW9uIF9maW5kQ2hhcnRTaXplKGV4cG9ydE9wdGlvbnMpIHtcclxuICAvLyBDaGVjayB0aGUgYG9wdGlvbnNgIGFuZCBgaW5zdHJgIGZvciBjaGFydCBhbmQgZXhwb3J0aW5nIHNlY3Rpb25zXHJcbiAgY29uc3QgeyBjaGFydDogb3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IG9wdGlvbnNFeHBvcnRpbmcgfSA9XHJcbiAgICBpc0FsbG93ZWRDb25maWcoZXhwb3J0T3B0aW9ucy5pbnN0cikgfHwgZmFsc2U7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBgZ2xvYmFsT3B0aW9uc2AgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiBnbG9iYWxPcHRpb25zQ2hhcnQsIGV4cG9ydGluZzogZ2xvYmFsT3B0aW9uc0V4cG9ydGluZyB9ID1cclxuICAgIGlzQWxsb3dlZENvbmZpZyhleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnMpIHx8IGZhbHNlO1xyXG5cclxuICAvLyBDaGVjayB0aGUgYHRoZW1lT3B0aW9uc2AgZm9yIGNoYXJ0IGFuZCBleHBvcnRpbmcgc2VjdGlvbnNcclxuICBjb25zdCB7IGNoYXJ0OiB0aGVtZU9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiB0aGVtZU9wdGlvbnNFeHBvcnRpbmcgfSA9XHJcbiAgICBpc0FsbG93ZWRDb25maWcoZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnMpIHx8IGZhbHNlO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgc2NhbGVgIHZhbHVlOlxyXG4gIC8vIC0gSXQgY2Fubm90IGJlIGxvd2VyIHRoYW4gMC4xXHJcbiAgLy8gLSBJdCBjYW5ub3QgYmUgaGlnaGVyIHRoYW4gNS4wXHJcbiAgLy8gLSBJdCBtdXN0IGJlIHJvdW5kZWQgdG8gMiBkZWNpbWFsIHBsYWNlcyAoZS5nLiAwLjIzMjM0IC0+IDAuMjMpXHJcbiAgY29uc3Qgc2NhbGUgPSByb3VuZE51bWJlcihcclxuICAgIE1hdGgubWF4KFxyXG4gICAgICAwLjEsXHJcbiAgICAgIE1hdGgubWluKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc2NhbGUgfHxcclxuICAgICAgICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5kZWZhdWx0U2NhbGUgfHxcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgNS4wXHJcbiAgICAgIClcclxuICAgICksXHJcbiAgICAyXHJcbiAgKTtcclxuXHJcbiAgLy8gRmluZCB0aGUgYGhlaWdodGAgdmFsdWVcclxuICBjb25zdCBoZWlnaHQgPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5oZWlnaHQgfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8uaGVpZ2h0IHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRIZWlnaHQgfHxcclxuICAgIDQwMDtcclxuXHJcbiAgLy8gRmluZCB0aGUgYHdpZHRoYCB2YWx1ZVxyXG4gIGNvbnN0IHdpZHRoID1cclxuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py53aWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRXaWR0aCB8fFxyXG4gICAgNjAwO1xyXG5cclxuICAvLyBHYXRoZXIgYGhlaWdodGAsIGB3aWR0aGAgYW5kIGBzY2FsZWAgaW5mb3JtYXRpb24gaW4gb25lIG9iamVjdFxyXG4gIGNvbnN0IHNpemUgPSB7IGhlaWdodCwgd2lkdGgsIHNjYWxlIH07XHJcblxyXG4gIC8vIEdldCByaWQgb2YgcG90ZW50aWFsIGBweGAgYW5kIGAlYFxyXG4gIGZvciAobGV0IFtwYXJhbSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHNpemUpKSB7XHJcbiAgICBzaXplW3BhcmFtXSA9XHJcbiAgICAgIHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgPyArdmFsdWUucmVwbGFjZSgvcHh8JS9naSwgJycpIDogdmFsdWU7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gdGhlIHNpemUgb2JqZWN0XHJcbiAgcmV0dXJuIHNpemU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleGVjdXRpb24gb2YgY3VzdG9tIGxvZ2ljIG9wdGlvbnMsIGluY2x1ZGluZyBsb2FkaW5nIGByZXNvdXJjZXNgLFxyXG4gKiBgY3VzdG9tQ29kZWAsIGFuZCBgY2FsbGJhY2tgLiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLCBpdCBwcm9jZXNzZXNcclxuICogdGhlIGN1c3RvbSBsb2dpYyBvcHRpb25zIGFjY29yZGluZ2x5LiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBub3QgYWxsb3dlZCxcclxuICogaXQgZGlzYWJsZXMgdGhlIHVzYWdlIG9mIHJlc291cmNlcywgY3VzdG9tIGNvZGUgYW5kIGNhbGxiYWNrLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dDb2RlRXhlY3V0aW9uIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBjb2RlXHJcbiAqIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY29kZSBleGVjdXRpb25cclxuICogaXMgbm90IGFsbG93ZWQgYnV0IGN1c3RvbSBsb2dpYyBvcHRpb25zIGFyZSBzdGlsbCBwcm92aWRlZC5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVDdXN0b21Mb2dpYyhjdXN0b21Mb2dpY09wdGlvbnMsIGFsbG93Q29kZUV4ZWN1dGlvbikge1xyXG4gIC8vIEluIGNhc2Ugb2YgYWxsb3dpbmcgY29kZSBleGVjdXRpb25cclxuICBpZiAoYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgcmVzb3VyY2VzYCBvcHRpb25cclxuICAgIGlmICh0eXBlb2YgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gQ3VzdG9tIHN0cmluZ2lmaWVkIHJlc291cmNlc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmICghY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIExvYWQgdGhlIGRlZmF1bHQgb25lXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9IF9oYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKCdyZXNvdXJjZXMuanNvbicpLCAndXRmOCcpLFxyXG4gICAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcyxcclxuICAgICAgICAgIHRydWVcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZygyLCAnW2NoYXJ0XSBVbmFibGUgdG8gbG9hZCB0aGUgZGVmYXVsdCBgcmVzb3VyY2VzLmpzb25gIGZpbGUuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBQcm9jZXNzIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uXHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBUcnkgdG8gbG9hZCBjdXN0b20gY29kZSBhbmQgd3JhcCBhcm91bmQgaXQgaW4gYSBzZWxmIGludm9raW5nIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY3VzdG9tQ29kZWAgY2Fubm90IGJlIGxvYWRlZC4nKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUHJvY2VzcyB0aGUgYGNhbGxiYWNrYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGNhbGxiYWNrIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IHdyYXBBcm91bmQoXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW2NoYXJ0XSBUaGUgYGNhbGxiYWNrYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjdXN0b21Db2RlYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBjdXN0b21Db2RlYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBjYWxsYmFja2AgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjaykpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYGNhbGxiYWNrYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGByZXNvdXJjZXNgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgcmVzb3VyY2VzYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIC8vIElmIHRoZSBgYWxsb3dDb2RlRXhlY3V0aW9uYCBmbGFnIGlzIHNldCB0byBmYWxzZSwgd2Ugc2hvdWxkIHJlZnVzZVxyXG4gICAgLy8gdGhlIHVzYWdlIG9mIHRoZSBgY2FsbGJhY2tgLCBgcmVzb3VyY2VzYCwgYW5kIGBjdXN0b21Db2RlYCBvcHRpb25zLlxyXG4gICAgLy8gQWRkaXRpb25hbGx5LCB0aGUgd29ya2VyIHdpbGwgcmVmdXNlIHRvIHJ1biBhcmJpdHJhcnkgSmF2YVNjcmlwdC5cclxuICAgIGlmIChcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGVcclxuICAgICkge1xyXG4gICAgICAvLyBSZXNldCBhbGwgY3VzdG9tIGNvZGUgb3B0aW9uc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBudWxsO1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gbnVsbDtcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBudWxsO1xyXG5cclxuICAgICAgLy8gU2VuZCBhIG1lc3NhZ2Ugc2F5aW5nIHRoYXQgdGhlIGV4cG9ydGVyIGRvZXMgbm90IHN1cHBvcnQgdGhlc2Ugc2V0dGluZ3NcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJywgYW5kICdjdXN0b21Db2RlJyBvcHRpb25zIGhhdmUgYmVlbiBkaXNhYmxlZCBmb3IgdGhpcyBzZXJ2ZXIuYCxcclxuICAgICAgICA0MDNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIGFuZCB2YWxpZGF0ZXMgcmVzb3VyY2VzIGZyb20gdGhlIGByZXNvdXJjZXNgIG9wdGlvbiBmb3IgZXhwb3J0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8c3RyaW5nfG51bGwpfSBbcmVzb3VyY2VzPW51bGxdIC0gVGhlIHJlc291cmNlcyB0byBiZSBoYW5kbGVkLlxyXG4gKiBDYW4gYmUgZWl0aGVyIGEgSlNPTiBvYmplY3QsIHN0cmluZ2lmaWVkIEpTT04sIGEgcGF0aCB0byBhIEpTT04gZmlsZSxcclxuICogb3IgbnVsbC4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYG51bGxgLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgbG9hZGluZ1xyXG4gKiByZXNvdXJjZXMgZnJvbSBmaWxlcyBpcyBhbGxvd2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93Q29kZUV4ZWN1dGlvbiAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgY29kZVxyXG4gKiBleGVjdXRpb24gaXMgYWxsb3dlZC5cclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8bnVsbCl9IFRoZSBoYW5kbGVkIHJlc291cmNlcyBvciBudWxsIGlmIG5vIHZhbGlkIHJlc291cmNlc1xyXG4gKiBhcmUgZm91bmQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaGFuZGxlUmVzb3VyY2VzKFxyXG4gIHJlc291cmNlcyA9IG51bGwsXHJcbiAgYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4pIHtcclxuICAvLyBMaXN0IG9mIGFsbG93ZWQgc2VjdGlvbnMgaW4gdGhlIHJlc291cmNlcyBKU09OXHJcbiAgY29uc3QgYWxsb3dlZFByb3BzID0gWydqcycsICdjc3MnLCAnZmlsZXMnXTtcclxuXHJcbiAgbGV0IGhhbmRsZWRSZXNvdXJjZXMgPSByZXNvdXJjZXM7XHJcbiAgbGV0IGNvcnJlY3RSZXNvdXJjZXMgPSBmYWxzZTtcclxuXHJcbiAgLy8gVHJ5IHRvIGxvYWQgcmVzb3VyY2VzIGZyb20gYSBmaWxlXHJcbiAgaWYgKGFsbG93RmlsZVJlc291cmNlcyAmJiByZXNvdXJjZXMuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChyZXNvdXJjZXMpLCAndXRmOCcpLFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCB7XHJcbiAgICAgIHJldHVybiBudWxsO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0FsbG93ZWRDb25maWcocmVzb3VyY2VzLCBmYWxzZSwgYWxsb3dDb2RlRXhlY3V0aW9uKTtcclxuXHJcbiAgICAvLyBHZXQgcmlkIG9mIHRoZSBmaWxlcyBzZWN0aW9uXHJcbiAgICBpZiAoaGFuZGxlZFJlc291cmNlcyAmJiAhYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRmlsdGVyIGZyb20gdW5uZWNlc3NhcnkgcHJvcGVydGllc1xyXG4gIGZvciAoY29uc3QgcHJvcE5hbWUgaW4gaGFuZGxlZFJlc291cmNlcykge1xyXG4gICAgaWYgKCFhbGxvd2VkUHJvcHMuaW5jbHVkZXMocHJvcE5hbWUpKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzW3Byb3BOYW1lXTtcclxuICAgIH0gZWxzZSBpZiAoIWNvcnJlY3RSZXNvdXJjZXMpIHtcclxuICAgICAgY29ycmVjdFJlc291cmNlcyA9IHRydWU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBhdCBsZWFzdCBvbmUgb2YgYWxsb3dlZCBwcm9wZXJ0aWVzIGlzIHByZXNlbnRcclxuICBpZiAoIWNvcnJlY3RSZXNvdXJjZXMpIHtcclxuICAgIHJldHVybiBudWxsO1xyXG4gIH1cclxuXHJcbiAgLy8gSGFuZGxlIGZpbGVzIHNlY3Rpb25cclxuICBpZiAoaGFuZGxlZFJlc291cmNlcy5maWxlcykge1xyXG4gICAgaGFuZGxlZFJlc291cmNlcy5maWxlcyA9IGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMubWFwKChpdGVtKSA9PiBpdGVtLnRyaW0oKSk7XHJcbiAgICBpZiAoIWhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgfHwgaGFuZGxlZFJlc291cmNlcy5maWxlcy5sZW5ndGggPD0gMCkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiByZXNvdXJjZXNcclxuICByZXR1cm4gaGFuZGxlZFJlc291cmNlcztcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGxvYWRpbmcgYW5kIHZhbGlkYXRpb24gb2YgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2BcclxuICogaW4gdGhlIGV4cG9ydCBvcHRpb25zLiBJZiB0aGUgb3B0aW9uIGlzIGEgc3RyaW5nIGFuZCByZWZlcmVuY2VzIGEgSlNPTiBmaWxlXHJcbiAqICh3aGVuIHRoZSBgYWxsb3dGaWxlUmVzb3VyY2VzYCBpcyB0cnVlKSwgaXQgcmVhZHMgYW5kIHBhcnNlcyB0aGUgZmlsZS5cclxuICogT3RoZXJ3aXNlLCBpdCBhdHRlbXB0cyB0byBwYXJzZSB0aGUgc3RyaW5nIG9yIG9iamVjdCBhcyBKU09OLiBJZiBhbnkgZXJyb3JzXHJcbiAqIG9jY3VyIGR1cmluZyB0aGlzIHByb2Nlc3MsIHRoZSBvcHRpb24gaXMgc2V0IHRvIG51bGwuIElmIHRoZXJlIGlzIGFuIGVycm9yXHJcbiAqIGxvYWRpbmcgb3IgcGFyc2luZyB0aGUgYGdsb2JhbE9wdGlvbnNgIG9yIGB0aGVtZU9wdGlvbnNgLCB0aGUgZXJyb3IgaXMgbG9nZ2VkXHJcbiAqIGFuZCB0aGUgb3B0aW9uIGlzIHNldCB0byBudWxsLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUdsb2JhbEFuZFRoZW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGxvYWRpbmdcclxuICogcmVzb3VyY2VzIGZyb20gZmlsZXMgaXMgYWxsb3dlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaGFuZGxlR2xvYmFsQW5kVGhlbWUoXHJcbiAgZXhwb3J0T3B0aW9ucyxcclxuICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbikge1xyXG4gIC8vIENoZWNrIHRoZSBgZ2xvYmFsT3B0aW9uc2AgYW5kIGB0aGVtZU9wdGlvbnNgIG9wdGlvbnNcclxuICBbJ2dsb2JhbE9wdGlvbnMnLCAndGhlbWVPcHRpb25zJ10uZm9yRWFjaCgob3B0aW9uc05hbWUpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBvcHRpb24gZXhpc3RzXHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSkge1xyXG4gICAgICAgIC8vIENoZWNrIGlmIGl0IGlzIGEgc3RyaW5nIGFuZCBhIGZpbGUgbmFtZSB3aXRoIHRoZSBgLmpzb25gIGV4dGVuc2lvblxyXG4gICAgICAgIGlmIChcclxuICAgICAgICAgIGFsbG93RmlsZVJlc291cmNlcyAmJlxyXG4gICAgICAgICAgdHlwZW9mIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0uZW5kc1dpdGgoJy5qc29uJylcclxuICAgICAgICApIHtcclxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSBmaWxlIGNvbnRlbnQgY2FuIGJlIGEgY29uZmlnLCBhbmQgc2F2ZSBpdCBhcyBhIHN0cmluZ1xyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgoZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0pLCAndXRmOCcpLFxyXG4gICAgICAgICAgICB0cnVlLFxyXG4gICAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSB2YWx1ZSBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0sXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGVycm9yLFxyXG4gICAgICAgIGBbY2hhcnRdIFRoZSBcXGAke29wdGlvbnNOYW1lfVxcYCBjYW5ub3QgYmUgbG9hZGVkLmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IG51bGw7XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGlzIHRoZSBgZ2xvYmFsT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnMpKSB7XHJcbiAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgZ2xvYmFsT3B0aW9uc2Agb3B0aW9uIGZvdW5kLicpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGB0aGVtZU9wdGlvbnNgIHByZXNlbnRcclxuICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnMpKSB7XHJcbiAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgdGhlbWVPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogVmFsaWRhdGVzIHRoZSBzaXplIG9mIHRoZSBkYXRhIGZvciB0aGUgZXhwb3J0IHByb2Nlc3MgYWdhaW5zdCBhIGZpeGVkIGxpbWl0XHJcbiAqIG9mIDEwME1CLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NoZWNrRGF0YVNpemVcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGltYWdlT3B0aW9ucyAtIFRoZSBkYXRhIG9iamVjdCwgd2hpY2ggaW5jbHVkZXMgb3B0aW9ucyBmcm9tXHJcbiAqIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucyBhbmQgd2lsbCBiZSBzZW50IHRvIGEgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgc2l6ZSBvZiB0aGUgZGF0YSBmb3JcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzIG9iamVjdCBleGNlZWRzIHRoZSAxMDBNQiBsaW1pdC5cclxuICovXHJcbmZ1bmN0aW9uIF9jaGVja0RhdGFTaXplKGltYWdlT3B0aW9ucykge1xyXG4gIC8vIFNldCB0aGUgZml4ZWQgZGF0YSBsaW1pdCAoMTAwTUIpIGZvciB0aGUgZGV2LXRvb2xzIHByb3RvY29sXHJcbiAgY29uc3QgZGF0YUxpbWl0ID0gMTAwICogMTAyNCAqIDEwMjQ7XHJcblxyXG4gIC8vIEdldCB0aGUgc2l6ZSBvZiB0aGUgZGF0YVxyXG4gIGNvbnN0IHRvdGFsU2l6ZSA9IEJ1ZmZlci5ieXRlTGVuZ3RoKEpTT04uc3RyaW5naWZ5KGltYWdlT3B0aW9ucyksICd1dGYtOCcpO1xyXG5cclxuICAvLyBMb2cgdGhlIHNpemUgaW4gTUJcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1cnJlbnQgdG90YWwgc2l6ZSBvZiB0aGUgZGF0YSBmb3IgdGhlIGV4cG9ydCBwcm9jZXNzIGlzIGFyb3VuZCAkeyhcclxuICAgICAgdG90YWxTaXplIC9cclxuICAgICAgKDEwMjQgKiAxMDI0KVxyXG4gICAgKS50b0ZpeGVkKDIpfU1CLmBcclxuICApO1xyXG5cclxuICAvLyBDaGVjayB0aGUgc2l6ZSBvZiBkYXRhIGJlZm9yZSBwYXNzaW5nIHRvIGEgcGFnZVxyXG4gIGlmICh0b3RhbFNpemUgPj0gZGF0YUxpbWl0KSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgIGBbY2hhcnRdIFRoZSBkYXRhIGZvciB0aGUgZXhwb3J0IHByb2Nlc3MgZXhjZWVkcyAxMDBNQiBsaW1pdC5gXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBiYXRjaEV4cG9ydCxcclxuICBzdGFydEV4cG9ydCxcclxuICBnZXRBbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBwcm92aWRlcyB1dGlsaXR5IGZ1bmN0aW9ucyBmb3IgbWFuYWdpbmcgaW50ZXJ2YWxzXHJcbiAqIGFuZCB0aW1lb3V0cyBpbiBhIGNlbnRyYWxpemVkIG1hbm5lci4gSXQgbWFpbnRhaW5zIGEgcmVnaXN0cnkgb2YgYWxsIGFjdGl2ZVxyXG4gKiB0aW1lcnMgYW5kIGFsbG93cyBmb3IgdGhlaXIgZWZmaWNpZW50IGNsZWFudXAgd2hlbiBuZWVkZWQuIFRoaXMgY2FuIGJlIHVzZWZ1bFxyXG4gKiBpbiBhcHBsaWNhdGlvbnMgd2hlcmUgcHJvcGVyIHJlc291cmNlIG1hbmFnZW1lbnQgYW5kIGNsZWFuIHNodXRkb3duIG9mIHRpbWVyc1xyXG4gKiBhcmUgY3JpdGljYWwgdG8gYXZvaWQgbWVtb3J5IGxlYWtzIG9yIHVuaW50ZW5kZWQgYmVoYXZpb3IuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuLy8gQXJyYXkgdGhhdCBjb250YWlucyBpZHMgb2YgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0c1xyXG5jb25zdCB0aW1lcklkcyA9IFtdO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgaWQgb2YgdGhlIGBzZXRJbnRlcnZhbGAgb3IgYHNldFRpbWVvdXRgIGFuZCB0byB0aGUgYHRpbWVySWRzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIGFkZFRpbWVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7Tm9kZUpTLlRpbWVvdXR9IGlkIC0gSWQgb2YgYW4gaW50ZXJ2YWwgb3IgYSB0aW1lb3V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGFkZFRpbWVyKGlkKSB7XHJcbiAgdGltZXJJZHMucHVzaChpZCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgYWxsIG9mIG9uZ29pbmcgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cyBieSBpZHMgZ2F0aGVyZWRcclxuICogaW4gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhckFsbFRpbWVyc1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyQWxsVGltZXJzKCkge1xyXG4gIGxvZyg0LCBgW3RpbWVyXSBDbGVhcmluZyBhbGwgcmVnaXN0ZXJlZCBpbnRlcnZhbHMgYW5kIHRpbWVvdXRzLmApO1xyXG4gIGZvciAoY29uc3QgaWQgb2YgdGltZXJJZHMpIHtcclxuICAgIGNsZWFySW50ZXJ2YWwoaWQpO1xyXG4gICAgY2xlYXJUaW1lb3V0KGlkKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBhZGRUaW1lcixcclxuICBjbGVhckFsbFRpbWVyc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWlkZGxld2FyZSBmdW5jdGlvbnMgZm9yIGxvZ2dpbmcgZXJyb3JzIHdpdGggc3RhY2sgdHJhY2VzXHJcbiAqIGFuZCBoYW5kbGluZyBlcnJvciByZXNwb25zZXMgaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlIGFuZCBoYW5kbGluZyBlcnJvciByZXNwb25zZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ0Vycm9yTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uIHdpdGhcclxuICogdGhlIHBhc3NlZCBlcnJvci5cclxuICovXHJcbmZ1bmN0aW9uIGxvZ0Vycm9yTWlkZGxld2FyZShlcnJvciwgcmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICAvLyBEaXNwbGF5IHRoZSBlcnJvciB3aXRoIHN0YWNrIGluIGEgY29ycmVjdCBmb3JtYXRcclxuICBsb2dXaXRoU3RhY2soMSwgZXJyb3IpO1xyXG5cclxuICAvLyBEZWxldGUgdGhlIHN0YWNrIGZvciB0aGUgZW52aXJvbm1lbnQgb3RoZXIgdGhhbiB0aGUgZGV2ZWxvcG1lbnRcclxuICBpZiAoZ2V0T3B0aW9ucygpLm90aGVyLm5vZGVFbnYgIT09ICdkZXZlbG9wbWVudCcpIHtcclxuICAgIGRlbGV0ZSBlcnJvci5zdGFjaztcclxuICB9XHJcblxyXG4gIC8vIENhbGwgdGhlIGByZXR1cm5FcnJvck1pZGRsZXdhcmVgIG1pZGRsZXdhcmVcclxuICByZXR1cm4gbmV4dChlcnJvcik7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciByZXR1cm5pbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICovXHJcbmZ1bmN0aW9uIHJldHVybkVycm9yTWlkZGxld2FyZShlcnJvciwgcmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICAvLyBHYXRoZXIgYWxsIHJlcXVpZWQgaW5mb3JtYXRpb24gZm9yIHRoZSByZXNwb25zZVxyXG4gIGNvbnN0IHsgbWVzc2FnZSwgc3RhY2sgfSA9IGVycm9yO1xyXG5cclxuICAvLyBVc2UgdGhlIGVycm9yJ3Mgc3RhdHVzIGNvZGUgb3IgdGhlIGRlZmF1bHQgNDAwXHJcbiAgY29uc3Qgc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGUgfHwgNDAwO1xyXG5cclxuICAvLyBTZXQgYW5kIHJldHVybiByZXNwb25zZVxyXG4gIHJlc3BvbnNlLnN0YXR1cyhzdGF0dXNDb2RlKS5qc29uKHsgc3RhdHVzQ29kZSwgbWVzc2FnZSwgc3RhY2sgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBlcnJvciBtaWRkbGV3YXJlcyB0byB0aGUgcGFzc2VkIGV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGVycm9yTWlkZGxld2FyZShhcHApIHtcclxuICAvLyBBZGQgbG9nIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKGxvZ0Vycm9yTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCBzZXQgc3RhdHVzIGFuZCByZXR1cm4gZXJyb3IgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UocmV0dXJuRXJyb3JNaWRkbGV3YXJlKTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWlkZGxld2FyZSBmdW5jdGlvbnMgZm9yIGNvbmZpZ3VyaW5nIGFuZCBlbmFibGluZyByYXRlXHJcbiAqIGxpbWl0aW5nIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHJhdGVMaW1pdCBmcm9tICdleHByZXNzLXJhdGUtbGltaXQnO1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIGVuYWJsaW5nIHJhdGUgbGltaXRpbmcgb24gdGhlIHNwZWNpZmllZCBFeHByZXNzIGFwcC5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSByYXRlTGltaXRpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHJhdGVMaW1pdGluZ2Agb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGNvdWxkIG5vdCBjb25maWd1cmUgYW5kIHNldFxyXG4gKiB0aGUgcmF0ZSBsaW1pdGluZyBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gcmF0ZUxpbWl0aW5nTWlkZGxld2FyZShhcHAsIHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgdGhlIHJhdGUgbGltaXRpbmcgaXMgZW5hYmxlZCBhbmQgdGhlIGFwcCBleGlzdHNcclxuICAgIGlmIChhcHAgJiYgcmF0ZUxpbWl0aW5nT3B0aW9ucy5lbmFibGUpIHtcclxuICAgICAgY29uc3QgbWVzc2FnZSA9XHJcbiAgICAgICAgJ1RvbyBtYW55IHJlcXVlc3RzLCB5b3UgaGF2ZSBiZWVuIHJhdGUgbGltaXRlZC4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4nO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBmb3IgdGhlIHJhdGUgbGltaXRlclxyXG4gICAgICBjb25zdCByYXRlT3B0aW9ucyA9IHtcclxuICAgICAgICB3aW5kb3c6IHJhdGVMaW1pdGluZ09wdGlvbnMud2luZG93IHx8IDEsXHJcbiAgICAgICAgbWF4UmVxdWVzdHM6IHJhdGVMaW1pdGluZ09wdGlvbnMubWF4UmVxdWVzdHMgfHwgMzAsXHJcbiAgICAgICAgZGVsYXk6IHJhdGVMaW1pdGluZ09wdGlvbnMuZGVsYXkgfHwgMCxcclxuICAgICAgICB0cnVzdFByb3h5OiByYXRlTGltaXRpbmdPcHRpb25zLnRydXN0UHJveHkgfHwgZmFsc2UsXHJcbiAgICAgICAgc2tpcEtleTogcmF0ZUxpbWl0aW5nT3B0aW9ucy5za2lwS2V5IHx8IG51bGwsXHJcbiAgICAgICAgc2tpcFRva2VuOiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBUb2tlbiB8fCBudWxsXHJcbiAgICAgIH07XHJcblxyXG4gICAgICAvLyBTZXQgaWYgYmVoaW5kIGEgcHJveHlcclxuICAgICAgaWYgKHJhdGVPcHRpb25zLnRydXN0UHJveHkpIHtcclxuICAgICAgICBhcHAuZW5hYmxlKCd0cnVzdCBwcm94eScpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDcmVhdGUgYSBsaW1pdGVyXHJcbiAgICAgIGNvbnN0IGxpbWl0ZXIgPSByYXRlTGltaXQoe1xyXG4gICAgICAgIC8vIFRpbWUgZnJhbWUgZm9yIHdoaWNoIHJlcXVlc3RzIGFyZSBjaGVja2VkIGFuZCByZW1lbWJlcmVkXHJcbiAgICAgICAgd2luZG93TXM6IHJhdGVPcHRpb25zLndpbmRvdyAqIDYwICogMTAwMCxcclxuICAgICAgICAvLyBMaW1pdCBlYWNoIElQIHRvIDEwMCByZXF1ZXN0cyBwZXIgYHdpbmRvd01zYFxyXG4gICAgICAgIGxpbWl0OiByYXRlT3B0aW9ucy5tYXhSZXF1ZXN0cyxcclxuICAgICAgICAvLyBEaXNhYmxlIGRlbGF5aW5nLCBmdWxsIHNwZWVkIHVudGlsIHRoZSBtYXggbGltaXQgaXMgcmVhY2hlZFxyXG4gICAgICAgIGRlbGF5TXM6IHJhdGVPcHRpb25zLmRlbGF5LFxyXG4gICAgICAgIGhhbmRsZXI6IChyZXF1ZXN0LCByZXNwb25zZSkgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2UuZm9ybWF0KHtcclxuICAgICAgICAgICAganNvbjogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQoeyBtZXNzYWdlIH0pO1xyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICBkZWZhdWx0OiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZChtZXNzYWdlKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSk7XHJcbiAgICAgICAgfSxcclxuICAgICAgICBza2lwOiAocmVxdWVzdCkgPT4ge1xyXG4gICAgICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcclxuICAgICAgICAgIGlmIChcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcEtleSAhPT0gbnVsbCAmJlxyXG4gICAgICAgICAgICByYXRlT3B0aW9ucy5za2lwVG9rZW4gIT09IG51bGwgJiZcclxuICAgICAgICAgICAgcmVxdWVzdC5xdWVyeS5rZXkgPT09IHJhdGVPcHRpb25zLnNraXBLZXkgJiZcclxuICAgICAgICAgICAgcmVxdWVzdC5xdWVyeS5hY2Nlc3NfdG9rZW4gPT09IHJhdGVPcHRpb25zLnNraXBUb2tlblxyXG4gICAgICAgICAgKSB7XHJcbiAgICAgICAgICAgIGxvZyg0LCAnW3JhdGUgbGltaXRpbmddIFNraXBwaW5nIHJhdGUgbGltaXRlci4nKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIC8vIFVzZSBhIGxpbWl0ZXIgYXMgYSBtaWRkbGV3YXJlXHJcbiAgICAgIGFwcC51c2UobGltaXRlcik7XHJcblxyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3JhdGUgbGltaXRpbmddIEVuYWJsZWQgcmF0ZSBsaW1pdGluZyB3aXRoICR7cmF0ZU9wdGlvbnMubWF4UmVxdWVzdHN9IHJlcXVlc3RzIHBlciAke3JhdGVPcHRpb25zLndpbmRvd30gbWludXRlIGZvciBlYWNoIElQLCB0cnVzdGluZyBwcm94eTogJHtyYXRlT3B0aW9ucy50cnVzdFByb3h5fS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tyYXRlIGxpbWl0aW5nXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWlkZGxld2FyZSBmdW5jdGlvbnMgZm9yIHZhbGlkYXRpbmcgaW5jb21pbmcgSFRUUCByZXF1ZXN0c1xyXG4gKiBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLiBUaGlzIG1vZHVsZSBlbnN1cmVzIHRoYXQgcmVxdWVzdHMgY29udGFpblxyXG4gKiBhcHByb3ByaWF0ZSBjb250ZW50IHR5cGVzIGFuZCB2YWxpZCByZXF1ZXN0IGJvZGllcywgaW5jbHVkaW5nIHByb3BlciBKU09OXHJcbiAqIHN0cnVjdHVyZXMgYW5kIGNoYXJ0IGRhdGEgZm9yIGV4cG9ydHMuIEl0IGNoZWNrcyBmb3IgcG90ZW50aWFsIGlzc3VlcyBzdWNoXHJcbiAqIGFzIG1pc3Npbmcgb3IgbWFsZm9ybWVkIGRhdGEsIHByaXZhdGUgcmFuZ2UgVVJMcyBpbiBTVkcgcGF5bG9hZHMsIGFuZCBhbGxvd3NcclxuICogZm9yIGZsZXhpYmxlIG9wdGlvbnMgdmFsaWRhdGlvbi4gVGhlIG1pZGRsZXdhcmUgbG9ncyBkZXRhaWxlZCBpbmZvcm1hdGlvblxyXG4gKiBhbmQgaGFuZGxlcyBlcnJvcnMgcmVsYXRlZCB0byBpbmNvcnJlY3QgcGF5bG9hZHMsIGNoYXJ0IGRhdGEsIGFuZCBwcml2YXRlIFVSTFxyXG4gKiB1c2FnZS5cclxuICovXHJcblxyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBbGxvd0NvZGVFeGVjdXRpb24gfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGlzQWxsb3dlZENvbmZpZyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGlzT2JqZWN0RW1wdHksIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciB2YWxpZGF0aW5nIHRoZSBjb250ZW50LXR5cGUgaGVhZGVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY29udGVudFR5cGVNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGNvbnRlbnQtdHlwZVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICovXHJcbmZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZShyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIGNvbnRlbnQgdHlwZSBoZWFkZXJcclxuICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gcmVxdWVzdC5oZWFkZXJzWydjb250ZW50LXR5cGUnXSB8fCAnJztcclxuXHJcbiAgICAvLyBBbGxvdyBvbmx5IEpTT04sIFVSTC1lbmNvZGVkIGFuZCBmb3JtIGRhdGEgd2l0aG91dCBmaWxlcyB0eXBlcyBvZiBkYXRhXHJcbiAgICBpZiAoXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24vanNvbicpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJykgJiZcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdtdWx0aXBhcnQvZm9ybS1kYXRhJylcclxuICAgICkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1t2YWxpZGF0aW9uXSBDb250ZW50LVR5cGUgbXVzdCBiZSBhcHBsaWNhdGlvbi9qc29uLCBhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQsIG9yIG11bHRpcGFydC9mb3JtLWRhdGEuJyxcclxuICAgICAgICA0MTVcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDYWxsIHRoZSBgcmVxdWVzdEJvZHlNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgICByZXR1cm4gbmV4dCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgcmVxdWVzdCdzIGJvZHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9IFRoZSBjYWxsIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgYm9keSBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBjaGFydCBkYXRhIGZyb20gdGhlIGJvZHlcclxuICogaXMgbm90IGNvcnJlY3QuXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpbiBjYXNlIG9mIHRoZSBwcml2YXRlIHJhbmdlXHJcbiAqIHVybCBlcnJvci5cclxuICovXHJcbmZ1bmN0aW9uIHJlcXVlc3RCb2R5TWlkZGxld2FyZShyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3QgYm9keVxyXG4gICAgY29uc3QgYm9keSA9IHJlcXVlc3QuYm9keTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSB1bmlxdWUgSUQgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgcmVxdWVzdElkID0gdXVpZCgpO1xyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRoZXJlIGlzIG5vIGNvcnJlY3QgYm9keVxyXG4gICAgaWYgKCFib2R5IHx8IGlzT2JqZWN0RW1wdHkoYm9keSkpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fCByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcGF5bG9hZCBpcyBlbXB0eS5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgYm9keSBpcyByZXF1aXJlZC4gUGxlYXNlIGVuc3VyZSB0aGF0IHlvdXIgQ29udGVudC1UeXBlIGhlYWRlciBpcyBjb3JyZWN0LiBBY2NlcHRlZCB0eXBlcyBhcmUgJ2FwcGxpY2F0aW9uL2pzb24nIGFuZCAnbXVsdGlwYXJ0L2Zvcm0tZGF0YScuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBvcHRpb24gZm9yIHRoZSBzZXJ2ZXJcclxuICAgIGNvbnN0IGFsbG93Q29kZUV4ZWN1dGlvbiA9IGdldEFsbG93Q29kZUV4ZWN1dGlvbigpO1xyXG5cclxuICAgIC8vIEZpbmQgYSBjb3JyZWN0IGNoYXJ0IG9wdGlvbnNcclxuICAgIGNvbnN0IGluc3RyID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAvLyBVc2Ugb25lIG9mIHRoZSBiZWxvd1xyXG4gICAgICBib2R5Lmluc3RyIHx8IGJvZHkub3B0aW9ucyB8fCBib2R5LmluZmlsZSB8fCBib2R5LmRhdGEsXHJcbiAgICAgIC8vIFN0cmluZ2lmeSBvcHRpb25zXHJcbiAgICAgIHRydWUsXHJcbiAgICAgIC8vIEFsbG93IG9yIGRpc2FsbG93IGZ1bmN0aW9uc1xyXG4gICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICk7XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgaXMgbm8gY29ycmVjdCBjaGFydCBkYXRhXHJcbiAgICBpZiAoaW5zdHIgPT09IG51bGwgJiYgIWJvZHkuc3ZnKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgbWlzc2luZyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm9yIGV4cG9ydDogJHtKU09OLnN0cmluZ2lmeShib2R5KX0uYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIE5vIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3VuZC4gRW5zdXJlIHRoYXQgeW91IGFyZSB1c2luZyBlaXRoZXIgYXBwbGljYXRpb24vanNvbiBvciBtdWx0aXBhcnQvZm9ybS1kYXRhIGhlYWRlcnMuIElmIHNlbmRpbmcgSlNPTiwgbWFrZSBzdXJlIHRoZSBjaGFydCBkYXRhIGlzIGluIHRoZSAnaW5maWxlJywgJ29wdGlvbnMnLCBvciAnZGF0YScgYXR0cmlidXRlLiBJZiBzZW5kaW5nIFNWRywgZW5zdXJlIGl0IGlzIGluIHRoZSAnc3ZnJyBhdHRyaWJ1dGUuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0ZXN0IG9mIHhsaW5rOmhyZWYgZWxlbWVudHMgZnJvbSBwYXlsb2FkJ3MgU1ZHIGZhaWxzXHJcbiAgICBpZiAoYm9keS5zdmcgJiYgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChib2R5LnN2ZykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFNWRyBwb3RlbnRpYWxseSBjb250YWluIGF0IGxlYXN0IG9uZSBmb3JiaWRkZW4gVVJMIGluICd4bGluazpocmVmJyBlbGVtZW50LiBQbGVhc2UgcmV2aWV3IHRoZSBTVkcgY29udGVudCBhbmQgZW5zdXJlIHRoYXQgYWxsIHJlZmVyZW5jZWQgVVJMcyBjb21wbHkgd2l0aCBzZWN1cml0eSBwb2xpY2llcy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgcmVxdWVzdCBvcHRpb25zIGFuZCBzdG9yZSBwYXJzZWQgc3RydWN0dXJlIGluIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnMgPSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY3JlYXRlZCBJRCBhcyBhIGByZXF1ZXN0SWRgIHByb3BlcnR5IGluIHRoZSBvcHRpb25zXHJcbiAgICAgIHJlcXVlc3RJZCxcclxuICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgaW5zdHIsXHJcbiAgICAgICAgc3ZnOiBib2R5LnN2ZyxcclxuICAgICAgICBvdXRmaWxlOlxyXG4gICAgICAgICAgYm9keS5vdXRmaWxlIHx8XHJcbiAgICAgICAgICBgJHtyZXF1ZXN0LnBhcmFtcy5maWxlbmFtZSB8fCAnY2hhcnQnfS4ke2JvZHkudHlwZSB8fCAncG5nJ31gLFxyXG4gICAgICAgIHR5cGU6IGJvZHkudHlwZSxcclxuICAgICAgICBjb25zdHI6IGJvZHkuY29uc3RyLFxyXG4gICAgICAgIGI2NDogYm9keS5iNjQsXHJcbiAgICAgICAgbm9Eb3dubG9hZDogYm9keS5ub0Rvd25sb2FkLFxyXG4gICAgICAgIGhlaWdodDogYm9keS5oZWlnaHQsXHJcbiAgICAgICAgd2lkdGg6IGJvZHkud2lkdGgsXHJcbiAgICAgICAgc2NhbGU6IGJvZHkuc2NhbGUsXHJcbiAgICAgICAgZ2xvYmFsT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS5nbG9iYWxPcHRpb25zLFxyXG4gICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICksXHJcbiAgICAgICAgdGhlbWVPcHRpb25zOiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICBib2R5LnRoZW1lT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApXHJcbiAgICAgIH0sXHJcbiAgICAgIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gICAgICAgIGFsbG93RmlsZVJlc291cmNlczogZmFsc2UsXHJcbiAgICAgICAgY3VzdG9tQ29kZTogYm9keS5jdXN0b21Db2RlLFxyXG4gICAgICAgIGNhbGxiYWNrOiBib2R5LmNhbGxiYWNrLFxyXG4gICAgICAgIHJlc291cmNlczogaXNBbGxvd2VkQ29uZmlnKGJvZHkucmVzb3VyY2VzLCB0cnVlLCBhbGxvd0NvZGVFeGVjdXRpb24pXHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgbmV4dCBtaWRkbGV3YXJlXHJcbiAgICByZXR1cm4gbmV4dCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlcyB0byB0aGUgcGFzc2VkIGV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCkge1xyXG4gIC8vIEFkZCBjb250ZW50IHR5cGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgYXBwLnBvc3QoWycvJywgJy86ZmlsZW5hbWUnXSwgY29udGVudFR5cGVNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHJlcXVlc3QgYm9keSByZXF1ZXN0IHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIHJlcXVlc3RCb2R5TWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgdGhlIGV4cG9ydCByb3V0ZXMgYW5kIGxvZ2ljIGZvciBoYW5kbGluZyBjaGFydCBleHBvcnRcclxuICogcmVxdWVzdHMgaW4gYW4gRXhwcmVzcyBzZXJ2ZXIuIFRoaXMgbW9kdWxlIHByb2Nlc3NlcyBpbmNvbWluZyByZXF1ZXN0c1xyXG4gKiB0byBleHBvcnQgY2hhcnRzIGluIHZhcmlvdXMgZm9ybWF0cyAoZS5nLiBKUEVHLCBQTkcsIFBERiwgU1ZHKS4gSXQgaW50ZWdyYXRlc1xyXG4gKiB3aXRoIEhpZ2hjaGFydHMnIGNvcmUgZnVuY3Rpb25hbGl0aWVzIGFuZCBzdXBwb3J0cyBib3RoIGltbWVkaWF0ZSBkb3dubG9hZFxyXG4gKiByZXNwb25zZXMgYW5kIEJhc2U2NC1lbmNvZGVkIGNvbnRlbnQgcmV0dXJucy4gVGhlIGNvZGUgYWxzbyBmZWF0dXJlc1xyXG4gKiBiZW5jaG1hcmtpbmcgZm9yIHBlcmZvcm1hbmNlIG1vbml0b3JpbmcuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgc3RhcnRFeHBvcnQgfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldEJhc2U2NCwgbWVhc3VyZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFJldmVyc2VkIE1JTUUgdHlwZXNcclxuY29uc3QgcmV2ZXJzZWRNaW1lID0ge1xyXG4gIHBuZzogJ2ltYWdlL3BuZycsXHJcbiAganBlZzogJ2ltYWdlL2pwZWcnLFxyXG4gIGdpZjogJ2ltYWdlL2dpZicsXHJcbiAgcGRmOiAnYXBwbGljYXRpb24vcGRmJyxcclxuICBzdmc6ICdpbWFnZS9zdmcreG1sJ1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4cG9ydCByZXF1ZXN0cyBmcm9tIHRoZSBjbGllbnQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAqIGlzIGNvbXBsZXRlLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gcmVxdWVzdEV4cG9ydChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBTdGFydCBjb3VudGluZyB0aW1lIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBJbiBjYXNlIHRoZSBjb25uZWN0aW9uIGlzIGNsb3NlZCwgZm9yY2UgdG8gYWJvcnQgZnVydGhlciBhY3Rpb25zXHJcbiAgICBsZXQgY29ubmVjdGlvbkFib3J0ZWQgPSBmYWxzZTtcclxuICAgIHJlcXVlc3Quc29ja2V0Lm9uKCdjbG9zZScsIChoYWRFcnJvcnMpID0+IHtcclxuICAgICAgaWYgKGhhZEVycm9ycykge1xyXG4gICAgICAgIGNvbm5lY3Rpb25BYm9ydGVkID0gdHJ1ZTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBvcHRpb25zIHByZXZpb3VzbHkgdmFsaWRhdGVkIGluIHRoZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICAgIGNvbnN0IG9wdGlvbnMgPSByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGlkXHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSBvcHRpb25zLnJlcXVlc3RJZDtcclxuXHJcbiAgICAvLyBJbmZvIGFib3V0IGFuIGluY29taW5nIHJlcXVlc3Qgd2l0aCBjb3JyZWN0IGRhdGFcclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3QuYCk7XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChvcHRpb25zLCAoZXJyb3IsIGRhdGEpID0+IHtcclxuICAgICAgLy8gUmVtb3ZlIHRoZSBjbG9zZSBldmVudCBmcm9tIHRoZSBzb2NrZXRcclxuICAgICAgcmVxdWVzdC5zb2NrZXQucmVtb3ZlQWxsTGlzdGVuZXJzKCdjbG9zZScpO1xyXG5cclxuICAgICAgLy8gSWYgdGhlIGNvbm5lY3Rpb24gd2FzIGNsb3NlZCwgZG8gbm90aGluZ1xyXG4gICAgICBpZiAoY29ubmVjdGlvbkFib3J0ZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgY2xpZW50IGNsb3NlZCB0aGUgY29ubmVjdGlvbiBiZWZvcmUgdGhlIGNoYXJ0IGZpbmlzaGVkIHByb2Nlc3NpbmcuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBlcnJvciwgbG9nIGl0IGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBkYXRhIGlzIG1pc3NpbmcsIGxvZyB0aGUgbWVzc2FnZSBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoIWRhdGEgfHwgIWRhdGEucmVzdWx0KSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gUmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHxcclxuICAgICAgICAgICAgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcmVzdWx0IGlzICR7ZGF0YS5yZXN1bHR9LmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFVuZXhwZWN0ZWQgcmV0dXJuIG9mIHRoZSBleHBvcnQgcmVzdWx0IGZyb20gdGhlIGNoYXJ0IGdlbmVyYXRpb24uIFBsZWFzZSBjaGVjayB5b3VyIHJlcXVlc3QgZGF0YS5gLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gUmV0dXJuIHRoZSByZXN1bHQgaW4gYW4gYXBwcm9wcmlhdGUgZm9ybWF0XHJcbiAgICAgIGlmIChkYXRhLnJlc3VsdCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSB3aG9sZSBleHBvcnRpbmcgcHJvY2VzcyB0b29rICR7cmVxdWVzdENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBgdHlwZWAsIGBiNjRgLCBgbm9Eb3dubG9hZGAsIGFuZCBgb3V0ZmlsZWAgZnJvbSBvcHRpb25zXHJcbiAgICAgICAgY29uc3QgeyB0eXBlLCBiNjQsIG5vRG93bmxvYWQsIG91dGZpbGUgfSA9IGRhdGEub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgICAgIC8vIElmIG9ubHkgQmFzZTY0IGlzIHJlcXVpcmVkLCByZXR1cm4gaXRcclxuICAgICAgICBpZiAoYjY0KSB7XHJcbiAgICAgICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChnZXRCYXNlNjQoZGF0YS5yZXN1bHQsIHR5cGUpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFNldCBjb3JyZWN0IGNvbnRlbnQgdHlwZVxyXG4gICAgICAgIHJlc3BvbnNlLmhlYWRlcignQ29udGVudC1UeXBlJywgcmV2ZXJzZWRNaW1lW3R5cGVdIHx8ICdpbWFnZS9wbmcnKTtcclxuXHJcbiAgICAgICAgLy8gRGVjaWRlIHdoZXRoZXIgdG8gZG93bmxvYWQgb3Igbm90IGNoYXJ0IGZpbGVcclxuICAgICAgICBpZiAoIW5vRG93bmxvYWQpIHtcclxuICAgICAgICAgIHJlc3BvbnNlLmF0dGFjaG1lbnQob3V0ZmlsZSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBJZiBTVkcsIHJldHVybiBwbGFpbiBjb250ZW50LCBvdGhlcndpc2UgYSBiNjQgc3RyaW5nIGZyb20gYSBidWZmZXJcclxuICAgICAgICByZXR1cm4gdHlwZSA9PT0gJ3N2ZydcclxuICAgICAgICAgID8gcmVzcG9uc2Uuc2VuZChkYXRhLnJlc3VsdClcclxuICAgICAgICAgIDogcmVzcG9uc2Uuc2VuZChCdWZmZXIuZnJvbShkYXRhLnJlc3VsdCwgJ2Jhc2U2NCcpKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgZXhwb3J0YCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHBvcnRSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBleHBvcnRSb3V0ZXMoYXBwKSB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnLycgLSBBIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIGF0IHRoZSByb290XHJcbiAgICogZW5kcG9pbnQuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy8nLCByZXF1ZXN0RXhwb3J0KTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnLzpmaWxlbmFtZScgLSBBIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIHdpdGhcclxuICAgKiBhIHNwZWNpZmllZCBmaWxlbmFtZSBwYXJhbWV0ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy86ZmlsZW5hbWUnLCByZXF1ZXN0RXhwb3J0KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciBzZXJ2ZXIgaGVhbHRoIG1vbml0b3JpbmcsIGluY2x1ZGluZ1xyXG4gKiB1cHRpbWUsIHN1Y2Nlc3MgcmF0ZXMsIGFuZCBvdGhlciBzZXJ2ZXIgc3RhdGlzdGljcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldEhpZ2hjaGFydHNWZXJzaW9uIH0gZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRQb29sU3RhdHMsIGdldFBvb2xJbmZvSlNPTiB9IGZyb20gJy4uLy4uL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBhZGRUaW1lciB9IGZyb20gJy4uLy4uL3RpbWVyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lLCBnZXROZXdEYXRlVGltZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbi8vIFNldCB0aGUgc3RhcnQgZGF0ZSBvZiB0aGUgc2VydmVyXHJcbmNvbnN0IHNlcnZlclN0YXJ0VGltZSA9IG5ldyBEYXRlKCk7XHJcblxyXG4vLyBHZXQgdGhlIGBwYWNrYWdlLmpzb25gIGNvbnRlbnRcclxuY29uc3QgcGFja2FnZUZpbGUgPSBKU09OLnBhcnNlKFxyXG4gIHJlYWRGaWxlU3luYyhqb2luKF9fZGlybmFtZSwgJ3BhY2thZ2UuanNvbicpLCAndXRmOCcpXHJcbik7XHJcblxyXG4vLyBBbiBhcnJheSBmb3Igc3VjY2VzcyByYXRlIHJhdGlvc1xyXG5jb25zdCBzdWNjZXNzUmF0ZXMgPSBbXTtcclxuXHJcbi8vIFJlY29yZCBldmVyeSBtaW51dGVcclxuY29uc3QgcmVjb3JkSW50ZXJ2YWwgPSA2MCAqIDEwMDA7XHJcblxyXG4vLyAzMCBtaW51dGVzXHJcbmNvbnN0IHdpbmRvd1NpemUgPSAzMDtcclxuXHJcbi8qKlxyXG4gKiBDYWxjdWxhdGVzIG1vdmluZyBhdmVyYWdlIGluZGljYXRvciBiYXNlZCBvbiB0aGUgZGF0YSBmcm9tIHRoZSBgc3VjY2Vzc1JhdGVzYFxyXG4gKiBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtudW1iZXJ9IEEgbW92aW5nIGF2ZXJhZ2UgZm9yIHN1Y2Nlc3MgcmF0aW8gb2YgdGhlIHNlcnZlciBleHBvcnRzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2UoKSB7XHJcbiAgcmV0dXJuIHN1Y2Nlc3NSYXRlcy5yZWR1Y2UoKGEsIGIpID0+IGEgKyBiLCAwKSAvIHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgdGhlIGludGVydmFsIHJlc3BvbnNpYmxlIGZvciBjYWxjdWxhdGluZyBjdXJyZW50IHN1Y2Nlc3MgcmF0ZSByYXRpb1xyXG4gKiBhbmQgY29sbGVjdHMgcmVjb3JkcyB0byB0aGUgYHN1Y2Nlc3NSYXRlc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfc3RhcnRTdWNjZXNzUmF0ZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Tm9kZUpTLlRpbWVvdXR9IElkIG9mIGFuIGludGVydmFsLlxyXG4gKi9cclxuZnVuY3Rpb24gX3N0YXJ0U3VjY2Vzc1JhdGUoKSB7XHJcbiAgcmV0dXJuIHNldEludGVydmFsKCgpID0+IHtcclxuICAgIGNvbnN0IHN0YXRzID0gZ2V0UG9vbFN0YXRzKCk7XHJcbiAgICBjb25zdCBzdWNjZXNzUmF0aW8gPVxyXG4gICAgICBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkID09PSAwXHJcbiAgICAgICAgPyAxXHJcbiAgICAgICAgOiAoc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCAvIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQpICogMTAwO1xyXG5cclxuICAgIHN1Y2Nlc3NSYXRlcy5wdXNoKHN1Y2Nlc3NSYXRpbyk7XHJcbiAgICBpZiAoc3VjY2Vzc1JhdGVzLmxlbmd0aCA+IHdpbmRvd1NpemUpIHtcclxuICAgICAgc3VjY2Vzc1JhdGVzLnNoaWZ0KCk7XHJcbiAgICB9XHJcbiAgfSwgcmVjb3JkSW50ZXJ2YWwpO1xyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgYGhlYWx0aGAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gaGVhbHRoUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gaGVhbHRoUm91dGVzKGFwcCkge1xyXG4gIC8vIFN0YXJ0IHByb2Nlc3Npbmcgc3VjY2VzcyByYXRlIHJhdGlvIGludGVydmFsIGFuZCBzYXZlIGl0cyBpZCB0byB0aGUgYXJyYXlcclxuICAvLyBmb3IgdGhlIGdyYWNlZnVsIGNsZWFyaW5nIG9uIHNodXRkb3duIHdpdGggaW5qZWN0ZWQgYGFkZFRpbWVyYCBmdW50aW9uXHJcbiAgYWRkVGltZXIoX3N0YXJ0U3VjY2Vzc1JhdGUoKSk7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIEdFVCAnL2hlYWx0aCcgLSBBIHJvdXRlIGZvciBnZXR0aW5nIHRoZSBiYXNpYyBzdGF0cyBvZiB0aGUgc2VydmVyLlxyXG4gICAqL1xyXG4gIGFwcC5nZXQoJy9oZWFsdGgnLCAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW2hlYWx0aF0gUmV0dXJuaW5nIHNlcnZlciBoZWFsdGguJyk7XHJcblxyXG4gICAgICBjb25zdCBzdGF0cyA9IGdldFBvb2xTdGF0cygpO1xyXG4gICAgICBjb25zdCBwZXJpb2QgPSBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG4gICAgICBjb25zdCBtb3ZpbmdBdmVyYWdlID0gX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2UoKTtcclxuXHJcbiAgICAgIC8vIFNlbmQgdGhlIHNlcnZlcidzIHN0YXRpc3RpY3NcclxuICAgICAgcmVzcG9uc2Uuc2VuZCh7XHJcbiAgICAgICAgLy8gU3RhdHVzIGFuZCB0aW1lc1xyXG4gICAgICAgIHN0YXR1czogJ09LJyxcclxuICAgICAgICBib290VGltZTogc2VydmVyU3RhcnRUaW1lLFxyXG4gICAgICAgIHVwdGltZTogYCR7TWF0aC5mbG9vcigoZ2V0TmV3RGF0ZVRpbWUoKSAtIHNlcnZlclN0YXJ0VGltZS5nZXRUaW1lKCkpIC8gMTAwMCAvIDYwKX0gbWludXRlc2AsXHJcblxyXG4gICAgICAgIC8vIFZlcnNpb25zXHJcbiAgICAgICAgc2VydmVyVmVyc2lvbjogcGFja2FnZUZpbGUudmVyc2lvbixcclxuICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuXHJcbiAgICAgICAgLy8gRXhwb3J0c1xyXG4gICAgICAgIGF2ZXJhZ2VFeHBvcnRUaW1lOiBzdGF0cy50aW1lU3BlbnRBdmVyYWdlLFxyXG4gICAgICAgIGF0dGVtcHRlZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQsXHJcbiAgICAgICAgcGVyZm9ybWVkRXhwb3J0czogc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCxcclxuICAgICAgICBmYWlsZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzRHJvcHBlZCxcclxuICAgICAgICBzdWNlc3NSYXRpbzogKHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQgLyBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkKSAqIDEwMCxcclxuXHJcbiAgICAgICAgLy8gUG9vbFxyXG4gICAgICAgIHBvb2w6IGdldFBvb2xJbmZvSlNPTigpLFxyXG5cclxuICAgICAgICAvLyBNb3ZpbmcgYXZlcmFnZVxyXG4gICAgICAgIHBlcmlvZCxcclxuICAgICAgICBtb3ZpbmdBdmVyYWdlLFxyXG4gICAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgICBpc05hTihtb3ZpbmdBdmVyYWdlKSB8fCAhc3VjY2Vzc1JhdGVzLmxlbmd0aFxyXG4gICAgICAgICAgICA/ICdUb28gZWFybHkgdG8gcmVwb3J0LiBObyBleHBvcnRzIG1hZGUgeWV0LiBQbGVhc2UgY2hlY2sgYmFjayBzb29uLidcclxuICAgICAgICAgICAgOiBgTGFzdCAke3BlcmlvZH0gbWludXRlcyBoYWQgYSBzdWNjZXNzIHJhdGUgb2YgJHttb3ZpbmdBdmVyYWdlLnRvRml4ZWQoMil9JS5gLFxyXG5cclxuICAgICAgICAvLyBTVkcgYW5kIEpTT04gZXhwb3J0c1xyXG4gICAgICAgIHN2Z0V4cG9ydHM6IHN0YXRzLmV4cG9ydHNGcm9tU3ZnLFxyXG4gICAgICAgIGpzb25FeHBvcnRzOiBzdGF0cy5leHBvcnRzRnJvbU9wdGlvbnMsXHJcbiAgICAgICAgc3ZnRXhwb3J0c0F0dGVtcHRzOiBzdGF0cy5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzLFxyXG4gICAgICAgIGpzb25FeHBvcnRzQXR0ZW1wdHM6IHN0YXRzLmV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzXHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBEZWZpbmVzIGFuIEV4cHJlc3Mgcm91dGUgZm9yIHNlcnZpbmcgdGhlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlclxyXG4gKiB3aGVuIGVuYWJsZWQuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgdWlgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIHVpUm91dGVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gdWlSb3V0ZXMoYXBwKSB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgR0VUICcvJyAtIEEgcm91dGUgZm9yIGEgVUkgd2hlbiBlbmFibGVkIG9uIHRoZSBleHBvcnQgc2VydmVyLlxyXG4gICAqL1xyXG4gIGFwcC5nZXQoZ2V0T3B0aW9ucygpLnVpLnJvdXRlIHx8ICcvJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1t1aV0gUmV0dXJuaW5nIFVJIGZvciB0aGUgZXhwb3J0LicpO1xyXG5cclxuICAgICAgcmVzcG9uc2Uuc2VuZEZpbGUoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnLCAnaW5kZXguaHRtbCcpLCB7XHJcbiAgICAgICAgYWNjZXB0UmFuZ2VzOiBmYWxzZVxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICAgIH1cclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciB1cGRhdGluZyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uXHJcbiAqIG9uIHRoZSBzZXJ2ZXIsIHdpdGggYXV0aGVudGljYXRpb24gYW5kIHZhbGlkYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZ2V0SGlnaGNoYXJ0c1ZlcnNpb24sIHVwZGF0ZUhpZ2hjaGFydHNWZXJzaW9uIH0gZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi4vLi4vZW52cy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgdmVyc2lvbl9jaGFuZ2VgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIHZlcnNpb25DaGFuZ2VSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicgLSBBIHJvdXRlIGZvciBjaGFuZ2luZ1xyXG4gICAqIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gb24gdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnL3ZlcnNpb25fY2hhbmdlLzpuZXdWZXJzaW9uJywgYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1t2ZXJzaW9uXSBDaGFuZ2luZyBIaWdoY2hhcnRzIHZlcnNpb24uJyk7XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIHRva2VuIGRpcmVjdGx5IGZyb20gZW52c1xyXG4gICAgICBjb25zdCBhZG1pblRva2VuID0gZW52cy5ISUdIQ0hBUlRTX0FETUlOX1RPS0VOO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGV4aXN0ZW5jZSBvZiB0aGUgdG9rZW5cclxuICAgICAgaWYgKCFhZG1pblRva2VuIHx8ICFhZG1pblRva2VuLmxlbmd0aCkge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbdmVyc2lvbl0gVGhlIHNlcnZlciBpcyBub3QgY29uZmlndXJlZCB0byBwZXJmb3JtIHJ1bi10aW1lIHZlcnNpb24gY2hhbmdlczogYEhJR0hDSEFSVFNfQURNSU5fVE9LRU5gIGlzIG5vdCBzZXQuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZnJvbSB0aGUgaGMtYXV0aCBoZWFkZXJcclxuICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXHJcbiAgICAgIGlmICghdG9rZW4gfHwgdG9rZW4gIT09IGFkbWluVG9rZW4pIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIEludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgbmV3IHZlcnNpb24gZnJvbSB0aGUgcGFyYW1zXHJcbiAgICAgIGNvbnN0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG5cclxuICAgICAgLy8gVXBkYXRlIHZlcnNpb25cclxuICAgICAgaWYgKG5ld1ZlcnNpb24pIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgYXdhaXQgdXBkYXRlSGlnaGNoYXJ0c1ZlcnNpb24obmV3VmVyc2lvbik7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgYFt2ZXJzaW9uXSBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgIDQwMFxyXG4gICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICBoaWdoY2hhcnRzVmVyc2lvbjogZ2V0SGlnaGNoYXJ0c1ZlcnNpb24oKSxcclxuICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbdmVyc2lvbl0gTm8gbmV3IHZlcnNpb24gc3VwcGxpZWQuJywgNDAwKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSB0aGF0IHNldHMgdXAgYW5kIG1hbmFnZXMgSFRUUCBhbmQgSFRUUFMgc2VydmVyc1xyXG4gKiBmb3IgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci4gSXQgaGFuZGxlcyBzZXJ2ZXIgaW5pdGlhbGl6YXRpb24sXHJcbiAqIGNvbmZpZ3VyYXRpb24sIGVycm9yIGhhbmRsaW5nLCBtaWRkbGV3YXJlIHNldHVwLCByb3V0ZSBkZWZpbml0aW9uLCBhbmQgcmF0ZVxyXG4gKiBsaW1pdGluZy4gVGhlIG1vZHVsZSBleHBvcnRzIGZ1bmN0aW9ucyB0byBzdGFydCwgc3RvcCwgYW5kIG1hbmFnZSBzZXJ2ZXJcclxuICogaW5zdGFuY2VzLCBhcyB3ZWxsIGFzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBkZWZpbmluZyByb3V0ZXMgYW5kIGF0dGFjaGluZ1xyXG4gKiBtaWRkbGV3YXJlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBjb3JzIGZyb20gJ2NvcnMnO1xyXG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcbmltcG9ydCBtdWx0ZXIgZnJvbSAnbXVsdGVyJztcclxuXHJcbmltcG9ydCB7IHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IGVycm9yTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL2Vycm9yLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdGluZ01pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy9yYXRlTGltaXRpbmcuanMnO1xyXG5pbXBvcnQgdmFsaWRhdGlvbk1pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzJztcclxuXHJcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcclxuaW1wb3J0IGhlYWx0aFJvdXRlcyBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xyXG5pbXBvcnQgdmVyc2lvbkNoYW5nZVJvdXRlcyBmcm9tICcuL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcclxuY29uc3QgYWN0aXZlU2VydmVycyA9IG5ldyBNYXAoKTtcclxuXHJcbi8vIENyZWF0ZSBleHByZXNzIGFwcFxyXG5jb25zdCBhcHAgPSBleHByZXNzKCk7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIEhUVFAgYW5kL29yIEhUVFBTIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi5cclxuICogVGhlIGBzZXJ2ZXJPcHRpb25zYCBvYmplY3QgY29udGFpbnMgc2VydmVyLXJlbGF0ZWQgcHJvcGVydGllcyAocmVmZXJcclxuICogdG8gdGhlIGBzZXJ2ZXJgIHNlY3Rpb24gaW4gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgZGV0YWlscykuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRTZXJ2ZXJcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlck9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIHRoZSBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBkZWZhdWx0XHJcbiAqIHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGVpdGhlclxyXG4gKiBub3QgZW5hYmxlZCBvciBubyB2YWxpZCBFeHByZXNzIGFwcCBpcyBmb3VuZCwgc2lnbmFsaW5nIHRoZSBlbmQgb2YgdGhlXHJcbiAqIGZ1bmN0aW9uJ3MgZXhlY3V0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIHNlcnZlciBjYW5ub3RcclxuICogYmUgY29uZmlndXJlZCBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFNlcnZlcihzZXJ2ZXJPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgc2VydmVyOiBzZXJ2ZXJPcHRpb25zXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBVc2UgdmFsaWRhdGVkIG9wdGlvbnNcclxuICAgIHNlcnZlck9wdGlvbnMgPSBvcHRpb25zLnNlcnZlcjtcclxuXHJcbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXHJcbiAgICBpZiAoIXNlcnZlck9wdGlvbnMuZW5hYmxlIHx8ICFhcHApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbc2VydmVyXSBTZXJ2ZXIgY2Fubm90IGJlIHN0YXJ0ZWQgKG5vdCBlbmFibGVkIG9yIG5vIGNvcnJlY3QgRXhwcmVzcyBhcHAgZm91bmQpLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVG9vIGJpZyBsaW1pdHMgbGVhZCB0byB0aW1lb3V0cyBpbiB0aGUgZXhwb3J0IHByb2Nlc3Mgd2hlblxyXG4gICAgLy8gdGhlIHJhc3Rlcml6YXRpb24gdGltZW91dCBpcyBzZXQgdG9vIGxvd1xyXG4gICAgY29uc3QgdXBsb2FkTGltaXRCeXRlcyA9IHNlcnZlck9wdGlvbnMudXBsb2FkTGltaXQgKiAxMDI0ICogMTAyNDtcclxuXHJcbiAgICAvLyBNZW1vcnkgc3RvcmFnZSBmb3IgbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHN0b3JhZ2UgPSBtdWx0ZXIubWVtb3J5U3RvcmFnZSgpO1xyXG5cclxuICAgIC8vIEVuYWJsZSBwYXJzaW5nIG9mIGZvcm0gZGF0YSAoZmlsZXMpIHdpdGggbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHVwbG9hZCA9IG11bHRlcih7XHJcbiAgICAgIHN0b3JhZ2UsXHJcbiAgICAgIGxpbWl0czoge1xyXG4gICAgICAgIGZpZWxkU2l6ZTogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXHJcbiAgICBhcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XHJcblxyXG4gICAgLy8gRW5hYmxlIENPUlMgc3VwcG9ydFxyXG4gICAgYXBwLnVzZShcclxuICAgICAgY29ycyh7XHJcbiAgICAgICAgbWV0aG9kczogWydQT1NUJywgJ0dFVCcsICdPUFRJT05TJ11cclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0dGluZyBhIGxvdCBvZiBgUmFuZ2VOb3RTYXRpc2ZpYWJsZUVycm9yYCBleGNlcHRpb25zIChldmVuIHRob3VnaCB0aGlzXHJcbiAgICAvLyBpcyBhIGRlcHJlY2F0ZWQgb3B0aW9ucywgbGV0J3MgdHJ5IHRvIHNldCBpdCB0byBmYWxzZSlcclxuICAgIGFwcC51c2UoKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgIHJlc3BvbnNlLnNldCgnQWNjZXB0LVJhbmdlcycsICdub25lJyk7XHJcbiAgICAgIG5leHQoKTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgSlNPTiBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLmpzb24oe1xyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgVVJMLWVuY29kZWQgZm9ybSBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLnVybGVuY29kZWQoe1xyXG4gICAgICAgIGV4dGVuZGVkOiB0cnVlLFxyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xyXG4gICAgYXBwLnVzZSh1cGxvYWQubm9uZSgpKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgc3RhdGljIGZvbGRlcidzIHJvdXRlXHJcbiAgICBhcHAudXNlKGV4cHJlc3Muc3RhdGljKGpvaW4oX19kaXJuYW1lLCAncHVibGljJykpKTtcclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5zc2wuZm9yY2UpIHtcclxuICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFApXHJcbiAgICAgIGNvbnN0IGh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApO1xyXG5cclxuICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIC8vIExpc3RlblxyXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnBvcnQsIHNlcnZlck9wdGlvbnMuaG9zdCwgKCkgPT4ge1xyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMucG9ydCwgaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnBvcnR9LmBcclxuICAgICAgICApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgICBpZiAoc2VydmVyT3B0aW9ucy5zc2wuZW5hYmxlKSB7XHJcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cclxuICAgICAgbGV0IGtleSwgY2VydDtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XHJcbiAgICAgICAga2V5ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmtleScpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wgY2VydGlmaWNhdGVcclxuICAgICAgICBjZXJ0ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmNydCcpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFVuYWJsZSB0byBsb2FkIGtleS9jZXJ0aWZpY2F0ZSBmcm9tIHRoZSAnJHtzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChrZXkgJiYgY2VydCkge1xyXG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcclxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xyXG5cclxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgICAgX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAvLyBMaXN0ZW5cclxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyT3B0aW9ucy5zc2wucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUFMgc2VydmVyXHJcbiAgICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAzLFxyXG4gICAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQUyBzZXJ2ZXIgb24gJHtzZXJ2ZXJPcHRpb25zLmhvc3R9OiR7c2VydmVyT3B0aW9ucy5zc2wucG9ydH0uYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgc2VydmVyT3B0aW9ucy5yYXRlTGltaXRpbmcpO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgdmFsaWRhdGlvbiBoYW5kbGVyXHJcbiAgICB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGV4cG9ydFJvdXRlcyhhcHApO1xyXG4gICAgaGVhbHRoUm91dGVzKGFwcCk7XHJcbiAgICB1aVJvdXRlcyhhcHApO1xyXG4gICAgdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgY2VudHJhbGl6ZWQgZXJyb3IgaGFuZGxlclxyXG4gICAgZXJyb3JNaWRkbGV3YXJlKGFwcCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tzZXJ2ZXJdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHN0YXJ0IHRoZSBzZXJ2ZXIuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbG9zZXMgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzKCkge1xyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBzZXJ2ZXJzIHdvcmtpbmdcclxuICBpZiAoYWN0aXZlU2VydmVycy5zaXplID4gMCkge1xyXG4gICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zaW5nIGFsbCBzZXJ2ZXJzLmApO1xyXG5cclxuICAgIC8vIENsb3NlIGVhY2ggb25lIG9mIHNlcnZlcnNcclxuICAgIGZvciAoY29uc3QgW3BvcnQsIHNlcnZlcl0gb2YgYWN0aXZlU2VydmVycykge1xyXG4gICAgICBzZXJ2ZXIuY2xvc2UoKCkgPT4ge1xyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuZGVsZXRlKHBvcnQpO1xyXG4gICAgICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2VkIHNlcnZlciBvbiBwb3J0OiAke3BvcnR9LmApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0U2VydmVyc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXkuPE9iamVjdD59IFNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFNlcnZlcnMoKSB7XHJcbiAgcmV0dXJuIGFjdGl2ZVNlcnZlcnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRFeHByZXNzXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRFeHByZXNzKCkge1xyXG4gIHJldHVybiBleHByZXNzO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEFwcFxyXG4gKlxyXG4gKiBAcmV0dXJucyB7RXhwcmVzc30gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEFwcCgpIHtcclxuICByZXR1cm4gYXBwO1xyXG59XHJcblxyXG4vKipcclxuICogRW5hYmxlIHJhdGUgbGltaXRpbmcgZm9yIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBlbmFibGVSYXRlTGltaXRpbmdcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHJhdGVMaW1pdGluZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0XHJcbiAqIG9mIHRoZSBgcmF0ZUxpbWl0aW5nYCBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXNcclxuICogd2lsbCBkZWZhdWx0IHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZyhyYXRlTGltaXRpbmdPcHRpb25zKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgIHNlcnZlcjoge1xyXG4gICAgICByYXRlTGltaXRpbmc6IHJhdGVMaW1pdGluZ09wdGlvbnNcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gU2V0IHRoZSByYXRlIGxpbWl0aW5nIG9wdGlvbnNcclxuICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgb3B0aW9ucy5zZXJ2ZXIucmF0ZUxpbWl0aW5nT3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBcHBseSBtaWRkbGV3YXJlKHMpIHRvIGEgc3BlY2lmaWMgcGF0aC5cclxuICpcclxuICogQGZ1bmN0aW9uIHVzZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gdXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLnVzZShwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIEdFVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5nZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBQT1NUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHBvc3RcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbihzKSB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAucG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgdG8gdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzXHJcbiAqXHJcbiAqIEBwYXJhbSB7KGh0dHAuU2VydmVyfGh0dHBzLlNlcnZlcil9IHNlcnZlciAtIFRoZSBIVFRQL0hUVFBTIHNlcnZlciBpbnN0YW5jZS5cclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKHNlcnZlcikge1xyXG4gIHNlcnZlci5vbignY2xpZW50RXJyb3InLCAoZXJyb3IsIHNvY2tldCkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAxLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgYFtzZXJ2ZXJdIENsaWVudCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfSwgZGVzdHJveWluZyBzb2NrZXQuYFxyXG4gICAgKTtcclxuICAgIHNvY2tldC5kZXN0cm95KCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNlcnZlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Nvbm5lY3Rpb24nLCAoc29ja2V0KSA9PiB7XHJcbiAgICBzb2NrZXQub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNvY2tldCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzdGFydFNlcnZlcixcclxuICBjbG9zZVNlcnZlcnMsXHJcbiAgZ2V0U2VydmVycyxcclxuICBnZXRFeHByZXNzLFxyXG4gIGdldEFwcCxcclxuICBlbmFibGVSYXRlTGltaXRpbmcsXHJcbiAgdXNlLFxyXG4gIGdldCxcclxuICBwb3N0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBIYW5kbGVzIGdyYWNlZnVsIHNodXRkb3duIG9mIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIsIGVuc3VyaW5nXHJcbiAqIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlcyBzdWNoIGFzIGJyb3dzZXIsIHBhZ2VzLCBzZXJ2ZXJzLCBhbmQgdGltZXJzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgY2xlYXJBbGxUaW1lcnMgfSBmcm9tICcuL3RpbWVyLmpzJztcclxuXHJcbmltcG9ydCB7IGNsb3NlU2VydmVycyB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogUGVyZm9ybXMgY2xlYW51cCBvcGVyYXRpb25zIHRvIGVuc3VyZSBhIGdyYWNlZnVsIHNodXRkb3duIG9mIHRoZSBwcm9jZXNzLlxyXG4gKiBUaGlzIGluY2x1ZGVzIGNsZWFyaW5nIGFsbCByZWdpc3RlcmVkIHRpbWVvdXRzL2ludGVydmFscywgY2xvc2luZyBhY3RpdmVcclxuICogc2VydmVycywgdGVybWluYXRpbmcgcmVzb3VyY2VzIChwYWdlcykgb2YgdGhlIHBvb2wsIHBvb2wgaXRzZWxmLCBhbmQgY2xvc2luZ1xyXG4gKiB0aGUgYnJvd3Nlci5cclxuICpcclxuICogQGZ1bmN0aW9uIHNodXRkb3duQ2xlYW5VcFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2V4aXRDb2RlPTBdIC0gVGhlIGV4aXQgY29kZSB0byB1c2Ugd2l0aCBgcHJvY2Vzcy5leGl0KClgLlxyXG4gKiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgMGAuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwKGV4aXRDb2RlID0gMCkge1xyXG4gIC8vIEF3YWl0IGZyZWVpbmcgYWxsIHJlc291cmNlc1xyXG4gIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChbXHJcbiAgICAvLyBDbGVhciBhbGwgb25nb2luZyBpbnRlcnZhbHNcclxuICAgIGNsZWFyQWxsVGltZXJzKCksXHJcblxyXG4gICAgLy8gR2V0IGF2YWlsYWJsZSBzZXJ2ZXIgaW5zdGFuY2VzIChIVFRQL0hUVFBTKSBhbmQgY2xvc2UgdGhlbVxyXG4gICAgY2xvc2VTZXJ2ZXJzKCksXHJcblxyXG4gICAgLy8gQ2xvc2UgYW4gYWN0aXZlIHBvb2wgYWxvbmcgd2l0aCBpdHMgd29ya2VycyBhbmQgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICAgIGtpbGxQb29sKClcclxuICBdKTtcclxuXHJcbiAgLy8gRXhpdCBwcm9jZXNzIHdpdGggYSBjb3JyZWN0IGNvZGVcclxuICBwcm9jZXNzLmV4aXQoZXhpdENvZGUpO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2h1dGRvd25DbGVhblVwXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBDb3JlIG1vZHVsZSBmb3IgaW5pdGlhbGl6aW5nIGFuZCBtYW5hZ2luZyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnRcclxuICogU2VydmVyLiBQcm92aWRlcyBmdW5jdGlvbmFsaXRpZXMgZm9yIGNvbmZpZ3VyaW5nIGV4cG9ydHMsIHNldHRpbmcgdXAgc2VydmVyXHJcbiAqIG9wZXJhdGlvbnMsIGxvZ2dpbmcsIHNjcmlwdHMgY2FjaGluZywgcmVzb3VyY2UgcG9vbGluZywgYW5kIGdyYWNlZnVsIHByb2Nlc3NcclxuICogY2xlYW51cC5cclxuICovXHJcblxyXG5pbXBvcnQgJ2NvbG9ycyc7XHJcblxyXG5pbXBvcnQgeyBjaGVja0FuZFVwZGF0ZUNhY2hlIH0gZnJvbSAnLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7XHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59IGZyb20gJy4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCB1cGRhdGVPcHRpb25zLCBtYXBUb05ld09wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbFxyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2h1dGRvd25DbGVhblVwIH0gZnJvbSAnLi9yZXNvdXJjZVJlbGVhc2UuanMnO1xyXG5cclxuaW1wb3J0IHNlcnZlciBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiB0aGUgY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHJlc291cmNlIHBvb2wgb2NjdXIgZHVyaW5nIHRoaXNcclxuICogc3RhZ2UuXHJcbiAqXHJcbiAqIFRoaXMgZnVuY3Rpb24gbXVzdCBiZSBjYWxsZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXRcclxuICogdXAgYSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW5pdE9wdGlvbnMgLSBUaGUgYGluaXRPcHRpb25zYCBvYmplY3QsIHdoaWNoIG1heVxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nXHJcbiAqIHZhbHVlcyB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdEV4cG9ydChpbml0T3B0aW9ucykge1xyXG4gIC8vIEluaXQgYW5kIHVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyhpbml0T3B0aW9ucyk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucy5oaWdoY2hhcnRzLCBvcHRpb25zLnNlcnZlci5wcm94eSk7XHJcblxyXG4gIC8vIEluaXQgdGhlIHBvb2xcclxuICBhd2FpdCBpbml0UG9vbChvcHRpb25zLnBvb2wsIG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MpO1xyXG59XHJcblxyXG4vKipcclxuICogQXR0YWNoZXMgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MsIGVuc3VyaW5nIHByb3BlciBjbGVhbnVwIG9mIHJlc291cmNlc1xyXG4gKiBhbmQgdGVybWluYXRpb24gb24gZXhpdCBzaWduYWxzLiBIYW5kbGVzICdleGl0JywgJ1NJR0lOVCcsICdTSUdURVJNJ1xyXG4gKiBhbmQgJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnNcclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LmApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0lOVCdcclxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHVEVSTSdcclxuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR0hVUCdcclxuICBwcm9jZXNzLm9uKCdTSUdIVVAnLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAndW5jYXVnaHRFeGNlcHRpb24nXHJcbiAgcHJvY2Vzcy5vbigndW5jYXVnaHRFeGNlcHRpb24nLCBhc3luYyAoZXJyb3IsIG5hbWUpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIC4uLnNlcnZlcixcclxuXHJcbiAgLy8gT3B0aW9uc1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgdXBkYXRlT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcblxyXG4gIC8vIEV4cG9ydGluZ1xyXG4gIGluaXRFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG5cclxuICAvLyBSZWxlYXNlXHJcbiAga2lsbFBvb2wsXHJcbiAgc2h1dGRvd25DbGVhblVwLFxyXG5cclxuICAvLyBMb2dzXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbDogZnVuY3Rpb24gKGxldmVsKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICBsZXZlbFxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgc2V0TG9nTGV2ZWwob3B0aW9ucy5sb2dnaW5nLmxldmVsKTtcclxuICB9LFxyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nOiBmdW5jdGlvbiAodG9Db25zb2xlKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICB0b0NvbnNvbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUNvbnNvbGVMb2dnaW5nKG9wdGlvbnMubG9nZ2luZy50b0NvbnNvbGUpO1xyXG4gIH0sXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmc6IGZ1bmN0aW9uIChkZXN0LCBmaWxlLCB0b0ZpbGUpIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgbG9nZ2luZzoge1xyXG4gICAgICAgIGRlc3QsXHJcbiAgICAgICAgZmlsZSxcclxuICAgICAgICB0b0ZpbGVcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIGVuYWJsZUZpbGVMb2dnaW5nKFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcuZGVzdCxcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLmZpbGUsXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy50b0ZpbGVcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG4iXSwibmFtZXMiOlsiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJiYXNlVVJJIiwiZGVlcENvcHkiLCJvYmpBcnIiLCJvYmpBcnJDb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwiT2JqZWN0IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiZml4Q29uc3RyIiwiY29uc3RyIiwiZml4ZWRDb25zdHIiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJpbmNsdWRlcyIsImZpeE91dGZpbGUiLCJ0eXBlIiwib3V0ZmlsZSIsImdldEFic29sdXRlUGF0aCIsInNwbGl0Iiwic2hpZnQiLCJmaXhUeXBlIiwibWltZVR5cGVzIiwiZm9ybWF0cyIsInZhbHVlcyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsInBhdGgiLCJpc0Fic29sdXRlIiwibm9ybWFsaXplIiwicmVzb2x2ZSIsImdldEJhc2U2NCIsImlucHV0IiwiQnVmZmVyIiwiZnJvbSIsInRvU3RyaW5nIiwiZ2V0TmV3RGF0ZSIsIkRhdGUiLCJ0cmltIiwiZ2V0TmV3RGF0ZVRpbWUiLCJnZXRUaW1lIiwiaXNPYmplY3QiLCJpdGVtIiwiaXNPYmplY3RFbXB0eSIsImtleXMiLCJsZW5ndGgiLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwic29tZSIsInBhdHRlcm4iLCJ0ZXN0IiwibWVhc3VyZVRpbWUiLCJzdGFydCIsInByb2Nlc3MiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJyb3VuZE51bWJlciIsInZhbHVlIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsIk1hdGgiLCJwb3ciLCJyb3VuZCIsIndyYXBBcm91bmQiLCJjdXN0b21Db2RlIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiaXNDYWxsYmFjayIsImVuZHNXaXRoIiwicmVhZEZpbGVTeW5jIiwic3RhcnRzV2l0aCIsImNvbG9ycyIsImxvZ2dpbmciLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsInBhdGhUb0xvZyIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibG9nIiwiYXJncyIsIm5ld0xldmVsIiwidGV4dHMiLCJsZXZlbCIsInByZWZpeCIsIl9sb2dUb0ZpbGUiLCJjb25zb2xlIiwiYXBwbHkiLCJ1bmRlZmluZWQiLCJjb25jYXQiLCJsb2dXaXRoU3RhY2siLCJlcnJvciIsImN1c3RvbU1lc3NhZ2UiLCJtYWluTWVzc2FnZSIsIm1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInB1c2giLCJpbml0TG9nZ2luZyIsImxvZ2dpbmdPcHRpb25zIiwiZGVzdCIsImZpbGUiLCJzZXRMb2dMZXZlbCIsImVuYWJsZUNvbnNvbGVMb2dnaW5nIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJpc0ludGVnZXIiLCJleGlzdHNTeW5jIiwibWtkaXJTeW5jIiwiam9pbiIsImFwcGVuZEZpbGUiLCJkZWZhdWx0Q29uZmlnIiwicHVwcGV0ZWVyIiwidHlwZXMiLCJlbnZMaW5rIiwiY2xpTmFtZSIsImRlc2NyaXB0aW9uIiwicHJvbXB0T3B0aW9ucyIsInNlcGFyYXRvciIsImhpZ2hjaGFydHMiLCJ2ZXJzaW9uIiwiY2RuVXJsIiwiZm9yY2VGZXRjaCIsImNhY2hlUGF0aCIsImNvcmVTY3JpcHRzIiwiaW5zdHJ1Y3Rpb25zIiwibW9kdWxlU2NyaXB0cyIsImluZGljYXRvclNjcmlwdHMiLCJjdXN0b21TY3JpcHRzIiwiZXhwb3J0IiwiaW5maWxlIiwiaW5zdHIiLCJvcHRpb25zIiwic3ZnIiwiYmF0Y2giLCJoaW50IiwiY2hvaWNlcyIsImI2NCIsIm5vRG93bmxvYWQiLCJoZWlnaHQiLCJ3aWR0aCIsInNjYWxlIiwiZGVmYXVsdEhlaWdodCIsImRlZmF1bHRXaWR0aCIsImRlZmF1bHRTY2FsZSIsIm1pbiIsIm1heCIsImdsb2JhbE9wdGlvbnMiLCJ0aGVtZU9wdGlvbnMiLCJyYXN0ZXJpemF0aW9uVGltZW91dCIsImN1c3RvbUxvZ2ljIiwiYWxsb3dDb2RlRXhlY3V0aW9uIiwiY2FsbGJhY2siLCJyZXNvdXJjZXMiLCJsb2FkQ29uZmlnIiwibGVnYWN5TmFtZSIsImNyZWF0ZUNvbmZpZyIsInNlcnZlciIsImVuYWJsZSIsImhvc3QiLCJwb3J0IiwidXBsb2FkTGltaXQiLCJiZW5jaG1hcmtpbmciLCJwcm94eSIsInRpbWVvdXQiLCJyYXRlTGltaXRpbmciLCJtYXhSZXF1ZXN0cyIsIndpbmRvdyIsImRlbGF5IiwidHJ1c3RQcm94eSIsInNraXBLZXkiLCJza2lwVG9rZW4iLCJzc2wiLCJmb3JjZSIsImNlcnRQYXRoIiwicG9vbCIsIm1pbldvcmtlcnMiLCJtYXhXb3JrZXJzIiwid29ya0xpbWl0IiwiYWNxdWlyZVRpbWVvdXQiLCJjcmVhdGVUaW1lb3V0IiwiZGVzdHJveVRpbWVvdXQiLCJpZGxlVGltZW91dCIsImNyZWF0ZVJldHJ5SW50ZXJ2YWwiLCJyZWFwZXJJbnRlcnZhbCIsInVpIiwicm91dGUiLCJvdGhlciIsIm5vZGVFbnYiLCJsaXN0ZW5Ub1Byb2Nlc3NFeGl0cyIsIm5vTG9nbyIsImhhcmRSZXNldFBhZ2UiLCJicm93c2VyU2hlbGxNb2RlIiwiZGVidWciLCJoZWFkbGVzcyIsImRldnRvb2xzIiwibGlzdGVuVG9Db25zb2xlIiwiZHVtcGlvIiwic2xvd01vIiwiZGVidWdnaW5nUG9ydCIsIm5lc3RlZFByb3BzIiwiX2NyZWF0ZU5lc3RlZFByb3BzIiwiYWJzb2x1dGVQcm9wcyIsIl9jcmVhdGVBYnNvbHV0ZVByb3BzIiwiY29uZmlnIiwicHJvcENoYWluIiwiZm9yRWFjaCIsImVudHJ5Iiwic3Vic3RyaW5nIiwiZG90ZW52IiwidiIsImFycmF5IiwiZmlsdGVyQXJyYXkiLCJ6Iiwic3RyaW5nIiwidHJhbnNmb3JtIiwibWFwIiwiZmlsdGVyIiwiYm9vbGVhbiIsImVudW0iLCJyZWZpbmUiLCJwb3NpdGl2ZU51bSIsImlzTmFOIiwicGFyc2VGbG9hdCIsIm5vbk5lZ2F0aXZlTnVtIiwiQ29uZmlnIiwib2JqZWN0IiwiUFVQUEVURUVSX0FSR1MiLCJISUdIQ0hBUlRTX1ZFUlNJT04iLCJISUdIQ0hBUlRTX0NETl9VUkwiLCJISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIIiwiSElHSENIQVJUU19DQUNIRV9QQVRIIiwiSElHSENIQVJUU19BRE1JTl9UT0tFTiIsIkhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTIiwiSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUyIsIkhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFMiLCJISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTIiwiRVhQT1JUX0lORklMRSIsIkVYUE9SVF9JTlNUUiIsIkVYUE9SVF9PUFRJT05TIiwiRVhQT1JUX1NWRyIsIkVYUE9SVF9CQVRDSCIsIkVYUE9SVF9PVVRGSUxFIiwiRVhQT1JUX1RZUEUiLCJFWFBPUlRfQ09OU1RSIiwiRVhQT1JUX0I2NCIsIkVYUE9SVF9OT19ET1dOTE9BRCIsIkVYUE9SVF9IRUlHSFQiLCJFWFBPUlRfV0lEVEgiLCJFWFBPUlRfU0NBTEUiLCJFWFBPUlRfREVGQVVMVF9IRUlHSFQiLCJFWFBPUlRfREVGQVVMVF9XSURUSCIsIkVYUE9SVF9ERUZBVUxUX1NDQUxFIiwiRVhQT1JUX0dMT0JBTF9PUFRJT05TIiwiRVhQT1JUX1RIRU1FX09QVElPTlMiLCJFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0NVU1RPTV9DT0RFIiwiQ1VTVE9NX0xPR0lDX0NBTExCQUNLIiwiQ1VTVE9NX0xPR0lDX1JFU09VUkNFUyIsIkNVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRyIsIkNVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHIiwiU0VSVkVSX0VOQUJMRSIsIlNFUlZFUl9IT1NUIiwiU0VSVkVSX1BPUlQiLCJTRVJWRVJfVVBMT0FEX0xJTUlUIiwiU0VSVkVSX0JFTkNITUFSS0lORyIsIlNFUlZFUl9QUk9YWV9IT1NUIiwiU0VSVkVSX1BST1hZX1BPUlQiLCJTRVJWRVJfUFJPWFlfVElNRU9VVCIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4iLCJTRVJWRVJfU1NMX0VOQUJMRSIsIlNFUlZFUl9TU0xfRk9SQ0UiLCJTRVJWRVJfU1NMX1BPUlQiLCJTRVJWRVJfU1NMX0NFUlRfUEFUSCIsIlBPT0xfTUlOX1dPUktFUlMiLCJQT09MX01BWF9XT1JLRVJTIiwiUE9PTF9XT1JLX0xJTUlUIiwiUE9PTF9BQ1FVSVJFX1RJTUVPVVQiLCJQT09MX0NSRUFURV9USU1FT1VUIiwiUE9PTF9ERVNUUk9ZX1RJTUVPVVQiLCJQT09MX0lETEVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1JFVFJZX0lOVEVSVkFMIiwiUE9PTF9SRUFQRVJfSU5URVJWQUwiLCJQT09MX0JFTkNITUFSS0lORyIsIkxPR0dJTkdfTEVWRUwiLCJMT0dHSU5HX0ZJTEUiLCJMT0dHSU5HX0RFU1QiLCJMT0dHSU5HX1RPX0NPTlNPTEUiLCJMT0dHSU5HX1RPX0ZJTEUiLCJVSV9FTkFCTEUiLCJVSV9ST1VURSIsIk9USEVSX05PREVfRU5WIiwiT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMiLCJPVEhFUl9OT19MT0dPIiwiT1RIRVJfSEFSRF9SRVNFVF9QQUdFIiwiT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFIiwiREVCVUdfRU5BQkxFIiwiREVCVUdfSEVBRExFU1MiLCJERUJVR19ERVZUT09MUyIsIkRFQlVHX0xJU1RFTl9UT19DT05TT0xFIiwiREVCVUdfRFVNUElPIiwiREVCVUdfU0xPV19NTyIsIkRFQlVHX0RFQlVHR0lOR19QT1JUIiwiZW52cyIsInBhcnRpYWwiLCJwYXJzZSIsImVudiIsIl9pbml0T3B0aW9ucyIsImdldE9wdGlvbnMiLCJnZXRDb3B5IiwidXBkYXRlT3B0aW9ucyIsIm5ld09wdGlvbnMiLCJfbWVyZ2VPcHRpb25zIiwibWFwVG9OZXdPcHRpb25zIiwib2xkT3B0aW9ucyIsImVudHJpZXMiLCJwcm9wZXJ0aWVzQ2hhaW4iLCJyZWR1Y2UiLCJvYmoiLCJwcm9wIiwiaW5kZXgiLCJpc0FsbG93ZWRDb25maWciLCJhbGxvd0Z1bmN0aW9ucyIsIm9iamVjdENvbmZpZyIsImV2YWwiLCJKU09OIiwic3RyaW5naWZpZWRPcHRpb25zIiwiX29wdGlvbnNTdHJpbmdpZnkiLCJwYXJzZWRPcHRpb25zIiwiXyIsIm5hbWUiLCJvcmlnaW5hbE9wdGlvbnMiLCJzdHJpbmdpZnlGdW5jdGlvbnMiLCJzdHJpbmdpZnkiLCJyZXBsYWNlQWxsIiwiRXJyb3IiLCJhc3luYyIsImZldGNoIiwidXJsIiwicmVxdWVzdE9wdGlvbnMiLCJQcm9taXNlIiwicmVqZWN0IiwiX2dldFByb3RvY29sTW9kdWxlIiwiZ2V0IiwicmVzcG9uc2UiLCJyZXNwb25zZURhdGEiLCJvbiIsImNodW5rIiwidGV4dCIsImh0dHBzIiwiaHR0cCIsIkV4cG9ydEVycm9yIiwiY29uc3RydWN0b3IiLCJzdGF0dXNDb2RlIiwic3VwZXIiLCJ0aGlzIiwic2V0U3RhdHVzIiwic2V0RXJyb3IiLCJjYWNoZSIsImFjdGl2ZU1hbmlmZXN0Iiwic291cmNlcyIsImhjVmVyc2lvbiIsImNoZWNrQW5kVXBkYXRlQ2FjaGUiLCJoaWdoY2hhcnRzT3B0aW9ucyIsInNlcnZlclByb3h5T3B0aW9ucyIsImZldGNoZWRNb2R1bGVzIiwiZ2V0Q2FjaGVQYXRoIiwibWFuaWZlc3RQYXRoIiwic291cmNlUGF0aCIsInJlY3Vyc2l2ZSIsIl91cGRhdGVDYWNoZSIsInJlcXVlc3RVcGRhdGUiLCJtYW5pZmVzdCIsIm1vZHVsZXMiLCJtb2R1bGVNYXAiLCJtIiwibnVtYmVyT2ZNb2R1bGVzIiwibW9kdWxlTmFtZSIsImV4dHJhY3RWZXJzaW9uIiwiX3NhdmVDb25maWdUb01hbmlmZXN0IiwiZ2V0SGlnaGNoYXJ0c1ZlcnNpb24iLCJ1cGRhdGVIaWdoY2hhcnRzVmVyc2lvbiIsIm5ld1ZlcnNpb24iLCJjYWNoZVNvdXJjZXMiLCJpbmRleE9mIiwiZXh0cmFjdE1vZHVsZU5hbWUiLCJzY3JpcHRQYXRoIiwiX2ZldGNoQW5kUHJvY2Vzc1NjcmlwdCIsInNjcmlwdCIsInNob3VsZFRocm93RXJyb3IiLCJuZXdNYW5pZmVzdCIsIndyaXRlRmlsZVN5bmMiLCJfZmV0Y2hTY3JpcHRzIiwicHJveHlBZ2VudCIsInByb3h5SG9zdCIsInByb3h5UG9ydCIsIkh0dHBzUHJveHlBZ2VudCIsImFnZW50IiwiYWxsRmV0Y2hQcm9taXNlcyIsImFsbCIsImMiLCJpIiwic2V0dXBIaWdoY2hhcnRzIiwiSGlnaGNoYXJ0cyIsImFuaW1PYmplY3QiLCJkdXJhdGlvbiIsImNyZWF0ZUNoYXJ0IiwiZXhwb3J0T3B0aW9ucyIsImN1c3RvbUxvZ2ljT3B0aW9ucyIsInNldE9wdGlvbnMiLCJtZXJnZSIsIndyYXAiLCJzZXRPcHRpb25zT2JqIiwiaXNSZW5kZXJDb21wbGV0ZSIsIkNoYXJ0IiwicHJvY2VlZCIsInVzZXJPcHRpb25zIiwiY2IiLCJleHBvcnRpbmciLCJlbmFibGVkIiwicGxvdE9wdGlvbnMiLCJzZXJpZXMiLCJsYWJlbCIsInRvb2x0aXAiLCJhbmltYXRpb24iLCJvbkhpZ2hjaGFydHNSZW5kZXIiLCJhZGRFdmVudCIsIlNlcmllcyIsImNoYXJ0IiwiYWRkaXRpb25hbE9wdGlvbnMiLCJGdW5jdGlvbiIsImZpbmFsT3B0aW9ucyIsImZpbmFsQ2FsbGJhY2siLCJkZWZhdWx0T3B0aW9ucyIsInRlbXBsYXRlIiwiYnJvd3NlciIsImNyZWF0ZUJyb3dzZXIiLCJwdXBwZXRlZXJBcmdzIiwiZW5hYmxlZERlYnVnIiwiZGVidWdPcHRpb25zIiwibGF1bmNoT3B0aW9ucyIsInVzZXJEYXRhRGlyIiwiaGFuZGxlU0lHSU5UIiwiaGFuZGxlU0lHVEVSTSIsImhhbmRsZVNJR0hVUCIsIndhaXRGb3JJbml0aWFsUGFnZSIsImRlZmF1bHRWaWV3cG9ydCIsInRyeUNvdW50Iiwib3BlbiIsImxhdW5jaCIsInNldFRpbWVvdXQiLCJjbG9zZUJyb3dzZXIiLCJjb25uZWN0ZWQiLCJjbG9zZSIsIm5ld1BhZ2UiLCJwb29sUmVzb3VyY2UiLCJwYWdlIiwic2V0Q2FjaGVFbmFibGVkIiwiX3NldFBhZ2VDb250ZW50IiwiX3NldFBhZ2VFdmVudHMiLCJpc0Nsb3NlZCIsImNsZWFyUGFnZSIsImhhcmRSZXNldCIsImdvdG8iLCJ3YWl0VW50aWwiLCJldmFsdWF0ZSIsImJvZHkiLCJpbm5lckhUTUwiLCJpZCIsIndvcmtDb3VudCIsImFkZFBhZ2VSZXNvdXJjZXMiLCJpbmplY3RlZFJlc291cmNlcyIsImluamVjdGVkSnMiLCJqcyIsImNvbnRlbnQiLCJmaWxlcyIsImlzTG9jYWwiLCJqc1Jlc291cmNlIiwiYWRkU2NyaXB0VGFnIiwiaW5qZWN0ZWRDc3MiLCJjc3MiLCJjc3NJbXBvcnRzIiwibWF0Y2giLCJjc3NJbXBvcnRQYXRoIiwiY3NzUmVzb3VyY2UiLCJhZGRTdHlsZVRhZyIsImNsZWFyUGFnZVJlc291cmNlcyIsInJlc291cmNlIiwiZGlzcG9zZSIsIm9sZENoYXJ0cyIsImNoYXJ0cyIsIm9sZENoYXJ0IiwiZGVzdHJveSIsInNjcmlwdHNUb1JlbW92ZSIsImdldEVsZW1lbnRzQnlUYWdOYW1lIiwic3R5bGVzVG9SZW1vdmUiLCJsaW5rc1RvUmVtb3ZlIiwiZWxlbWVudCIsInJlbW92ZSIsInNldENvbnRlbnQiLCJjc3NUZW1wbGF0ZSIsInN2Z1RlbXBsYXRlIiwicHVwcGV0ZWVyRXhwb3J0IiwiaXNTVkciLCJzaXplIiwic3ZnRWxlbWVudCIsInF1ZXJ5U2VsZWN0b3IiLCJjaGFydEhlaWdodCIsImJhc2VWYWwiLCJjaGFydFdpZHRoIiwic3R5bGUiLCJ6b29tIiwibWFyZ2luIiwieCIsInkiLCJfZ2V0Q2xpcFJlZ2lvbiIsInZpZXdwb3J0SGVpZ2h0IiwiYWJzIiwiY2VpbCIsInZpZXdwb3J0V2lkdGgiLCJyZXN1bHQiLCJzZXRWaWV3cG9ydCIsImRldmljZVNjYWxlRmFjdG9yIiwiX2NyZWF0ZVNWRyIsIl9jcmVhdGVJbWFnZSIsIl9jcmVhdGVQREYiLCIkZXZhbCIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsInRydW5jIiwib3V0ZXJIVE1MIiwiY2xpcCIsInJhY2UiLCJzY3JlZW5zaG90IiwiZW5jb2RpbmciLCJmdWxsUGFnZSIsIm9wdGltaXplRm9yU3BlZWQiLCJjYXB0dXJlQmV5b25kVmlld3BvcnQiLCJxdWFsaXR5Iiwib21pdEJhY2tncm91bmQiLCJfcmVzb2x2ZSIsImVtdWxhdGVNZWRpYVR5cGUiLCJwZGYiLCJwb29sU3RhdHMiLCJleHBvcnRzQXR0ZW1wdGVkIiwiZXhwb3J0c1BlcmZvcm1lZCIsImV4cG9ydHNEcm9wcGVkIiwiZXhwb3J0c0Zyb21TdmciLCJleHBvcnRzRnJvbU9wdGlvbnMiLCJleHBvcnRzRnJvbVN2Z0F0dGVtcHRzIiwiZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHMiLCJ0aW1lU3BlbnQiLCJ0aW1lU3BlbnRBdmVyYWdlIiwiaW5pdFBvb2wiLCJwb29sT3B0aW9ucyIsIlBvb2wiLCJfZmFjdG9yeSIsImFjcXVpcmVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlVGltZW91dE1pbGxpcyIsImRlc3Ryb3lUaW1lb3V0TWlsbGlzIiwiaWRsZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzIiwicmVhcEludGVydmFsTWlsbGlzIiwicHJvcGFnYXRlQ3JlYXRlRXJyb3IiLCJjbGVhclN0YXR1cyIsIl9ldmVudElkIiwiaW5pdGlhbFJlc291cmNlcyIsImFjcXVpcmUiLCJwcm9taXNlIiwicmVsZWFzZSIsImtpbGxQb29sIiwid29ya2VyIiwidXNlZCIsImRlc3Ryb3llZCIsInBvc3RXb3JrIiwid29ya2VySGFuZGxlIiwiZ2V0UG9vbEluZm8iLCJhY3F1aXJlQ291bnRlciIsInJlcXVlc3RJZCIsIndvcmtTdGFydCIsImV4cG9ydENvdW50ZXIiLCJleHBvcnRUaW1lIiwiZ2V0UG9vbFN0YXRzIiwiZ2V0UG9vbEluZm9KU09OIiwibnVtVXNlZCIsImF2YWlsYWJsZSIsIm51bUZyZWUiLCJhbGxDcmVhdGVkIiwicGVuZGluZ0FjcXVpcmVzIiwibnVtUGVuZGluZ0FjcXVpcmVzIiwicGVuZGluZ0NyZWF0ZXMiLCJudW1QZW5kaW5nQ3JlYXRlcyIsInBlbmRpbmdWYWxpZGF0aW9ucyIsIm51bVBlbmRpbmdWYWxpZGF0aW9ucyIsInBlbmRpbmdEZXN0cm95cyIsImFic29sdXRlQWxsIiwiY3JlYXRlIiwidXVpZCIsInJhbmRvbSIsInN0YXJ0RGF0ZSIsInZhbGlkYXRlIiwibWFpbkZyYW1lIiwiZGV0YWNoZWQiLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJzYW5pdGl6ZSIsIkpTRE9NIiwiRE9NUHVyaWZ5IiwiQUREX1RBR1MiLCJzaW5nbGVFeHBvcnQiLCJzdGFydEV4cG9ydCIsImRhdGEiLCJiYXRjaEV4cG9ydCIsImJhdGNoRnVuY3Rpb25zIiwicGFpciIsImJhdGNoUmVzdWx0cyIsImFsbFNldHRsZWQiLCJyZWFzb24iLCJpbWFnZU9wdGlvbnMiLCJlbmRDYWxsYmFjayIsImZpbGVDb250ZW50IiwiX2V4cG9ydEZyb21TdmciLCJfZXhwb3J0RnJvbU9wdGlvbnMiLCJnZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJzZXRBbGxvd0NvZGVFeGVjdXRpb24iLCJpbnB1dFRvRXhwb3J0IiwiX3ByZXBhcmVFeHBvcnQiLCJfaGFuZGxlQ3VzdG9tTG9naWMiLCJfaGFuZGxlR2xvYmFsQW5kVGhlbWUiLCJfZmluZENoYXJ0U2l6ZSIsIl9jaGVja0RhdGFTaXplIiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJwcm9wTmFtZSIsIm9wdGlvbnNOYW1lIiwidG90YWxTaXplIiwiYnl0ZUxlbmd0aCIsInRvRml4ZWQiLCJ0aW1lcklkcyIsImFkZFRpbWVyIiwiY2xlYXJBbGxUaW1lcnMiLCJjbGVhckludGVydmFsIiwiY2xlYXJUaW1lb3V0IiwibG9nRXJyb3JNaWRkbGV3YXJlIiwicmVxdWVzdCIsIm5leHQiLCJyZXR1cm5FcnJvck1pZGRsZXdhcmUiLCJzdGF0dXMiLCJqc29uIiwiZXJyb3JNaWRkbGV3YXJlIiwiYXBwIiwidXNlIiwicmF0ZUxpbWl0aW5nTWlkZGxld2FyZSIsInJhdGVMaW1pdGluZ09wdGlvbnMiLCJyYXRlT3B0aW9ucyIsImxpbWl0ZXIiLCJyYXRlTGltaXQiLCJ3aW5kb3dNcyIsImxpbWl0IiwiZGVsYXlNcyIsImhhbmRsZXIiLCJmb3JtYXQiLCJzZW5kIiwiZGVmYXVsdCIsInNraXAiLCJxdWVyeSIsImFjY2Vzc190b2tlbiIsImNvbnRlbnRUeXBlTWlkZGxld2FyZSIsImNvbnRlbnRUeXBlIiwiaGVhZGVycyIsInJlcXVlc3RCb2R5TWlkZGxld2FyZSIsImNvbm5lY3Rpb24iLCJyZW1vdGVBZGRyZXNzIiwidmFsaWRhdGVkT3B0aW9ucyIsInBhcmFtcyIsImZpbGVuYW1lIiwidmFsaWRhdGlvbk1pZGRsZXdhcmUiLCJwb3N0IiwicmV2ZXJzZWRNaW1lIiwicG5nIiwianBlZyIsImdpZiIsInJlcXVlc3RFeHBvcnQiLCJyZXF1ZXN0Q291bnRlciIsImNvbm5lY3Rpb25BYm9ydGVkIiwic29ja2V0IiwiaGFkRXJyb3JzIiwiaGVhZGVyIiwiYXR0YWNobWVudCIsImV4cG9ydFJvdXRlcyIsInNlcnZlclN0YXJ0VGltZSIsInBhY2thZ2VGaWxlIiwic3VjY2Vzc1JhdGVzIiwicmVjb3JkSW50ZXJ2YWwiLCJ3aW5kb3dTaXplIiwiX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2UiLCJhIiwiYiIsIl9zdGFydFN1Y2Nlc3NSYXRlIiwic2V0SW50ZXJ2YWwiLCJzdGF0cyIsInN1Y2Nlc3NSYXRpbyIsImhlYWx0aFJvdXRlcyIsInBlcmlvZCIsIm1vdmluZ0F2ZXJhZ2UiLCJib290VGltZSIsInVwdGltZSIsImZsb29yIiwic2VydmVyVmVyc2lvbiIsImhpZ2hjaGFydHNWZXJzaW9uIiwiYXZlcmFnZUV4cG9ydFRpbWUiLCJhdHRlbXB0ZWRFeHBvcnRzIiwicGVyZm9ybWVkRXhwb3J0cyIsImZhaWxlZEV4cG9ydHMiLCJzdWNlc3NSYXRpbyIsInN2Z0V4cG9ydHMiLCJqc29uRXhwb3J0cyIsInN2Z0V4cG9ydHNBdHRlbXB0cyIsImpzb25FeHBvcnRzQXR0ZW1wdHMiLCJ1aVJvdXRlcyIsInNlbmRGaWxlIiwiYWNjZXB0UmFuZ2VzIiwidmVyc2lvbkNoYW5nZVJvdXRlcyIsImFkbWluVG9rZW4iLCJ0b2tlbiIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJPcHRpb25zIiwidXBsb2FkTGltaXRCeXRlcyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRTaXplIiwiZGlzYWJsZSIsImNvcnMiLCJtZXRob2RzIiwic2V0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsInN0YXRpYyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyIsImxpc3RlbiIsImNlcnQiLCJodHRwc1NlcnZlciIsImNsb3NlU2VydmVycyIsImRlbGV0ZSIsImdldFNlcnZlcnMiLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwiZW5hYmxlUmF0ZUxpbWl0aW5nIiwibWlkZGxld2FyZXMiLCJzaHV0ZG93bkNsZWFuVXAiLCJleGl0Q29kZSIsImV4aXQiLCJpbml0RXhwb3J0IiwiaW5pdE9wdGlvbnMiLCJfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMiLCJjb2RlIl0sIm1hcHBpbmdzIjoid2xCQTJCTyxNQUFNQSxZQUFZQyxJQUFhQSxjQUFDLElBQUlDLElBQUksT0FBUSxvQkFBQUMsU0FBQUMsUUFBQSxPQUFBQyxjQUFBQyxZQUFBQyxLQUFBQyx3QkFBQSxXQUFBQSx1QkFBQUMsUUFBQUMsZUFBQUYsdUJBQUFHLEtBQUEsSUFBQVQsSUFBQSxZQUFBQyxTQUFBUyxTQUFBTCxPQStCaEQsU0FBU00sU0FBU0MsR0FFdkIsR0FBZSxPQUFYQSxHQUFxQyxpQkFBWEEsRUFDNUIsT0FBT0EsRUFJVCxNQUFNQyxFQUFhQyxNQUFNQyxRQUFRSCxHQUFVLEdBQUssR0FHaEQsSUFBSyxNQUFNSSxLQUFPSixFQUNaSyxPQUFPQyxVQUFVQyxlQUFlQyxLQUFLUixFQUFRSSxLQUMvQ0gsRUFBV0csR0FBT0wsU0FBU0MsRUFBT0ksS0FLdEMsT0FBT0gsQ0FDVCxDQTJETyxTQUFTUSxVQUFVQyxHQUN4QixJQUVFLE1BQU1DLEVBQWMs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 +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),require("colors");var fs=require("fs"),path=require("path"),httpsProxyAgent=require("https-proxy-agent"),dotenv=require("dotenv"),zod=require("zod"),url=require("url"),http=require("http"),https=require("https"),tarn=require("tarn"),uuid=require("uuid"),puppeteer=require("puppeteer"),DOMPurify=require("dompurify"),jsdom=require("jsdom"),cors=require("cors"),express=require("express"),multer=require("multer"),rateLimit=require("express-rate-limit"),_documentCurrentScript="undefined"!=typeof document?document.currentScript:null;const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}};dotenv.config();const v={array:e=>zod.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>zod.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>zod.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>zod.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)),positiveNum:()=>zod.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)),nonNegativeNum:()=>zod.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))},Config=zod.z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION:zod.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:zod.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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL:zod.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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),__dirname$1=url.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:_documentCurrentScript&&"SCRIPT"===_documentCurrentScript.tagName.toUpperCase()&&_documentCurrentScript.src||new URL("index.cjs",document.baseURI).href));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function getAbsolutePath(e){return path.isAbsolute(e)?path.normalize(e):path.resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"log",logging.file=t||"highcharts-export-server.log")}function _logToFile(e,t){logging.pathCreated||(!fs.existsSync(getAbsolutePath(logging.dest))&&fs.mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(path.join(logging.dest,logging.file)),logging.pathCreated=!0),fs.appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const globalOptions=_initOptions(defaultConfig),nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty object.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}async function get$1(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkCache(e,t){try{let o;const r=getCachePath(),n=path.join(r,"manifest.json"),i=path.join(r,"sources.js");if(!fs.existsSync(r)&&fs.mkdirSync(r,{recursive:!0}),!fs.existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(fs.readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=fs.readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=_extractHcVersion(cache.sources))}await _saveConfigToManifest(e.version,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHcVersion(){return cache.hcVersion}async function updateHcVersion(e){const t=updateOptions({highcharts:{version:e}});await checkCache(t.highcharts,t.server.proxy)}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _saveConfigToManifest(e,t={}){cache.activeManifest={version:e,modules:t},log(3,"[cache] Writing a new manifest.");try{fs.writeFileSync(path.join(getCachePath(),"manifest.json"),JSON.stringify(cache.activeManifest),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _updateCache(e,t,o){try{const r="latest"===e.version?null:`${e.version}`;log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`);const n=e.cdnUrl||cache.cdnUrl,i=_configureRequest(t),s={};return cache.sources=(await Promise.all([...e.coreScripts.map((e=>_fetchScript(r?`${n}/${r}/${e}`:`${n}/${e}`,i,s,!0))),...e.moduleScripts.map((e=>_fetchScript("map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`,i,s))),...e.indicatorScripts.map((e=>_fetchScript(r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`,i,s))),...e.customScripts.map((e=>_fetchScript(`${e}`,i)))])).join(";\n"),cache.hcVersion=_extractHcVersion(cache.sources),fs.writeFileSync(o,cache.sources),s}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}async function _fetchScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await get$1(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[_extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}function _configureRequest(e){const t=e.host,o=e.port;if(t&&o)try{return{agent:new httpsProxyAgent.HttpsProxyAgent({host:t,port:o}),timeout:e.timeout}}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}return{}}function _extractHcVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function _extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=n(!1,l,a,s),p=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a);const u=new Function(`return ${e.globalOptions}`)();u&&r(u),Highcharts[e.constr]("container",c,p);const g=Array.from(document.querySelectorAll(".highcharts-container image"));await Promise.race([Promise.all(g.map((e=>e.complete&&0!==e.naturalHeight?Promise.resolve():new Promise((t=>e.addEventListener("load",t,{once:!0})))))),new Promise((e=>setTimeout(e,2e3)))]);const d=o();for(const e in d)"function"!=typeof d[e]&&delete d[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const pageTemplate=fs.readFileSync(path.join(__dirname$1,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to launch and get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:fs.readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){const n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(pageTemplate,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:path.join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=await _getChartSize(e,n,t.scale),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _getChartSize(e,t,o){return t?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(o)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new tarn.Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&_getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const r=measureTime(),n=await puppeteerExport(t.page,e.export,e.customLogic);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===n.name||"Rasterization timeout"===n.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(n):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${r()}ms.`).setError(n);return e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${r()}ms.`),pool.release(t),poolStats.timeSpent+=r(),poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${r()}ms.`),{result:n,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function _getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:uuid.v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new jsdom.JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?fs.writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):fs.writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=fs.readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.options=null,t.export.instr=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.options=null,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.constr=_fixConstr(t.constr),t.type=_fixType(t.type,t.outfile),t.outfile=_fixOutfile(t.type,t.outfile),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o),_handleGlobalAndTheme(t,o),_handleSize(t),_checkDataSize({export:t,customLogic:o}),postWork(e)}function _fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function _fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e||"png"}`}function _fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function _handleSize(e){const{chart:t,exporting:o}=isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,l=e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,c=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2);e.height=a,e.width=l,e.scale=c;for(let t of["height","width","scale"])"string"==typeof e[t]&&(e[t]=+e[t].replace(/px|%/gi,""))}function _handleCustomLogic(e){if(e.allowCodeExecution){try{e.resources=_handleResources(e.resources,e.allowFileResources,!0)}catch(t){log(2,"[chart] The `resources` cannot be loaded."),e.resources=null}try{e.customCode=_handleCustomCode(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=_handleCustomCode(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){let r=e;r||(e="resources.json");const n=["js","css","files"];let i=!1;t&&"string"==typeof e&&e.endsWith(".json")?r=isAllowedConfig(fs.readFileSync(getAbsolutePath(e),"utf8"),!1,o):(r=isAllowedConfig(e,!1,o),r&&!t&&delete r.files);for(const e in r)n.includes(e)?i||(i=!0):delete r[e];return i?(r.files&&(r.files=r.files.map((e=>e.trim())),(!r.files||r.files.length<=0)&&delete r.files),r):null}function _handleCustomCode(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?_handleCustomCode(fs.readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}function _handleGlobalAndTheme(e,t){const{allowFileResources:o,allowCodeExecution:r}=t;["globalOptions","themeOptions"].forEach((t=>{try{e[t]&&(o&&"string"==typeof e[t]&&e[t].endsWith(".json")?e[t]=isAllowedConfig(fs.readFileSync(getAbsolutePath(e[t]),"utf8"),!0,r):e[t]=isAllowedConfig(e[t],!0,r))}catch(o){logWithStack(2,o,`[chart] The \`${t}\` cannot be loaded.`),e[t]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}function _checkDataSize(e){const t=Buffer.byteLength(JSON.stringify(e),"utf-8");if(log(3,`[chart] The current total size of the data for the export process is around ${(t/1048576).toFixed(2)}MB.`),t>=104857600)throw new ExportError("[chart] The data for the export process exceeds 100MB limit.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=uuid.v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`[validation] Request [${r}] - 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`[validation] Request [${r}] - 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(fs.readFileSync(path.join(__dirname$1,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHcVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){getOptions().ui.enable&&e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(path.join(__dirname$1,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: `HIGHCHARTS_ADMIN_TOKEN` is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHcVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHcVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(path.join(__dirname$1,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=fs.readFileSync(path.join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code: ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};exports.default=index,exports.initExport=initExport; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvc2NoZW1hcy9jb25maWcuanMiLCIuLi9saWIvZW52cy5qcyIsIi4uL2xpYi91dGlscy5qcyIsIi4uL2xpYi9sb2dnZXIuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi90ZW1wbGF0ZXMvc3ZnRXhwb3J0L2Nzcy5qcyIsIi4uL3RlbXBsYXRlcy9zdmdFeHBvcnQvc3ZnRXhwb3J0LmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3RpbWVyLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy9lcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvbWlkZGxld2FyZXMvcmF0ZUxpbWl0aW5nLmpzIiwiLi4vbGliL3NlcnZlci9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvcmVzb3VyY2VSZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBDb25maWd1cmF0aW9uIG1hbmFnZW1lbnQgbW9kdWxlIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLlxyXG4gKiBJdCBwcm92aWRlcyBhIGRlZmF1bHQgY29uZmlndXJhdGlvbiBvYmplY3Qgd2l0aCBwcmVkZWZpbmVkIGRlZmF1bHQgdmFsdWVzLFxyXG4gKiBkZXNjcmlwdGlvbnMsIGFuZCBjaGFyYWN0ZXJpc3RpY3MgZm9yIGVhY2ggb3B0aW9uIHVzZWQgaW4gdGhlIEV4cG9ydCBTZXJ2ZXIuXHJcbiAqL1xyXG5cclxuLyoqXHJcbiAqIFRoZSBkZWZhdWx0IGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYWxsIGF2YWlsYWJsZSBvcHRpb25zLCBvcmdhbml6ZWRcclxuICogYnkgc2VjdGlvbnMuXHJcbiAqXHJcbiAqIFRoaXMgb2JqZWN0IGluY2x1ZGVzOlxyXG4gKiAtIERlZmF1bHQgdmFsdWVzIGZvciBlYWNoIG9wdGlvbi5cclxuICogLSBEYXRhIHR5cGVzIGZvciB2YWxpZGF0aW9uLlxyXG4gKiAtIE5hbWVzIG9mIGNvcnJlc3BvbmRpbmcgZW52aXJvbm1lbnQgdmFyaWFibGVzLlxyXG4gKiAtIERlc2NyaXB0aW9ucyBvZiBlYWNoIHByb3BlcnR5LlxyXG4gKiAtIEluZm9ybWF0aW9uIHVzZWQgZm9yIHByb21wdHMgaW4gaW50ZXJhY3RpdmUgY29uZmlndXJhdGlvbi5cclxuICogLSBbT3B0aW9uYWxdIENvcnJlc3BvbmRpbmcgQ0xJIGFyZ3VtZW50IG5hbWVzIGZvciBDTEkgdXNhZ2UuXHJcbiAqIC0gW09wdGlvbmFsXSBMZWdhY3kgbmFtZXMgZnJvbSB0aGUgcHJldmlvdXMgUGhhbnRvbUpTLWJhc2VkIHNlcnZlci5cclxuICovXHJcbmNvbnN0IGRlZmF1bHRDb25maWcgPSB7XHJcbiAgcHVwcGV0ZWVyOiB7XHJcbiAgICBhcmdzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJy0tYWxsb3ctcnVubmluZy1pbnNlY3VyZS1jb250ZW50JyxcclxuICAgICAgICAnLS1hc2gtbm8tbnVkZ2VzJyxcclxuICAgICAgICAnLS1hdXRvcGxheS1wb2xpY3k9dXNlci1nZXN0dXJlLXJlcXVpcmVkJyxcclxuICAgICAgICAnLS1ibG9jay1uZXctd2ViLWNvbnRlbnRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWFjY2VsZXJhdGVkLTJkLWNhbnZhcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLW5ldHdvcmtpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1icmVha3BhZCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jaGVja2VyLWltYWdpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC1leHRlbnNpb25zLXdpdGgtYmFja2dyb3VuZC1wYWdlcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtdXBkYXRlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRlZmF1bHQtYXBwcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZXYtc2htLXVzYWdlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRvbWFpbi1yZWxpYWJpbGl0eScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1leHRlbnNpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWZlYXR1cmVzPUNhbGN1bGF0ZU5hdGl2ZVdpbk9jY2x1c2lvbixJbnRlcmVzdEZlZWRDb250ZW50U3VnZ2VzdGlvbnMsV2ViT1RQJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1sb2dnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXBvcHVwLWJsb2NraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1yZW5kZXJlci1iYWNrZ3JvdW5kaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlYXJjaC1lbmdpbmUtY2hvaWNlLXNjcmVlbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNpdGUtaXNvbGF0aW9uLXRyaWFscycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zcGVlY2gtYXBpJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXN5bmMnLFxyXG4gICAgICAgICctLWVuYWJsZS11bnNhZmUtd2ViZ3B1JyxcclxuICAgICAgICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcclxuICAgICAgICAnLS1oaWRlLXNjcm9sbGJhcnMnLFxyXG4gICAgICAgICctLW1ldHJpY3MtcmVjb3JkaW5nLW9ubHknLFxyXG4gICAgICAgICctLW11dGUtYXVkaW8nLFxyXG4gICAgICAgICctLW5vLWRlZmF1bHQtYnJvd3Nlci1jaGVjaycsXHJcbiAgICAgICAgJy0tbm8tZmlyc3QtcnVuJyxcclxuICAgICAgICAnLS1uby1waW5ncycsXHJcbiAgICAgICAgJy0tbm8tc2FuZGJveCcsXHJcbiAgICAgICAgJy0tbm8tc3RhcnR1cC13aW5kb3cnLFxyXG4gICAgICAgICctLW5vLXp5Z290ZScsXHJcbiAgICAgICAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxyXG4gICAgICAgICctLXByb2Nlc3MtcGVyLXRhYicsXHJcbiAgICAgICAgJy0tdXNlLW1vY2sta2V5Y2hhaW4nXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZ1tdJ10sXHJcbiAgICAgIGVudkxpbms6ICdQVVBQRVRFRVJfQVJHUycsXHJcbiAgICAgIGNsaU5hbWU6ICdwdXBwZXRlZXJBcmdzJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBcnJheSBvZiBQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgICBzZXBhcmF0b3I6ICc7J1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBoaWdoY2hhcnRzOiB7XHJcbiAgICB2ZXJzaW9uOiB7XHJcbiAgICAgIHZhbHVlOiAnbGF0ZXN0JyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgdmVyc2lvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNkblVybDoge1xyXG4gICAgICB2YWx1ZTogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmb3JjZUZldGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmxhZyB0byByZWZldGNoIHNjcmlwdHMgYWZ0ZXIgZWFjaCBzZXJ2ZXIgcmVydW4nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNhY2hlUGF0aDoge1xyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DQUNIRV9QQVRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEaXJlY3RvcnkgcGF0aCBmb3IgY2FjaGVkIEhpZ2hjaGFydHMgc2NyaXB0cycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbJ2hpZ2hjaGFydHMnLCAnaGlnaGNoYXJ0cy1tb3JlJywgJ2hpZ2hjaGFydHMtM2QnXSxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nW10nXSxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbW9kdWxlU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdzdG9jaycsXHJcbiAgICAgICAgJ21hcCcsXHJcbiAgICAgICAgJ2dhbnR0JyxcclxuICAgICAgICAnZXhwb3J0aW5nJyxcclxuICAgICAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgICAgICdhY2Nlc3NpYmlsaXR5JyxcclxuICAgICAgICAvLyAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgICAgICdib29zdCcsXHJcbiAgICAgICAgJ2RhdGEnLFxyXG4gICAgICAgICdkYXRhLXRvb2xzJyxcclxuICAgICAgICAnZHJhZ2dhYmxlLXBvaW50cycsXHJcbiAgICAgICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAgICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICAgICAnaGVhdG1hcCcsXHJcbiAgICAgICAgJ3RpbGVtYXAnLFxyXG4gICAgICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAgICAgJ3RpbWVsaW5lJyxcclxuICAgICAgICAndHJlZW1hcCcsXHJcbiAgICAgICAgJ3RyZWVncmFwaCcsXHJcbiAgICAgICAgJ2l0ZW0tc2VyaWVzJyxcclxuICAgICAgICAnZHJpbGxkb3duJyxcclxuICAgICAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAgICAgJ2J1bGxldCcsXHJcbiAgICAgICAgJ2Z1bm5lbCcsXHJcbiAgICAgICAgJ2Z1bm5lbDNkJyxcclxuICAgICAgICAnZ2VvaGVhdG1hcCcsXHJcbiAgICAgICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAgICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAgICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzZXJpZXMtb24tcG9pbnQnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgLy8gJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaScsXHJcbiAgICAgICAgJ2Zsb3dtYXAnLFxyXG4gICAgICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAgICAgJ25hdmlnYXRvcicsXHJcbiAgICAgICAgJ3RleHRwYXRoJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBtb2R1bGUgc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogWydpbmRpY2F0b3JzLWFsbCddLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBpbmRpY2F0b3Igc2NyaXB0cyB0byBmZXRjaCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgY3VzdG9tU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQuanMvMi4zMC4xL21vbWVudC5taW4uanMnLFxyXG4gICAgICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQtdGltZXpvbmUvMC41LjQ1L21vbWVudC10aW1lem9uZS13aXRoLWRhdGEubWluLmpzJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlczogWydzdHJpbmdbXSddLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DVVNUT01fU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBjdXN0b20gc2NyaXB0cyBvciBkZXBlbmRlbmNpZXMgdG8gZmV0Y2gnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICAgIHNlcGFyYXRvcjogJzsnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGV4cG9ydDoge1xyXG4gICAgaW5maWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0lORklMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnB1dCBmaWxlbmFtZSB3aXRoIHR5cGUsIGZvcm1hdHRlZCBjb3JyZWN0bHkgYXMgSlNPTiBvciBTVkcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpbnN0cjoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSU5TVFInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3ZlcnJpZGVzIHRoZSBgaW5maWxlYCB3aXRoIEpTT04sIHN0cmluZ2lmaWVkIEpTT04sIG9yIFNWRyBpbnB1dCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ09iamVjdCcsICdzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09QVElPTlMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FsaWFzIGZvciB0aGUgYGluc3RyYCBvcHRpb24nLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzdmc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU1ZHJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTVkcgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydCB0byByZW5kZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CQVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdCYXRjaCBqb2Igc3RyaW5nIHdpdGggaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX09VVEZJTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnT3V0cHV0IGZpbGVuYW1lIHdpdGggdHlwZS4gQ2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcgYW5kIGlnbm9yZXMgYHR5cGVgIG9wdGlvbicsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdGaWxlIGV4cG9ydCBmb3JtYXQuIENhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICAgIGhpbnQ6ICdEZWZhdWx0OiBwbmcnLFxyXG4gICAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0NPTlNUUicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDaGFydCBjb25zdHJ1Y3Rvci4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgICBoaW50OiAnRGVmYXVsdDogY2hhcnQnLFxyXG4gICAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGI2NDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9CNjQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gdGhlIGNoYXJ0IHNob3VsZCBiZSByZWNlaXZlZCBpbiBCYXNlNjQgZm9ybWF0IGluc3RlYWQgb2YgYmluYXJ5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBub0Rvd25sb2FkOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX05PX0RPV05MT0FEJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZXRoZXIgb3Igbm90IHRvIGluY2x1ZGUgb3IgZXhjbHVkZSBhdHRhY2htZW50IGhlYWRlcnMgaW4gdGhlIHJlc3BvbnNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfSEVJR0hUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1dJRFRIJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRlcyBjaGFydCBzZXR0aW5ncycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlcicsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkZXMgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCBpZiBub3Qgc2V0JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQgaWYgbm90IHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1NDQUxFJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0RlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0IGlmIG5vdCBzZXQuIFJhbmdlcyBmcm9tIDAuMSB0byA1LjAnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgbWluOiAwLjEsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9HTE9CQUxfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggZ2xvYmFsIG9wdGlvbnMgZm9yIEhpZ2hjaGFydHMuc2V0T3B0aW9ucycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnT2JqZWN0JywgJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVEhFTUVfT1BUSU9OUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKU09OLCBzdHJpbmdpZmllZCBKU09OIG9yIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyBmb3IgSGlnaGNoYXJ0cy5zZXRPcHRpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHdlYnBhZ2UgcmVuZGVyaW5nJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWxsb3dzIG9yIGRpc2FsbG93cyBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgZHVyaW5nIGV4cG9ydGluZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FsbG93cyBvciBkaXNhbGxvd3MgaW5qZWN0aW9uIG9mIGZpbGVzeXN0ZW0gcmVzb3VyY2VzIChkaXNhYmxlZCBpbiBzZXJ2ZXIgbW9kZSknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgaW4gYSBmdW5jdGlvbiwgb3IgYSAuanMgZmlsZW5hbWUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19DQUxMQkFDSycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIENhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgLmpzIGZpbGVuYW1lJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICB0eXBlczogWydPYmplY3QnLCAnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZXMgYXMgSlNPTiwgc3RyaW5naWZpZWQgSlNPTiwgb3IgZmlsZW5hbWUsIGNvbnRhaW5pbmcgZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmlsZSB3aXRoIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQ1JFQVRFX0NPTkZJRycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdQcm9tcHQtYmFzZWQgb3B0aW9uIHNldHRpbmcsIHNhdmVkIHRvIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVTZXJ2ZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1N0YXJ0cyB0aGUgc2VydmVyIHdoZW4gdHJ1ZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgaG9zdDoge1xyXG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxyXG4gICAgICB0eXBlczogWydzdHJpbmcnXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIb3N0bmFtZSBvZiB0aGUgc2VydmVyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUG9ydCBudW1iZXIgZm9yIHRoZSBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHVwbG9hZExpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiAzLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9VUExPQURfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ01heGltdW0gcmVxdWVzdCBib2R5IHNpemUgaW4gTUInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAnc2VydmVyQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Rpc3BsYXlzIG9yIG5vdCBhY3Rpb24gZHVyYXRpb25zIGluIG1pbGxpc2Vjb25kcyBkdXJpbmcgc2VydmVyIHJlcXVlc3RzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBwcm94eToge1xyXG4gICAgICBob3N0OiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eUhvc3QnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnSG9zdCBvZiB0aGUgcHJveHkgc2VydmVyLCBpZiBhcHBsaWNhYmxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogbnVsbCxcclxuICAgICAgICB0eXBlczogWydudW1iZXInLCAnbnVsbCddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGFwcGxpY2FibGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgYXBwbGljYWJsZScsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc2VydmVyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdNYXhpbXVtIG51bWJlciBvZiByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB3aW5kb3c6IHtcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RpbWUgd2luZG93IGluIG1pbnV0ZXMgZm9yIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnRGVsYXkgZHVyYXRpb24gYmV0d2VlbiBzdWNjZXNzaXZlIHJlcXVlc3RzIGJlZm9yZSByZWFjaGluZyB0aGUgbGltaXQnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRvIHRydWUgaWYgdGhlIHNlcnZlciBpcyBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgc2tpcEtleToge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0tleSB0byBieXBhc3MgdGhlIHJhdGUgbGltaXRlciwgdXNlZCB3aXRoIGBza2lwVG9rZW5gJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBUb2tlbjoge1xyXG4gICAgICAgIHZhbHVlOiBudWxsLFxyXG4gICAgICAgIHR5cGVzOiBbJ3N0cmluZycsICdudWxsJ10sXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVG9rZW4gdG8gYnlwYXNzIHRoZSByYXRlIGxpbWl0ZXIsIHVzZWQgd2l0aCBgc2tpcEtleWAnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0ZXh0J1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdGb3JjZXMgdGhlIHNlcnZlciB0byB1c2UgSFRUUFMgb25seSB3aGVuIHRydWUnLFxyXG4gICAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdQb3J0IGZvciB0aGUgU1NMIHNlcnZlcicsXHJcbiAgICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IG51bGwsXHJcbiAgICAgICAgdHlwZXM6IFsnc3RyaW5nJywgJ251bGwnXSxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xDZXJ0UGF0aCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlJyxcclxuICAgICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWluaW11bSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTWF4aW11bSBudW1iZXIgb2YgcG9vbCB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICB3b3JrTGltaXQ6IHtcclxuICAgICAgdmFsdWU6IDQwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnTnVtYmVyIG9mIHRhc2tzIGEgd29ya2VyIGNhbiBoYW5kbGUgYmVmb3JlIHJlc3RhcnRpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBpZGxlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMzAwMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciBkZXN0cm95aW5nIGlkbGUgcmVzb3VyY2VzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAyMDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGJlZm9yZSByZXRyeWluZyByZXNvdXJjZSBjcmVhdGlvbiBvbiBmYWlsdXJlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgdG8gY2hlY2sgYW5kIGRlc3Ryb3kgaWRsZSByZXNvdXJjZXMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ251bWJlcidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGJlbmNobWFya2luZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgY2xpTmFtZTogJ3Bvb2xCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1Nob3dzIHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZXM6IFsnbnVtYmVyJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdMb2dnaW5nIHZlcmJvc2l0eSBsZXZlbCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICByb3VuZDogMCxcclxuICAgICAgICBtaW46IDAsXHJcbiAgICAgICAgbWF4OiA1XHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnTG9nIGZpbGUgbmFtZS4gUmVxdWlyZXMgYGxvZ1RvRmlsZWAgYW5kIGBsb2dEZXN0YCB0byBiZSBzZXQnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZXN0OiB7XHJcbiAgICAgIHZhbHVlOiAnbG9nJyxcclxuICAgICAgdHlwZXM6IFsnc3RyaW5nJ10sXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnUGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFJlcXVpcmVzIGBsb2dUb0ZpbGVgIHRvIGJlIHNldCcsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndGV4dCdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvQ29uc29sZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvQ29uc29sZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBjb25zb2xlIGxvZ2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHRvRmlsZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ1RvRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICB1aToge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVVpJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGVuZHBvaW50IHJvdXRlIGZvciB0aGUgVUknLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGVzOiBbJ3N0cmluZyddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBOb2RlLmpzIGVudmlyb25tZW50IHR5cGUnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RleHQnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0Rpc3BsYXkgb3Igc2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBzdGFydHVwJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBoYXJkUmVzZXRQYWdlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfSEFSRF9SRVNFVF9QQUdFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdXaGV0aGVyIG9yIG5vdCB0byByZXNldCB0aGUgcGFnZSBjb250ZW50IGVudGlyZWx5JyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBicm93c2VyU2hlbGxNb2RlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1doZXRoZXIgb3Igbm90IHRvIHNldCB0aGUgYnJvd3NlciB0byBydW4gaW4gc2hlbGwgbW9kZScsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAndG9nZ2xlJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBkZWJ1Zzoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZURlYnVnJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSB1bmRlcmx5aW5nIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGhlYWRsZXNzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnV2hldGhlciBvciBub3QgdG8gc2V0IHRoZSBicm93c2VyIHRvIHJ1biBpbiBoZWFkbGVzcyBtb2RlIGR1cmluZyBkZWJ1Z2dpbmcnLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGRldnRvb2xzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZXM6IFsnYm9vbGVhbiddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgRGV2VG9vbHMgaW4gaGVhZGZ1bCBtb2RlJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlczogWydib29sZWFuJ10sXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIGxpc3RlbmluZyB0byBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBwcm9tcHRPcHRpb25zOiB7XHJcbiAgICAgICAgdHlwZTogJ3RvZ2dsZSdcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIGR1bXBpbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGVzOiBbJ2Jvb2xlYW4nXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RVTVBJTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdSZWRpcmVjdHMgb3Igbm90IGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gcHJvY2Vzcy5zdGRvdXQgYW5kIHByb2Nlc3Muc3RkZXJyJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICd0b2dnbGUnXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBzbG93TW86IHtcclxuICAgICAgdmFsdWU6IDAsXHJcbiAgICAgIHR5cGVzOiBbJ251bWJlciddLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVsYXlzIFB1cHBldGVlciBvcGVyYXRpb25zIGJ5IHRoZSBzcGVjaWZpZWQgbWlsbGlzZWNvbmRzJyxcclxuICAgICAgcHJvbXB0T3B0aW9uczoge1xyXG4gICAgICAgIHR5cGU6ICdudW1iZXInXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlczogWydudW1iZXInXSxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdQb3J0IHVzZWQgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIHByb21wdE9wdGlvbnM6IHtcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJ1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgZGVmYXVsdENvbmZpZztcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgZmlsZSBpcyByZXNwb25zaWJsZSBmb3IgcGFyc2luZyB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbiAqIHdpdGggdGhlICd6b2QnIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkXHJcbiAqIHRvIGJlIHVzZWQgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIGBlbnZzYC4gV2Ugc2hvdWxkIG5vdCB1c2UgdGhlIGBwcm9jZXNzLmVudmBcclxuICogZGlyZWN0bHkgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuXHJcbiAqIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lclxyXG4gKiBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICovXHJcblxyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5cclxuaW1wb3J0IGRlZmF1bHRDb25maWcgZnJvbSAnLi9zY2hlbWFzL2NvbmZpZy5qcyc7XHJcblxyXG4vLyBMb2FkIC5lbnYgaW50byBlbnZpcm9ubWVudCB2YXJpYWJsZXNcclxuZG90ZW52LmNvbmZpZygpO1xyXG5cclxuLy8gT2JqZWN0IHdpdGggY3VzdG9tIHZhbGlkYXRvcnMgYW5kIHRyYW5zZm9ybWVycywgdG8gYXZvaWQgcmVwZXRpdGlvblxyXG4vLyBpbiB0aGUgQ29uZmlnIG9iamVjdFxyXG5jb25zdCB2ID0ge1xyXG4gIC8vIFNwbGl0cyBzdHJpbmcgdmFsdWUgaW50byBlbGVtZW50cyBpbiBhbiBhcnJheSwgdHJpbXMgZXZlcnkgZWxlbWVudCwgY2hlY2tzXHJcbiAgLy8gaWYgYW4gYXJyYXkgaXMgY29ycmVjdCwgaWYgaXQgaXMgZW1wdHksIGFuZCBpZiBpdCBpcywgcmV0dXJucyB1bmRlZmluZWRcclxuICBhcnJheTogKGZpbHRlckFycmF5KSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWVcclxuICAgICAgICAgIC5zcGxpdCgnLCcpXHJcbiAgICAgICAgICAubWFwKCh2YWx1ZSkgPT4gdmFsdWUudHJpbSgpKVxyXG4gICAgICAgICAgLmZpbHRlcigodmFsdWUpID0+IGZpbHRlckFycmF5LmluY2x1ZGVzKHZhbHVlKSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlLmxlbmd0aCA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBvbmx5IHRydWUsIGZhbHNlIGFuZCBjb3JyZWN0bHkgcGFyc2UgdGhlIHZhbHVlIHRvIGJvb2xlYW5cclxuICAvLyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsIGJlIHVuZGVmaW5lZFxyXG4gIGJvb2xlYW46ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsndHJ1ZScsICdmYWxzZScsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA9PT0gJ3RydWUnIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwYXNzZWQgdmFsdWVzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBlbnVtOiAodmFsdWVzKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbLi4udmFsdWVzLCAnJ10pXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gVHJpbXMgdGhlIHN0cmluZyB2YWx1ZSBhbmQgY2hlY2tzIGlmIGl0IGlzIGVtcHR5IG9yIGNvbnRhaW5zIHN0cmluZ2lmaWVkXHJcbiAgLy8gdmFsdWVzIHN1Y2ggYXMgZmFsc2UsIHVuZGVmaW5lZCwgbnVsbCwgTmFOLCBpZiBpdCBkb2VzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIHN0cmluZzogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICAhWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nXS5pbmNsdWRlcyh2YWx1ZSkgfHxcclxuICAgICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHN0cmluZyBjb250YWlucyBmb3JiaWRkZW4gdmFsdWVzLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICAgIH0pXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3MgcG9zaXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsXHJcbiAgLy8gYmUgdW5kZWZpbmVkXHJcbiAgcG9zaXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID4gMCksXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSB2YWx1ZSBtdXN0IGJlIG51bWVyaWMgYW5kIHBvc2l0aXZlLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICAgIH0pXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3Mgbm9uLW5lZ2F0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWVcclxuICAvLyB3aWxsIGJlIHVuZGVmaW5lZFxyXG4gIG5vbk5lZ2F0aXZlTnVtOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+PSAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgbm9uLW5lZ2F0aXZlLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICAgIH0pXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpXHJcbn07XHJcblxyXG5leHBvcnQgY29uc3QgQ29uZmlnID0gei5vYmplY3Qoe1xyXG4gIC8vIHB1cHBldGVlclxyXG4gIFBVUFBFVEVFUl9BUkdTOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBoaWdoY2hhcnRzXHJcbiAgSElHSENIQVJUU19WRVJTSU9OOiB6XHJcbiAgICAuc3RyaW5nKClcclxuICAgIC50cmltKClcclxuICAgIC5yZWZpbmUoXHJcbiAgICAgICh2YWx1ZSkgPT4gL14obGF0ZXN0fFxcZCsoXFwuXFxkKyl7MCwyfSkkLy50ZXN0KHZhbHVlKSB8fCB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSElHSENIQVJUU19WRVJTSU9OIG11c3QgYmUgJ2xhdGVzdCcsIGEgbWFqb3IgdmVyc2lvbiwgb3IgaW4gdGhlIGZvcm0gWFguWVkuWlosIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcbiAgSElHSENIQVJUU19DRE5fVVJMOiB6XHJcbiAgICAuc3RyaW5nKClcclxuICAgIC50cmltKClcclxuICAgIC5yZWZpbmUoXHJcbiAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwczovLycpIHx8XHJcbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cDovLycpIHx8XHJcbiAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIEhJR0hDSEFSVFNfQ0ROX1VSTC4gSXQgc2hvdWxkIHN0YXJ0IHdpdGggaHR0cDovLyBvciBodHRwczovLywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIOiB2LmJvb2xlYW4oKSxcclxuICBISUdIQ0hBUlRTX0NBQ0hFX1BBVEg6IHYuc3RyaW5nKCksXHJcbiAgSElHSENIQVJUU19BRE1JTl9UT0tFTjogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0NPUkVfU0NSSVBUUzogdi5hcnJheShkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY29yZVNjcmlwdHMudmFsdWUpLFxyXG4gIEhJR0hDSEFSVFNfTU9EVUxFX1NDUklQVFM6IHYuYXJyYXkoXHJcbiAgICBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMubW9kdWxlU2NyaXB0cy52YWx1ZVxyXG4gICksXHJcbiAgSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUzogdi5hcnJheShcclxuICAgIGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5pbmRpY2F0b3JTY3JpcHRzLnZhbHVlXHJcbiAgKSxcclxuICBISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTOiB2LmFycmF5KFxyXG4gICAgZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmN1c3RvbVNjcmlwdHMudmFsdWVcclxuICApLFxyXG5cclxuICAvLyBleHBvcnRcclxuICBFWFBPUlRfSU5GSUxFOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9JTlNUUjogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfT1BUSU9OUzogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfU1ZHOiB2LnN0cmluZygpLFxyXG4gIEVYUE9SVF9CQVRDSDogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfT1VURklMRTogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfVFlQRTogdi5lbnVtKFsnanBlZycsICdwbmcnLCAncGRmJywgJ3N2ZyddKSxcclxuICBFWFBPUlRfQ09OU1RSOiB2LmVudW0oWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXSksXHJcbiAgRVhQT1JUX0I2NDogdi5ib29sZWFuKCksXHJcbiAgRVhQT1JUX05PX0RPV05MT0FEOiB2LmJvb2xlYW4oKSxcclxuICBFWFBPUlRfSEVJR0hUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX1dJRFRIOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX1NDQUxFOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfSEVJR0hUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9HTE9CQUxfT1BUSU9OUzogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfVEhFTUVfT1BUSU9OUzogdi5zdHJpbmcoKSxcclxuICBFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIGN1c3RvbVxyXG4gIENVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTjogdi5ib29sZWFuKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQ1VTVE9NX0NPREU6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0NBTExCQUNLOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19SRVNPVVJDRVM6IHYuc3RyaW5nKCksXHJcbiAgQ1VTVE9NX0xPR0lDX0xPQURfQ09ORklHOiB2LnN0cmluZygpLFxyXG4gIENVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXJcclxuICBTRVJWRVJfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9VUExPQURfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gc2VydmVyIHByb3h5XHJcbiAgU0VSVkVSX1BST1hZX0hPU1Q6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1BST1hZX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUFJPWFlfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcmF0ZSBsaW1pdGluZ1xyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVk6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVk6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTjogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gc2VydmVyIHNzbFxyXG4gIFNFUlZFUl9TU0xfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfU1NMX0ZPUkNFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfU1NMX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfU1NMX0NFUlRfUEFUSDogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gcG9vbFxyXG4gIFBPT0xfTUlOX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX01BWF9XT1JLRVJTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9XT1JLX0xJTUlUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgUE9PTF9BQ1FVSVJFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0NSRUFURV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9ERVNUUk9ZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0lETEVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1JFVFJZX0lOVEVSVkFMOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9SRUFQRVJfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0JFTkNITUFSS0lORzogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIGxvZ2dlclxyXG4gIExPR0dJTkdfTEVWRUw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlID09PSAnJyB8fFxyXG4gICAgICAgICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmXHJcbiAgICAgICAgICBwYXJzZUZsb2F0KHZhbHVlKSA+PSAwICYmXHJcbiAgICAgICAgICBwYXJzZUZsb2F0KHZhbHVlKSA8PSA1KSxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBMT0dHSU5HX0xFVkVMLiBXZSBvbmx5IGFjY2VwdCB2YWx1ZXMgZnJvbSAwIHRvIDUgYXMgbG9nZ2luZyBsZXZlbHMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcbiAgTE9HR0lOR19GSUxFOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfREVTVDogdi5zdHJpbmcoKSxcclxuICBMT0dHSU5HX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIExPR0dJTkdfVE9fRklMRTogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIHVpXHJcbiAgVUlfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBVSV9ST1VURTogdi5zdHJpbmcoKSxcclxuXHJcbiAgLy8gb3RoZXJcclxuICBPVEhFUl9OT0RFX0VOVjogdi5lbnVtKFsnZGV2ZWxvcG1lbnQnLCAncHJvZHVjdGlvbicsICd0ZXN0J10pLFxyXG4gIE9USEVSX0xJU1RFTl9UT19QUk9DRVNTX0VYSVRTOiB2LmJvb2xlYW4oKSxcclxuICBPVEhFUl9OT19MT0dPOiB2LmJvb2xlYW4oKSxcclxuICBPVEhFUl9IQVJEX1JFU0VUX1BBR0U6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0JST1dTRVJfU0hFTExfTU9ERTogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIGRlYnVnZ2VyXHJcbiAgREVCVUdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19IRUFETEVTUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfREVWVE9PTFM6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0xJU1RFTl9UT19DT05TT0xFOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19EVU1QSU86IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX1NMT1dfTU86IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBERUJVR19ERUJVR0dJTkdfUE9SVDogdi5wb3NpdGl2ZU51bSgpXHJcbn0pO1xyXG5cclxuZXhwb3J0IGNvbnN0IGVudnMgPSBDb25maWcucGFydGlhbCgpLnBhcnNlKHByb2Nlc3MuZW52KTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdXRpbGl0eSBtb2R1bGUgcHJvdmlkZXNcclxuICogYSBjb21wcmVoZW5zaXZlIHNldCBvZiBoZWxwZXIgZnVuY3Rpb25zIGFuZCBjb25zdGFudHMgZGVzaWduZWQgdG8gc3RyZWFtbGluZVxyXG4gKiBhbmQgZW5oYW5jZSB2YXJpb3VzIG9wZXJhdGlvbnMgcmVxdWlyZWQgZm9yIEhpZ2hjaGFydHMgZXhwb3J0IHRhc2tzLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGlzQWJzb2x1dGUsIG5vcm1hbGl6ZSwgcmVzb2x2ZSB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBkaXJlY3RvcnkgcGF0aFxyXG5leHBvcnQgY29uc3QgX19kaXJuYW1lID0gZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuLi8uJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFuZCBzdGFuZGFyZGl6ZXMgdGV4dCBieSByZXBsYWNpbmcgbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZVxyXG4gKiBjaGFyYWN0ZXJzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIHRyaW1taW5nIGFueSBsZWFkaW5nIG9yIHRyYWlsaW5nXHJcbiAqIHdoaXRlc3BhY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhclRleHRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyB0aGUgJy9cXHNcXHMrL2cnIFJlZ0V4cC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtyZXBsYWNlcj0nICddIC0gVGhlIHN0cmluZyB1c2VkIHRvIHJlcGxhY2UgbXVsdGlwbGVcclxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyB0aGUgJyAnIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyVGV4dCh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpIHtcclxuICByZXR1cm4gdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBkZWVwQ29weVxyXG4gKlxyXG4gKiBAcGFyYW0geyhPYmplY3R8QXJyYXkpfSBvYmpBcnIgLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fEFycmF5KX0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDb3B5KG9iakFycikge1xyXG4gIC8vIElmIHRoZSBgb2JqQXJyYCBpcyBudWxsIG9yIG5vdCBvZiB0aGUgYG9iamVjdGAgdHlwZSwgcmV0dXJuIGl0XHJcbiAgaWYgKG9iakFyciA9PT0gbnVsbCB8fCB0eXBlb2Ygb2JqQXJyICE9PSAnb2JqZWN0Jykge1xyXG4gICAgcmV0dXJuIG9iakFycjtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZWl0aGVyIGEgbmV3IGFycmF5IG9yIGEgbmV3IG9iamVjdFxyXG4gIGNvbnN0IG9iakFyckNvcHkgPSBBcnJheS5pc0FycmF5KG9iakFycikgPyBbXSA6IHt9O1xyXG5cclxuICAvLyBSZWN1cnNpdmVseSBjb3B5IGVhY2ggcHJvcGVydHlcclxuICBmb3IgKGNvbnN0IGtleSBpbiBvYmpBcnIpIHtcclxuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqQXJyLCBrZXkpKSB7XHJcbiAgICAgIG9iakFyckNvcHlba2V5XSA9IGRlZXBDb3B5KG9iakFycltrZXldKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgY29waWVkIG9iamVjdFxyXG4gIHJldHVybiBvYmpBcnJDb3B5O1xyXG59XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZXhwQmFja29mZlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBmdW5jdGlvbiB0byBiZSByZXRyaWVkLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci4gVGhlIGRlZmF1bHQgdmFsdWVcclxuICogaXMgYDBgLlxyXG4gKiBAcGFyYW0gey4uLnVua25vd259IGFyZ3MgLSBBcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHRoZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSByZXN1bHRcclxuICogb2YgdGhlIGZ1bmN0aW9uIGlmIHN1Y2Nlc3NmdWwuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCBpZiB0aGUgbWF4aW11bSBudW1iZXIgb2YgYXR0ZW1wdHNcclxuICogaXMgcmVhY2hlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBleHBCYWNrb2ZmKGZuLCBhdHRlbXB0ID0gMCwgLi4uYXJncykge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBUcnkgdG8gY2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIHJldHVybiBhd2FpdCBmbiguLi5hcmdzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gQ2FsY3VsYXRlIGRlbGF5IGluIG1zXHJcbiAgICBjb25zdCBkZWxheUluTXMgPSAyICoqIGF0dGVtcHQgKiAxMDAwO1xyXG5cclxuICAgIC8vIElmIHRoZSBhdHRlbXB0IGV4Y2VlZHMgdGhlIG1heGltdW0gYXR0ZW1wdHMgb2YgcmVwZWF0LCB0aHJvdyBhbiBlcnJvclxyXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXYWl0IGdpdmVuIGFtb3VudCBvZiB0aW1lXHJcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIGRlbGF5SW5NcykpO1xyXG5cclxuICAgIC8vLyBUTyBETzogQ29ycmVjdFxyXG4gICAgLy8gLy8gSW5mb3JtYXRpb24gYWJvdXQgdGhlIHJlc291cmNlIHRpbWVvdXRcclxuICAgIC8vIGxvZyhcclxuICAgIC8vICAgMyxcclxuICAgIC8vICAgYFt1dGlsc10gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIG9mIElEOiAke2FyZ3NbMF19LmBcclxuICAgIC8vICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBwYXRoIGlzIHJlbGF0aXZlIG9yIGFic29sdXRlIGFuZCByZXR1cm5zIHRoZSBjb3JyZWN0ZWQsXHJcbiAqIGFic29sdXRlIHBhdGguXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBYnNvbHV0ZVBhdGhcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byBiZSBjaGVja2VkIG9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgYWJzb2x1dGUgcGF0aC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBYnNvbHV0ZVBhdGgocGF0aCkge1xyXG4gIHJldHVybiBpc0Fic29sdXRlKHBhdGgpID8gbm9ybWFsaXplKHBhdGgpIDogcmVzb2x2ZShwYXRoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGlucHV0IGRhdGEgdG8gYSBCYXNlNjQgc3RyaW5nIGJhc2VkIG9uIHRoZSBleHBvcnQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEJhc2U2NFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgLSBUaGUgaW5wdXQgdG8gYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZvcm1hdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBCYXNlNjQgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBpbnB1dC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRCYXNlNjQoaW5wdXQsIHR5cGUpIHtcclxuICAvLyBGb3IgcGRmIGFuZCBzdmcgdHlwZXMgdGhlIGlucHV0IG11c3QgYmUgdHJhbnNmb3JtZWQgdG8gQmFzZTY0IGZyb20gYSBidWZmZXJcclxuICBpZiAodHlwZSA9PT0gJ3BkZicgfHwgdHlwZSA9PSAnc3ZnJykge1xyXG4gICAgcmV0dXJuIEJ1ZmZlci5mcm9tKGlucHV0LCAndXRmOCcpLnRvU3RyaW5nKCdiYXNlNjQnKTtcclxuICB9XHJcblxyXG4gIC8vIEZvciBwbmcgYW5kIGpwZWcgaW5wdXQgaXMgYWxyZWFkeSBhIEJhc2U2NCBzdHJpbmdcclxuICByZXR1cm4gaW5wdXQ7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHN0cmluZ2lmaWVkIGRhdGUgd2l0aG91dCB0aGUgR01UIHRleHQgaW5mb3JtYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmV3RGF0ZSgpIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIHJldHVybiBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBzdG9yZWQgdGltZSB2YWx1ZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXROZXdEYXRlVGltZVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldE5ld0RhdGVUaW1lKCkge1xyXG4gIHJldHVybiBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiB0aGUgZ2l2ZW4gaXRlbSBpcyBhbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpc09iamVjdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgdGhlIGl0ZW0gaXMgYW4gb2JqZWN0LCBgZmFsc2VgXHJcbiAqIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc09iamVjdChpdGVtKSB7XHJcbiAgcmV0dXJuIE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChpdGVtKSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIG9iamVjdCBpcyBlbXB0eS5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW0gLSBUaGUgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiB0aGUgaXRlbSBpcyBhbiBlbXB0eSBvYmplY3QsIGBmYWxzZWBcclxuICogb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzT2JqZWN0RW1wdHkoaXRlbSkge1xyXG4gIHJldHVybiAoXHJcbiAgICB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcgJiZcclxuICAgICFBcnJheS5pc0FycmF5KGl0ZW0pICYmXHJcbiAgICBpdGVtICE9PSBudWxsICYmXHJcbiAgICBPYmplY3Qua2V5cyhpdGVtKS5sZW5ndGggPT09IDBcclxuICApO1xyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQgaW4gdGhlIGdpdmVuIHN0cmluZy5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmRcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGl0ZW0gLSBUaGUgc3RyaW5nIHRvIGJlIGNoZWNrZWQgZm9yIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kLCBgZmFsc2VgXHJcbiAqIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKGl0ZW0pIHtcclxuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pP2xvY2FsaG9zdFxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTBcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTcyXFwuKDFbNi05XXwyWzAtOV18M1swLTFdKVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE5MlxcLjE2OFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvXHJcbiAgXTtcclxuXHJcbiAgcmV0dXJuIHJlZ2V4UGF0dGVybnMuc29tZSgocGF0dGVybikgPT4gcGF0dGVybi50ZXN0KGl0ZW0pKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFV0aWxpdHkgdG8gbWVhc3VyZSBlbGFwc2VkIHRpbWUgdXNpbmcgdGhlIE5vZGUuanMgYHByb2Nlc3MuaHJ0aW1lKClgIG1ldGhvZC5cclxuICpcclxuICogQGZ1bmN0aW9uIG1lYXN1cmVUaW1lXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtGdW5jdGlvbn0gQSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGVsYXBzZWQgdGltZSBpbiBtaWxsaXNlY29uZHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbWVhc3VyZVRpbWUoKSB7XHJcbiAgY29uc3Qgc3RhcnQgPSBwcm9jZXNzLmhydGltZS5iaWdpbnQoKTtcclxuICByZXR1cm4gKCkgPT4gTnVtYmVyKHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpIC0gc3RhcnQpIC8gMTAwMDAwMDtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJvdW5kcyBhIG51bWJlciB0byB0aGUgc3BlY2lmaWVkIHByZWNpc2lvbi5cclxuICpcclxuICogQGZ1bmN0aW9uIHJvdW5kTnVtYmVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB2YWx1ZSAtIFRoZSBudW1iZXIgdG8gYmUgcm91bmRlZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHByZWNpc2lvbiAtIFRoZSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgdG8gcm91bmQgdG8uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtudW1iZXJ9IFRoZSByb3VuZGVkIG51bWJlci5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiByb3VuZE51bWJlcih2YWx1ZSwgcHJlY2lzaW9uID0gMSkge1xyXG4gIGNvbnN0IG11bHRpcGxpZXIgPSBNYXRoLnBvdygxMCwgcHJlY2lzaW9uIHx8IDApO1xyXG4gIHJldHVybiBNYXRoLnJvdW5kKCt2YWx1ZSAqIG11bHRpcGxpZXIpIC8gbXVsdGlwbGllcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGEgdmFsdWUgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdG9Cb29sZWFuXHJcbiAqXHJcbiAqIEBwYXJhbSB7dW5rbm93bn0gaXRlbSAtIFRoZSB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVGhlIGJvb2xlYW4gcmVwcmVzZW50YXRpb24gb2YgdGhlIGlucHV0IHZhbHVlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHRvQm9vbGVhbihpdGVtKSB7XHJcbiAgcmV0dXJuIFsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJywgJzAnLCAnJ10uaW5jbHVkZXMoaXRlbSlcclxuICAgID8gZmFsc2VcclxuICAgIDogISFpdGVtO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgX19kaXJuYW1lLFxyXG4gIGNsZWFyVGV4dCxcclxuICBkZWVwQ29weSxcclxuICBleHBCYWNrb2ZmLFxyXG4gIGdldEFic29sdXRlUGF0aCxcclxuICBnZXRCYXNlNjQsXHJcbiAgZ2V0TmV3RGF0ZSxcclxuICBnZXROZXdEYXRlVGltZSxcclxuICBpc09iamVjdCxcclxuICBpc09iamVjdEVtcHR5LFxyXG4gIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQsXHJcbiAgbWVhc3VyZVRpbWUsXHJcbiAgcm91bmROdW1iZXIsXHJcbiAgdG9Cb29sZWFuXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBBIG1vZHVsZSBmb3IgbWFuYWdpbmcgbG9nZ2luZyBmdW5jdGlvbmFsaXR5IHdpdGggY3VzdG9taXphYmxlXHJcbiAqIGxvZyBsZXZlbHMsIGNvbnNvbGUgYW5kIGZpbGUgbG9nZ2luZyBvcHRpb25zLCBhbmQgZXJyb3IgaGFuZGxpbmcgc3VwcG9ydC5cclxuICogVGhlIG1vZHVsZSBhbHNvIGVuc3VyZXMgdGhhdCBmaWxlLWJhc2VkIGxvZ3MgYXJlIHN0b3JlZCBpbiBhIHN0cnVjdHVyZWRcclxuICogZGlyZWN0b3J5LCBjcmVhdGluZyB0aGUgbmVjZXNzYXJ5IHBhdGhzIGF1dG9tYXRpY2FsbHkgaWYgdGhleSBkbyBub3QgZXhpc3QuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYXBwZW5kRmlsZSwgZXhpc3RzU3luYywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGgsIGdldE5ld0RhdGUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmNvbnN0IGxvZ2dpbmcgPSB7XHJcbiAgLy8gRmxhZ3MgZm9yIGxvZ2dpbmcgc3RhdHVzXHJcbiAgdG9Db25zb2xlOiB0cnVlLFxyXG4gIHRvRmlsZTogZmFsc2UsXHJcbiAgcGF0aENyZWF0ZWQ6IGZhbHNlLFxyXG4gIC8vIEZ1bGwgcGF0aCB0byB0aGUgbG9nIGZpbGVcclxuICBwYXRoVG9Mb2c6ICcnLFxyXG4gIC8vIExvZyBsZXZlbHNcclxuICBsZXZlbHNEZXNjOiBbXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnZXJyb3InLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzBdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3dhcm5pbmcnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzFdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ25vdGljZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMl1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAndmVyYm9zZScsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbM11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnYmVuY2htYXJrJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1s0XVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGEgbWVzc2FnZSB3aXRoIGEgc3BlY2lmaWVkIGxvZyBsZXZlbC4gQWNjZXB0cyBhIHZhcmlhYmxlIG51bWJlclxyXG4gKiBvZiBhcmd1bWVudHMuIFRoZSBhcmd1bWVudHMgYWZ0ZXIgdGhlIGBsZXZlbGAgYXJlIHBhc3NlZCB0byBgY29uc29sZS5sb2dgXHJcbiAqIGFuZC9vciB1c2VkIHRvIGNvbnN0cnVjdCBhbmQgYXBwZW5kIG1lc3NhZ2VzIHRvIGEgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBsb2dcclxuICpcclxuICogQHBhcmFtIHsuLi51bmtub3dufSBhcmdzIC0gQW4gYXJyYXkgb2YgYXJndW1lbnRzIHdoZXJlIHRoZSBmaXJzdCBpcyB0aGUgbG9nXHJcbiAqIGxldmVsIGFuZCB0aGUgcmVtYWluaW5nIGFyZSBzdHJpbmdzIHVzZWQgdG8gYnVpbGQgdGhlIGxvZyBtZXNzYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nKC4uLmFyZ3MpIHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbHNEZXNjLCBsZXZlbCB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlIG9yIGlzIGl0IGEgYmVuY2htYXJrIGxvZ1xyXG4gIGlmIChcclxuICAgIG5ld0xldmVsICE9PSA1ICYmXHJcbiAgICAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKVxyXG4gICkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke2dldE5ld0RhdGUoKX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdCh0ZXh0cylcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIGFsb25nIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbVxyXG4gKiBtZXNzYWdlIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ1dpdGhTdGFja1xyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbG9nIGxldmVsLlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QgY29udGFpbmluZyB0aGUgc3RhY2sgdHJhY2UuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21NZXNzYWdlIC0gQW4gb3B0aW9uYWwgY3VzdG9tIG1lc3NhZ2UgdG8gYmUgaW5jbHVkZWRcclxuICogaW4gdGhlIGxvZyBhbG9uZ3NpZGUgdGhlIGVycm9yLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gRXhpdHMgdGhlIGZ1bmN0aW9uIGV4ZWN1dGlvbiBpZiBhdHRlbXB0aW5nIHRvIGxvZyBhdCBhIGxldmVsXHJcbiAqIGhpZ2hlciB0aGFuIGFsbG93ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gbG9nV2l0aFN0YWNrKG5ld0xldmVsLCBlcnJvciwgY3VzdG9tTWVzc2FnZSkge1xyXG4gIC8vIEdldCB0aGUgbWFpbiBtZXNzYWdlXHJcbiAgY29uc3QgbWFpbk1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlIHx8IChlcnJvciAmJiBlcnJvci5tZXNzYWdlKSB8fCAnJztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7Z2V0TmV3RGF0ZSgpfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQWRkIHRoZSB3aG9sZSBzdGFjayBtZXNzYWdlXHJcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID0gZXJyb3IgJiYgZXJyb3Iuc3RhY2s7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2UsIGlmIGV4aXN0c1xyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlXTtcclxuICBpZiAoc3RhY2tNZXNzYWdlKSB7XHJcbiAgICB0ZXh0cy5wdXNoKCdcXG4nLCBzdGFja01lc3NhZ2UpO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIF9sb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcclxuICAgICAgICB0ZXh0cy5zaGlmdCgpW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAuLi50ZXh0c1xyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBpbml0TG9nZ2luZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgbG9nZ2luZ2Agb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpbml0TG9nZ2luZyhsb2dnaW5nT3B0aW9ucykge1xyXG4gIC8vIEdldCBvcHRpb25zIGZyb20gdGhlIGBsb2dnaW5nT3B0aW9uc2Agb2JqZWN0XHJcbiAgY29uc3QgeyBsZXZlbCwgZGVzdCwgZmlsZSwgdG9Db25zb2xlLCB0b0ZpbGUgfSA9IGxvZ2dpbmdPcHRpb25zO1xyXG5cclxuICAvLyBSZXNldCBmbGFncyB0byB0aGUgZGVmYXVsdCB2YWx1ZXNcclxuICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gZmFsc2U7XHJcbiAgbG9nZ2luZy5wYXRoVG9Mb2cgPSAnJztcclxuXHJcbiAgLy8gU2V0IHRoZSBsb2dnaW5nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobGV2ZWwpO1xyXG5cclxuICAvLyBTZXQgdGhlIGNvbnNvbGUgbG9nZ2luZ1xyXG4gIGVuYWJsZUNvbnNvbGVMb2dnaW5nKHRvQ29uc29sZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgZmlsZSBsb2dnaW5nXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGxvZyBsZXZlbCB0byB0aGUgc3BlY2lmaWVkIHZhbHVlLiBMb2cgbGV2ZWxzIGFyZSAoYDBgID0gbm8gbG9nZ2luZyxcclxuICogYDFgID0gZXJyb3IsIGAyYCA9IHdhcm5pbmcsIGAzYCA9IG5vdGljZSwgYDRgID0gdmVyYm9zZSwgb3IgYDVgID0gYmVuY2htYXJrKS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldExvZ0xldmVsXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsb2cgbGV2ZWwgdG8gYmUgc2V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldExvZ0xldmVsKGxldmVsKSB7XHJcbiAgaWYgKFxyXG4gICAgTnVtYmVyLmlzSW50ZWdlcihsZXZlbCkgJiZcclxuICAgIGxldmVsID49IDAgJiZcclxuICAgIGxldmVsIDw9IGxvZ2dpbmcubGV2ZWxzRGVzYy5sZW5ndGhcclxuICApIHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgbW9kdWxlIGxvZ2dpbmcncyBgbGV2ZWxgIG9wdGlvblxyXG4gICAgbG9nZ2luZy5sZXZlbCA9IGxldmVsO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgY29uc29sZSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZW5hYmxlQ29uc29sZUxvZ2dpbmdcclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB0b0NvbnNvbGUgLSBUaGUgZmxhZyBmb3Igc2V0dGluZyB0aGUgbG9nZ2luZyB0byB0aGUgY29uc29sZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBlbmFibGVDb25zb2xlTG9nZ2luZyh0b0NvbnNvbGUpIHtcclxuICAvLyBVcGRhdGUgdGhlIG1vZHVsZSBsb2dnaW5nJ3MgYHRvQ29uc29sZWAgb3B0aW9uXHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSAhIXRvQ29uc29sZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUgbmFtZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZUZpbGVMb2dnaW5nXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBkZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggd2hlcmUgdGhlIGxvZyBmaWxlIHNob3VsZFxyXG4gKiBiZSBzYXZlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGZpbGUgLSBUaGUgbmFtZSBvZiB0aGUgbG9nIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gdG9GaWxlIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciBsb2dnaW5nIHNob3VsZFxyXG4gKiBiZSBkaXJlY3RlZCB0byBhIGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlRmlsZUxvZ2dpbmcoZGVzdCwgZmlsZSwgdG9GaWxlKSB7XHJcbiAgLy8gVXBkYXRlIHRoZSBtb2R1bGUgbG9nZ2luZydzIGB0b0ZpbGVgIG9wdGlvblxyXG4gIGxvZ2dpbmcudG9GaWxlID0gISF0b0ZpbGU7XHJcblxyXG4gIC8vIFNldCB0aGUgYGRlc3RgIGFuZCBgZmlsZWAgb3B0aW9ucyBvbmx5IGlmIHRoZSBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZFxyXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ID0gZGVzdCB8fCAnbG9nJztcclxuICAgIGxvZ2dpbmcuZmlsZSA9IGZpbGUgfHwgJ2hpZ2hjaGFydHMtZXhwb3J0LXNlcnZlci5sb2cnO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIExvZ3MgdGhlIHByb3ZpZGVkIHRleHRzIHRvIGEgZmlsZSwgaWYgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWQuIEl0IGNyZWF0ZXNcclxuICogdGhlIG5lY2Vzc2FyeSBkaXJlY3Rvcnkgc3RydWN0dXJlIGlmIG5vdCBhbHJlYWR5IGNyZWF0ZWQgYW5kIGFwcGVuZHNcclxuICogdGhlIGNvbnRlbnQsIGluY2x1ZGluZyBhbiBvcHRpb25hbCBwcmVmaXgsIHRvIHRoZSBzcGVjaWZpZWQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9nVG9GaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXk8c3RyaW5nPn0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBBbiBvcHRpb25hbCBwcmVmaXggdG8gYmUgYWRkZWQgdG8gZWFjaCBsb2cgZW50cnkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfbG9nVG9GaWxlKHRleHRzLCBwcmVmaXgpIHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMoZ2V0QWJzb2x1dGVQYXRoKGxvZ2dpbmcuZGVzdCkpICYmXHJcbiAgICAgIG1rZGlyU3luYyhnZXRBYnNvbHV0ZVBhdGgobG9nZ2luZy5kZXN0KSk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIHRoZSBmdWxsIHBhdGhcclxuICAgIGxvZ2dpbmcucGF0aFRvTG9nID0gZ2V0QWJzb2x1dGVQYXRoKGpvaW4obG9nZ2luZy5kZXN0LCBsb2dnaW5nLmZpbGUpKTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBsb2dnaW5nLnBhdGhUb0xvZyxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yICYmIGxvZ2dpbmcudG9GaWxlICYmIGxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGUuYCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVGhpcyBtb2R1bGUgbWFuYWdlcyBjb25maWd1cmF0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcbiAqIGJ5IGxvYWRpbmcgYW5kIG1lcmdpbmcgb3B0aW9ucyBmcm9tIG11bHRpcGxlIHNvdXJjZXMsIHN1Y2ggYXMgdGhlIGRlZmF1bHRcclxuICogc2V0dGluZ3MsIGVudmlyb25tZW50IHZhcmlhYmxlcywgdXNlci1wcm92aWRlZCBvcHRpb25zLCBhbmQgY29tbWFuZC1saW5lXHJcbiAqIGFyZ3VtZW50cy4gRW5zdXJlcyB0aGUgZ2xvYmFsIG9wdGlvbnMgYXJlIHVwLXRvLWRhdGUgd2l0aCB0aGUgaGlnaGVzdFxyXG4gKiBwcmlvcml0eSB2YWx1ZXMuIFByb3ZpZGVzIGZ1bmN0aW9ucyBmb3IgYWNjZXNzaW5nIGFuZCB1cGRhdGluZyBjb25maWd1cmF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZGVlcENvcHksIGdldEFic29sdXRlUGF0aCwgaXNPYmplY3QgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBkZWZhdWx0Q29uZmlnIGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuLy8gU2V0cyB0aGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCBpbml0aWFsIHZhbHVlcyBmcm9tIHRoZSBkZWZhdWx0IGNvbmZpZ1xyXG5jb25zdCBnbG9iYWxPcHRpb25zID0gX2luaXRPcHRpb25zKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLy8gUHJvcGVydGllcyBuZXN0aW5nIGxldmVsIG9mIGFsbCBvcHRpb25zXHJcbmNvbnN0IG5lc3RlZFByb3BzID0gX2NyZWF0ZU5lc3RlZFByb3BzKGRlZmF1bHRDb25maWcpO1xyXG5cclxuLy8gUHJvcGVydGllcyBuYW1lcyB0aGF0IHNob3VsZCBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkXHJcbmNvbnN0IGFic29sdXRlUHJvcHMgPSBfY3JlYXRlQWJzb2x1dGVQcm9wcyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYSBjb3B5IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3Qgb3IgYW4gb3JpZ2luYWwgZ2xvYmFsIG9wdGlvbnNcclxuICogb2JqZWN0LCBiYXNlZCBvbiB0aGUgYGdldENvcHlgIGZsYWcuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldENvcHk9dHJ1ZV0gLSBTcGVjaWZpZXMgd2hldGhlciB0byByZXR1cm4gYSBjb3BpZWRcclxuICogb2JqZWN0IG9mIHRoZSBnbG9iYWwgb3B0aW9ucyAoYHRydWVgKSBvciBhIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsIG9wdGlvbnNcclxuICogb2JqZWN0IChgZmFsc2VgKS4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYHRydWVgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBIGNvcHkgb2YgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCwgb3IgYSByZWZlcmVuY2VcclxuICogdG8gdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRPcHRpb25zKGdldENvcHkgPSB0cnVlKSB7XHJcbiAgLy8gUmV0dXJuIGEgY29weSBvciBhbiBvcmlnaW5hbCBnbG9iYWwgb3B0aW9ucyBvYmplY3RcclxuICByZXR1cm4gZ2V0Q29weSA/IGRlZXBDb3B5KGdsb2JhbE9wdGlvbnMpIDogZ2xvYmFsT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgYW5kIHJldHVybnMgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCBvciBhIGNvcHkgb2YgdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAqIG9iamVjdCwgYmFzZWQgb24gdGhlIGBnZXRDb3B5YCBmbGFnLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXBkYXRlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBuZXcgb3B0aW9ucyB0byBiZVxyXG4gKiBtZXJnZWQgaW50byB0aGUgZ2xvYmFsIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2dldENvcHk9ZmFsc2VdIC0gRGV0ZXJtaW5lcyB3aGV0aGVyIHRvIG1lcmdlIHRoZSBuZXdcclxuICogb3B0aW9ucyBpbnRvIGEgY29weSBvZiB0aGUgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0IChgdHJ1ZWApIG9yIGRpcmVjdGx5IGludG9cclxuICogdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCAoYGZhbHNlYCkuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0LCBlaXRoZXIgdGhlIG1vZGlmaWVkIGdsb2JhbFxyXG4gKiBvcHRpb25zIG9yIGEgbW9kaWZpZWQgY29weSwgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGBnZXRDb3B5YC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1cGRhdGVPcHRpb25zKG5ld09wdGlvbnMsIGdldENvcHkgPSBmYWxzZSkge1xyXG4gIC8vIE1lcmdlIG5ldyBvcHRpb25zIHRvIHRoZSBnbG9iYWwgb3B0aW9ucyBvciBpdHMgY29weSBhbmQgcmV0dXJuIHRoZSByZXN1bHRcclxuICByZXR1cm4gX21lcmdlT3B0aW9ucyhnZXRPcHRpb25zKGdldENvcHkpLCBuZXdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgYW5kIHJldHVybnMgdGhlIGdsb2JhbCBvcHRpb25zIG9iamVjdCB3aXRoIHZhbHVlcyBwcm92aWRlZCB0aHJvdWdoXHJcbiAqIHRoZSBDTEksIGtlZXBpbmcgdGhlIHByaW5jaXBsZSBvZiBvcHRpb25zIGxvYWQgcHJpb3JpdHkuIFRoZSBmdW5jdGlvbiBhY2NlcHRzXHJcbiAqIGEgYGNsaUFyZ3NgIGFycmF5IGNvbnRhaW5pbmcgYXJndW1lbnRzIGZyb20gdGhlIENMSSwgd2hpY2ggd2lsbCBiZSB2YWxpZGF0ZWRcclxuICogYW5kIGFwcGxpZWQgaWYgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICpcclxuICogMS4gVmFsdWVzIGZyb20gdGhlIGNvbW1hbmQgbGluZSBpbnRlcmZhY2UgKENMSSkuXHJcbiAqIDIuIFZhbHVlcyBmcm9tIGEgY3VzdG9tIEpTT04gZmlsZSAobG9hZGVkIGJ5IHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24pLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2V0Q2xpT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5PHN0cmluZz59IGNsaUFyZ3MgLSBBbiBhcnJheSBvZiBjb21tYW5kIGxpbmUgYXJndW1lbnRzIHVzZWRcclxuICogZm9yIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIHVwZGF0ZWQgZ2xvYmFsIG9wdGlvbnMgb2JqZWN0LCByZWZsZWN0aW5nIHRoZSBtZXJnZWRcclxuICogY29uZmlndXJhdGlvbiBmcm9tIHNvdXJjZXMgcHJvdmlkZWQgdGhyb3VnaCB0aGUgQ0xJLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldENsaU9wdGlvbnMoY2xpQXJncykge1xyXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcclxuICBpZiAoY2xpQXJncyAmJiBBcnJheS5pc0FycmF5KGNsaUFyZ3MpICYmIGNsaUFyZ3MubGVuZ3RoKSB7XHJcbiAgICAvLyBHZXQgb3B0aW9ucyBmcm9tIHRoZSBjdXN0b20gSlNPTiBsb2FkZWQgdmlhIHRoZSBgLS1sb2FkQ29uZmlnYFxyXG4gICAgY29uc3QgY29uZmlnT3B0aW9ucyA9IF9sb2FkQ29uZmlnRmlsZShjbGlBcmdzKTtcclxuXHJcbiAgICAvLyBVcGRhdGUgZ2xvYmFsIG9wdGlvbnMgd2l0aCB0aGUgdmFsdWVzIGZyb20gdGhlIGBjb25maWdPcHRpb25zYCBvYmplY3RcclxuICAgIHVwZGF0ZU9wdGlvbnMoY29uZmlnT3B0aW9ucyk7XHJcblxyXG4gICAgLy8gR2V0IG9wdGlvbnMgZnJvbSB0aGUgQ0xJXHJcbiAgICBjb25zdCBjbGlPcHRpb25zID0gX3BhaXJBcmd1bWVudFZhbHVlKGNsaUFyZ3MpO1xyXG5cclxuICAgIC8vIFVwZGF0ZSBnbG9iYWwgb3B0aW9ucyB3aXRoIHRoZSB2YWx1ZXMgZnJvbSB0aGUgYGNsaU9wdGlvbnNgIG9iamVjdFxyXG4gICAgdXBkYXRlT3B0aW9ucyhjbGlPcHRpb25zKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvcHRpb25zXHJcbiAgcmV0dXJuIGdldE9wdGlvbnMoZmFsc2UpO1xyXG59XHJcblxyXG4vKipcclxuICogTWFwcyBvbGQtc3RydWN0dXJlZCBjb25maWd1cmF0aW9uIG9wdGlvbnMgKFBoYW50b21KUy1iYXNlZCkgdG8gYSBuZXcgZm9ybWF0XHJcbiAqIChQdXBwZXRlZXItYmFzZWQpLiBUaGlzIGZ1bmN0aW9uIGNvbnZlcnRzIGZsYXQsIG9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnMgaW50b1xyXG4gKiBhIG5ldywgbmVzdGVkIGNvbmZpZ3VyYXRpb24gZm9ybWF0IGJhc2VkIG9uIGEgcHJlZGVmaW5lZCBtYXBwaW5nIHByb3ZpZGVkXHJcbiAqIGluIHRoZSBgbmVzdGVkUHJvcHNgIG9iamVjdC4gVGhlIG5ldyBmb3JtYXQgaXMgdXNlZCBmb3IgUHVwcGV0ZWVyLCB3aGlsZVxyXG4gKiB0aGUgb2xkIGZvcm1hdCB3YXMgdXNlZCBmb3IgUGhhbnRvbUpTLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gbWFwVG9OZXdPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvbGRPcHRpb25zIC0gVGhlIG9sZCwgZmxhdCBjb25maWd1cmF0aW9uIG9wdGlvbnNcclxuICogdG8gYmUgY29udmVydGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBIG5ldyBvYmplY3QgY29udGFpbmluZyBvcHRpb25zIHN0cnVjdHVyZWQgYWNjb3JkaW5nXHJcbiAqIHRvIHRoZSBtYXBwaW5nIGRlZmluZWQgaW4gdGhlIGBuZXN0ZWRQcm9wc2Agb2JqZWN0IG9yIGFuIGVtcHR5IG9iamVjdFxyXG4gKiBpZiB0aGUgcHJvdmlkZWQgYG9sZE9wdGlvbnNgIGlzIG5vdCBhIGNvcnJlY3Qgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIG1hcFRvTmV3T3B0aW9ucyhvbGRPcHRpb25zKSB7XHJcbiAgLy8gQW4gb2JqZWN0IGZvciB0aGUgbmV3IHN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gIGNvbnN0IG5ld09wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgdmFsdWUgaXMgYSBjb3JyZWN0IG9iamVjdFxyXG4gIGlmIChpc09iamVjdChvbGRPcHRpb25zKSkge1xyXG4gICAgLy8gSXRlcmF0ZSBvdmVyIGVhY2gga2V5LXZhbHVlIHBhaXIgaW4gdGhlIG9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnNcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG9sZE9wdGlvbnMpKSB7XHJcbiAgICAgIC8vIElmIHRoZXJlIGlzIGEgbmVzdGVkIG1hcHBpbmcsIHNwbGl0IGl0IGludG8gYSBwcm9wZXJ0aWVzIGNoYWluXHJcbiAgICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW2tleV1cclxuICAgICAgICA/IG5lc3RlZFByb3BzW2tleV0uc3BsaXQoJy4nKVxyXG4gICAgICAgIDogW107XHJcblxyXG4gICAgICAvLyBJZiBpdCBpcyB0aGUgbGFzdCBwcm9wZXJ0eSBpbiB0aGUgY2hhaW4sIGFzc2lnbiB0aGUgdmFsdWUsIG90aGVyd2lzZSxcclxuICAgICAgLy8gY3JlYXRlIG9yIHJldXNlIHRoZSBuZXN0ZWQgb2JqZWN0XHJcbiAgICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoXHJcbiAgICAgICAgKG9iaiwgcHJvcCwgaW5kZXgpID0+XHJcbiAgICAgICAgICAob2JqW3Byb3BdID1cclxuICAgICAgICAgICAgcHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4ID8gdmFsdWUgOiBvYmpbcHJvcF0gfHwge30pLFxyXG4gICAgICAgIG5ld09wdGlvbnNcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9IGVsc2Uge1xyXG4gICAgbG9nKFxyXG4gICAgICAyLFxyXG4gICAgICAnW2NvbmZpZ10gTm8gY29ycmVjdCBvYmplY3Qgd2l0aCBvcHRpb25zIHdhcyBwcm92aWRlZC4gUmV0dXJuaW5nIGFuIGVtcHR5IG9iamVjdC4nXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBuZXcsIHN0cnVjdHVyZWQgb3B0aW9ucyBvYmplY3RcclxuICByZXR1cm4gbmV3T3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFZhbGlkYXRlcywgcGFyc2VzLCBhbmQgY2hlY2tzIGlmIHRoZSBwcm92aWRlZCBjb25maWcgaXMgYWxsb3dlZCBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQGZ1bmN0aW9uIGlzQWxsb3dlZENvbmZpZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge3Vua25vd259IGNvbmZpZyAtIFRoZSBjb25maWcgdG8gYmUgdmFsaWRhdGVkIGFuZCBwYXJzZWQgYXMgYSBzZXRcclxuICogb2Ygb3B0aW9ucy4gTXVzdCBiZSBlaXRoZXIgYW4gb2JqZWN0IG9yIGEgc3RyaW5nLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFt0b1N0cmluZz1mYWxzZV0gLSBXaGV0aGVyIHRvIHJldHVybiBhIHN0cmluZ2lmaWVkIHZlcnNpb25cclxuICogb2YgdGhlIHBhcnNlZCBjb25maWcuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW2FsbG93RnVuY3Rpb25zPWZhbHNlXSAtIFdoZXRoZXIgdG8gYWxsb3cgZnVuY3Rpb25zXHJcbiAqIGluIHRoZSBwYXJzZWQgY29uZmlnLiBJZiBgdHJ1ZWAsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkLiBPdGhlcndpc2UsIHdoZW5cclxuICogYSBmdW5jdGlvbiBpcyBmb3VuZCwgYG51bGxgIGlzIHJldHVybmVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBgZmFsc2VgLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KE9iamVjdHxzdHJpbmd8bnVsbCl9IFJldHVybnMgYSBwYXJzZWQgc2V0IG9mIG9wdGlvbnMgb2JqZWN0LFxyXG4gKiBhIHN0cmluZ2lmaWVkIHNldCBvZiBvcHRpb25zIG9iamVjdCBpZiB0aGUgYHRvU3RyaW5nYCBpcyBgdHJ1ZWAsIGFuZCBgbnVsbGBcclxuICogaWYgdGhlIGNvbmZpZyBpcyBub3QgYSB2YWxpZCBzZXQgb2Ygb3B0aW9ucyBvciBwYXJzaW5nIGZhaWxzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzQWxsb3dlZENvbmZpZyhcclxuICBjb25maWcsXHJcbiAgdG9TdHJpbmcgPSBmYWxzZSxcclxuICBhbGxvd0Z1bmN0aW9ucyA9IGZhbHNlXHJcbikge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBBY2NlcHQgb25seSBvYmplY3RzIGFuZCBzdHJpbmdzXHJcbiAgICBpZiAoIWlzT2JqZWN0KGNvbmZpZykgJiYgdHlwZW9mIGNvbmZpZyAhPT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gUmV0dXJuIGBudWxsYCBpZiBhbnkgb3RoZXIgdHlwZVxyXG4gICAgICByZXR1cm4gbnVsbDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIG9iamVjdCByZXByZXNlbnRhdGlvbiBvZiB0aGUgb3JpZ2luYWwgY29uZmlnXHJcbiAgICBjb25zdCBvYmplY3RDb25maWcgPVxyXG4gICAgICB0eXBlb2YgY29uZmlnID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gZXZhbChgKCR7Y29uZmlnfSlgKVxyXG4gICAgICAgICAgOiBKU09OLnBhcnNlKGNvbmZpZylcclxuICAgICAgICA6IGNvbmZpZztcclxuXHJcbiAgICAvLyBQcmVzZXJ2ZSBvciByZW1vdmUgcG90ZW50aWFsIGZ1bmN0aW9ucyBiYXNlZCBvbiB0aGUgYGFsbG93RnVuY3Rpb25zYCBmbGFnXHJcbiAgICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBfb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgb2JqZWN0Q29uZmlnLFxyXG4gICAgICBhbGxvd0Z1bmN0aW9ucyxcclxuICAgICAgZmFsc2VcclxuICAgICk7XHJcblxyXG4gICAgLy8gUGFyc2UgdGhlIGNvbmZpZyB0byBjaGVjayBpZiBpdCBpcyB2YWxpZCBzZXQgb2Ygb3B0aW9uc1xyXG4gICAgY29uc3QgcGFyc2VkT3B0aW9ucyA9IGFsbG93RnVuY3Rpb25zXHJcbiAgICAgID8gSlNPTi5wYXJzZShcclxuICAgICAgICAgIF9vcHRpb25zU3RyaW5naWZ5KG9iamVjdENvbmZpZywgYWxsb3dGdW5jdGlvbnMsIHRydWUpLFxyXG4gICAgICAgICAgKF8sIHZhbHVlKSA9PlxyXG4gICAgICAgICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uJylcclxuICAgICAgICAgICAgICA/IGV2YWwoYCgke3ZhbHVlfSlgKVxyXG4gICAgICAgICAgICAgIDogdmFsdWVcclxuICAgICAgICApXHJcbiAgICAgIDogSlNPTi5wYXJzZShzdHJpbmdpZmllZE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFJldHVybiBzdHJpbmdpZmllZCBvciBvYmplY3Qgb3B0aW9ucyBiYXNlZCBvbiB0aGUgYHRvU3RyaW5nYCBmbGFnXHJcbiAgICByZXR1cm4gdG9TdHJpbmcgPyBzdHJpbmdpZmllZE9wdGlvbnMgOiBwYXJzZWRPcHRpb25zO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBSZXR1cm4gYG51bGxgIGlmIHBhcnNpbmcgZmFpbHNcclxuICAgIHJldHVybiBudWxsO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIGFuZCByZXR1cm5zIHRoZSBnbG9iYWwgb3B0aW9ucyBvYmplY3QgYmFzZWQgb24gdGhlIHByb3ZpZGVkXHJcbiAqIGNvbmZpZ3VyYXRpb24sIHNldHRpbmcgdmFsdWVzIGZyb20gbmVzdGVkIHByb3BlcnRpZXMgcmVjdXJzaXZlbHkuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBwcmlvcml0aXplcyB2YWx1ZXMgaW4gdGhlIGZvbGxvd2luZyBvcmRlcjpcclxuICpcclxuICogMS4gVmFsdWVzIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzIChzcGVjaWZpZWQgaW4gdGhlIGAuZW52YCBmaWxlKS5cclxuICogMi4gVmFsdWVzIGZyb20gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSAoZGVmYXVsdHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2luaXRPcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgdXNlZCBmb3IgaW5pdGlhbGl6aW5nXHJcbiAqIHRoZSBnbG9iYWwgb3B0aW9ucy4gSXQgc2hvdWxkIGluY2x1ZGUgbmVzdGVkIHByb3BlcnRpZXMgd2l0aCBhIGB2YWx1ZWBcclxuICogYW5kIGFuIGBlbnZMaW5rYCBmb3IgbGlua2luZyB0byBlbnZpcm9ubWVudCB2YXJpYWJsZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBpbml0aWFsaXplZCBnbG9iYWwgb3B0aW9ucyBvYmplY3QsIHBvcHVsYXRlZCB3aXRoXHJcbiAqIHZhbHVlcyBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5XHJcbiAqIG9yZGVyLlxyXG4gKi9cclxuZnVuY3Rpb24gX2luaXRPcHRpb25zKGNvbmZpZykge1xyXG4gIC8vIEluaXQgdGhlIG9iamVjdCBmb3Igb3B0aW9uc1xyXG4gIGNvbnN0IG9wdGlvbnMgPSB7fTtcclxuXHJcbiAgLy8gU3RhcnQgaW5pdGlhbGl6aW5nIHRoZSBgb3B0aW9uc2Agb2JqZWN0IHJlY3Vyc2l2ZWx5XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoY29uZmlnKSkge1xyXG4gICAgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChpdGVtLCAndmFsdWUnKSkge1xyXG4gICAgICAvLyBTZXQgdGhlIGNvcnJlY3QgdmFsdWUgYmFzZWQgb24gdGhlIGVzdGFibGlzaGVkIHByaW9yaXR5IG9yZGVyXHJcbiAgICAgIGlmIChlbnZzW2l0ZW0uZW52TGlua10gIT09IHVuZGVmaW5lZCAmJiBlbnZzW2l0ZW0uZW52TGlua10gIT09IG51bGwpIHtcclxuICAgICAgICAvLyBUaGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIHZhbHVlXHJcbiAgICAgICAgb3B0aW9uc1tuYW1lXSA9IGVudnNbaXRlbS5lbnZMaW5rXTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBUaGUgdmFsdWUgZnJvbSB0aGUgY29uZmlnIGZpbGVcclxuICAgICAgICBvcHRpb25zW25hbWVdID0gaXRlbS52YWx1ZTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ3JlYXRlIGEgY2F0ZWdvcnkgb2Ygb3B0aW9ucyBpbiB0aGUgYG9wdGlvbnNgIG9iamVjdFxyXG4gICAgICBvcHRpb25zW25hbWVdID0gX2luaXRPcHRpb25zKGl0ZW0pO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBjcmVhdGVkIGBvcHRpb25zYCBvYmplY3RcclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IG1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIHRha2luZyBpbnRvIGFjY291bnRcclxuICogcHJvcGVydGllcyBzcGVjaWZpZWQgaW4gdGhlIGBhYnNvbHV0ZVByb3BzYCBhcnJheSB0aGF0IHJlcXVpcmUgYWJzb2x1dGVcclxuICogbWVyZ2luZy4gVGhlIGBvcmlnaW5hbE9wdGlvbnNgIG9iamVjdCB3aWxsIGJlIGV4dGVuZGVkIHdpdGggb3B0aW9ucyBmcm9tXHJcbiAqIHRoZSBgbmV3T3B0aW9uc2Agb2JqZWN0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX21lcmdlT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3JpZ2luYWxPcHRpb25zIC0gVGhlIG9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyBvYmplY3RcclxuICogdG8gYmUgZXh0ZW5kZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBuZXdPcHRpb25zIC0gVGhlIG5ldyBjb25maWd1cmF0aW9uIG9wdGlvbnMgb2JqZWN0IHRvIG1lcmdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgZXh0ZW5kZWQgYG9yaWdpbmFsT3B0aW9uc2Agb2JqZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gX21lcmdlT3B0aW9ucyhvcmlnaW5hbE9wdGlvbnMsIG5ld09wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgYG9yaWdpbmFsT3B0aW9uc2AgYW5kIGBuZXdPcHRpb25zYCBhcmUgY29ycmVjdCBvYmplY3RzXHJcbiAgaWYgKGlzT2JqZWN0KG9yaWdpbmFsT3B0aW9ucykgJiYgaXNPYmplY3QobmV3T3B0aW9ucykpIHtcclxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICAgIG9yaWdpbmFsT3B0aW9uc1trZXldID1cclxuICAgICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXHJcbiAgICAgICAgb3JpZ2luYWxPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgPyBfbWVyZ2VPcHRpb25zKG9yaWdpbmFsT3B0aW9uc1trZXldLCB2YWx1ZSlcclxuICAgICAgICAgIDogdmFsdWUgIT09IHVuZGVmaW5lZFxyXG4gICAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICAgIDogb3JpZ2luYWxPcHRpb25zW2tleV0gfHwgbnVsbDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiB0aGUgb3JpZ2luYWwgKG1vZGlmaWVkIG9yIG5vdCkgb3B0aW9uc1xyXG4gIHJldHVybiBvcmlnaW5hbE9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OIHN0cmluZyB3aXRoIHRoZSBvcHRpb25cclxuICogdG8gcHJlc2VydmUgZnVuY3Rpb25zLiBJbiBvcmRlciBmb3IgYSBmdW5jdGlvbiB0byBiZSBwcmVzZXJ2ZWQsIGl0IG5lZWRzXHJcbiAqIHRvIGZvbGxvdyB0aGUgZm9ybWF0IGBmdW5jdGlvbiAoLi4uKSB7Li4ufWAuIFN1Y2ggYSBmdW5jdGlvbiBjYW4gYWxzb1xyXG4gKiBiZSBzdHJpbmdpZmllZC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9vcHRpb25zU3RyaW5naWZ5XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IHRvIGJlIGNvbnZlcnRlZCB0byBhIHN0cmluZy5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0Z1bmN0aW9ucyAtIElmIHNldCB0byBgdHJ1ZWAsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkXHJcbiAqIGluIHRoZSBvdXRwdXQuIE90aGVyd2lzZSBhbiBlcnJvciBpcyB0aHJvd24uXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gc3RyaW5naWZ5RnVuY3Rpb25zIC0gSWYgc2V0IHRvIGB0cnVlYCwgZnVuY3Rpb25zIGFyZSBzYXZlZFxyXG4gKiBhcyBzdHJpbmdzLiBUaGUgYGFsbG93RnVuY3Rpb25zYCBtdXN0IGJlIHNldCB0byBgdHJ1ZWAgYXMgd2VsbCBmb3IgdGhpc1xyXG4gKiB0byB0YWtlIGFuIGVmZmVjdC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gYEVycm9yYCB3aGVuIGZ1bmN0aW9ucyBhcmUgbm90IGFsbG93ZWQgYnV0IGFyZVxyXG4gKiBmb3VuZCBpbiBwcm92aWRlZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmZ1bmN0aW9uIF9vcHRpb25zU3RyaW5naWZ5KG9wdGlvbnMsIGFsbG93RnVuY3Rpb25zLCBzdHJpbmdpZnlGdW5jdGlvbnMpIHtcclxuICBjb25zdCByZXBsYWNlckNhbGxiYWNrID0gKF8sIHZhbHVlKSA9PiB7XHJcbiAgICAvLyBUcmltIHN0cmluZyB2YWx1ZXNcclxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIHZhbHVlID0gdmFsdWUudHJpbSgpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIElmIGB2YWx1ZWAgaXMgYSBmdW5jdGlvbiBvciBzdHJpbmdpZmllZCBmdW5jdGlvblxyXG4gICAgaWYgKFxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbicgfHxcclxuICAgICAgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdmdW5jdGlvbicpICYmXHJcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBJZiB0aGUgYGFsbG93RnVuY3Rpb25zYCBpcyBzZXQgdG8gYHRydWVgLCBwcmVzZXJ2ZSBmdW5jdGlvbnNcclxuICAgICAgaWYgKGFsbG93RnVuY3Rpb25zKSB7XHJcbiAgICAgICAgLy8gQmFzZWQgb24gdGhlIGBzdHJpbmdpZnlGdW5jdGlvbnNgIG9wdGlvbnMsIHNldCBmdW5jdGlvbiB2YWx1ZXNcclxuICAgICAgICByZXR1cm4gc3RyaW5naWZ5RnVuY3Rpb25zXHJcbiAgICAgICAgICA/IC8vIEFzIHN0cmluZ2lmaWVkIGZ1bmN0aW9uc1xyXG4gICAgICAgICAgICBgXCJFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5cImBcclxuICAgICAgICAgIDogLy8gQXMgZnVuY3Rpb25zXHJcbiAgICAgICAgICAgIGBFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxzKy9nLCAnICcpfUVYUF9GVU5gO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIFRocm93IGFuIGVycm9yIG90aGVyd2lzZVxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcigpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSW4gYWxsIG90aGVyIGNhc2VzLCBzaW1wbHkgcmV0dXJuIHRoZSB2YWx1ZVxyXG4gICAgcmV0dXJuIHZhbHVlO1xyXG4gIH07XHJcblxyXG4gIC8vIFN0cmluZ2lmeSBvcHRpb25zIGFuZCBpZiByZXF1aXJlZCwgcmVwbGFjZSBzcGVjaWFsIGZ1bmN0aW9ucyBtYXJrc1xyXG4gIHJldHVybiBKU09OLnN0cmluZ2lmeShvcHRpb25zLCByZXBsYWNlckNhbGxiYWNrKS5yZXBsYWNlQWxsKFxyXG4gICAgc3RyaW5naWZ5RnVuY3Rpb25zID8gL1xcXFxcIkVYUF9GVU58RVhQX0ZVTlxcXFxcIi9nIDogL1wiRVhQX0ZVTnxFWFBfRlVOXCIvZyxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgcHJvdmlkZWQgdmlhXHJcbiAqIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gaW4gdGhlIGNvbW1hbmQtbGluZSBhcmd1bWVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfbG9hZENvbmZpZ0ZpbGVcclxuICpcclxuICogQHBhcmFtIHtBcnJheTxzdHJpbmc+fSBjbGlBcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyB0byBzZWFyY2hcclxuICogZm9yIHRoZSBgLS1sb2FkQ29uZmlnYCBvcHRpb24gYW5kIHRoZSBjb3JyZXNwb25kaW5nIGZpbGUgcGF0aC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBsb2FkZWQgZnJvbSB0aGUgc3BlY2lmaWVkXHJcbiAqIGZpbGUsIG9yIGFuIGVtcHR5IG9iamVjdCBpZiB0aGUgZmlsZSBpcyBub3QgZm91bmQsIGludmFsaWQsIG9yIGFuIGVycm9yXHJcbiAqIG9jY3Vycy5cclxuICovXHJcbmZ1bmN0aW9uIF9sb2FkQ29uZmlnRmlsZShjbGlBcmdzKSB7XHJcbiAgLy8gR2V0IHRoZSBhbGxvdyBmbGFncyBmb3IgdGhlIGN1c3RvbSBsb2dpYyBjaGVja1xyXG4gIGNvbnN0IHsgYWxsb3dDb2RlRXhlY3V0aW9uLCBhbGxvd0ZpbGVSZXNvdXJjZXMgfSA9IGdldE9wdGlvbnMoKS5jdXN0b21Mb2dpYztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGAtLWxvYWRDb25maWdgIG9wdGlvbiB3YXMgdXNlZFxyXG4gIGNvbnN0IGNvbmZpZ0luZGV4ID0gY2xpQXJncy5maW5kSW5kZXgoXHJcbiAgICAoYXJnKSA9PiBhcmcucmVwbGFjZSgvLS9nLCAnJykgPT09ICdsb2FkQ29uZmlnJ1xyXG4gICk7XHJcblxyXG4gIC8vIEdldCB0aGUgYC0tbG9hZENvbmZpZ2Agb3B0aW9uIHZhbHVlXHJcbiAgY29uc3QgY29uZmlnRmlsZU5hbWUgPSBjb25maWdJbmRleCA+IC0xICYmIGNsaUFyZ3NbY29uZmlnSW5kZXggKyAxXTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGAtLWxvYWRDb25maWdgIGlzIHByZXNlbnQgYW5kIGhhcyBhIGNvcnJlY3QgdmFsdWVcclxuICBpZiAoY29uZmlnRmlsZU5hbWUgJiYgYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBMb2FkIGFuIG9wdGlvbmFsIGN1c3RvbSBKU09OIGNvbmZpZyBmaWxlXHJcbiAgICAgIHJldHVybiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChjb25maWdGaWxlTmFtZSksICd1dGY4JyksXHJcbiAgICAgICAgZmFsc2UsXHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgMixcclxuICAgICAgICBlcnJvcixcclxuICAgICAgICBgW2NvbmZpZ10gVW5hYmxlIHRvIGxvYWQgdGhlIGNvbmZpZ3VyYXRpb24gZnJvbSB0aGUgJHtjb25maWdGaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBObyBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gcmV0dXJuXHJcbiAgcmV0dXJuIHt9O1xyXG59XHJcblxyXG4vKipcclxuICogUGFyc2VzIGNvbW1hbmQtbGluZSBhcmd1bWVudHMgYW5kIHBhaXJzIGVhY2ggYXJndW1lbnQgd2l0aCBpdHMgY29ycmVzcG9uZGluZ1xyXG4gKiBvcHRpb24gaW4gdGhlIGNvbmZpZ3VyYXRpb24uIFRoZSB2YWx1ZXMgYXJlIHN0cnVjdHVyZWQgaW50byBhIG5lc3RlZCBvcHRpb25zXHJcbiAqIG9iamVjdCwgYmFzZWQgb24gcHJlZGVmaW5lZCBtYXBwaW5ncyBpbiB0aGUgYG5lc3RlZFByb3BzYCBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfcGFpckFyZ3VtZW50VmFsdWVcclxuICpcclxuICogQHBhcmFtIHtBcnJheTxzdHJpbmc+fSBjbGlBcmdzIC0gQW4gYXJyYXkgb2YgY29tbWFuZC1saW5lIGFyZ3VtZW50c1xyXG4gKiBjb250YWluaW5nIG9wdGlvbnMgYW5kIHRoZWlyIGFzc29jaWF0ZWQgdmFsdWVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiB1cGRhdGVkIG9wdGlvbnMgb2JqZWN0IHdoZXJlIGVhY2ggb3B0aW9uIGZyb21cclxuICogdGhlIGNvbW1hbmQtbGluZSBpcyBwYWlyZWQgd2l0aCBpdHMgdmFsdWUsIHN0cnVjdHVyZWQgaW50byBuZXN0ZWQgb2JqZWN0c1xyXG4gKiBhcyBkZWZpbmVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX3BhaXJBcmd1bWVudFZhbHVlKGNsaUFyZ3MpIHtcclxuICAvLyBBbiBlbXB0eSBvYmplY3QgdG8gY29sbGVjdCBhbmQgc3RydWN0dXJpemUgZGF0YSBmcm9tIHRoZSBhcmdzXHJcbiAgY29uc3QgY2xpT3B0aW9ucyA9IHt9O1xyXG5cclxuICAvLyBDeWNsZSB0aHJvdWdoIGFsbCBDTEkgYXJncyBhbmQgZmlsdGVyIHRoZW1cclxuICBmb3IgKGxldCBpID0gMDsgaSA8IGNsaUFyZ3MubGVuZ3RoOyBpKyspIHtcclxuICAgIGNvbnN0IG9wdGlvbiA9IGNsaUFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZFByb3BzW29wdGlvbl1cclxuICAgICAgPyBuZXN0ZWRQcm9wc1tvcHRpb25dLnNwbGl0KCcuJylcclxuICAgICAgOiBbXTtcclxuXHJcbiAgICAvLyBDcmVhdGUgb3B0aW9ucyBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSBDTEkgZm9yIGxhdGVyIHBhcnNpbmcgYW5kIG1lcmdpbmdcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcclxuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xyXG4gICAgICAgIGNvbnN0IHZhbHVlID0gY2xpQXJnc1srK2ldO1xyXG4gICAgICAgIGlmICghdmFsdWUpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSBDTEkgJy0tJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgb2JqW3Byb3BdID0gdmFsdWUgfHwgbnVsbDtcclxuICAgICAgfSBlbHNlIGlmIChvYmpbcHJvcF0gPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIG9ialtwcm9wXSA9IHt9O1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBvYmpbcHJvcF07XHJcbiAgICB9LCBjbGlPcHRpb25zKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBwYXJzZWQgQ0xJIG9wdGlvbnNcclxuICByZXR1cm4gY2xpT3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGdlbmVyYXRlcyBhIG1hcHBpbmcgb2YgbmVzdGVkIGFyZ3VtZW50IGNoYWlucyBmcm9tIGEgbmVzdGVkXHJcbiAqIGNvbmZpZyBvYmplY3QuIFRoaXMgZnVuY3Rpb24gdHJhdmVyc2VzIGEgbmVzdGVkIG9iamVjdCBhbmQgY3JlYXRlcyBhIG1hcHBpbmdcclxuICogd2hlcmUgZWFjaCBrZXkgaXMgYW4gYXJndW1lbnQgbmFtZSAoZWl0aGVyIGZyb20gYGNsaU5hbWVgLCBgbGVnYWN5TmFtZWAsXHJcbiAqIG9yIHRoZSBvcmlnaW5hbCBrZXkpIGFuZCBlYWNoIHZhbHVlIGlzIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgY2hhaW5cclxuICogb2YgbmVzdGVkIHByb3BlcnRpZXMgbGVhZGluZyB0byB0aGF0IGFyZ3VtZW50LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZU5lc3RlZFByb3BzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbbmVzdGVkUHJvcHM9e31dIC0gVGhlIGFjY3VtdWxhdG9yIG9iamVjdCBmb3Igc3RvcmluZ1xyXG4gKiB0aGUgcmVzdWx0aW5nIGFyZ3VtZW50cyBjaGFpbnMuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtwcm9wQ2hhaW49JyddIC0gVGhlIGN1cnJlbnQgY2hhaW4gb2YgbmVzdGVkIHByb3BlcnRpZXMsXHJcbiAqIHVzZWQgaW50ZXJuYWxseSBkdXJpbmcgcmVjdXJzaW9uLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBzdHJpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFuIG9iamVjdCBtYXBwaW5nIGFyZ3VtZW50IG5hbWVzIHRvIHRoZWlyIGNvcnJlc3BvbmRpbmdcclxuICogbmVzdGVkIHByb3BlcnR5IGNoYWlucy5cclxuICovXHJcbmZ1bmN0aW9uIF9jcmVhdGVOZXN0ZWRQcm9wcyhjb25maWcsIG5lc3RlZFByb3BzID0ge30sIHByb3BDaGFpbiA9ICcnKSB7XHJcbiAgT2JqZWN0LmtleXMoY29uZmlnKS5mb3JFYWNoKChrZXkpID0+IHtcclxuICAgIC8vIEdldCB0aGUgc3BlY2lmaWMgc2VjdGlvblxyXG4gICAgY29uc3QgZW50cnkgPSBjb25maWdba2V5XTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGVyZSBpcyBzdGlsbCBtb3JlIGRlcHRoIHRvIHRyYXZlcnNlXHJcbiAgICBpZiAodHlwZW9mIGVudHJ5LnZhbHVlID09PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAvLyBSZWN1cnNlIGludG8gZGVlcGVyIGxldmVscyBvZiBuZXN0ZWQgYXJndW1lbnRzXHJcbiAgICAgIF9jcmVhdGVOZXN0ZWRQcm9wcyhlbnRyeSwgbmVzdGVkUHJvcHMsIGAke3Byb3BDaGFpbn0uJHtrZXl9YCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDcmVhdGUgdGhlIGNoYWluIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgbmVzdGVkUHJvcHNbZW50cnkuY2xpTmFtZSB8fCBrZXldID0gYCR7cHJvcENoYWlufS4ke2tleX1gLnN1YnN0cmluZygxKTtcclxuXHJcbiAgICAgIC8vIFN1cHBvcnQgZm9yIHRoZSBsZWdhY3ksIFBoYW50b21KUyBwcm9wZXJ0aWVzIG5hbWVzXHJcbiAgICAgIGlmIChlbnRyeS5sZWdhY3lOYW1lICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICBuZXN0ZWRQcm9wc1tlbnRyeS5sZWdhY3lOYW1lXSA9IGAke3Byb3BDaGFpbn0uJHtrZXl9YC5zdWJzdHJpbmcoMSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBvYmplY3Qgd2l0aCBuZXN0ZWQgYXJndW1lbnQgY2hhaW5zXHJcbiAgcmV0dXJuIG5lc3RlZFByb3BzO1xyXG59XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgZ2F0aGVycyB0aGUgbmFtZXMgb2YgcHJvcGVydGllcyBmcm9tIGEgY29uZmlndXJhdGlvbiBvYmplY3QgdGhhdFxyXG4gKiBzaG91bGQgYmUgdHJlYXRlZCBhcyBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGVzZSBwcm9wZXJ0aWVzIGhhdmUgdmFsdWVzIHRoYXRcclxuICogYXJlIG9iamVjdHMgYW5kIGRvIG5vdCBjb250YWluIGZ1cnRoZXIgbmVzdGVkIGRlcHRoIHdoZW4gbWVyZ2luZyBhbiBvYmplY3RcclxuICogY29udGFpbmluZyB0aGVzZSBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2NyZWF0ZUFic29sdXRlUHJvcHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICogQHBhcmFtIHtBcnJheTxzdHJpbmc+fSBbYWJzb2x1dGVQcm9wcz1bXV0gLSBBbiBhcnJheSB0byBjb2xsZWN0IHRoZSBuYW1lc1xyXG4gKiBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5PHN0cmluZz59IEFuIGFycmF5IGNvbnRhaW5pbmcgdGhlIG5hbWVzIG9mIGFic29sdXRlXHJcbiAqIHByb3BlcnRpZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY3JlYXRlQWJzb2x1dGVQcm9wcyhjb25maWcsIGFic29sdXRlUHJvcHMgPSBbXSkge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZykuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICAvLyBHZXQgdGhlIHNwZWNpZmljIHNlY3Rpb25cclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnW2tleV07XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgc3RpbGwgbW9yZSBkZXB0aCB0byB0cmF2ZXJzZVxyXG4gICAgaWYgKHR5cGVvZiBlbnRyeS50eXBlcyA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgLy8gUmVjdXJzZSBpbnRvIGRlZXBlciBsZXZlbHNcclxuICAgICAgX2NyZWF0ZUFic29sdXRlUHJvcHMoZW50cnksIGFic29sdXRlUHJvcHMpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSWYgdGhlIG9wdGlvbiBjYW4gYmUgYW4gb2JqZWN0LCBzYXZlIGl0cyB0eXBlIGluIHRoZSBhcnJheVxyXG4gICAgICBpZiAoZW50cnkudHlwZXMuaW5jbHVkZXMoJ09iamVjdCcpKSB7XHJcbiAgICAgICAgYWJzb2x1dGVQcm9wcy5wdXNoKGtleSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHRoZSBhcnJheSB3aXRoIHRoZSBuYW1lcyBvZiBhYnNvbHV0ZSBwcm9wZXJ0aWVzXHJcbiAgcmV0dXJuIGFic29sdXRlUHJvcHM7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHVwZGF0ZU9wdGlvbnMsXHJcbiAgc2V0Q2xpT3B0aW9ucyxcclxuICBtYXBUb05ld09wdGlvbnMsXHJcbiAgaXNBbGxvd2VkQ29uZmlnXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBIVFRQIHV0aWxpdHkgbW9kdWxlIGZvciBmZXRjaGluZyBhbmQgcG9zdGluZyBkYXRhLiBTdXBwb3J0cyBib3RoXHJcbiAqIEhUVFAgYW5kIEhUVFBTIHByb3RvY29scywgcHJvdmlkaW5nIG1ldGhvZHMgdG8gbWFrZSBHRVQgYW5kIFBPU1QgcmVxdWVzdHNcclxuICogd2l0aCBjdXN0b21pemFibGUgb3B0aW9ucy4gSW5jbHVkZXMgcHJvdG9jb2wgZGV0ZXJtaW5hdGlvbiBiYXNlZCBvbiBVUkxcclxuICogYW5kIGF1Z21lbnRzIHJlc3BvbnNlIG9iamVjdHMgd2l0aCBhICd0ZXh0JyBwcm9wZXJ0eSBmb3IgZWFzaWVyIGRhdGEgYWNjZXNzLlxyXG4gKi9cclxuXHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgR0VUIHJlcXVlc3QgdG8gdGhlIHNwZWNpZmllZCBVUkwgdXNpbmcgZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gZ2V0XHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGdldCBkYXRhIGZyb20uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAvSFRUUFMgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgSFRUUC9IVFRQUyByZXNwb25zZVxyXG4gKiBvYmplY3Qgd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICAvLyBEZWNpZGUgb24gdGhlIHByb3RvY29sXHJcbiAgICBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAuZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIGlmICghcmVzcG9uc2VEYXRhKSB7XHJcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XHJcbiAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgLy8gR2V0IHRoZSBmdWxsIHJlc3VsdCBhbmQgcmVzb2x2ZSB0aGUgcmVxdWVzdFxyXG4gICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgIHJlc29sdmUocmVzcG9uc2UpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZW5kcyBhIFBPU1QgcmVxdWVzdCB0byB0aGUgc3BlY2lmaWVkIFVSTCB3aXRoIHRoZSBwcm92aWRlZCBKU09OIGJvZHkgdXNpbmdcclxuICogZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBzZW5kIHRoZSBQT1NUIHJlcXVlc3QgdG8uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbYm9keT17fV0gLSBUaGUgSlNPTiBib2R5IHRvIGluY2x1ZGUgaW4gdGhlIFBPU1QgcmVxdWVzdC5cclxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgYW4gZW1wdHkgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW3JlcXVlc3RPcHRpb25zPXt9XSAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQL0hUVFBTIHJlcXVlc3QuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGFuIGVtcHR5IG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIEhUVFAvSFRUUFMgcmVzcG9uc2VcclxuICogb2JqZWN0IHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBvc3QodXJsLCBib2R5ID0ge30sIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KGJvZHkpO1xyXG5cclxuICAgIC8vIFNldCBkZWZhdWx0IGhlYWRlcnMgYW5kIG1lcmdlIHdpdGggYHJlcXVlc3RPcHRpb25zYFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oXHJcbiAgICAgIHtcclxuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcclxuICAgICAgICBoZWFkZXJzOiB7XHJcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxyXG4gICAgICAgICAgJ0NvbnRlbnQtTGVuZ3RoJzogZGF0YS5sZW5ndGhcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zXHJcbiAgICApO1xyXG5cclxuICAgIC8vIERlY2lkZSBvbiB0aGUgcHJvdG9jb2xcclxuICAgIGNvbnN0IHJlcXVlc3QgPSBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKVxyXG4gICAgICAucmVxdWVzdCh1cmwsIG9wdGlvbnMsIChyZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkXHJcbiAgICAgICAgcmVzcG9uc2Uub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIC8vIEdldCB0aGUgZnVsbCByZXN1bHQgYW5kIHJlc29sdmUgdGhlIHJlcXVlc3RcclxuICAgICAgICAgICAgcmVzcG9uc2UudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgICAgcmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgLy8gV3JpdGUgdGhlIHJlcXVlc3QgYm9keSBhbmQgZW5kIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LndyaXRlKGRhdGEpO1xyXG4gICAgcmVxdWVzdC5lbmQoKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgdGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBVUkwuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZGV0ZXJtaW5lIHRoZSBwcm90b2NvbC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIChgaHR0cGAgb3IgYGh0dHBzYCkuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZ2V0UHJvdG9jb2xNb2R1bGUodXJsKSB7XHJcbiAgcmV0dXJuIHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQSBjdXN0b20gZXJyb3IgY2xhc3MgZm9yIGhhbmRsaW5nIGV4cG9ydC1yZWxhdGVkIGVycm9ycy4gRXh0ZW5kcyB0aGUgbmF0aXZlXHJcbiAqIGBFcnJvcmAgY2xhc3MgdG8gaW5jbHVkZSBhZGRpdGlvbmFsIHByb3BlcnRpZXMgbGlrZSBzdGF0dXMgY29kZSBhbmQgc3RhY2tcclxuICogdHJhY2UgZGV0YWlscy5cclxuICovXHJcbmNsYXNzIEV4cG9ydEVycm9yIGV4dGVuZHMgRXJyb3Ige1xyXG4gIC8qKlxyXG4gICAqIENyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgdGhlIGBFeHBvcnRFcnJvcmAuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge3N0cmluZ30gbWVzc2FnZSAtIFRoZSBlcnJvciBtZXNzYWdlIHRvIGJlIGRpc3BsYXllZC5cclxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhdHVzQ29kZSAtIE9wdGlvbmFsIEhUVFAgc3RhdHVzIGNvZGUgYXNzb2NpYXRlZFxyXG4gICAqIHdpdGggdGhlIGVycm9yIChlLmcuLCA0MDAsIDUwMCkuXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSwgc3RhdHVzQ29kZSkge1xyXG4gICAgc3VwZXIoKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGBtZXNzYWdlYCBhbmQgYHN0YWNrTWVzc2FnZWAgd2l0aCBwcm92aWRlZCBtZXNzYWdlXHJcbiAgICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gICAgdGhpcy5zdGFja01lc3NhZ2UgPSBtZXNzYWdlO1xyXG5cclxuICAgIC8vIFNldCB0aGUgYHN0YXR1c0NvZGVgIGlmIHByb3ZpZGVkXHJcbiAgICBpZiAoc3RhdHVzQ29kZSkge1xyXG4gICAgICB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXNDb2RlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0cyBhZGRpdGlvbmFsIGVycm9yIGRldGFpbHMgYmFzZWQgb24gYW4gZXhpc3RpbmcgZXJyb3Igb2JqZWN0LlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBBbiBlcnJvciBvYmplY3QgY29udGFpbmluZyBkZXRhaWxzIHRvIHBvcHVsYXRlXHJcbiAgICogdGhlIGBFeHBvcnRFcnJvcmAgaW5zdGFuY2UuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyB7RXhwb3J0RXJyb3J9IFRoZSB1cGRhdGVkIGluc3RhbmNlIG9mIHRoZSBgRXhwb3J0RXJyb3JgIGNsYXNzLlxyXG4gICAqL1xyXG4gIHNldEVycm9yKGVycm9yKSB7XHJcbiAgICAvLyBTYXZlIHRoZSBwcm92aWRlZCBlcnJvclxyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZXJyb3IncyBuYW1lIGlmIHByZXNlbnRcclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHRoZSBlcnJvcidzIHN0YXR1cyBjb2RlIGlmIHByZXNlbnRcclxuICAgIGlmIChlcnJvci5zdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHRoZSBlcnJvcidzIHN0YWNrIGFuZCBzdGFjaydzIG1lc3NhZ2UgaWYgcHJlc2VudFxyXG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XHJcbiAgICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcclxuICAgICAgdGhpcy5zdGFjayA9IGVycm9yLnN0YWNrO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJldHVybiB1cGRhdGVkIGBFeHBvcnRFcnJvcmAgaW5zdGFuY2VcclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgRXhwb3J0RXJyb3I7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGUgY2FjaGUgbWFuYWdlciBpcyByZXNwb25zaWJsZSBmb3IgaGFuZGxpbmcgYW5kIG1hbmFnaW5nXHJcbiAqIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYWxvbmcgd2l0aCBpdHMgZGVwZW5kZW5jaWVzLiBJdCBlbnN1cmVzIHRoYXQgdGhlc2VcclxuICogcmVzb3VyY2VzIGFyZSBzdG9yZWQgYW5kIHJldHJpZXZlZCBlZmZpY2llbnRseSB0byBvcHRpbWl6ZSBwZXJmb3JtYW5jZVxyXG4gKiBhbmQgcmVkdWNlIHJlZHVuZGFudCBuZXR3b3JrIHJlcXVlc3RzLiBUaGUgY2FjaGUgaXMgc3RvcmVkIGluIHRoZSBgLmNhY2hlYFxyXG4gKiBkaXJlY3RvcnkgYnkgZGVmYXVsdCwgd2hpY2ggc2VydmVzIGFzIGEgZGVkaWNhdGVkIGZvbGRlciBmb3Iga2VlcGluZyBjYWNoZWRcclxuICogZmlsZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IEh0dHBzUHJveHlBZ2VudCB9IGZyb20gJ2h0dHBzLXByb3h5LWFnZW50JztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGdldCB9IGZyb20gJy4vZmV0Y2guanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldEFic29sdXRlUGF0aCB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBpbml0aWFsIGNhY2hlIHRlbXBsYXRlXHJcbmNvbnN0IGNhY2hlID0ge1xyXG4gIGNkblVybDogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbScsXHJcbiAgYWN0aXZlTWFuaWZlc3Q6IHt9LFxyXG4gIHNvdXJjZXM6ICcnLFxyXG4gIGhjVmVyc2lvbjogJydcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgdGhlIGNhY2hlIGZvciBIaWdoY2hhcnRzIGRlcGVuZGVuY2llcywgdXBkYXRlcyB0aGUgY2FjaGUgaWYgbmVlZGVkLFxyXG4gKiBhbmQgbG9hZHMgdGhlIHNvdXJjZXMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2hlY2tDYWNoZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaGlnaGNoYXJ0c09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgaGlnaGNoYXJ0c2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucy0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjaGVja0NhY2hlKGhpZ2hjaGFydHNPcHRpb25zLCBzZXJ2ZXJQcm94eU9wdGlvbnMpIHtcclxuICB0cnkge1xyXG4gICAgbGV0IGZldGNoZWRNb2R1bGVzO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY2FjaGUgcGF0aFxyXG4gICAgY29uc3QgY2FjaGVQYXRoID0gZ2V0Q2FjaGVQYXRoKCk7XHJcblxyXG4gICAgLy8gUHJlcGFyZSBwYXRocyB0byBtYW5pZmVzdCBhbmQgc291cmNlcyBmcm9tIHRoZSBjYWNoZSBmb2xkZXJcclxuICAgIGNvbnN0IG1hbmlmZXN0UGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnbWFuaWZlc3QuanNvbicpO1xyXG4gICAgY29uc3Qgc291cmNlUGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnc291cmNlcy5qcycpO1xyXG5cclxuICAgIC8vIENyZWF0ZSB0aGUgY2FjaGUgZGVzdGluYXRpb24gaWYgaXQgZG9lc24ndCBleGlzdCBhbHJlYWR5XHJcbiAgICAhZXhpc3RzU3luYyhjYWNoZVBhdGgpICYmIG1rZGlyU3luYyhjYWNoZVBhdGgsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xyXG5cclxuICAgIC8vIEZldGNoIGFsbCB0aGUgc2NyaXB0cyBlaXRoZXIgaWYgdGhlIGBtYW5pZmVzdC5qc29uYCBkb2VzIG5vdCBleGlzdFxyXG4gICAgLy8gb3IgaWYgdGhlIGBmb3JjZUZldGNoYCBvcHRpb24gaXMgZW5hYmxlZFxyXG4gICAgaWYgKCFleGlzdHNTeW5jKG1hbmlmZXN0UGF0aCkgfHwgaGlnaGNoYXJ0c09wdGlvbnMuZm9yY2VGZXRjaCkge1xyXG4gICAgICBsb2coMywgJ1tjYWNoZV0gRmV0Y2hpbmcgYW5kIGNhY2hpbmcgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMuJyk7XHJcblxyXG4gICAgICAvLyBUaGUgaW5pdGlhbCBjYWNoZSB1cGRhdGVcclxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCBfdXBkYXRlQ2FjaGUoXHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnMsXHJcbiAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgIHNvdXJjZVBhdGhcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XHJcblxyXG4gICAgICAvLyBSZWFkIHRoZSBtYW5pZmVzdCBKU09OXHJcbiAgICAgIGNvbnN0IG1hbmlmZXN0ID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMobWFuaWZlc3RQYXRoKSwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBtb2R1bGVzIGlzIGFuIGFycmF5LCBpZiBzbywgd2UgcmV3cml0ZSBpdCB0byBhIG1hcCB0byBtYWtlXHJcbiAgICAgIC8vIGl0IGVhc2llciB0byByZXNvbHZlIG1vZHVsZXNcclxuICAgICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIGFjdHVhbCBudW1iZXIgb2Ygc2NyaXB0cyB0byBiZSBmZXRjaGVkXHJcbiAgICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9XHJcbiAgICAgICAgaGlnaGNoYXJ0c09wdGlvbnM7XHJcbiAgICAgIGNvbnN0IG51bWJlck9mTW9kdWxlcyA9XHJcbiAgICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgICAgLy8gSWYgdGhlcmUgYXJlIGNoYW5nZXMsIGZldGNoIHJlcXVlc3RlZCBtb2R1bGVzIGFuZCBwcm9kdWN0cyxcclxuICAgICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24pIHtcclxuICAgICAgICAvLyBDaGVjayB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgICdbY2FjaGVdIEEgSGlnaGNoYXJ0cyB2ZXJzaW9uIG1pc21hdGNoIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC4nXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgICAgfSBlbHNlIGlmIChcclxuICAgICAgICBPYmplY3Qua2V5cyhtYW5pZmVzdC5tb2R1bGVzIHx8IHt9KS5sZW5ndGggIT09IG51bWJlck9mTW9kdWxlc1xyXG4gICAgICApIHtcclxuICAgICAgICAvLyBDaGVjayB0aGUgbnVtYmVyIG9mIG1vZHVsZXNcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgJ1tjYWNoZV0gVGhlIGNhY2hlIGFuZCB0aGUgcmVxdWVzdGVkIG1vZHVsZXMgZG8gbm90IG1hdGNoLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgICApO1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENoZWNrIGVhY2ggbW9kdWxlLCBpZiBhbnl0aGluZyBpcyBtaXNzaW5nIHJlZmV0Y2ggZXZlcnl0aGluZ1xyXG4gICAgICAgIHJlcXVlc3RVcGRhdGUgPSAobW9kdWxlU2NyaXB0cyB8fCBbXSkuc29tZSgobW9kdWxlTmFtZSkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFtYW5pZmVzdC5tb2R1bGVzW21vZHVsZU5hbWVdKSB7XHJcbiAgICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICAgIGBbY2FjaGVdIFRoZSAke21vZHVsZU5hbWV9IGlzIG1pc3NpbmcgaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLmBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFVwZGF0ZSBjYWNoZSBpZiBuZWVkZWRcclxuICAgICAgaWYgKHJlcXVlc3RVcGRhdGUpIHtcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IF91cGRhdGVDYWNoZShcclxuICAgICAgICAgIGhpZ2hjaGFydHNPcHRpb25zLFxyXG4gICAgICAgICAgc2VydmVyUHJveHlPcHRpb25zLFxyXG4gICAgICAgICAgc291cmNlUGF0aFxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDMsICdbY2FjaGVdIERlcGVuZGVuY3kgY2FjaGUgaXMgdXAgdG8gZGF0ZSwgcHJvY2VlZGluZy4nKTtcclxuXHJcbiAgICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xyXG4gICAgICAgIGNhY2hlLnNvdXJjZXMgPSByZWFkRmlsZVN5bmMoc291cmNlUGF0aCwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IGN1cnJlbnQgbW9kdWxlcyBtYXBcclxuICAgICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XHJcblxyXG4gICAgICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICAgICAgY2FjaGUuaGNWZXJzaW9uID0gX2V4dHJhY3RIY1ZlcnNpb24oY2FjaGUuc291cmNlcyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcclxuICAgIC8vIGluIGEgc2xpZ2h0bHkgZGlmZmVyZW50IGZvcm1hdFxyXG4gICAgYXdhaXQgX3NhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb24sIGZldGNoZWRNb2R1bGVzKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY29uZmlndXJlIGNhY2hlIGFuZCBjcmVhdGUgb3IgdXBkYXRlIHRoZSBjb25maWcgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSB2ZXJzaW9uIG9mIEhpZ2hjaGFydHMgZnJvbSB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRIY1ZlcnNpb25cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNhY2hlZCBIaWdoY2hhcnRzIHZlcnNpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0SGNWZXJzaW9uKCkge1xyXG4gIHJldHVybiBjYWNoZS5oY1ZlcnNpb247XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gaW4gdGhlIGFwcGxpZWQgY29uZmlndXJhdGlvbiBhbmQgY2hlY2tzXHJcbiAqIHRoZSBjYWNoZSBmb3IgdGhlIHNjcmlwdHMgb2YgYSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiB1cGRhdGVIY1ZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IG5ld1ZlcnNpb24gLSBUaGUgbmV3IEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHVwZGF0ZUhjVmVyc2lvbihuZXdWZXJzaW9uKSB7XHJcbiAgLy8gVXBkYXRlIHRvIHRoZSBuZXcgdmVyc2lvblxyXG4gIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgIGhpZ2hjaGFydHM6IHtcclxuICAgICAgdmVyc2lvbjogbmV3VmVyc2lvblxyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXHJcbiAgYXdhaXQgY2hlY2tDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMsIG9wdGlvbnMuc2VydmVyLnByb3h5KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBjYWNoZSBvYmplY3QuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRDYWNoZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgY2FjaGUgb2JqZWN0IGNvbnRhaW5pbmcgdmFyaW91cyBjYWNoZWQgZGF0YS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRDYWNoZSgpIHtcclxuICByZXR1cm4gY2FjaGU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBjYWNoZSBwYXRoIGZvciBIaWdoY2hhcnRzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBhYnNvbHV0ZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnkgZm9yIEhpZ2hjaGFydHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q2FjaGVQYXRoKCkge1xyXG4gIHJldHVybiBnZXRBYnNvbHV0ZVBhdGgoZ2V0T3B0aW9ucygpLmhpZ2hjaGFydHMuY2FjaGVQYXRoLCAndXRmOCcpOyAvLyAjNTYyXHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTYXZlcyB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgZmV0Y2hlZCBtb2R1bGVzIHRvIHRoZSBjYWNoZSBtYW5pZmVzdFxyXG4gKiBmaWxlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9zYXZlQ29uZmlnVG9NYW5pZmVzdFxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gdmVyc2lvbiAtIFRoZSBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzIHZlcnNpb24uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbZmV0Y2hlZE1vZHVsZXM9e31dIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBtb2R1bGVzXHJcbiAqIGhhdmUgYmVlbiBmZXRjaGVkLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBhbiBlbXB0eSBvYmplY3QuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGVcclxuICogd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfc2F2ZUNvbmZpZ1RvTWFuaWZlc3QodmVyc2lvbiwgZmV0Y2hlZE1vZHVsZXMgPSB7fSkge1xyXG4gIC8vIFVwZGF0ZSBjYWNoZSBvYmplY3Qgd2l0aCB0aGUgY3VycmVudCBtb2R1bGVzXHJcbiAgY2FjaGUuYWN0aXZlTWFuaWZlc3QgPSB7XHJcbiAgICB2ZXJzaW9uLFxyXG4gICAgbW9kdWxlczogZmV0Y2hlZE1vZHVsZXNcclxuICB9O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihnZXRDYWNoZVBhdGgoKSwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkoY2FjaGUuYWN0aXZlTWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBjb250ZW50IGFuZCBpbmZvcm1hdGlvbixcclxuICogYW5kIHVzZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF91cGRhdGVDYWNoZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaGlnaGNoYXJ0c09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgaGlnaGNoYXJ0c2Agb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlclByb3h5T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzb3VyY2VQYXRoIC0gVGhlIHBhdGggdG8gdGhlIHNvdXJjZSBmaWxlIGluIHRoZSBjYWNoZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYW4gb2JqZWN0IHJlcHJlc2VudGluZ1xyXG4gKiB0aGUgZmV0Y2hlZCBtb2R1bGVzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgYW4gaXNzdWUgdXBkYXRpbmdcclxuICogdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfdXBkYXRlQ2FjaGUoaGlnaGNoYXJ0c09wdGlvbnMsIHNlcnZlclByb3h5T3B0aW9ucywgc291cmNlUGF0aCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgSGlnaGNoYXJ0cyB2ZXJzaW9uIGZvciBzY3JpcHRzXHJcbiAgICBjb25zdCBoY1ZlcnNpb24gPVxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy52ZXJzaW9uID09PSAnbGF0ZXN0J1xyXG4gICAgICAgID8gbnVsbFxyXG4gICAgICAgIDogYCR7aGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbn1gO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIENETiB1cmwgZm9yIHNjcmlwdHNcclxuICAgIGNvbnN0IGNkblVybCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVybCB8fCBjYWNoZS5jZG5Vcmw7XHJcblxyXG4gICAgLy8gUHJlcGFyZSBvcHRpb25zIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RPcHRpb25zID0gX2NvbmZpZ3VyZVJlcXVlc3Qoc2VydmVyUHJveHlPcHRpb25zKTtcclxuXHJcbiAgICAvLyBBbiBvYmplY3QgdG8gcmVjb3JkIHdoaWNoIHNjcmlwdHMgYXJlIGZldGNoZWRcclxuICAgIGNvbnN0IGZldGNoZWRNb2R1bGVzID0ge307XHJcblxyXG4gICAgLy8gSm9pbiBhbGwgZmV0Y2hlZCBzY3JpcHRzIGFuZCBzYXZlIGluIHRoZSBtYW5pZmVzdCdzIHNvdXJjZXNcclxuICAgIGNhY2hlLnNvdXJjZXMgPSAoXHJcbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKFtcclxuICAgICAgICAvLyBIaWdoY2hhcnRzIGNvcmUgc2NyaXB0cyBmZXRjaFxyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmNvcmVTY3JpcHRzLm1hcCgoY3MpID0+XHJcbiAgICAgICAgICBfZmV0Y2hTY3JpcHQoXHJcbiAgICAgICAgICAgIGhjVmVyc2lvbiA/IGAke2NkblVybH0vJHtoY1ZlcnNpb259LyR7Y3N9YCA6IGAke2NkblVybH0vJHtjc31gLFxyXG4gICAgICAgICAgICByZXF1ZXN0T3B0aW9ucyxcclxuICAgICAgICAgICAgZmV0Y2hlZE1vZHVsZXMsXHJcbiAgICAgICAgICAgIHRydWVcclxuICAgICAgICAgIClcclxuICAgICAgICApLFxyXG4gICAgICAgIC8vIEhpZ2hjaGFydHMgbW9kdWxlIHNjcmlwdHMgZmV0Y2hcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5tb2R1bGVTY3JpcHRzLm1hcCgobXMpID0+XHJcbiAgICAgICAgICBfZmV0Y2hTY3JpcHQoXHJcbiAgICAgICAgICAgIG1zID09PSAnbWFwJ1xyXG4gICAgICAgICAgICAgID8gaGNWZXJzaW9uXHJcbiAgICAgICAgICAgICAgICA/IGAke2NkblVybH0vbWFwcy8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke21zfWBcclxuICAgICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tYXBzL21vZHVsZXMvJHttc31gXHJcbiAgICAgICAgICAgICAgOiBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICAgID8gYCR7Y2RuVXJsfS8ke2hjVmVyc2lvbn0vbW9kdWxlcy8ke21zfWBcclxuICAgICAgICAgICAgICAgIDogYCR7Y2RuVXJsfS9tb2R1bGVzLyR7bXN9YCxcclxuICAgICAgICAgICAgcmVxdWVzdE9wdGlvbnMsXHJcbiAgICAgICAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICAgICAgICApXHJcbiAgICAgICAgKSxcclxuICAgICAgICAvLyBIaWdoY2hhcnRzIGluZGljYXRvciBzY3JpcHRzIGZldGNoXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGlzKSA9PlxyXG4gICAgICAgICAgX2ZldGNoU2NyaXB0KFxyXG4gICAgICAgICAgICBoY1ZlcnNpb25cclxuICAgICAgICAgICAgICA/IGAke2NkblVybH0vc3RvY2svJHtoY1ZlcnNpb259L2luZGljYXRvcnMvJHtpc31gXHJcbiAgICAgICAgICAgICAgOiBgJHtjZG5Vcmx9L3N0b2NrL2luZGljYXRvcnMvJHtpc31gLFxyXG4gICAgICAgICAgICByZXF1ZXN0T3B0aW9ucyxcclxuICAgICAgICAgICAgZmV0Y2hlZE1vZHVsZXNcclxuICAgICAgICAgIClcclxuICAgICAgICApLFxyXG4gICAgICAgIC8vIEN1c3RvbSBzY3JpcHRzIGZldGNoXHJcbiAgICAgICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuY3VzdG9tU2NyaXB0cy5tYXAoKGNzKSA9PlxyXG4gICAgICAgICAgX2ZldGNoU2NyaXB0KGAke2NzfWAsIHJlcXVlc3RPcHRpb25zKVxyXG4gICAgICAgIClcclxuICAgICAgXSlcclxuICAgICkuam9pbignO1xcbicpO1xyXG5cclxuICAgIC8vIEV4dHJhY3QgYW5kIHNhdmUgdmVyc2lvbiBvZiBjdXJyZW50bHkgdXNlZCBIaWdoY2hhcnRzXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBfZXh0cmFjdEhjVmVyc2lvbihjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG5cclxuICAgIC8vIFJldHVybiB0aGUgZmV0Y2hlZCBtb2R1bGVzXHJcbiAgICByZXR1cm4gZmV0Y2hlZE1vZHVsZXM7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gVW5hYmxlIHRvIHVwZGF0ZSB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBgZmV0Y2hlZE1vZHVsZXNgIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9mZXRjaFNjcmlwdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0IC0gQSBwYXRoIHRvIHNjcmlwdCB0byBnZXQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSByZXF1ZXN0T3B0aW9ucyAtIEFkZGl0aW9uYWwgcmVxdWVzdHMgb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxyXG4gKiBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuIFRoZSBkZWZhdWx0XHJcbiAqIHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbVxyXG4gKiB3aXRoIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZmV0Y2hTY3JpcHQoXHJcbiAgc2NyaXB0LFxyXG4gIHJlcXVlc3RPcHRpb25zLFxyXG4gIGZldGNoZWRNb2R1bGVzLFxyXG4gIHNob3VsZFRocm93RXJyb3IgPSBmYWxzZVxyXG4pIHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSAuanMgZnJvbSB0aGUgY3VzdG9tIHN0cmluZ3NcclxuICBpZiAoc2NyaXB0LmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgc2NyaXB0ID0gc2NyaXB0LnN1YnN0cmluZygwLCBzY3JpcHQubGVuZ3RoIC0gMyk7XHJcbiAgfVxyXG4gIGxvZyg0LCBgW2NhY2hlXSBGZXRjaGluZyBzY3JpcHQgLSAke3NjcmlwdH0uanNgKTtcclxuXHJcbiAgLy8gRmV0Y2ggdGhlIHNjcmlwdFxyXG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZ2V0KGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IF9leHRyYWN0TW9kdWxlTmFtZShzY3JpcHQpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlc1ttb2R1bGVOYW1lXSA9IDE7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gcmVzcG9uc2UudGV4dDtcclxuICB9XHJcblxyXG4gIC8vIEJhc2VkIG9uIHRoZSBgc2hvdWxkVGhyb3dFcnJvcmAgZmxhZywgZGVjaWRlIGhvdyB0byBzZXJ2ZSBlcnJvciBtZXNzYWdlXHJcbiAgaWYgKHNob3VsZFRocm93RXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbiAoc3RhdHVzIGNvZGU6ICR7cmVzcG9uc2Uuc3RhdHVzQ29kZX0pLmAsXHJcbiAgICAgIDQwNFxyXG4gICAgKS5zZXRFcnJvcihyZXNwb25zZSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxvZyhcclxuICAgICAgMixcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIENvbmZpZ3VyZXMgYSBwcm94eSBhZ2VudCBmb3Igb3V0Z29pbmcgSFRUUCByZXF1ZXN0cyBiYXNlZCBvbiB0aGUgcHJvdmlkZWRcclxuICogYHNlcnZlci5wcm94eWAgb3B0aW9ucy4gSWYgYSB2YWxpZCBgaG9zdGAgYW5kIGBwb3J0YCBhcmUgc3BlY2lmaWVkLCBpdCB0cmllc1xyXG4gKiB0byBjcmVhdGUgYW4gYEh0dHBzUHJveHlBZ2VudGAuIElmIHRoZSBjcmVhdGlvbiBmYWlscywgYW4gYEV4cG9ydEVycm9yYFxyXG4gKiBpcyB0aHJvd24uIElmIG5vIHByb3h5IGlzIGNvbmZpZ3VyZWQsIGFuIGVtcHR5IG9iamVjdCBpcyByZXR1cm5lZC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9jb25maWd1cmVSZXF1ZXN0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJQcm94eU9wdGlvbnMtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBzZXJ2ZXIucHJveHlgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSByZXF1ZXN0IG9wdGlvbnMsIGluY2x1ZGluZyB0aGUgcHJveHkgYWdlbnQgaWYgY3JlYXRlZCxcclxuICogb3IgYW4gZW1wdHkgb2JqZWN0IGlmIG5vIHByb3h5IGNvbmZpZ3VyYXRpb24gaXMgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgcHJveHkgYWdlbnQgY3JlYXRpb25cclxuICogZmFpbHMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfY29uZmlndXJlUmVxdWVzdChzZXJ2ZXJQcm94eU9wdGlvbnMpIHtcclxuICAvLyBHZXQgdGhlIGBob3N0YCBhbmQgYHBvcnRgIG9mIHRoZSBwcm94eVxyXG4gIGNvbnN0IHByb3h5SG9zdCA9IHNlcnZlclByb3h5T3B0aW9ucy5ob3N0O1xyXG4gIGNvbnN0IHByb3h5UG9ydCA9IHNlcnZlclByb3h5T3B0aW9ucy5wb3J0O1xyXG5cclxuICAvLyBUcnkgdG8gY3JlYXRlIGEgcHJveHkgYWdlbnRcclxuICBpZiAocHJveHlIb3N0ICYmIHByb3h5UG9ydCkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ3JlYXRlIHRoZSBhZ2VudFxyXG4gICAgICBjb25zdCBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIC8vIEFkZCB0aGUgYWdlbnQgdG8gdGhlIHJlcXVlc3QncyBvcHRpb25zXHJcbiAgICAgIHJldHVybiB7XHJcbiAgICAgICAgYWdlbnQ6IHByb3h5QWdlbnQsXHJcbiAgICAgICAgdGltZW91dDogc2VydmVyUHJveHlPcHRpb25zLnRpbWVvdXRcclxuICAgICAgfTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYW4gZW1wdHkgb2JqZWN0IHdoZW4gbm8gcHJveHkgYWdlbnQgaXMgY3JlYXRlZFxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEV4dHJhY3RzIEhpZ2hjaGFydHMgdmVyc2lvbiBmcm9tIHRoZSBjYWNoZSdzIHNvdXJjZXMgc3RyaW5nLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2V4dHJhY3RIY1ZlcnNpb25cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNhY2hlU291cmNlcyAtIFRoZSBjYWNoZSBzb3VyY2VzIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBIaWdoY2hhcnRzIHZlcnNpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiBfZXh0cmFjdEhjVmVyc2lvbihjYWNoZVNvdXJjZXMpIHtcclxuICByZXR1cm4gY2FjaGVTb3VyY2VzXHJcbiAgICAuc3Vic3RyaW5nKDAsIGNhY2hlU291cmNlcy5pbmRleE9mKCcqLycpKVxyXG4gICAgLnJlcGxhY2UoJy8qJywgJycpXHJcbiAgICAucmVwbGFjZSgnKi8nLCAnJylcclxuICAgIC5yZXBsYWNlKC9cXG4vZywgJycpXHJcbiAgICAudHJpbSgpO1xyXG59XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgdGhlIEhpZ2hjaGFydHMgbW9kdWxlIG5hbWUgYmFzZWQgb24gdGhlIGBzY3JpcHRQYXRoYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9leHRyYWN0TW9kdWxlTmFtZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0UGF0aCAtIFRoZSBwYXRoIG9mIHRoZSBzY3JpcHQgZnJvbSB3aGljaCB0aGUgbW9kdWxlXHJcbiAqIG5hbWUgd2lsbCBiZSBleHRyYWN0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgbW9kdWxlIG5hbWUuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0UGF0aCkge1xyXG4gIHJldHVybiBzY3JpcHRQYXRoLnJlcGxhY2UoXHJcbiAgICAvKC4qKVxcL3woLiopbW9kdWxlc1xcL3xzdG9ja1xcLyguKilpbmRpY2F0b3JzXFwvfG1hcHNcXC8oLiopbW9kdWxlc1xcLy9naSxcclxuICAgICcnXHJcbiAgKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGNoZWNrQ2FjaGUsXHJcbiAgZ2V0SGNWZXJzaW9uLFxyXG4gIHVwZGF0ZUhjVmVyc2lvbixcclxuICBnZXRDYWNoZSxcclxuICBnZXRDYWNoZVBhdGhcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFByb3ZpZGVzIG1ldGhvZHMgZm9yIGluaXRpYWxpemluZyBIaWdoY2hhcnRzIHdpdGggY3VzdG9taXplZFxyXG4gKiBhbmltYXRpb24gc2V0dGluZ3MgYW5kIHRyaWdnZXJpbmcgdGhlIGNyZWF0aW9uIG9mIEhpZ2hjaGFydHMgY2hhcnRzIHdpdGhcclxuICogZXhwb3J0LXNwZWNpZmljIGNvbmZpZ3VyYXRpb25zIGluIHRoZSBwYWdlIGNvbnRleHQuIFN1cHBvcnRzIGR5bmFtaWMgb3B0aW9uXHJcbiAqIG1lcmdpbmcsIGN1c3RvbSBsb2dpYyBpbmplY3Rpb24sIGFuZCBjb250cm9sIG92ZXIgcmVuZGVyaW5nIGJlaGF2aW9ycy4gVXNlZFxyXG4gKiBieSB0aGUgUHVwcGV0ZWVyIHBhZ2UuXHJcbiAqL1xyXG5cclxuLyogZXNsaW50LWRpc2FibGUgbm8tdW5kZWYgKi9cclxuXHJcbi8qKlxyXG4gKiBTZXR0aW5nIHRoZSBgSGlnaGNoYXJ0cy5hbmltT2JqZWN0YCBmdW5jdGlvbi4gQ2FsbGVkIHdoZW4gaW5pdGluZyB0aGUgcGFnZS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0c1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldHVwSGlnaGNoYXJ0cygpIHtcclxuICBIaWdoY2hhcnRzLmFuaW1PYmplY3QgPSBmdW5jdGlvbiAoKSB7XHJcbiAgICByZXR1cm4geyBkdXJhdGlvbjogMCB9O1xyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIHRoZSBhY3R1YWwgSGlnaGNoYXJ0cyBjaGFydCBvbiBhIHBhZ2UuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY3JlYXRlQ2hhcnRcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUNoYXJ0KGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEdldCByZXF1aXJlZCBmdW5jdGlvbnNcclxuICBjb25zdCB7IGdldE9wdGlvbnMsIHNldE9wdGlvbnMsIG1lcmdlLCB3cmFwIH0gPSBIaWdoY2hhcnRzO1xyXG5cclxuICAvLyBDcmVhdGUgYSBzZXBhcmF0ZSBvYmplY3QgZm9yIGEgcG90ZW50aWFsIGBzZXRPcHRpb25zYCB1c2FnZXMgaW4gb3JkZXJcclxuICAvLyB0byBwcmV2ZW50IGZyb20gcG9sbHV0aW5nIG90aGVyIGV4cG9ydHMgdGhhdCBjYW4gaGFwcGVuIG9uIHRoZSBzYW1lIHBhZ2VcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSBtZXJnZShmYWxzZSwge30sIGdldE9wdGlvbnMoKSk7XHJcblxyXG4gIC8vIE5PVEU6IElzIHRoaXMgdXNlZCBmb3IgYW55dGhpbmcgdXNlZnVsP1xyXG4gIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gZmFsc2U7XHJcbiAgd3JhcChIaWdoY2hhcnRzLkNoYXJ0LnByb3RvdHlwZSwgJ2luaXQnLCBmdW5jdGlvbiAocHJvY2VlZCwgdXNlck9wdGlvbnMsIGNiKSB7XHJcbiAgICAvLyBPdmVycmlkZSB0aGUgYHVzZXJPcHRpb25zYCB3aXRoIGltYWdlIGZyaWVuZGx5IG9wdGlvbnNcclxuICAgIHVzZXJPcHRpb25zID0gbWVyZ2UodXNlck9wdGlvbnMsIHtcclxuICAgICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgfSxcclxuICAgICAgcGxvdE9wdGlvbnM6IHtcclxuICAgICAgICBzZXJpZXM6IHtcclxuICAgICAgICAgIGxhYmVsOiB7XHJcbiAgICAgICAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICAvKiBFeHBlY3RzIHRvb2x0aXAgaW4gdGhlIGB1c2VyT3B0aW9uc2Agd2hlbiBgZm9yRXhwb3J0YCBpcyB0cnVlLlxyXG4gICAgICAgIGh0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL2hpZ2hjaGFydHMvYmxvYi8zYWQ0MzBhMzUzYjgwNTZiOWU3NjRhYTRlNWNkNjgyOGFhNDc5ZGIyL2pzL3BhcnRzL0NoYXJ0LmpzI0wyNDFcclxuICAgICAgICAqL1xyXG4gICAgICB0b29sdGlwOiB7fVxyXG4gICAgfSk7XHJcblxyXG4gICAgKHVzZXJPcHRpb25zLnNlcmllcyB8fCBbXSkuZm9yRWFjaChmdW5jdGlvbiAoc2VyaWVzKSB7XHJcbiAgICAgIHNlcmllcy5hbmltYXRpb24gPSBmYWxzZTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEFkZCBmbGFnIHRvIGtub3cgaWYgY2hhcnQgcmVuZGVyIGhhcyBiZWVuIGNhbGxlZC5cclxuICAgIGlmICghd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlcikge1xyXG4gICAgICB3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyID0gSGlnaGNoYXJ0cy5hZGRFdmVudCh0aGlzLCAncmVuZGVyJywgKCkgPT4ge1xyXG4gICAgICAgIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gdHJ1ZTtcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbdXNlck9wdGlvbnMsIGNiXSk7XHJcbiAgfSk7XHJcblxyXG4gIHdyYXAoSGlnaGNoYXJ0cy5TZXJpZXMucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCBjaGFydCwgb3B0aW9ucykge1xyXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbY2hhcnQsIG9wdGlvbnNdKTtcclxuICB9KTtcclxuXHJcbiAgLy8gU29tZSBtYW5kYXRvcnkgYWRkaXRpb25hbCBgY2hhcnRgIGFuZCBgZXhwb3J0aW5nYCBvcHRpb25zXHJcbiAgY29uc3QgYWRkaXRpb25hbE9wdGlvbnMgPSB7XHJcbiAgICBjaGFydDoge1xyXG4gICAgICAvLyBCeSBkZWZhdWx0IGFuaW1hdGlvbiBpcyBkaXNhYmxlZFxyXG4gICAgICBhbmltYXRpb246IGZhbHNlLFxyXG4gICAgICAvLyBHZXQgdGhlIHJpZ2h0IHNpemUgdmFsdWVzXHJcbiAgICAgIGhlaWdodDogZXhwb3J0T3B0aW9ucy5oZWlnaHQsXHJcbiAgICAgIHdpZHRoOiBleHBvcnRPcHRpb25zLndpZHRoXHJcbiAgICB9LFxyXG4gICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgIC8vIE5vIG5lZWQgZm9yIHRoZSBleHBvcnRpbmcgYnV0dG9uXHJcbiAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IHRoZSBpbnB1dCB0byBleHBvcnQgZnJvbSB0aGUgYGluc3RyYCBvcHRpb25cclxuICBjb25zdCB1c2VyT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy5pbnN0cn1gKSgpO1xyXG5cclxuICAvLyBHZXQgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvblxyXG4gIGNvbnN0IHRoZW1lT3B0aW9ucyA9IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZXhwb3J0T3B0aW9ucy50aGVtZU9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gTWVyZ2UgdGhlIGZvbGxvd2luZyBvcHRpb25zIG9iamVjdHMgdG8gY3JlYXRlIGZpbmFsIG9wdGlvbnNcclxuICBjb25zdCBmaW5hbE9wdGlvbnMgPSBtZXJnZShcclxuICAgIGZhbHNlLFxyXG4gICAgdGhlbWVPcHRpb25zLFxyXG4gICAgdXNlck9wdGlvbnMsXHJcbiAgICAvLyBQbGFjZWQgaXQgaGVyZSBpbnN0ZWFkIGluIHRoZSBpbml0IGJlY2F1c2Ugb2YgdGhlIHNpemUgaXNzdWVzXHJcbiAgICBhZGRpdGlvbmFsT3B0aW9uc1xyXG4gICk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBjYWxsYmFja2Agb3B0aW9uXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja1xyXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke2N1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFja31gKSgpXHJcbiAgICA6IG51bGw7XHJcblxyXG4gIC8vIFRyaWdnZXIgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbignb3B0aW9ucycsIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb25cclxuICBjb25zdCBnbG9iYWxPcHRpb25zID0gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtleHBvcnRPcHRpb25zLmdsb2JhbE9wdGlvbnN9YCkoKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBnbG9iYWwgb3B0aW9ucyBpZiBleGlzdFxyXG4gIGlmIChnbG9iYWxPcHRpb25zKSB7XHJcbiAgICBzZXRPcHRpb25zKGdsb2JhbE9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsbCB0aGUgY2hhcnQgY3JlYXRpb25cclxuICBIaWdoY2hhcnRzW2V4cG9ydE9wdGlvbnMuY29uc3RyXSgnY29udGFpbmVyJywgZmluYWxPcHRpb25zLCBmaW5hbENhbGxiYWNrKTtcclxuXHJcbiAgLy8gR2V0IGFsbCBpbWFnZXMgZnJvbSB3aXRoaW4gdGhlIGNoYXJ0XHJcbiAgY29uc3QgaW1hZ2VzID0gQXJyYXkuZnJvbShcclxuICAgIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJy5oaWdoY2hhcnRzLWNvbnRhaW5lciBpbWFnZScpXHJcbiAgKTtcclxuXHJcbiAgLy8gV2FpdCBmb3IgYWxsIGltYWdlcyBmb3IgMiBzZWNvbmRzXHJcbiAgYXdhaXQgUHJvbWlzZS5yYWNlKFtcclxuICAgIFByb21pc2UuYWxsKFxyXG4gICAgICBpbWFnZXMubWFwKChpbWFnZSkgPT5cclxuICAgICAgICBpbWFnZS5jb21wbGV0ZSAmJiBpbWFnZS5uYXR1cmFsSGVpZ2h0ICE9PSAwXHJcbiAgICAgICAgICA/IFByb21pc2UucmVzb2x2ZSgpXHJcbiAgICAgICAgICA6IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PlxyXG4gICAgICAgICAgICAgIGltYWdlLmFkZEV2ZW50TGlzdGVuZXIoJ2xvYWQnLCByZXNvbHZlLCB7IG9uY2U6IHRydWUgfSlcclxuICAgICAgICAgICAgKVxyXG4gICAgICApXHJcbiAgICApLFxyXG4gICAgLy8gUHJvY2VlZCBmdXJ0aGVyIGV2ZW4gaWYgaW1hZ2VzIGRpZCBub3QgbG9hZFxyXG4gICAgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgMjAwMCkpXHJcbiAgXSk7XHJcblxyXG4gIC8vIEdldCB0aGUgY3VycmVudCBnbG9iYWwgb3B0aW9uc1xyXG4gIGNvbnN0IGRlZmF1bHRPcHRpb25zID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBDbGVhciBpdCBqdXN0IGluIGNhc2UgKGUuZy4gdGhlIGBzZXRPcHRpb25zYCB3YXMgdXNlZCBpbiB0aGUgYGN1c3RvbUNvZGVgKVxyXG4gIGZvciAoY29uc3QgcHJvcCBpbiBkZWZhdWx0T3B0aW9ucykge1xyXG4gICAgaWYgKHR5cGVvZiBkZWZhdWx0T3B0aW9uc1twcm9wXSAhPT0gJ2Z1bmN0aW9uJykge1xyXG4gICAgICBkZWxldGUgZGVmYXVsdE9wdGlvbnNbcHJvcF07XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGRlZmF1bHQgb3B0aW9ucyBiYWNrXHJcbiAgc2V0T3B0aW9ucyhIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmopO1xyXG5cclxuICAvLyBFbXB0eSB0aGUgY3VzdG9tIGdsb2JhbCBvcHRpb25zIG9iamVjdFxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IHt9O1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2V0dXBIaWdoY2hhcnRzLFxyXG4gIGNyZWF0ZUNoYXJ0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBwcm92aWRlcyBmdW5jdGlvbnMgZm9yIG1hbmFnaW5nIFB1cHBldGVlciBicm93c2VyXHJcbiAqIGluc3RhbmNlLCBjcmVhdGluZyBhbmQgY2xlYXJpbmcgcGFnZXMsIGluamVjdGluZyBjdXN0b20gSlMgYW5kIENTUyByZXNvdXJjZXMsXHJcbiAqIGFuZCBzZXR0aW5nIHVwIEhpZ2hjaGFydHMgZm9yIHNlcnZlci1zaWRlIHJlbmRlcmluZy4gVGhlIG1vZHVsZSBlbnN1cmVzXHJcbiAqIHRoYXQgdGhlIGJyb3dzZXIgYW5kIHBhZ2VzIGFyZSBjb3JyZWN0bHkgbWFuYWdlZCBhbmQgY2FuIGhhbmRsZSBmYWlsdXJlc1xyXG4gKiBkdXJpbmcgb3BlcmF0aW9ucyBsaWtlIGxhdW5jaGluZyB0aGUgYnJvd3NlciBvciBjcmVhdGluZyBuZXcgcGFnZXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgcHVwcGV0ZWVyIGZyb20gJ3B1cHBldGVlcic7XHJcblxyXG5pbXBvcnQgeyBnZXRDYWNoZVBhdGggfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgc2V0dXBIaWdoY2hhcnRzIH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgcGFnZXNcclxuY29uc3QgcGFnZVRlbXBsYXRlID0gcmVhZEZpbGVTeW5jKFxyXG4gIGpvaW4oX19kaXJuYW1lLCAndGVtcGxhdGVzJywgJ3RlbXBsYXRlLmh0bWwnKSxcclxuICAndXRmOCdcclxuKTtcclxuXHJcbi8vIFRvIHNhdmUgdGhlIGJyb3dzZXJcclxubGV0IGJyb3dzZXIgPSBudWxsO1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgZXhpc3RpbmcgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRCcm93c2VyXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIG5vIHZhbGlkIGJyb3dzZXJcclxuICogaGFzIGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRCcm93c2VyKCkge1xyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gTm8gdmFsaWQgYnJvd3NlciBoYXMgYmVlbiBjcmVhdGVkLicsIDUwMCk7XHJcbiAgfVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlIHdpdGggdGhlIHNwZWNpZmllZCBhcmd1bWVudHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY3JlYXRlQnJvd3NlclxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5PHN0cmluZz59IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyXHJcbiAqIGJyb3dzZXIncyBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBjcmVhdGVkIFB1cHBldGVlclxyXG4gKiBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbWF4IHJldHJpZXMgdG8gb3BlblxyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYXJlIHJlYWNoZWQsIG9yIGlmIG5vIGJyb3dzZXIgaW5zdGFuY2UgaXMgZm91bmQgYWZ0ZXJcclxuICogcmV0cmllcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVCcm93c2VyKHB1cHBldGVlckFyZ3MpIHtcclxuICAvLyBHZXQgYGRlYnVnYCBhbmQgYG90aGVyYCBvcHRpb25zXHJcbiAgY29uc3QgeyBkZWJ1Zywgb3RoZXIgfSA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBgZGVidWdgIG9wdGlvbnNcclxuICBjb25zdCB7IGVuYWJsZTogZW5hYmxlZERlYnVnLCAuLi5kZWJ1Z09wdGlvbnMgfSA9IGRlYnVnO1xyXG5cclxuICAvLyBMYXVuY2ggb3B0aW9ucyBmb3IgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICBjb25zdCBsYXVuY2hPcHRpb25zID0ge1xyXG4gICAgaGVhZGxlc3M6IG90aGVyLmJyb3dzZXJTaGVsbE1vZGUgPyAnc2hlbGwnIDogdHJ1ZSxcclxuICAgIHVzZXJEYXRhRGlyOiAndG1wJyxcclxuICAgIGFyZ3M6IHB1cHBldGVlckFyZ3MgfHwgW10sXHJcbiAgICBoYW5kbGVTSUdJTlQ6IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHVEVSTTogZmFsc2UsXHJcbiAgICBoYW5kbGVTSUdIVVA6IGZhbHNlLFxyXG4gICAgd2FpdEZvckluaXRpYWxQYWdlOiBmYWxzZSxcclxuICAgIGRlZmF1bHRWaWV3cG9ydDogbnVsbCxcclxuICAgIC4uLihlbmFibGVkRGVidWcgJiYgZGVidWdPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIC8vIEEgY291bnRlciBmb3IgdGhlIGJyb3dzZXIncyBsYXVuY2ggcmV0cmllc1xyXG4gICAgbGV0IHRyeUNvdW50ID0gMDtcclxuICAgIGNvbnN0IG9wZW5Ccm93c2VyID0gYXN5bmMgKCkgPT4ge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2Jyb3dzZXJdIEF0dGVtcHRpbmcgdG8gbGF1bmNoIGFuZCBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIExhdW5jaCB0aGUgYnJvd3NlclxyXG4gICAgICAgIGJyb3dzZXIgPSBhd2FpdCBwdXBwZXRlZXIubGF1bmNoKGxhdW5jaE9wdGlvbnMpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuXHJcbiAgICAgICAgICAvLyBXYWl0IGZvciBhIDQgc2Vjb25kcyBiZWZvcmUgdHJ5aW5nIGFnYWluXHJcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIDQwMDApKTtcclxuICAgICAgICAgIGF3YWl0IG9wZW5Ccm93c2VyKCk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBUcnkgdG8gb3BlbiBhIGJyb3dzZXJcclxuICAgICAgYXdhaXQgb3BlbkJyb3dzZXIoKTtcclxuXHJcbiAgICAgIC8vIFNoZWxsIG1vZGUgaW5mb3JtXHJcbiAgICAgIGlmIChsYXVuY2hPcHRpb25zLmhlYWRsZXNzID09PSAnc2hlbGwnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBzaGVsbCBtb2RlLmApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBEZWJ1ZyBtb2RlIGluZm9ybVxyXG4gICAgICBpZiAoZW5hYmxlZERlYnVnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1ticm93c2VyXSBNYXhpbXVtIHJldHJpZXMgdG8gb3BlbiBhIGJyb3dzZXIgaW5zdGFuY2UgcmVhY2hlZC4nLFxyXG4gICAgICAgIDUwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBjb3JyZWN0IGJyb3dzZXJcclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nLCA1MDApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xvc2VCcm93c2VyXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xvc2VCcm93c2VyKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIgJiYgYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgYnJvd3NlciA9IG51bGw7XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgcGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHRoZSBgX3NldFBhZ2VDb250ZW50KClgLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBuZXdQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIGBpZGAsXHJcbiAqIGB3b3JrQ291bnRgLCBhbmQgYHBhZ2VgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgbm8gdmFsaWQgYnJvd3NlclxyXG4gKiBoYXMgYmVlbiBjb25uZWN0ZWQgb3IgaWYgYSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG5ld1BhZ2UocG9vbFJlc291cmNlKSB7XHJcbiAgLy8gRXJyb3IgaW4gY2FzZSBvZiBubyBjb25uZWN0ZWQgYnJvd3NlclxyXG4gIGlmICghYnJvd3NlciB8fCAhYnJvd3Nlci5jb25uZWN0ZWQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW2Jyb3dzZXJdIEJyb3dzZXIgaXMgbm90IHlldCBjb25uZWN0ZWQuYCwgNTAwKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBwb29sUmVzb3VyY2UucGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBEaXNhYmxlIGNhY2hlXHJcbiAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgX3NldFBhZ2VDb250ZW50KHBvb2xSZXNvdXJjZS5wYWdlKTtcclxuXHJcbiAgLy8gU2V0IHBhZ2UgZXZlbnRzXHJcbiAgX3NldFBhZ2VFdmVudHMocG9vbFJlc291cmNlLnBhZ2UpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgcGFnZSBpcyBjb3JyZWN0bHkgY3JlYXRlZFxyXG4gIGlmICghcG9vbFJlc291cmNlLnBhZ2UgfHwgcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgdGhlIGNvbnRlbnQgb2YgYSBQdXBwZXRlZXIgcGFnZSBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG1vZGUuIExvZ3NcclxuICogdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIG9mIGEgcGFnZSdzIGNvbnRlbnQgZmFpbHMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gY2xlYXJQYWdlXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgcG9vbCByZXNvdXJjZSB0aGF0IGNvbnRhaW5zIHBhZ2UgYW5kIGlkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IFtoYXJkUmVzZXQ9ZmFsc2VdIC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiBgdHJ1ZWAsIG5hdmlnYXRlcyB0byBgYWJvdXQ6YmxhbmtgIGFuZCByZXNldHMgY29udGVudFxyXG4gKiBhbmQgc2NyaXB0cy4gSWYgYGZhbHNlYCwgY2xlYXJzIHRoZSBib2R5IGNvbnRlbnQgYnkgc2V0dGluZyBhIHByZWRlZmluZWQgSFRNTFxyXG4gKiBzdHJ1Y3R1cmUuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBmYWxzZWAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBgdHJ1ZWAgd2hlbiBwYWdlXHJcbiAqIGlzIGNvcnJlY3RseSBjbGVhcmVkIGFuZCBgZmFsc2VgIHdoZW4gaXQgaXMgbm90LlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZShwb29sUmVzb3VyY2UsIGhhcmRSZXNldCA9IGZhbHNlKSB7XHJcbiAgdHJ5IHtcclxuICAgIGlmIChwb29sUmVzb3VyY2UucGFnZSAmJiAhcG9vbFJlc291cmNlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICBpZiAoaGFyZFJlc2V0KSB7XHJcbiAgICAgICAgLy8gTmF2aWdhdGUgdG8gYGFib3V0OmJsYW5rYFxyXG4gICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmdvdG8oJ2Fib3V0OmJsYW5rJywge1xyXG4gICAgICAgICAgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCdcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gU2V0IHRoZSBjb250ZW50IGFuZCBhbmQgc2NyaXB0cyBhZ2FpblxyXG4gICAgICAgIGF3YWl0IF9zZXRQYWdlQ29udGVudChwb29sUmVzb3VyY2UucGFnZSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2xlYXIgYm9keSBjb250ZW50XHJcbiAgICAgICAgYXdhaXQgcG9vbFJlc291cmNlLnBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5pbm5lckhUTUwgPVxyXG4gICAgICAgICAgICAnPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPjxkaXYgaWQ9XCJjb250YWluZXJcIj48L2Rpdj48L2Rpdj4nO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDIsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3Bvb2xdIFBvb2wgcmVzb3VyY2UgWyR7cG9vbFJlc291cmNlLmlkfV0gLSBDb250ZW50IG9mIHRoZSBwYWdlIGNvdWxkIG5vdCBiZSBjbGVhcmVkLmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBgd29ya0xpbWl0YCB0byBleGNlZWRlZCBpbiBvcmRlciB0byByZWNyZWF0ZSB0aGUgcmVzb3VyY2VcclxuICAgIHBvb2xSZXNvdXJjZS53b3JrQ291bnQgPSBnZXRPcHRpb25zKCkucG9vbC53b3JrTGltaXQgKyAxO1xyXG4gIH1cclxuICByZXR1cm4gZmFsc2U7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIGN1c3RvbSBKUyBhbmQgQ1NTIHJlc291cmNlcyB0byBhIFB1cHBldGVlciBwYWdlIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBwYWdlIG9iamVjdCB0byB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBhZGRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QXJyYXk8T2JqZWN0Pj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIGFycmF5XHJcbiAqIG9mIGluamVjdGVkIHJlc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheVxyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIC8vIFVzZSB0aGUgY29udGVudCBvZiB0aGUgYHJlc291cmNlc2BcclxuICBjb25zdCByZXNvdXJjZXMgPSBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzO1xyXG4gIGlmIChyZXNvdXJjZXMpIHtcclxuICAgIGNvbnN0IGluamVjdGVkSnMgPSBbXTtcclxuXHJcbiAgICAvLyBMb2FkIGN1c3RvbSBKUyBjb2RlXHJcbiAgICBpZiAocmVzb3VyY2VzLmpzKSB7XHJcbiAgICAgIGluamVjdGVkSnMucHVzaCh7XHJcbiAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmpzXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExvYWQgc2NyaXB0cyBmcm9tIGFsbCBjdXN0b20gZmlsZXNcclxuICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICAgIGNvbnN0IGlzTG9jYWwgPSBmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IGZhbHNlIDogdHJ1ZTtcclxuXHJcbiAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICBpbmplY3RlZEpzLnB1c2goXHJcbiAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgID8ge1xyXG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChmaWxlKSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFRoZSBhY3R1YWwgaW5qZWN0aW9uIG9mIGNvbGxlY3RlZCBzY3JpcHRzXHJcbiAgICBmb3IgKGNvbnN0IGpzUmVzb3VyY2Ugb2YgaW5qZWN0ZWRKcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoanNSZXNvdXJjZSkpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFticm93c2VyXSBUaGUgSlMgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gICAgaW5qZWN0ZWRKcy5sZW5ndGggPSAwO1xyXG5cclxuICAgIC8vIExvYWQgQ1NTXHJcbiAgICBjb25zdCBpbmplY3RlZENzcyA9IFtdO1xyXG4gICAgaWYgKHJlc291cmNlcy5jc3MpIHtcclxuICAgICAgY29uc3QgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgcGF0aDogZ2V0QWJzb2x1dGVQYXRoKGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgLy8gVGhlIGFjdHVhbCBpbmplY3Rpb24gb2YgY29sbGVjdGVkIENTU1xyXG4gICAgICBmb3IgKGNvbnN0IGNzc1Jlc291cmNlIG9mIGluamVjdGVkQ3NzKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyhjc3NSZXNvdXJjZSkpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICBgW2Jyb3dzZXJdIFRoZSBDU1MgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBpbmplY3RlZENzcy5sZW5ndGggPSAwO1xyXG4gICAgfVxyXG4gIH1cclxuICByZXR1cm4gaW5qZWN0ZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgb3V0IGFsbCBzdGF0ZSBzZXQgb24gdGhlIHBhZ2Ugd2l0aCBgYWRkU2NyaXB0VGFnYCBhbmQgYGFkZFN0eWxlVGFnYC5cclxuICogUmVtb3ZlcyBpbmplY3RlZCByZXNvdXJjZXMgYW5kIHJlc2V0cyBDU1MgYW5kIHNjcmlwdCB0YWdzIG9uIHRoZSBwYWdlLlxyXG4gKiBBZGRpdGlvbmFsbHksIGl0IGRlc3Ryb3lzIHByZXZpb3VzbHkgZXhpc3RpbmcgY2hhcnRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgZnJvbSB3aGljaCByZXNvdXJjZXMgd2lsbFxyXG4gKiBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge0FycmF5PE9iamVjdD59IGluamVjdGVkUmVzb3VyY2VzIC0gQXJyYXkgb2YgaW5qZWN0ZWQgcmVzb3VyY2VzXHJcbiAqIHRvIGJlIGNsZWFyZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgdHJ5IHtcclxuICAgIGZvciAoY29uc3QgcmVzb3VyY2Ugb2YgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICAgICAgYXdhaXQgcmVzb3VyY2UuZGlzcG9zZSgpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0cyBhZnRlciBleHBvcnQgaXMgZG9uZSBhbmQgcmVzZXQgYWxsIENTUyBhbmQgc2NyaXB0IHRhZ3NcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAvLyBXZSBhcmUgbm90IGd1YXJhbnRlZWQgdGhhdCBIaWdoY2hhcnRzIGlzIGxvYWRlZCwgd2hlbiBkb2luZyBTVkcgZXhwb3J0c1xyXG4gICAgICBpZiAodHlwZW9mIEhpZ2hjaGFydHMgIT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgY29uc3Qgb2xkQ2hhcnRzID0gSGlnaGNoYXJ0cy5jaGFydHM7XHJcblxyXG4gICAgICAgIC8vIENoZWNrIGluIGFueSBhbHJlYWR5IGV4aXN0aW5nIGNoYXJ0c1xyXG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KG9sZENoYXJ0cykgJiYgb2xkQ2hhcnRzLmxlbmd0aCkge1xyXG4gICAgICAgICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzXHJcbiAgICAgICAgICBmb3IgKGNvbnN0IG9sZENoYXJ0IG9mIG9sZENoYXJ0cykge1xyXG4gICAgICAgICAgICBvbGRDaGFydCAmJiBvbGRDaGFydC5kZXN0cm95KCk7XHJcbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgICBIaWdoY2hhcnRzLmNoYXJ0cy5zaGlmdCgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5zY3JpcHRzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3NjcmlwdCcpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWywgLi4uc3R5bGVzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3N0eWxlJyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4ubGlua3NUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnbGluaycpO1xyXG5cclxuICAgICAgLy8gUmVtb3ZlIHRhZ3NcclxuICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIFtcclxuICAgICAgICAuLi5zY3JpcHRzVG9SZW1vdmUsXHJcbiAgICAgICAgLi4uc3R5bGVzVG9SZW1vdmUsXHJcbiAgICAgICAgLi4ubGlua3NUb1JlbW92ZVxyXG4gICAgICBdKSB7XHJcbiAgICAgICAgZWxlbWVudC5yZW1vdmUoKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFticm93c2VyXSBDb3VsZCBub3QgY2xlYXIgcGFnZSdzIHJlc291cmNlcy5gKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb250ZW50IGZvciBhIFB1cHBldGVlciBwYWdlIHVzaW5nIGEgcHJlZGVmaW5lZCB0ZW1wbGF0ZVxyXG4gKiBhbmQgYWRkaXRpb25hbCBzY3JpcHRzLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9zZXRQYWdlQ29udGVudFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgcGFnZSBvYmplY3QgdG8gd2hpY2ggdGhlIGNvbnRlbnRcclxuICogaXMgYmVpbmcgc2V0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3NldFBhZ2VDb250ZW50KHBhZ2UpIHtcclxuICAvLyBTZXQgdGhlIGluaXRpYWwgcGFnZSBjb250ZW50XHJcbiAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHBhZ2VUZW1wbGF0ZSwgeyB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJyB9KTtcclxuXHJcbiAgLy8gQWRkIGFsbCByZWdpc3RlcmVkIEhpZ2NoYXJ0cyBzY3JpcHRzLCBxdWl0ZSBkZW1hbmRpbmdcclxuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGpvaW4oZ2V0Q2FjaGVQYXRoKCksICdzb3VyY2VzLmpzJykgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBgYW5pbU9iamVjdGAgZm9yIEhpZ2hjaGFydHNcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKHNldHVwSGlnaGNoYXJ0cyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgZXZlbnRzIChsaWtlIGBwYWdlZXJyb3JgIGFuZCBgY29uc29sZWApIGZvciBhIFB1cHBldGVlciBwYWdlIGluIG9yZGVyXHJcbiAqIHRvIGNhdGNoIGFuZCBkaXNwbGF5IGVycm9ycyBhbmQgY29uc29sZSBsb2dzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3NldFBhZ2VFdmVudHNcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHRoZSBsaXN0ZW5lcnNcclxuICogYXJlIGJlaW5nIHNldC5cclxuICovXHJcbmZ1bmN0aW9uIF9zZXRQYWdlRXZlbnRzKHBhZ2UpIHtcclxuICAvLyBHZXQgYGRlYnVnYCBvcHRpb25zXHJcbiAgY29uc3QgeyBkZWJ1ZyB9ID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBTZXQgdGhlIGBwYWdlZXJyb3JgIGxpc3RlbmVyXHJcbiAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKCkgPT4ge1xyXG4gICAgLy8gSXQgd291bGQgc2VlbSBsaWtlIHRoaXMgbWF5IGZpcmUgYXQgdGhlIHNhbWUgdGltZSBvciBzaG9ydGx5IGJlZm9yZVxyXG4gICAgLy8gYSBwYWdlIGlzIGNsb3NlZC5cclxuICAgIGlmIChwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIGBjb25zb2xlYCBsaXN0ZW5lciwgaWYgbmVlZGVkXHJcbiAgaWYgKGRlYnVnLmVuYWJsZSAmJiBkZWJ1Zy5saXN0ZW5Ub0NvbnNvbGUpIHtcclxuICAgIHBhZ2Uub24oJ2NvbnNvbGUnLCAobWVzc2FnZSkgPT4ge1xyXG4gICAgICBjb25zb2xlLmxvZyhgW2RlYnVnXSAke21lc3NhZ2UudGV4dCgpfWApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0QnJvd3NlcixcclxuICBjcmVhdGVCcm93c2VyLFxyXG4gIGNsb3NlQnJvd3NlcixcclxuICBuZXdQYWdlLFxyXG4gIGNsZWFyUGFnZSxcclxuICBhZGRQYWdlUmVzb3VyY2VzLFxyXG4gIGNsZWFyUGFnZVJlc291cmNlc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBUaGUgQ1NTIHRvIGJlIHVzZWQgb24gdGhlIGV4cG9ydGVkIHBhZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBDU1MgY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0ICgpID0+IGBcclxuXHJcbmh0bWwsIGJvZHkge1xyXG4gIG1hcmdpbjogMDtcclxuICBwYWRkaW5nOiAwO1xyXG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XHJcbn1cclxuXHJcbiN0YWJsZS1kaXYsICNzbGlkZXJzLCAjZGF0YXRhYmxlLCAjY29udHJvbHMsIC5sZC1yb3cge1xyXG4gIGRpc3BsYXk6IG5vbmU7XHJcbiAgaGVpZ2h0OiAwO1xyXG59XHJcblxyXG4jY2hhcnQtY29udGFpbmVyIHtcclxuICBib3gtc2l6aW5nOiBib3JkZXItYm94O1xyXG4gIG1hcmdpbjogMDtcclxuICBvdmVyZmxvdzogYXV0bztcclxuICBmb250LXNpemU6IDA7XHJcbn1cclxuXHJcbiNjaGFydC1jb250YWluZXIgPiBmaWd1cmUsIGRpdiB7XHJcbiAgbWFyZ2luLXRvcDogMCAhaW1wb3J0YW50O1xyXG4gIG1hcmdpbi1ib3R0b206IDAgIWltcG9ydGFudDtcclxufVxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY3NzVGVtcGxhdGUgZnJvbSAnLi9jc3MuanMnO1xyXG5cclxuLyoqXHJcbiAqIFRoZSBTVkcgdGVtcGxhdGUgdG8gdXNlIHdoZW4gbG9hZGluZyBTVkcgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHN2ZyAtIFRoZSBTVkcgaW5wdXQgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIFNWRyB0ZW1wbGF0ZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChzdmcpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNoYXJ0cyBFeHBvcnQ8L3RpdGxlPlxyXG4gIDwvaGVhZD5cclxuICA8c3R5bGU+XHJcbiAgICAke2Nzc1RlbXBsYXRlKCl9XHJcbiAgPC9zdHlsZT5cclxuICA8Ym9keT5cclxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cclxuICAgICAgJHtzdmd9XHJcbiAgICA8L2Rpdj5cclxuICA8L2JvZHk+XHJcbjwvaHRtbD5cclxuXHJcbmA7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBoYW5kbGVzIGNoYXJ0IGV4cG9ydCBmdW5jdGlvbmFsaXR5IHVzaW5nIFB1cHBldGVlci5cclxuICogSXQgc3VwcG9ydHMgZXhwb3J0aW5nIGNoYXJ0cyBhcyBTVkcsIFBORywgSlBFRywgYW5kIFBERiBmb3JtYXRzLiBUaGUgbW9kdWxlXHJcbiAqIG1hbmFnZXMgcGFnZSByZXNvdXJjZXMsIHNldHMgdXAgdGhlIGV4cG9ydCBlbnZpcm9ubWVudCwgYW5kIHByb2Nlc3NlcyBjaGFydFxyXG4gKiBjb25maWd1cmF0aW9ucyBvciBTVkcgaW5wdXRzIGZvciByZW5kZXJpbmcuIEV4cG9ydHMgdG8gYSBjaGFydCBmcm9tIGEgcGFnZVxyXG4gKiB1c2luZyBQdXBwZXRlZXIuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgYWRkUGFnZVJlc291cmNlcywgY2xlYXJQYWdlUmVzb3VyY2VzIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgY3JlYXRlQ2hhcnQgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgc3ZnVGVtcGxhdGUgZnJvbSAnLi4vdGVtcGxhdGVzL3N2Z0V4cG9ydC9zdmdFeHBvcnQuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2UgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgZXhwb3J0YFxyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tTG9naWNPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYGN1c3RvbUxvZ2ljYCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTwoc3RyaW5nfEJ1ZmZlcnxFeHBvcnRFcnJvcik+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlc1xyXG4gKiB0byB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBgRXhwb3J0RXJyb3JgLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgZXhwb3J0IHRvIGFuIHVuc3VwcG9ydGVkXHJcbiAqIG91dHB1dCBmb3JtYXQgb2NjdXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHB1cHBldGVlckV4cG9ydChwYWdlLCBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXkgKGFkZGl0aW9uYWwgSlMgYW5kIENTUylcclxuICBjb25zdCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICB0cnkge1xyXG4gICAgbGV0IGlzU1ZHID0gZmFsc2U7XHJcblxyXG4gICAgLy8gRGVjaWRlIG9uIHRoZSBleHBvcnQgbWV0aG9kXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy5zdmcpIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBJZiB0aGUgYHR5cGVgIGlzIGFsc28gU1ZHLCByZXR1cm4gdGhlIGlucHV0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGV4cG9ydE9wdGlvbnMuc3ZnO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBNYXJrIGFzIFNWRyBleHBvcnQgZm9yIHRoZSBsYXRlciBzaXplIGNvcnJlY3Rpb25zXHJcbiAgICAgIGlzU1ZHID0gdHJ1ZTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRcclxuICAgICAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKGV4cG9ydE9wdGlvbnMuc3ZnKSwge1xyXG4gICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgIH0pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBKU09OIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0XHJcbiAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoY3JlYXRlQ2hhcnQsIGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljT3B0aW9ucyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXHJcbiAgICAvLyBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAgLy8gb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAuLi4oYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBjdXN0b21Mb2dpY09wdGlvbnMpKVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZSBhbmQgc2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XHJcbiAgICBjb25zdCBzaXplID0gYXdhaXQgX2dldENoYXJ0U2l6ZShwYWdlLCBpc1NWRywgZXhwb3J0T3B0aW9ucy5zY2FsZSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjbGlwIHJlZ2lvbiBmb3IgdGhlIHBhZ2VcclxuICAgIGNvbnN0IHsgeCwgeSB9ID0gYXdhaXQgX2dldENsaXBSZWdpb24ocGFnZSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGBoZWlnaHRgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmFicyhcclxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRIZWlnaHQgfHwgZXhwb3J0T3B0aW9ucy5oZWlnaHQpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBgd2lkdGhgIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRXaWR0aCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcclxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgIH0pO1xyXG5cclxuICAgIGxldCByZXN1bHQ7XHJcbiAgICAvLyBSYXN0ZXJpemF0aW9uIHByb2Nlc3NcclxuICAgIHN3aXRjaCAoZXhwb3J0T3B0aW9ucy50eXBlKSB7XHJcbiAgICAgIGNhc2UgJ3N2Zyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZVNWRyhwYWdlKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncG5nJzpcclxuICAgICAgY2FzZSAnanBlZyc6XHJcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgX2NyZWF0ZUltYWdlKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMudHlwZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgICAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIHgsXHJcbiAgICAgICAgICAgIHlcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICAgKTtcclxuICAgICAgICBicmVhaztcclxuICAgICAgY2FzZSAncGRmJzpcclxuICAgICAgICByZXN1bHQgPSBhd2FpdCBfY3JlYXRlUERGKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICAgICAgdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyZWFrO1xyXG4gICAgICBkZWZhdWx0OlxyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbZXhwb3J0XSBVbnN1cHBvcnRlZCBvdXRwdXQgZm9ybWF0OiAke2V4cG9ydE9wdGlvbnMudHlwZX0uYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2xlYXIgcHJldmlvdXNseSBpbmplY3RlZCBKUyBhbmQgQ1NTIHJlc291cmNlc1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiByZXN1bHQ7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMgb2YgdGhlIHNwZWNpZmllZCBwYWdlIGVsZW1lbnRcclxuICogd2l0aCB0aGUgJ2NoYXJ0LWNvbnRhaW5lcicgaWQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2dldENsaXBSZWdpb25cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGFuIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGB4YCwgYHlgLCBgd2lkdGhgLCBhbmQgYGhlaWdodGAgcHJvcGVydGllcy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9nZXRDbGlwUmVnaW9uKHBhZ2UpIHtcclxuICByZXR1cm4gcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XHJcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB4LFxyXG4gICAgICB5LFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXHJcbiAgICB9O1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSByZWFsIGNoYXJ0IGRpbWVuc2lvbnMgZnJvbSBhIFB1cHBldGVlciBwYWdlLiBUaGUgZnVuY3Rpb25cclxuICogYmVoYXZlcyBkaWZmZXJlbnRseSBiYXNlZCBvbiB3aGV0aGVyIHRoZSBleHBvcnQgdHlwZSBpcyBTVkcgb3IgYW5vdGhlclxyXG4gKiBmb3JtYXQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gX2dldENoYXJ0U2l6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtib29sZWFufSBpc1NWRyAtIERldGVybWluZXMgd2hldGhlciB0aGUgY2hhcnQgYmVpbmcgcHJvY2Vzc2VkXHJcbiAqIGlzIGFuIFNWRyBvciBhbm90aGVyIGZvcm1hdC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHNjYWxlIC0gVGhlIHNjYWxlIGZhY3RvciB0byBiZSBhcHBsaWVkIHRvIHRoZSBjaGFydFxyXG4gKiBkaW1lbnNpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiB0aGUgYWN0dWFsIGhlaWdodCBhbmQgd2lkdGggb2YgdGhlIGNoYXJ0IGFmdGVyIHNjYWxpbmcuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBfZ2V0Q2hhcnRTaXplKHBhZ2UsIGlzU1ZHLCBzY2FsZSkge1xyXG4gIC8vIFRyaWdnZXIgYXBwcm9wcmlhdGUgZnVuY3Rpb24gYmFzZWQgb24gdGhlIGBpc1N2Z2AgZmxhZyB0byBnZXQgY2hhcnQgc2l6ZVxyXG4gIHJldHVybiBpc1NWR1xyXG4gICAgPyBhd2FpdCBwYWdlLmV2YWx1YXRlKChzY2FsZSkgPT4ge1xyXG4gICAgICAgIGNvbnN0IHN2Z0VsZW1lbnQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKFxyXG4gICAgICAgICAgJyNjaGFydC1jb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSB2YWx1ZXMgY29ycmVjdGx5IHNjYWxlZFxyXG4gICAgICAgIGNvbnN0IGNoYXJ0SGVpZ2h0ID0gc3ZnRWxlbWVudC5oZWlnaHQuYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xyXG4gICAgICAgIGNvbnN0IGNoYXJ0V2lkdGggPSBzdmdFbGVtZW50LndpZHRoLmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuXHJcbiAgICAgICAgLy8gSW4gY2FzZSBvZiBTVkcgdGhlIHpvb20gbXVzdCBiZSBzZXQgZGlyZWN0bHkgZm9yIGJvZHkgYXMgc2NhbGVcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSBzY2FsZTtcclxuXHJcbiAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcclxuXHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgY2hhcnRXaWR0aFxyXG4gICAgICAgIH07XHJcbiAgICAgIH0sIHBhcnNlRmxvYXQoc2NhbGUpKVxyXG4gICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBjb25zdCB7IGNoYXJ0SGVpZ2h0LCBjaGFydFdpZHRoIH0gPSB3aW5kb3cuSGlnaGNoYXJ0cy5jaGFydHNbMF07XHJcblxyXG4gICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXNcclxuICAgICAgICAvLyBvZiBleHBvcnRzLiBSZXNldCB0aGUgem9vbSBmb3Igb3RoZXIgZXhwb3J0cyB0aGFuIHRvIFNWR3NcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xyXG5cclxuICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgfTtcclxuICAgICAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIFNWRyBieSBldmFsdWF0aW5nIHRoZSBgb3V0ZXJIVE1MYCBvZiB0aGUgZmlyc3QgJ3N2ZycgZWxlbWVudFxyXG4gKiBpbnNpZGUgYW4gZWxlbWVudCB3aXRoIHRoZSBpZCAnY29udGFpbmVyJy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfY3JlYXRlU1ZHXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgU1ZHIHN0cmluZy5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9jcmVhdGVTVkcocGFnZSkge1xyXG4gIHJldHVybiBwYWdlLiRldmFsKFxyXG4gICAgJyNjb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnLFxyXG4gICAgKGVsZW1lbnQpID0+IGVsZW1lbnQub3V0ZXJIVE1MXHJcbiAgKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gaW1hZ2UgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBgc2NyZWVuc2hvdGAgZnVuY3Rpb25hbGl0eSB3aXRoXHJcbiAqIHNwZWNpZmllZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVJbWFnZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBJbWFnZSB0eXBlLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2xpcCAtIENsaXBwaW5nIHJlZ2lvbiBjb29yZGluYXRlcy5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBpbWFnZSBidWZmZXJcclxuICogb3IgcmVqZWN0aW5nIHdpdGggYW4gYEV4cG9ydEVycm9yYCBmb3IgdGltZW91dC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9jcmVhdGVJbWFnZShwYWdlLCB0eXBlLCBjbGlwLCByYXN0ZXJpemF0aW9uVGltZW91dCkge1xyXG4gIHJldHVybiBQcm9taXNlLnJhY2UoW1xyXG4gICAgcGFnZS5zY3JlZW5zaG90KHtcclxuICAgICAgdHlwZSxcclxuICAgICAgY2xpcCxcclxuICAgICAgZW5jb2Rpbmc6ICdiYXNlNjQnLFxyXG4gICAgICBmdWxsUGFnZTogZmFsc2UsXHJcbiAgICAgIG9wdGltaXplRm9yU3BlZWQ6IHRydWUsXHJcbiAgICAgIGNhcHR1cmVCZXlvbmRWaWV3cG9ydDogdHJ1ZSxcclxuICAgICAgLi4uKHR5cGUgIT09ICdwbmcnID8geyBxdWFsaXR5OiA4MCB9IDoge30pLFxyXG4gICAgICAvLyBBbHdheXMgcmVuZGVyIG9uIGEgdHJhbnNwYXJlbnQgcGFnZSBpZiB0aGUgZXhwZWN0ZWQgdHlwZSBmb3JtYXQgaXMgUE5HXHJcbiAgICAgIG9taXRCYWNrZ3JvdW5kOiB0eXBlID09ICdwbmcnIC8vICM0NDcsICM0NjNcclxuICAgIH0pLFxyXG4gICAgbmV3IFByb21pc2UoKF9yZXNvbHZlLCByZWplY3QpID0+XHJcbiAgICAgIHNldFRpbWVvdXQoXHJcbiAgICAgICAgKCkgPT4gcmVqZWN0KG5ldyBFeHBvcnRFcnJvcignUmFzdGVyaXphdGlvbiB0aW1lb3V0JywgNDA4KSksXHJcbiAgICAgICAgcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxyXG4gICAgICApXHJcbiAgICApXHJcbiAgXSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUERGIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgYHBkZmAgZnVuY3Rpb25hbGl0eSB3aXRoIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9jcmVhdGVQREZcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHQgLSBQREYgaGVpZ2h0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGggLSBQREYgd2lkdGguXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgUERGIGJ1ZmZlci5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIF9jcmVhdGVQREYocGFnZSwgaGVpZ2h0LCB3aWR0aCwgcmFzdGVyaXphdGlvblRpbWVvdXQpIHtcclxuICBhd2FpdCBwYWdlLmVtdWxhdGVNZWRpYVR5cGUoJ3NjcmVlbicpO1xyXG4gIHJldHVybiBwYWdlLnBkZih7XHJcbiAgICAvLyBUaGlzIHdpbGwgcmVtb3ZlIGFuIGV4dHJhIGVtcHR5IHBhZ2UgaW4gUERGIGV4cG9ydHNcclxuICAgIGhlaWdodDogaGVpZ2h0ICsgMSxcclxuICAgIHdpZHRoLFxyXG4gICAgZW5jb2Rpbmc6ICdiYXNlNjQnLFxyXG4gICAgdGltZW91dDogcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgcHVwcGV0ZWVyRXhwb3J0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI1LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBUaGlzIG1vZHVsZSBwcm92aWRlcyBhIHdvcmtlciBwb29sIGltcGxlbWVudGF0aW9uIGZvciBtYW5hZ2luZ1xyXG4gKiB0aGUgYnJvd3NlciBpbnN0YW5jZSBhbmQgcGFnZXMsIHNwZWNpZmljYWxseSBkZXNpZ25lZCBmb3IgdXNlIHdpdGhcclxuICogdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlci4gSXQgb3B0aW1pemVzIHJlc291cmNlcyB1c2FnZSBhbmQgcGVyZm9ybWFuY2VcclxuICogYnkgbWFpbnRhaW5pbmcgYSBwb29sIG9mIHdvcmtlcnMgdGhhdCBjYW4gaGFuZGxlIGNvbmN1cnJlbnQgZXhwb3J0IHRhc2tzXHJcbiAqIHVzaW5nIFB1cHBldGVlci5cclxuICovXHJcblxyXG5pbXBvcnQgeyBQb29sIH0gZnJvbSAndGFybic7XHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7IGNyZWF0ZUJyb3dzZXIsIGNsb3NlQnJvd3NlciwgbmV3UGFnZSwgY2xlYXJQYWdlIH0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHsgcHVwcGV0ZWVyRXhwb3J0IH0gZnJvbSAnLi9leHBvcnQuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0TmV3RGF0ZVRpbWUsIG1lYXN1cmVUaW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIHBvb2wgaW5zdGFuY2VcclxubGV0IHBvb2wgPSBudWxsO1xyXG5cclxuLy8gUG9vbCBzdGF0aXN0aWNzXHJcbmNvbnN0IHBvb2xTdGF0cyA9IHtcclxuICBleHBvcnRzQXR0ZW1wdGVkOiAwLFxyXG4gIGV4cG9ydHNQZXJmb3JtZWQ6IDAsXHJcbiAgZXhwb3J0c0Ryb3BwZWQ6IDAsXHJcbiAgZXhwb3J0c0Zyb21Tdmc6IDAsXHJcbiAgZXhwb3J0c0Zyb21PcHRpb25zOiAwLFxyXG4gIGV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHM6IDAsXHJcbiAgZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHM6IDAsXHJcbiAgdGltZVNwZW50OiAwLFxyXG4gIHRpbWVTcGVudEF2ZXJhZ2U6IDBcclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyB0aGUgZXhwb3J0IHBvb2wgd2l0aCB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiwgY3JlYXRpbmdcclxuICogYSBicm93c2VyIGluc3RhbmNlIGFuZCBzZXR0aW5nIHVwIHdvcmtlciByZXNvdXJjZXMuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdFBvb2xcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7QXJyYXk8c3RyaW5nPn0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXJcclxuICogbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gZW5kaW5nIHRoZSBmdW5jdGlvblxyXG4gKiBleGVjdXRpb24gd2hlbiBhbiBhbHJlYWR5IGluaXRpYWxpemVkIHBvb2wgb2YgcmVzb3VyY2VzIGlzIGZvdW5kLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgY291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbFxyXG4gKiBvZiB3b3JrZXJzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluaXRQb29sKHBvb2xPcHRpb25zLCBwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBwdXBwZXRlZXIgYXJndW1lbnRzXHJcbiAgYXdhaXQgY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBJbml0aWFsaXppbmcgcG9vbCB3aXRoIHdvcmtlcnM6IG1pbiAke3Bvb2xPcHRpb25zLm1pbldvcmtlcnN9LCBtYXggJHtwb29sT3B0aW9ucy5tYXhXb3JrZXJzfS5gXHJcbiAgICApO1xyXG5cclxuICAgIGlmIChwb29sKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgICdbcG9vbF0gQWxyZWFkeSBpbml0aWFsaXplZCwgcGxlYXNlIGtpbGwgaXQgYmVmb3JlIGNyZWF0aW5nIGEgbmV3IG9uZS4nXHJcbiAgICAgICk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBLZWVwIGFuIGV5ZSBvbiBhIGNvcnJlY3QgbWluIGFuZCBtYXggd29ya2VycyBudW1iZXJcclxuICAgIGlmIChwb29sT3B0aW9ucy5taW5Xb3JrZXJzID4gcG9vbE9wdGlvbnMubWF4V29ya2Vycykge1xyXG4gICAgICBwb29sT3B0aW9ucy5taW5Xb3JrZXJzID0gcG9vbE9wdGlvbnMubWF4V29ya2VycztcclxuICAgIH1cclxuXHJcbiAgICAvLyBDcmVhdGUgYSBwb29sIGFsb25nIHdpdGggYSBtaW5pbWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIHBvb2wgPSBuZXcgUG9vbCh7XHJcbiAgICAgIC8vIEdldCB0aGUgYGNyZWF0ZWAsIGB2YWxpZGF0ZWAsIGFuZCBgZGVzdHJveWAgZnVuY3Rpb25zXHJcbiAgICAgIC4uLl9mYWN0b3J5KHBvb2xPcHRpb25zKSxcclxuICAgICAgbWluOiBwb29sT3B0aW9ucy5taW5Xb3JrZXJzLFxyXG4gICAgICBtYXg6IHBvb2xPcHRpb25zLm1heFdvcmtlcnMsXHJcbiAgICAgIGFjcXVpcmVUaW1lb3V0TWlsbGlzOiBwb29sT3B0aW9ucy5hY3F1aXJlVGltZW91dCxcclxuICAgICAgY3JlYXRlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuY3JlYXRlVGltZW91dCxcclxuICAgICAgZGVzdHJveVRpbWVvdXRNaWxsaXM6IHBvb2xPcHRpb25zLmRlc3Ryb3lUaW1lb3V0LFxyXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcG9vbE9wdGlvbnMuaWRsZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXM6IHBvb2xPcHRpb25zLmNyZWF0ZVJldHJ5SW50ZXJ2YWwsXHJcbiAgICAgIHJlYXBJbnRlcnZhbE1pbGxpczogcG9vbE9wdGlvbnMucmVhcGVySW50ZXJ2YWwsXHJcbiAgICAgIHByb3BhZ2F0ZUNyZWF0ZUVycm9yOiBmYWxzZVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGV2ZW50c1xyXG4gICAgcG9vbC5vbigncmVsZWFzZScsIGFzeW5jIChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBDbGVhciBwYWdlXHJcbiAgICAgIGNvbnN0IGNsZWFyU3RhdHVzID0gYXdhaXQgY2xlYXJQYWdlKHJlc291cmNlLCBmYWxzZSk7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gUmVsZWFzaW5nIGEgd29ya2VyLiBDbGVhciBwYWdlIHN0YXR1czogJHtjbGVhclN0YXR1c30uYFxyXG4gICAgICApO1xyXG4gICAgfSk7XHJcblxyXG4gICAgcG9vbC5vbignZGVzdHJveVN1Y2Nlc3MnLCAoX2V2ZW50SWQsIHJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA0LFxyXG4gICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtyZXNvdXJjZS5pZH1dIC0gRGVzdHJveWVkIGEgd29ya2VyIHN1Y2Nlc3NmdWxseS5gXHJcbiAgICAgICk7XHJcbiAgICAgIHJlc291cmNlLnBhZ2UgPSBudWxsO1xyXG4gICAgfSk7XHJcblxyXG4gICAgY29uc3QgaW5pdGlhbFJlc291cmNlcyA9IFtdO1xyXG4gICAgLy8gQ3JlYXRlIGFuIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwb29sT3B0aW9ucy5taW5Xb3JrZXJzOyBpKyspIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcbiAgICAgICAgaW5pdGlhbFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSBhbiBpbml0aWFsIHJlc291cmNlLicpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHJlc291cmNlKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGggPyBgIHdpdGggJHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aH0gaW5pdGlhbCByZXNvdXJjZXMgd2FpdGluZy5gIDogJy4nfWBcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1twb29sXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBjcmVhdGUgdGhlIHBvb2wgb2Ygd29ya2Vycy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFRlcm1pbmF0ZXMgYWxsIHdvcmtlcnMgaW4gdGhlIHBvb2wsIGRlc3Ryb3lzIHRoZSBwb29sLCBhbmQgY2xvc2VzIHRoZSBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIGtpbGxQb29sXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIGFsbCB3b3JrZXJzIGFyZVxyXG4gKiB0ZXJtaW5hdGVkLCB0aGUgcG9vbCBpcyBkZXN0cm95ZWQsIGFuZCB0aGUgYnJvd3NlciBpcyBzdWNjZXNzZnVsbHkgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGtpbGxQb29sKCkge1xyXG4gIGxvZygzLCAnW3Bvb2xdIEtpbGxpbmcgcG9vbCB3aXRoIGFsbCB3b3JrZXJzIGFuZCBjbG9zaW5nIGJyb3dzZXIuJyk7XHJcblxyXG4gIC8vIElmIHN0aWxsIGFsaXZlLCBkZXN0cm95IHRoZSBwb29sIG9mIHBhZ2VzIGJlZm9yZSBjbG9zaW5nIGEgYnJvd3NlclxyXG4gIGlmIChwb29sKSB7XHJcbiAgICAvLyBGcmVlIHVwIG5vdCByZWxlYXNlZCB3b3JrZXJzXHJcbiAgICBmb3IgKGNvbnN0IHdvcmtlciBvZiBwb29sLnVzZWQpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlci5yZXNvdXJjZSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSB0aGUgcG9vbCBpZiBpdCBpcyBzdGlsbCBhdmFpbGFibGVcclxuICAgIGlmICghcG9vbC5kZXN0cm95ZWQpIHtcclxuICAgICAgYXdhaXQgcG9vbC5kZXN0cm95KCk7XHJcbiAgICAgIGxvZyg0LCAnW3Bvb2xdIERlc3Ryb3llZCB0aGUgcG9vbCBvZiByZXNvdXJjZXMuJyk7XHJcbiAgICB9XHJcbiAgICBwb29sID0gbnVsbDtcclxuICB9XHJcblxyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgYXdhaXQgY2xvc2VCcm93c2VyKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcm9jZXNzZXMgdGhlIGV4cG9ydCB3b3JrIHVzaW5nIGEgd29ya2VyIGZyb20gdGhlIHBvb2wuIEFjcXVpcmVzIGEgd29ya2VyXHJcbiAqIGhhbmRsZSBmcm9tIHRoZSBwb29sLCBwZXJmb3JtcyB0aGUgZXhwb3J0IHVzaW5nIHB1cHBldGVlciwgYW5kIHJlbGVhc2VzXHJcbiAqIHRoZSB3b3JrZXIgaGFuZGxlIGJhY2sgdG8gdGhlIHBvb2wuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcG9zdFdvcmtcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIGV4cG9ydCByZXN1bHRcclxuICogYW5kIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0V29yayhvcHRpb25zKSB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIHN0YXJ0aW5nIHRvIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gQW4gZXhwb3J0IGF0dGVtcHQgY291bnRlZFxyXG4gICAgKytwb29sU3RhdHMuZXhwb3J0c0F0dGVtcHRlZDtcclxuXHJcbiAgICAvLyBEaXNwbGF5IHRoZSBwb29sIGluZm9ybWF0aW9uIGlmIG5lZWRlZFxyXG4gICAgaWYgKG9wdGlvbnMucG9vbC5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgX2dldFBvb2xJbmZvKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaW4gY2FzZSBvZiBsYWNraW5nIHRoZSBwb29sIGluc3RhbmNlXHJcbiAgICBpZiAoIXBvb2wpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbcG9vbF0gV29yayByZWNlaXZlZCwgYnV0IHBvb2wgaGFzIG5vdCBiZWVuIHN0YXJ0ZWQuJyxcclxuICAgICAgICA1MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaGUgYWNxdWlyZSBjb3VudGVyXHJcbiAgICBjb25zdCBhY3F1aXJlQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gVHJ5IHRvIGFjcXVpcmUgdGhlIHdvcmtlciBhbG9uZyB3aXRoIHRoZSBpZCwgd29ya3MgY291bnQgYW5kIHBhZ2VcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmluZyBhIHdvcmtlciBoYW5kbGUuJyk7XHJcblxyXG4gICAgICAvLyBBY3F1aXJlIGEgcG9vbCByZXNvdXJjZVxyXG4gICAgICB3b3JrZXJIYW5kbGUgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIHBhZ2UgYWNxdWlyZSB0aW1lXHJcbiAgICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICA1LFxyXG4gICAgICAgICAgYFtiZW5jaG1hcmtdICR7b3B0aW9ucy5yZXF1ZXN0SWQgPyBgUmVxdWVzdCBbJHtvcHRpb25zLnJlcXVlc3RJZH1dIC0gYCA6ICcnfWAsXHJcbiAgICAgICAgICBgQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZSB0b29rICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbcG9vbF0gJHtcclxuICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgIH1FcnJvciBlbmNvdW50ZXJlZCB3aGVuIGFjcXVpcmluZyBhbiBhdmFpbGFibGUgZW50cnk6ICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuICAgIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmVkIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuXHJcbiAgICBpZiAoIXdvcmtlckhhbmRsZS5wYWdlKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgYHdvcmtMaW1pdGAgdG8gZXhjZWVkZWQgaW4gb3JkZXIgdG8gcmVjcmVhdGUgdGhlIHJlc291cmNlXHJcbiAgICAgIHdvcmtlckhhbmRsZS53b3JrQ291bnQgPSBvcHRpb25zLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbcG9vbF0gUmVzb2x2ZWQgd29ya2VyIHBhZ2UgaXMgaW52YWxpZDogdGhlIHBvb2wgc2V0dXAgaXMgd29ua3kuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHt3b3JrZXJIYW5kbGUuaWR9XSAtIFN0YXJ0aW5nIHdvcmsgb24gdGhpcyBwb29sIGVudHJ5LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gU3RhcnQgbWVhc3VyaW5nIGV4cG9ydCB0aW1lXHJcbiAgICBjb25zdCBleHBvcnRDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgZXhwb3J0UmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KFxyXG4gICAgICB3b3JrZXJIYW5kbGUucGFnZSxcclxuICAgICAgb3B0aW9ucy5leHBvcnQsXHJcbiAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWNcclxuICAgICk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKGV4cG9ydFJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIE5PVEU6XHJcbiAgICAgIC8vIElmIHRoZXJlJ3MgYSByYXN0ZXJpemF0aW9uIHRpbWVvdXQsIHdlIHdhbnQgbmVlZCB0byBmbHVzaCB0aGUgcGFnZS5cclxuICAgICAgLy8gVGhpcyBpcyBiZWNhdXNlIHRoZSBwYWdlIG1heSBiZSBpbiBhIHN0YXRlIHdoZXJlIGl0J3Mgd2FpdGluZyBmb3JcclxuICAgICAgLy8gdGhlIHNjcmVlbnNob3QgdG8gZmluaXNoIGV2ZW4gdGhvdWdoIHRoZSB0aW1lb3V0IGhhcyBvY2N1cmVkLlxyXG4gICAgICAvLyBXaGljaCBvZiBjb3Vyc2UgY2F1c2VzIGEgbG90IG9mIGlzc3VlcyB3aXRoIHRoZSBldmVudCBzeXN0ZW0sXHJcbiAgICAgIC8vIGFuZCBwYWdlIGNvbnNpc3RlbmN5LlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBPbmx5IGBwYWdlLnNjcmVlbnNob3RgIHdpbGwgdGhyb3cgdGhpcywgdGltZW91dHMgZm9yIFBERidzIGFyZVxyXG4gICAgICAvLyBoYW5kbGVkIGJ5IHRoZSBgcGFnZS5wZGZgIGZ1bmN0aW9uIGl0c2VsZi5cclxuICAgICAgLy9cclxuICAgICAgLy8gLi4ueWVzLCB0aGlzIGlzIHVnbHkuXHJcbiAgICAgIGlmIChleHBvcnRSZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpIHtcclxuICAgICAgICAvLyBTZXQgdGhlIGB3b3JrTGltaXRgIHRvIGV4Y2VlZGVkIGluIG9yZGVyIHRvIHJlY3JlYXRlIHRoZSByZXNvdXJjZVxyXG4gICAgICAgIHdvcmtlckhhbmRsZS53b3JrQ291bnQgPSBvcHRpb25zLnBvb2wud29ya0xpbWl0ICsgMTtcclxuICAgICAgICB3b3JrZXJIYW5kbGUucGFnZSA9IG51bGw7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChcclxuICAgICAgICBleHBvcnRSZXN1bHQubmFtZSA9PT0gJ1RpbWVvdXRFcnJvcicgfHxcclxuICAgICAgICBleHBvcnRSZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCdcclxuICAgICAgKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtwb29sXSAke1xyXG4gICAgICAgICAgICBvcHRpb25zLnJlcXVlc3RJZCA/IGBSZXF1ZXN0IFske29wdGlvbnMucmVxdWVzdElkfV0gLSBgIDogJydcclxuICAgICAgICAgIH1SYXN0ZXJpemF0aW9uIHRpbWVvdXQ6IHlvdXIgY2hhcnQgbWF5IGJlIHRvbyBjb21wbGV4IG9yIGxhcmdlLCBhbmQgZmFpbGVkIHRvIHJlbmRlciB3aXRoaW4gdGhlIGFsbG90dGVkIHRpbWUuYFxyXG4gICAgICAgICkuc2V0RXJyb3IoZXhwb3J0UmVzdWx0KTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW3Bvb2xdICR7XHJcbiAgICAgICAgICAgIG9wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ1xyXG4gICAgICAgICAgfUVycm9yIGVuY291bnRlcmVkIGR1cmluZyBleHBvcnQ6ICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgICApLnNldEVycm9yKGV4cG9ydFJlc3VsdCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayB0aGUgUHVwcGV0ZWVyIGV4cG9ydCB0aW1lXHJcbiAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA1LFxyXG4gICAgICAgIGBbYmVuY2htYXJrXSAke29wdGlvbnMucmVxdWVzdElkID8gYFJlcXVlc3QgWyR7b3B0aW9ucy5yZXF1ZXN0SWR9XSAtIGAgOiAnJ31gLFxyXG4gICAgICAgIGBFeHBvcnRpbmcgYSBjaGFydCBzdWNlc3NmdWxseSB0b29rICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVcGRhdGUgc3RhdGlzdGljc1xyXG4gICAgcG9vbFN0YXRzLnRpbWVTcGVudCArPSBleHBvcnRDb3VudGVyKCk7XHJcbiAgICBwb29sU3RhdHMudGltZVNwZW50QXZlcmFnZSA9XHJcbiAgICAgIHBvb2xTdGF0cy50aW1lU3BlbnQgLyArK3Bvb2xTdGF0cy5leHBvcnRzUGVyZm9ybWVkO1xyXG5cclxuICAgIGxvZyg0LCBgW3Bvb2xdIFdvcmsgY29tcGxldGVkIGluICR7ZXhwb3J0Q291bnRlcigpfW1zLmApO1xyXG5cclxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gYW4gb2JqZWN0IHdpdGggdGhlIHJlc3VsdCBhbmQgb3B0aW9uc1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzdWx0OiBleHBvcnRSZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICsrcG9vbFN0YXRzLmV4cG9ydHNEcm9wcGVkO1xyXG5cclxuICAgIC8vIFRyeSB0byByZWxlYXNlIHRoZSB3b3JrZXIsIGlmIGl0IGV4aXN0c1xyXG4gICAgaWYgKHdvcmtlckhhbmRsZSkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuICAgIH1cclxuXHJcbiAgICB0aHJvdyBlcnJvcjtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xcclxuICpcclxuICogQHJldHVybnMgeyhPYmplY3R8bnVsbCl9IFRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UgaWYgaW5pdGlhbGl6ZWQsIG9yIGBudWxsYFxyXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sKCkge1xyXG4gIHJldHVybiBwb29sO1xyXG59XHJcblxyXG4vKipcclxuICogR2V0cyB0aGUgc3RhdGlzdGljIG9mIGEgcG9vbCBpbnN0YWNlIGFib3V0IGV4cG9ydHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRQb29sU3RhdHNcclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGN1cnJlbnQgcG9vbCBzdGF0aXN0aWNzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xTdGF0cygpIHtcclxuICByZXR1cm4gcG9vbFN0YXRzO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXHJcbiAqIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZSByZXF1ZXN0cy5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBQb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvSlNPTigpIHtcclxuICByZXR1cm4ge1xyXG4gICAgbWluOiBwb29sLm1pbixcclxuICAgIG1heDogcG9vbC5tYXgsXHJcbiAgICB1c2VkOiBwb29sLm51bVVzZWQoKSxcclxuICAgIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXHJcbiAgICBhbGxDcmVhdGVkOiBwb29sLm51bVVzZWQoKSArIHBvb2wubnVtRnJlZSgpLFxyXG4gICAgcGVuZGluZ0FjcXVpcmVzOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpLFxyXG4gICAgcGVuZGluZ0NyZWF0ZXM6IHBvb2wubnVtUGVuZGluZ0NyZWF0ZXMoKSxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9uczogcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSxcclxuICAgIHBlbmRpbmdEZXN0cm95czogcG9vbC5wZW5kaW5nRGVzdHJveXMubGVuZ3RoLFxyXG4gICAgYWJzb2x1dGVBbGw6XHJcbiAgICAgIHBvb2wubnVtVXNlZCgpICtcclxuICAgICAgcG9vbC5udW1GcmVlKCkgK1xyXG4gICAgICBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nQ3JlYXRlcygpICtcclxuICAgICAgcG9vbC5udW1QZW5kaW5nVmFsaWRhdGlvbnMoKSArXHJcbiAgICAgIHBvb2wucGVuZGluZ0Rlc3Ryb3lzLmxlbmd0aFxyXG4gIH07XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cclxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXHJcbiAqIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2dldFBvb2xJbmZvXHJcbiAqL1xyXG5mdW5jdGlvbiBfZ2V0UG9vbEluZm8oKSB7XHJcbiAgY29uc3Qge1xyXG4gICAgbWluLFxyXG4gICAgbWF4LFxyXG4gICAgdXNlZCxcclxuICAgIGF2YWlsYWJsZSxcclxuICAgIGFsbENyZWF0ZWQsXHJcbiAgICBwZW5kaW5nQWNxdWlyZXMsXHJcbiAgICBwZW5kaW5nQ3JlYXRlcyxcclxuICAgIHBlbmRpbmdWYWxpZGF0aW9ucyxcclxuICAgIHBlbmRpbmdEZXN0cm95cyxcclxuICAgIGFic29sdXRlQWxsXHJcbiAgfSA9IGdldFBvb2xJbmZvSlNPTigpO1xyXG5cclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWluaW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttaW59LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtYXhpbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21heH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiB1c2VkIHJlc291cmNlczogJHt1c2VkfS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGZyZWUgcmVzb3VyY2VzOiAke2F2YWlsYWJsZX0uYCk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgY3JlYXRlZCAodXNlZCBhbmQgZnJlZSkgcmVzb3VyY2VzOiAke2FsbENyZWF0ZWR9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgYWNxdWlyZWQ6ICR7cGVuZGluZ0FjcXVpcmVzfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGNyZWF0ZWQ6ICR7cGVuZGluZ0NyZWF0ZXN9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgdmFsaWRhdGVkOiAke3BlbmRpbmdWYWxpZGF0aW9uc30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBkZXN0cm95ZWQ6ICR7cGVuZGluZ0Rlc3Ryb3lzfS5gXHJcbiAgKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCByZXNvdXJjZXM6ICR7YWJzb2x1dGVBbGx9LmApO1xyXG59XHJcblxyXG4vKipcclxuICogRmFjdG9yeSBmdW5jdGlvbiB0aGF0IHJldHVybnMgYW4gb2JqZWN0IHdpdGggYGNyZWF0ZWAsIGB2YWxpZGF0ZWAsIGBkZXN0cm95YFxyXG4gKiBmdW5jdGlvbnMgZm9yIHRoZSBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2ZhY3RvcnlcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBvb2xPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYHBvb2xgXHJcbiAqIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZmFjdG9yeShwb29sT3B0aW9ucykge1xyXG4gIHJldHVybiB7XHJcbiAgICAvKipcclxuICAgICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiBjcmVhdGVcclxuICAgICAqXHJcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byBhbiBvYmplY3RcclxuICAgICAqIGNvbnRhaW5pbmcgdGhlIHdvcmtlciBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIGluaXRpYWxcclxuICAgICAqIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGFuIGVycm9yIGR1cmluZ1xyXG4gICAgICogdGhlIGNyZWF0aW9uIG9mIHRoZSBuZXcgcGFnZS5cclxuICAgICAqL1xyXG4gICAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XHJcbiAgICAgIC8vIEluaXQgdGhlIHJlc291cmNlIHdpdGggdW5pcXVlIGlkIGFuZCB3b3JrIGNvdW50XHJcbiAgICAgIGNvbnN0IHBvb2xSZXNvdXJjZSA9IHtcclxuICAgICAgICBpZDogdXVpZCgpLFxyXG4gICAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sT3B0aW9ucy53b3JrTGltaXQgLyAyKSlcclxuICAgICAgfTtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gU3RhcnQgbWVhc3VyaW5nIGEgcGFnZSBjcmVhdGlvbiB0aW1lXHJcbiAgICAgICAgY29uc3Qgc3RhcnREYXRlID0gZ2V0TmV3RGF0ZVRpbWUoKTtcclxuXHJcbiAgICAgICAgLy8gQ3JlYXRlIGEgbmV3IHBhZ2VcclxuICAgICAgICBhd2FpdCBuZXdQYWdlKHBvb2xSZXNvdXJjZSk7XHJcblxyXG4gICAgICAgIC8vIE1lYXN1cmUgdGhlIHRpbWUgb2YgZnVsbCBjcmVhdGlvbiBhbmQgY29uZmlndXJhdGlvbiBvZiBhIHBhZ2VcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIsIHRvb2sgJHtcclxuICAgICAgICAgICAgZ2V0TmV3RGF0ZVRpbWUoKSAtIHN0YXJ0RGF0ZVxyXG4gICAgICAgICAgfW1zLmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBSZXR1cm4gcmVhZHkgcG9vbCByZXNvdXJjZVxyXG4gICAgICAgIHJldHVybiBwb29sUmVzb3VyY2U7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIEVycm9yIGVuY291bnRlcmVkIHdoZW4gY3JlYXRpbmcgYSBuZXcgcGFnZS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG4gICAgfSxcclxuXHJcbiAgICAvKipcclxuICAgICAqIFZhbGlkYXRlcyBhIHdvcmtlciBwYWdlIGluIHRoZSBleHBvcnQgcG9vbCwgY2hlY2tpbmcgaWYgaXQgaGFzIGV4Y2VlZGVkXHJcbiAgICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgICAqXHJcbiAgICAgKiBAYXN5bmNcclxuICAgICAqIEBmdW5jdGlvbiB2YWxpZGF0ZVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKlxyXG4gICAgICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgaWYgdGhlIHdvcmtlclxyXG4gICAgICogaXMgdmFsaWQgYW5kIHdpdGhpbiB0aGUgd29yayBsaW1pdDsgb3RoZXJ3aXNlLCB0byBmYWxzZS5cclxuICAgICAqL1xyXG4gICAgdmFsaWRhdGU6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gTk9URTpcclxuICAgICAgLy8gSW4gY2VydGFpbiBjYXNlcyBhY3F1aXJpbmcgdGhyb3dzIGEgYFRhcmdldENsb3NlRXJyb3JgLCB3aGljaCBtYXlcclxuICAgICAgLy8gYmUgY2F1c2VkIGJ5IHR3byB0aGluZ3M6XHJcbiAgICAgIC8vIC0gVGhlIHBhZ2UgaXMgY2xvc2VkIGFuZCBhdHRlbXB0ZWQgdG8gYmUgcmV1c2VkLlxyXG4gICAgICAvLyAtIExvc3QgY29udGFjdCB3aXRoIHRoZSBicm93c2VyLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBXaGF0IHdlJ3JlIHNlZWluZyBpbiBsb2dzIGlzIHRoYXQgc3VjY2Vzc2l2ZSBleHBvcnRzIHR5cGljYWxseVxyXG4gICAgICAvLyBzdWNjZWVkcywgYW5kIHRoZSBzZXJ2ZXIgcmVjb3ZlcnMsIGluZGljYXRpbmcgdGhhdCBpdCdzIGxpa2VseVxyXG4gICAgICAvLyB0aGUgZmlyc3QgY2FzZS4gVGhpcyBpcyBhbiBhdHRlbXB0IGF0IGFsbGlldmF0aW5nIHRoZSBpc3N1ZSBieVxyXG4gICAgICAvLyBzaW1wbHkgbm90IHZhbGlkYXRpbmcgdGhlIHdvcmtlciBpZiB0aGUgcGFnZSBpcyBudWxsIG9yIGNsb3NlZC5cclxuICAgICAgLy9cclxuICAgICAgLy8gVGhlIGFjdHVhbCByZXN1bHQgZnJvbSB3aGVuIHRoaXMgaGFwcGVuZWQsIHdhcyB0aGF0IGEgd29ya2VyIHdvdWxkXHJcbiAgICAgIC8vIGJlIGNvbXBsZXRlbHkgbG9ja2VkLCBzdG9wcGluZyBpdCBmcm9tIGJlaW5nIGFjcXVpcmVkIHVudGlsXHJcbiAgICAgIC8vIGl0cyB3b3JrIGNvdW50IHJlYWNoZWQgdGhlIGxpbWl0LlxyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGBwYWdlYCBpcyB2YWxpZFxyXG4gICAgICBpZiAoIXBvb2xSZXNvdXJjZS5wYWdlKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChubyB2YWxpZCBwYWdlIGlzIGZvdW5kKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgcGFnZWAgaXMgY2xvc2VkXHJcbiAgICAgIGlmIChwb29sUmVzb3VyY2UucGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbcG9vbF0gUG9vbCByZXNvdXJjZSBbJHtwb29sUmVzb3VyY2UuaWR9XSAtIFZhbGlkYXRpb24gZmFpbGVkIChwYWdlIGlzIGNsb3NlZCBvciBpbnZhbGlkKS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIENoZWNrIGlmIHRoZSBgbWFpbkZyYW1lYCBpcyBkZXRhY2hlZFxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UubWFpbkZyYW1lKCkuZGV0YWNoZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKHBhZ2UncyBmcmFtZSBpcyBkZXRhY2hlZCkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDaGVjayBpZiB0aGUgYHdvcmtMaW1pdGAgaXMgZXhjZWVkZWRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHBvb2xPcHRpb25zLndvcmtMaW1pdCAmJlxyXG4gICAgICAgICsrcG9vbFJlc291cmNlLndvcmtDb3VudCA+IHBvb2xPcHRpb25zLndvcmtMaW1pdFxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gVmFsaWRhdGlvbiBmYWlsZWQgKGV4Y2VlZGVkIHRoZSAke3Bvb2xPcHRpb25zLndvcmtMaW1pdH0gd29ya3MgcGVyIHJlc291cmNlIGxpbWl0KS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSBgcG9vbFJlc291cmNlYCBpcyB2YWxpZGF0ZWRcclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9LFxyXG5cclxuICAgIC8qKlxyXG4gICAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXHJcbiAgICAgKlxyXG4gICAgICogQGFzeW5jXHJcbiAgICAgKiBAZnVuY3Rpb24gZGVzdHJveVxyXG4gICAgICpcclxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBwb29sUmVzb3VyY2UgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgICAqIHRoZSB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICAgKi9cclxuICAgIGRlc3Ryb3k6IGFzeW5jIChwb29sUmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gRGVzdHJveWluZyBhIHdvcmtlci5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICBpZiAocG9vbFJlc291cmNlLnBhZ2UgJiYgIXBvb2xSZXNvdXJjZS5wYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgLy8gUmVtb3ZlIGFsbCBhdHRhY2hlZCBldmVudCBsaXN0ZW5lcnMgZnJvbSB0aGUgcmVzb3VyY2VcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygncGFnZWVycm9yJyk7XHJcbiAgICAgICAgICBwb29sUmVzb3VyY2UucGFnZS5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2NvbnNvbGUnKTtcclxuICAgICAgICAgIHBvb2xSZXNvdXJjZS5wYWdlLnJlbW92ZUFsbExpc3RlbmVycygnZnJhbWVkZXRhY2hlZCcpO1xyXG5cclxuICAgICAgICAgIC8vIFdlIG5lZWQgdG8gd2FpdCBhcm91bmQgZm9yIHRoaXNcclxuICAgICAgICAgIGF3YWl0IHBvb2xSZXNvdXJjZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFtwb29sXSBQb29sIHJlc291cmNlIFske3Bvb2xSZXNvdXJjZS5pZH1dIC0gUGFnZSBjb3VsZCBub3QgYmUgY2xvc2VkIHVwb24gZGVzdHJveWluZy5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGluaXRQb29sLFxyXG4gIGtpbGxQb29sLFxyXG4gIHBvc3RXb3JrLFxyXG4gIGdldFBvb2wsXHJcbiAgZ2V0UG9vbFN0YXRzLFxyXG4gIGdldFBvb2xJbmZvSlNPTlxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgVXNlZCB0byBzYW5pdGl6ZSB0aGUgc3RyaW5ncyBjb21pbmcgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZVxyXG4gKiB0byBwcmV2ZW50IFhTUyBhdHRhY2tzICh3aXRoIHRoZSBET01QdXJpZnkgbGlicmFyeSkuXHJcbiAqL1xyXG5cclxuaW1wb3J0IERPTVB1cmlmeSBmcm9tICdkb21wdXJpZnknO1xyXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcclxuXHJcbi8qKlxyXG4gKiBTYW5pdGl6ZXMgYSBnaXZlbiBIVE1MIHN0cmluZyBieSByZW1vdmluZyA8c2NyaXB0PiB0YWdzLiBUaGlzIGZ1bmN0aW9uIHVzZXNcclxuICogYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gZmluZCBhbmQgcmVtb3ZlIGFsbCBvY2N1cnJlbmNlcyBvZiA8c2NyaXB0Pjwvc2NyaXB0PlxyXG4gKiB0YWdzIGFuZCBhbnkgY29udGVudCB3aXRoaW4gdGhlbS5cclxuICpcclxuICogQGZ1bmN0aW9uIHNhbml0aXplXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCAtIFRoZSBIVE1MIHN0cmluZyB0byBiZSBzYW5pdGl6ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTCBzdHJpbmcuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXQpIHtcclxuICAvLyBHZXQgdGhlIHZpcnR1YWwgRE9NXHJcbiAgY29uc3Qgd2luZG93ID0gbmV3IEpTRE9NKCcnKS53aW5kb3c7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHB1cmlmeWluZyBpbnN0YW5jZVxyXG4gIGNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cpO1xyXG5cclxuICAvLyBSZXR1cm4gc2FuaXRpemVkIGlucHV0LCBhbGxvd2luZyBmb3IgdGhlIGBmb3JlaWduT2JqZWN0YCBlbGVtZW50c1xyXG4gIHJldHVybiBwdXJpZnkuc2FuaXRpemUoaW5wdXQsIHsgQUREX1RBR1M6IFsnZm9yZWlnbk9iamVjdCddIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2FuaXRpemVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIGZ1bmN0aW9ucyB0aGF0IHByZXBhcmUgZm9yIHRoZSBleHBvcnRpbmdcclxuICogY2hhcnRzIGludG8gdmFyaW91cyBpbWFnZSBvdXRwdXQgZm9ybWF0cyBzdWNoIGFzIEpQRUcsIFBORywgUERGLCBhbmQgU1ZHcy5cclxuICogSXQgc3VwcG9ydHMgc2luZ2xlIGFuZCBiYXRjaCBleHBvcnQgb3BlcmF0aW9ucyBhbmQgYWxsb3dzIGN1c3RvbWl6YXRpb25cclxuICogdGhyb3VnaCBvcHRpb25zIHBhc3NlZCBmcm9tIGNvbmZpZ3VyYXRpb25zIG9yIEFQSXMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHsgaXNBbGxvd2VkQ29uZmlnLCB1cGRhdGVPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQgeyBnZXRBYnNvbHV0ZVBhdGgsIGdldEJhc2U2NCwgaXNPYmplY3QsIHJvdW5kTnVtYmVyIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIGdsb2JhbCBmbGFnIGZvciB0aGUgY29kZSBleGVjdXRpb24gcGVybWlzc2lvblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucyBhbmQgc2F2ZXNcclxuICogdGhlIHJlc3VsdGluZyBpbWFnZSB0byB0aGUgcHJvdmlkZWQgb3V0cHV0IGZpbGUuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc2luZ2xlRXhwb3J0XHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGBvcHRpb25zYCBvYmplY3QsIHdoaWNoIHNob3VsZCBpbmNsdWRlIHNldHRpbmdzXHJcbiAqIGZyb20gdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zLiBJdCBjYW4gYmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlXHJcbiAqIHNldCBvZiBvcHRpb25zIGZyb20gdGhlc2Ugc2VjdGlvbnMuIFRoZSBvYmplY3QgbXVzdCBjb250YWluIGF0IGxlYXN0IG9uZVxyXG4gKiBvZiB0aGUgZm9sbG93aW5nIGBleHBvcnRgIHByb3BlcnRpZXM6IGBpbmZpbGVgLCBgaW5zdHJgLCBgb3B0aW9uc2AsIG9yIGBzdmdgXHJcbiAqIHRvIGdlbmVyYXRlIGEgdmFsaWQgaW1hZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNpbmdsZUV4cG9ydChvcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgaWYgdGhlIGV4cG9ydCBtYWtlcyBzZW5zZVxyXG4gIGlmIChvcHRpb25zICYmIG9wdGlvbnMuZXhwb3J0KSB7XHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQoXHJcbiAgICAgIHsgZXhwb3J0OiBvcHRpb25zLmV4cG9ydCwgY3VzdG9tTG9naWM6IG9wdGlvbnMuY3VzdG9tTG9naWMgfSxcclxuICAgICAgYXN5bmMgKGVycm9yLCBkYXRhKSA9PiB7XHJcbiAgICAgICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3IgZXhpc3RzXHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgYGI2NGAsIGBvdXRmaWxlYCwgYW5kIGB0eXBlYCBmb3IgYSBjaGFydFxyXG4gICAgICAgIGNvbnN0IHsgYjY0LCBvdXRmaWxlLCB0eXBlIH0gPSBkYXRhLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgICAgICAvLyBTYXZlIHRoZSByZXN1bHRcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgaWYgKGI2NCkge1xyXG4gICAgICAgICAgICAvLyBBcyBhIEJhc2U2NCBzdHJpbmcgdG8gYSB0eHQgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGAke291dGZpbGUuc3BsaXQoJy4nKS5zaGlmdCgpIHx8ICdjaGFydCd9LnR4dGAsXHJcbiAgICAgICAgICAgICAgZ2V0QmFzZTY0KGRhdGEucmVzdWx0LCB0eXBlKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgLy8gQXMgYSBjb3JyZWN0IGltYWdlIGZvcm1hdFxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oZGF0YS5yZXN1bHQsICdiYXNlNjQnKSA6IGRhdGEucmVzdWx0XHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICAgJ1tjaGFydF0gRXJyb3Igd2hpbGUgc2F2aW5nIGEgY2hhcnQuJyxcclxuICAgICAgICAgICAgNTAwXHJcbiAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSBvbmUgb2YgdGhlIGZvbGxvd2luZyBvcHRpb25zOiBgaW5maWxlYCwgYGluc3RyYCwgYG9wdGlvbnNgLCBvciBgc3ZnYCB0byBnZW5lcmF0ZSBhIHZhbGlkIGltYWdlLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIGluZm9ybWF0aW9uXHJcbiAqIHByb3ZpZGVkIGluIHRoZSBgYmF0Y2hgIG9wdGlvbi4gVGhlIGBiYXRjaGAgaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZ1xyXG4gKiBmb3JtYXQ6IFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiLiBSZXN1bHRzXHJcbiAqIGFyZSBzYXZlZCB0byB0aGUgc3BlY2lmaWVkIG91dHB1dCBmaWxlcy5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBiYXRjaEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBgb3B0aW9uc2Agb2JqZWN0LCB3aGljaCBzaG91bGQgaW5jbHVkZSBzZXR0aW5nc1xyXG4gKiBmcm9tIHRoZSBgZXhwb3J0YCBhbmQgYGN1c3RvbUxvZ2ljYCBzZWN0aW9ucy4gSXQgY2FuIGJlIGEgcGFydGlhbCBvciBjb21wbGV0ZVxyXG4gKiBzZXQgb2Ygb3B0aW9ucyBmcm9tIHRoZXNlIHNlY3Rpb25zLiBJdCBtdXN0IGNvbnRhaW4gdGhlIGBiYXRjaGAgb3B0aW9uIGZyb21cclxuICogdGhlIGBleHBvcnRgIHNlY3Rpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3NlcyBhcmUgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGJhdGNoRXhwb3J0KG9wdGlvbnMpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgZXhwb3J0IG1ha2VzIHNlbnNlXHJcbiAgaWYgKG9wdGlvbnMgJiYgb3B0aW9ucy5leHBvcnQgJiYgb3B0aW9ucy5leHBvcnQuYmF0Y2gpIHtcclxuICAgIC8vIEFuIGFycmF5IGZvciBjb2xsZWN0aW5nIGJhdGNoIGV4cG9ydHNcclxuICAgIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIGBiYXRjaGAgYXJndW1lbnRzXHJcbiAgICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykgfHwgW10pIHtcclxuICAgICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAgIGN1c3RvbUxvZ2ljOiBvcHRpb25zLmN1c3RvbUxvZ2ljXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIChlcnJvciwgZGF0YSkgPT4ge1xyXG4gICAgICAgICAgICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yIGV4aXN0c1xyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBHZXQgdGhlIGBiNjRgLCBgb3V0ZmlsZWAsIGFuZCBgdHlwZWAgZm9yIGEgY2hhcnRcclxuICAgICAgICAgICAgICBjb25zdCB7IGI2NCwgb3V0ZmlsZSwgdHlwZSB9ID0gZGF0YS5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAgICAgICAgICAgLy8gU2F2ZSB0aGUgcmVzdWx0XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIGlmIChiNjQpIHtcclxuICAgICAgICAgICAgICAgICAgLy8gQXMgYSBCYXNlNjQgc3RyaW5nIHRvIGEgdHh0IGZpbGVcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBgJHtvdXRmaWxlLnNwbGl0KCcuJykuc2hpZnQoKSB8fCAnY2hhcnQnfS50eHRgLFxyXG4gICAgICAgICAgICAgICAgICAgIGdldEJhc2U2NChkYXRhLnJlc3VsdCwgdHlwZSlcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgIC8vIEFzIGEgY29ycmVjdCBpbWFnZSBmb3JtYXRcclxuICAgICAgICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICAgICAgICBvdXRmaWxlLFxyXG4gICAgICAgICAgICAgICAgICAgIHR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGRhdGEucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5yZXN1bHRcclxuICAgICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICAnW2NoYXJ0XSBFcnJvciB3aGlsZSBzYXZpbmcgYSBjaGFydC4nLFxyXG4gICAgICAgICAgICAgICAgICA1MDBcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbG9nKDIsICdbY2hhcnRdIE5vIGNvcnJlY3QgcGFpciBmb3VuZCBmb3IgdGhlIGJhdGNoIGV4cG9ydC4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBjb25zdCBiYXRjaFJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG5cclxuICAgIC8vIExvZyBlcnJvcnMgaWYgZm91bmRcclxuICAgIGJhdGNoUmVzdWx0cy5mb3JFYWNoKChyZXN1bHQsIGluZGV4KSA9PiB7XHJcbiAgICAgIC8vIExvZyB0aGUgZXJyb3Igd2l0aCBzdGFjayBhYm91dCB0aGUgc3BlY2lmaWMgYmF0Y2ggZXhwb3J0XHJcbiAgICAgIGlmIChyZXN1bHQucmVhc29uKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMSxcclxuICAgICAgICAgIHJlc3VsdC5yZWFzb24sXHJcbiAgICAgICAgICBgW2NoYXJ0XSBCYXRjaCBleHBvcnQgbnVtYmVyICR7aW5kZXggKyAxfSBjb3VsZCBub3QgYmUgY29ycmVjdGx5IGNvbXBsZXRlZC5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gTm8gZXhwZWN0ZWQgYGV4cG9ydGAgb3B0aW9ucyB3ZXJlIGZvdW5kLiBQbGVhc2UgcHJvdmlkZSB0aGUgYGJhdGNoYCBvcHRpb24gdG8gZ2VuZXJhdGUgdmFsaWQgaW1hZ2VzLicsXHJcbiAgICAgIDQwMFxyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgaW1hZ2VPcHRpb25zYCBwYXJhbWV0ZXIgaXMgYW4gb2JqZWN0IHRoYXRcclxuICogc2hvdWxkIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgcGFydGlhbFxyXG4gKiBvcHRpb25zIGFyZSBwcm92aWRlZCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBiZSBtZXJnZWQgd2l0aCB0aGUgY3VycmVudCBnbG9iYWxcclxuICogb3B0aW9ucy5cclxuICpcclxuICogVGhlIGBlbmRDYWxsYmFja2AgZnVuY3Rpb24gaXMgaW52b2tlZCB1cG9uIHRoZSBjb21wbGV0aW9uIG9mIHRoZSBleHBvcnQsXHJcbiAqIGVpdGhlciBzdWNjZXNzZnVsbHkgb3Igd2l0aCBhbiBlcnJvci4gVGhlIGBlcnJvcmAgb2JqZWN0IGlzIHByb3ZpZGVkXHJcbiAqIGFzIHRoZSBmaXJzdCBhcmd1bWVudCwgYW5kIHRoZSBgZGF0YWAgb2JqZWN0IGlzIHRoZSBzZWNvbmQsIGNvbnRhaW5pbmdcclxuICogdGhlIEJhc2U2NCByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQgaW4gdGhlIGByZXN1bHRgIHByb3BlcnR5XHJcbiAqIGFuZCB0aGUgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBzdGFydEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW1hZ2VPcHRpb25zIC0gVGhlIGBpbWFnZU9wdGlvbnNgIG9iamVjdCwgd2hpY2ggc2hvdWxkXHJcbiAqIGluY2x1ZGUgc2V0dGluZ3MgZnJvbSB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agc2VjdGlvbnMuIEl0IGNhblxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMgZnJvbSB0aGVzZSBzZWN0aW9ucy4gSWYgdGhlIHByb3ZpZGVkXHJcbiAqIG9wdGlvbnMgYXJlIHBhcnRpYWwsIG1pc3NpbmcgdmFsdWVzIHdpbGwgYmUgbWVyZ2VkIHdpdGggdGhlIGN1cnJlbnQgZ2xvYmFsXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHRoZSBleHBvcnQgcHJvY2VzcyBvciB1cG9uIGVuY291bnRlcmluZyBhbiBlcnJvci4gVGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGlzIHRoZSBgZXJyb3JgIG9iamVjdCwgYW5kIHRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIGBkYXRhYCBvYmplY3QsXHJcbiAqIHdoaWNoIGluY2x1ZGVzIHRoZSBCYXNlNjQgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0IGluIHRoZSBgcmVzdWx0YFxyXG4gKiBwcm9wZXJ0eSBhbmQgdGhlIGZ1bGwgc2V0IG9mIG9wdGlvbnMgaW4gdGhlIGBvcHRpb25zYCBwcm9wZXJ0eS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHkuXHJcbiAqIEluc3RlYWQsIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgYGVuZENhbGxiYWNrYC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIHByb2Nlc3NpbmcgaW5wdXQgb2YgYW55IHR5cGUuIFRoZSBlcnJvciBpcyBwYXNzZWQgaW50byB0aGUgYGVuZENhbGxiYWNrYFxyXG4gKiBmdW5jdGlvbiBhbmQgcHJvY2Vzc2VkIHRoZXJlLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN0YXJ0RXhwb3J0KGltYWdlT3B0aW9ucywgZW5kQ2FsbGJhY2spIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgb3B0aW9ucyBhcmUgaW4gYW4gb2JqZWN0XHJcbiAgICBpZiAoIWlzT2JqZWN0KGltYWdlT3B0aW9ucykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgcHJvdmlkZWQgYGltYWdlT3B0aW9uc2AuIE5lZWRzIHRvIGJlIGFuIG9iamVjdC4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE1lcmdlIGFkZGl0aW9uYWwgb3B0aW9ucyB0byB0aGUgY29weSBvZiB0aGUgaW5zdGFuY2Ugb3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoXHJcbiAgICAgIHtcclxuICAgICAgICBleHBvcnQ6IGltYWdlT3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgY3VzdG9tTG9naWM6IGltYWdlT3B0aW9ucy5jdXN0b21Mb2dpY1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVlXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEdldCB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZSBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgZmlsZSBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGxldCBmaWxlQ29udGVudDtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgICAgIGZpbGVDb250ZW50ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgZ2V0QWJzb2x1dGVQYXRoKGV4cG9ydE9wdGlvbnMuaW5maWxlKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBjb250ZW50IGZyb20gYSBmaWxlIGlucHV0LicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGZpbGUncyBleHRlbnNpb25cclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlLmVuZHNXaXRoKCcuc3ZnJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBzdmdgIG9wdGlvblxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc3ZnID0gZmlsZUNvbnRlbnQ7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICAvLyBTZXQgdG8gdGhlIGBpbnN0cmAgb3B0aW9uXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciA9IGZpbGVDb250ZW50O1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbY2hhcnRdIEluY29ycmVjdCB2YWx1ZSBvZiB0aGUgYGluZmlsZWAgb3B0aW9uLicsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIFNWRyBhcyBhbiBpbnB1dFxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnICE9PSBudWxsKSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFNWRyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbVN2Z0F0dGVtcHRzO1xyXG5cclxuICAgICAgLy8gRXhwb3J0IGZyb20gYW4gU1ZHIHN0cmluZ1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBfZXhwb3J0RnJvbVN2ZyhcclxuICAgICAgICBzYW5pdGl6ZShleHBvcnRPcHRpb25zLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTVkcgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21Tdmc7XHJcblxyXG4gICAgICAvLyBQYXNzIFNWRyBleHBvcnQgcmVzdWx0IHRvIHRoZSBlbmQgY2FsbGJhY2tcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKG51bGwsIHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgYXMgYW4gaW5wdXRcclxuICAgIGlmIChleHBvcnRPcHRpb25zLmluc3RyICE9PSBudWxsIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gbnVsbCkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBvcHRpb25zIGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBleHBvcnRzIGF0dGVtcHRzIGNvdW50ZXJcclxuICAgICAgKytnZXRQb29sU3RhdHMoKS5leHBvcnRzRnJvbU9wdGlvbnNBdHRlbXB0cztcclxuXHJcbiAgICAgIC8vIEV4cG9ydCBmcm9tIG9wdGlvbnNcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgX2V4cG9ydEZyb21PcHRpb25zKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgIG9wdGlvbnNcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIE9wdGlvbnMgZXhwb3J0cyBjb3VudGVyXHJcbiAgICAgICsrZ2V0UG9vbFN0YXRzKCkuZXhwb3J0c0Zyb21PcHRpb25zO1xyXG5cclxuICAgICAgLy8gUGFzcyBvcHRpb25zIGV4cG9ydCByZXN1bHQgdG8gdGhlIGVuZCBjYWxsYmFja1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2sobnVsbCwgcmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IFRoZSB2YWx1ZSBvZiB0aGUgZ2xvYmFsIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbi5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSB7XHJcbiAgcmV0dXJuIGFsbG93Q29kZUV4ZWN1dGlvbjtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSB2YWx1ZSAtIFRoZSBib29sZWFuIHZhbHVlIHRvIGJlIGFzc2lnbmVkIHRvIHRoZSBnbG9iYWxcclxuICogYGFsbG93Q29kZUV4ZWN1dGlvbmAgb3B0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNldEFsbG93Q29kZUV4ZWN1dGlvbih2YWx1ZSkge1xyXG4gIGFsbG93Q29kZUV4ZWN1dGlvbiA9IHZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBmcm9tIGFuIFNWRyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tU3ZnXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dFRvRXhwb3J0IC0gVGhlIFNWRyBiYXNlZCBpbnB1dCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBjb21wbGV0ZSBzZXRcclxuICogb2Ygb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dW5rbm93bj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIGEgcmVzdWx0IG9mIHRoZSBleHBvcnRcclxuICogcHJvY2Vzcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZXJlIGlzIG5vdCBhIGNvcnJlY3QgU1ZHXHJcbiAqIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21TdmcoaW5wdXRUb0V4cG9ydCwgb3B0aW9ucykge1xyXG4gIC8vIENoZWNrIGlmIGl0IGlzIFNWR1xyXG4gIGlmIChcclxuICAgIHR5cGVvZiBpbnB1dFRvRXhwb3J0ID09PSAnc3RyaW5nJyAmJlxyXG4gICAgKGlucHV0VG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgaW5wdXRUb0V4cG9ydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgU1ZHXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdmcgPSBpbnB1dFRvRXhwb3J0O1xyXG5cclxuICAgIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IG51bGw7XHJcbiAgICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IG51bGw7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb24gd2l0aCBhbiBTVkcgc3RyaW5nIGFzIGFuIGV4cG9ydCBpbnB1dFxyXG4gICAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gTm90IGEgY29ycmVjdCBTVkcgaW5wdXQuJywgNDAwKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIGZyb20gYW4gb3B0aW9ucyBiYXNlZCBpbnB1dCB3aXRoIHRoZSBwcm92aWRlZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAYXN5bmNcclxuICogQGZ1bmN0aW9uIF9leHBvcnRGcm9tT3B0aW9uc1xyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXRUb0V4cG9ydCAtIFRoZSBvcHRpb25zIGJhc2VkIGlucHV0IHRvIGJlIGV4cG9ydGVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlcmUgaXMgbm90IGEgY29ycmVjdFxyXG4gKiBjaGFydCBvcHRpb25zIGlucHV0LlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX2V4cG9ydEZyb21PcHRpb25zKGlucHV0VG9FeHBvcnQsIG9wdGlvbnMpIHtcclxuICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBmcm9tIG9wdGlvbnMuJyk7XHJcblxyXG4gIC8vIFRyeSB0byBjaGVjaywgdmFsaWRhdGUgYW5kIHBhcnNlIHRvIHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBjb25zdCBzdHJpbmdpZmllZE9wdGlvbnMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICBpbnB1dFRvRXhwb3J0LFxyXG4gICAgdHJ1ZSxcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgYSBjb3JyZWN0IHN0cmluZ2lmaWVkIG9wdGlvbnNcclxuICBpZiAoXHJcbiAgICBzdHJpbmdpZmllZE9wdGlvbnMgPT09IG51bGwgfHxcclxuICAgIHR5cGVvZiBzdHJpbmdpZmllZE9wdGlvbnMgIT09ICdzdHJpbmcnIHx8XHJcbiAgICAhc3RyaW5naWZpZWRPcHRpb25zLnN0YXJ0c1dpdGgoJ3snKSB8fFxyXG4gICAgIXN0cmluZ2lmaWVkT3B0aW9ucy5lbmRzV2l0aCgnfScpXHJcbiAgKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEludmFsaWQgY29uZmlndXJhdGlvbiBwcm92aWRlZCAtIE9ubHkgb3B0aW9ucyBjb25maWd1cmF0aW9ucyBhbmQgU1ZHIGFyZSBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWYgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBjdXN0b20gY29kZSBjYW4gYmUgZW5hYmxlZCBieSBzdGFydGluZyB0aGUgc2VydmVyIHdpdGggdGhlIGBhbGxvd0NvZGVFeGVjdXRpb25gIG9wdGlvbnMgc2V0IHRvIHRydWUuJyxcclxuICAgICAgNDAzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHRoZSBleHBvcnQgaW5wdXQgYXMgYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBzdHJpbmdpZmllZE9wdGlvbnM7XHJcblxyXG4gIC8vIFJlc2V0IHRoZSByZXN0IG9mIHRoZSBleHBvcnQgaW5wdXQgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnMgPSBudWxsO1xyXG4gIG9wdGlvbnMuZXhwb3J0LnN2ZyA9IG51bGw7XHJcblxyXG4gIC8vIENhbGwgdGhlIGZ1bmN0aW9uIHdpdGggYSBzdHJpbmdpZmllZCBjaGFydCBvcHRpb25zXHJcbiAgcmV0dXJuIF9wcmVwYXJlRXhwb3J0KG9wdGlvbnMpO1xyXG59XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbnMgYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQGFzeW5jXHJcbiAqIEBmdW5jdGlvbiBfcHJlcGFyZUV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGNvbXBsZXRlIHNldFxyXG4gKiBvZiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx1bmtub3duPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBwcm9jZXNzLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gX3ByZXBhcmVFeHBvcnQob3B0aW9ucykge1xyXG4gIC8vIEdldCB0aGUgYGV4cG9ydGAgYW5kIGBjdXN0b21Mb2dpY2Agb3B0aW9uc1xyXG4gIGNvbnN0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgY29uc3RyYCBvcHRpb25cclxuICBleHBvcnRPcHRpb25zLmNvbnN0ciA9IF9maXhDb25zdHIoZXhwb3J0T3B0aW9ucy5jb25zdHIpO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgdHlwZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gX2ZpeFR5cGUoZXhwb3J0T3B0aW9ucy50eXBlLCBleHBvcnRPcHRpb25zLm91dGZpbGUpO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgb3V0ZmlsZWAgb3B0aW9uXHJcbiAgZXhwb3J0T3B0aW9ucy5vdXRmaWxlID0gX2ZpeE91dGZpbGUoXHJcbiAgICBleHBvcnRPcHRpb25zLnR5cGUsXHJcbiAgICBleHBvcnRPcHRpb25zLm91dGZpbGVcclxuICApO1xyXG5cclxuICAvLyBOb3RpZnkgYWJvdXQgdGhlIGN1c3RvbSBsb2dpYyB1c2FnZSBzdGF0dXNcclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjaGFydF0gVGhlIGN1c3RvbSBsb2dpYyBpcyAke2N1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPyAnYWxsb3dlZCcgOiAnZGlzYWxsb3dlZCd9LmBcclxuICApO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgY3VzdG9tQ29kZWAsIGBjYWxsYmFja2AsIGFuZCBgcmVzb3VyY2VzYCBvcHRpb25zXHJcbiAgX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucyk7XHJcblxyXG4gIC8vIFByZXBhcmUgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBgaGVpZ2h0YCwgYHdpZHRoYCwgYW5kIGBzY2FsZWAgb3B0aW9uc1xyXG4gIF9oYW5kbGVTaXplKGV4cG9ydE9wdGlvbnMpO1xyXG5cclxuICAvLyBDaGVjayBpZiB0aGUgaW1hZ2Ugb3B0aW9ucyBvYmplY3QgZG9lcyBub3QgZXhjZWVkIHRoZSBzaXplIGxpbWl0XHJcbiAgX2NoZWNrRGF0YVNpemUoeyBleHBvcnQ6IGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljOiBjdXN0b21Mb2dpY09wdGlvbnMgfSk7XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICByZXR1cm4gcG9zdFdvcmsob3B0aW9ucyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIGFkanVzdGluZyB0aGUgY29uc3RydWN0b3IgbmFtZSBieSB0cmFuc2Zvcm1pbmcgYW5kIG5vcm1hbGl6aW5nXHJcbiAqIGl0IGJhc2VkIG9uIGNvbW1vbiBjaGFydCB0eXBlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIF9maXhDb25zdHJcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbnN0ciAtIFRoZSBvcmlnaW5hbCBjb25zdHJ1Y3RvciBuYW1lIHRvIGJlIGFkanVzdGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgY29ycmVjdGVkIGNvbnN0cnVjdG9yIG5hbWUsIG9yICdjaGFydCcgaWYgdGhlIGlucHV0XHJcbiAqIGlzIG5vdCByZWNvZ25pemVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2ZpeENvbnN0cihjb25zdHIpIHtcclxuICB0cnkge1xyXG4gICAgLy8gRml4IHRoZSBjb25zdHJ1Y3RvciBieSBsb3dlcmluZyBjYXNpbmdcclxuICAgIGNvbnN0IGZpeGVkQ29uc3RyID0gYCR7Y29uc3RyLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgnY2hhcnQnLCAnJyl9Q2hhcnRgO1xyXG5cclxuICAgIC8vIEhhbmRsZSB0aGUgY2FzZSB3aGVyZSB0aGUgcmVzdWx0IGlzIGp1c3QgJ0NoYXJ0J1xyXG4gICAgaWYgKGZpeGVkQ29uc3RyID09PSAnQ2hhcnQnKSB7XHJcbiAgICAgIGZpeGVkQ29uc3RyLnRvTG93ZXJDYXNlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIHRoZSBjb3JyZWN0ZWQgY29uc3RydWN0b3IsIG90aGVyd2lzZSBkZWZhdWx0IHRvICdjaGFydCdcclxuICAgIHJldHVybiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddLmluY2x1ZGVzKFxyXG4gICAgICBmaXhlZENvbnN0clxyXG4gICAgKVxyXG4gICAgICA/IGZpeGVkQ29uc3RyXHJcbiAgICAgIDogJ2NoYXJ0JztcclxuICB9IGNhdGNoIHtcclxuICAgIC8vIERlZmF1bHQgdG8gJ2NoYXJ0JyBpbiBjYXNlIG9mIGFueSBlcnJvclxyXG4gICAgcmV0dXJuICdjaGFydCc7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyBmaXhpbmcgdGhlIG91dGZpbGUgYmFzZWQgb24gcHJvdmlkZWQgdHlwZS5cclxuICpcclxuICogQGZ1bmN0aW9uIF9maXhPdXRmaWxlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gb3V0ZmlsZSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGNvcnJlY3RlZCBvdXRmaWxlLCBvciAnY2hhcnQucG5nJyBpZiB0aGUgaW5wdXRcclxuICogaXMgbm90IHJlY29nbml6ZWQuXHJcbiAqL1xyXG5mdW5jdGlvbiBfZml4T3V0ZmlsZSh0eXBlLCBvdXRmaWxlKSB7XHJcbiAgLy8gR2V0IHRoZSBmaWxlIG5hbWUgZnJvbSB0aGUgYG91dGZpbGVgIG9wdGlvblxyXG4gIGNvbnN0IGZpbGVOYW1lID0gZ2V0QWJzb2x1dGVQYXRoKG91dGZpbGUgfHwgJ2NoYXJ0JylcclxuICAgIC5zcGxpdCgnLicpXHJcbiAgICAuc2hpZnQoKTtcclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCBvdXRmaWxlXHJcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke3R5cGUgfHwgJ3BuZyd9YDtcclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgZml4aW5nIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfZml4VHlwZVxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIFRoZSBvcmlnaW5hbCBleHBvcnQgdHlwZS5cclxuICogQHBhcmFtIHtzdHJpbmd9IFtvdXRmaWxlPW51bGxdIC0gVGhlIGZpbGUgcGF0aCBvciBuYW1lLiBUaGUgZGVmYXVsdCB2YWx1ZVxyXG4gKiBpcyBgbnVsbGAuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBjb3JyZWN0ZWQgZXhwb3J0IHR5cGUsIG9yICdwbmcnIGlmIHRoZSBpbnB1dFxyXG4gKiBpcyBub3QgcmVjb2duaXplZC5cclxuICovXHJcbmZ1bmN0aW9uIF9maXhUeXBlKHR5cGUsIG91dGZpbGUgPSBudWxsKSB7XHJcbiAgLy8gTUlNRSB0eXBlc1xyXG4gIGNvbnN0IG1pbWVUeXBlcyA9IHtcclxuICAgICdpbWFnZS9wbmcnOiAncG5nJyxcclxuICAgICdpbWFnZS9qcGVnJzogJ2pwZWcnLFxyXG4gICAgJ2FwcGxpY2F0aW9uL3BkZic6ICdwZGYnLFxyXG4gICAgJ2ltYWdlL3N2Zyt4bWwnOiAnc3ZnJ1xyXG4gIH07XHJcblxyXG4gIC8vIEdldCBmb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IE9iamVjdC52YWx1ZXMobWltZVR5cGVzKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdHlwZSBhbmQgb3V0ZmlsZSdzIGV4dGVuc2lvbnMgYXJlIHRoZSBzYW1lXHJcbiAgaWYgKG91dGZpbGUpIHtcclxuICAgIGNvbnN0IG91dFR5cGUgPSBvdXRmaWxlLnNwbGl0KCcuJykucG9wKCk7XHJcblxyXG4gICAgLy8gU3VwcG9ydCB0aGUgSlBHIHR5cGVcclxuICAgIGlmIChvdXRUeXBlID09PSAnanBnJykge1xyXG4gICAgICB0eXBlID0gJ2pwZWcnO1xyXG4gICAgfSBlbHNlIGlmIChmb3JtYXRzLmluY2x1ZGVzKG91dFR5cGUpICYmIHR5cGUgIT09IG91dFR5cGUpIHtcclxuICAgICAgdHlwZSA9IG91dFR5cGU7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBjb3JyZWN0IHR5cGVcclxuICByZXR1cm4gbWltZVR5cGVzW3R5cGVdIHx8IGZvcm1hdHMuZmluZCgodCkgPT4gdCA9PT0gdHlwZSkgfHwgJ3BuZyc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGUgY2FsY3VsYXRpbmcgdGhlIGBoZWlnaHRgLCBgd2lkdGhgIGFuZCBgc2NhbGVgIGZvciBjaGFydCBleHBvcnRzIGJhc2VkXHJcbiAqIG9uIHRoZSBwcm92aWRlZCBleHBvcnQgb3B0aW9ucy5cclxuICpcclxuICogVGhlIGZ1bmN0aW9uIHByaW9yaXRpemVzIHZhbHVlcyBpbiB0aGUgZm9sbG93aW5nIG9yZGVyOlxyXG4gKlxyXG4gKiAxLiBUaGUgYGhlaWdodGAsIGB3aWR0aGAsIGBzY2FsZWAgZnJvbSB0aGUgYGV4cG9ydE9wdGlvbnNgLlxyXG4gKiAyLiBPcHRpb25zIGZyb20gdGhlIGNoYXJ0IGNvbmZpZ3VyYXRpb24gKGZyb20gYGV4cG9ydGluZ2AgYW5kIGBjaGFydGApLlxyXG4gKiAzLiBPcHRpb25zIGZyb20gdGhlIGdsb2JhbCBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgKS5cclxuICogNC4gT3B0aW9ucyBmcm9tIHRoZSB0aGVtZSBvcHRpb25zIChmcm9tIGBleHBvcnRpbmdgIGFuZCBgY2hhcnRgIHNlY3Rpb25zKS5cclxuICogNS4gRmFsbGJhY2sgZGVmYXVsdCB2YWx1ZXMgKGBoZWlnaHQgPSA0MDBgLCBgd2lkdGggPSA2MDBgLCBgc2NhbGUgPSAxYCkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfaGFuZGxlU2l6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGBleHBvcnRgXHJcbiAqIG9wdGlvbnMuXHJcbiAqL1xyXG5mdW5jdGlvbiBfaGFuZGxlU2l6ZShleHBvcnRPcHRpb25zKSB7XHJcbiAgLy8gQ2hlY2sgdGhlIGBvcHRpb25zYCBhbmQgYGluc3RyYCBmb3IgY2hhcnQgYW5kIGV4cG9ydGluZyBzZWN0aW9uc1xyXG4gIGNvbnN0IHsgY2hhcnQ6IG9wdGlvbnNDaGFydCwgZXhwb3J0aW5nOiBvcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMuaW5zdHIpIHx8IGZhbHNlO1xyXG5cclxuICAvLyBDaGVjayB0aGUgYGdsb2JhbE9wdGlvbnNgIGZvciBjaGFydCBhbmQgZXhwb3J0aW5nIHNlY3Rpb25zXHJcbiAgY29uc3QgeyBjaGFydDogZ2xvYmFsT3B0aW9uc0NoYXJ0LCBleHBvcnRpbmc6IGdsb2JhbE9wdGlvbnNFeHBvcnRpbmcgfSA9XHJcbiAgICBpc0FsbG93ZWRDb25maWcoZXhwb3J0T3B0aW9ucy5nbG9iYWxPcHRpb25zKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGB0aGVtZU9wdGlvbnNgIGZvciBjaGFydCBhbmQgZXhwb3J0aW5nIHNlY3Rpb25zXHJcbiAgY29uc3QgeyBjaGFydDogdGhlbWVPcHRpb25zQ2hhcnQsIGV4cG9ydGluZzogdGhlbWVPcHRpb25zRXhwb3J0aW5nIH0gPVxyXG4gICAgaXNBbGxvd2VkQ29uZmlnKGV4cG9ydE9wdGlvbnMudGhlbWVPcHRpb25zKSB8fCBmYWxzZTtcclxuXHJcbiAgLy8gRmluZCB0aGUgYGhlaWdodGAgdmFsdWVcclxuICBjb25zdCBoZWlnaHQgPVxyXG4gICAgZXhwb3J0T3B0aW9ucy5oZWlnaHQgfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgb3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIGdsb2JhbE9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py5oZWlnaHQgfHxcclxuICAgIHRoZW1lT3B0aW9uc0V4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICB0aGVtZU9wdGlvbnNDaGFydD8uaGVpZ2h0IHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRIZWlnaHQgfHxcclxuICAgIDQwMDtcclxuXHJcbiAgLy8gRmluZCB0aGUgYHdpZHRoYCB2YWx1ZVxyXG4gIGNvbnN0IHdpZHRoID1cclxuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggfHxcclxuICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICBvcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgZ2xvYmFsT3B0aW9uc0NoYXJ0Py53aWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgdGhlbWVPcHRpb25zQ2hhcnQ/LndpZHRoIHx8XHJcbiAgICBleHBvcnRPcHRpb25zLmRlZmF1bHRXaWR0aCB8fFxyXG4gICAgNjAwO1xyXG5cclxuICAvLyBGaW5kIHRoZSBgc2NhbGVgIHZhbHVlOlxyXG4gIC8vIC0gQ2Fubm90IGJlIGxvd2VyIHRoYW4gMC4xXHJcbiAgLy8gLSBDYW5ub3QgYmUgaGlnaGVyIHRoYW4gNS4wXHJcbiAgLy8gLSBNdXN0IGJlIHJvdW5kZWQgdG8gMiBkZWNpbWFsIHBsYWNlcyAoZS5nLiAwLjIzMjM0IC0+IDAuMjMpXHJcbiAgY29uc3Qgc2NhbGUgPSByb3VuZE51bWJlcihcclxuICAgIE1hdGgubWF4KFxyXG4gICAgICAwLjEsXHJcbiAgICAgIE1hdGgubWluKFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMuc2NhbGUgfHxcclxuICAgICAgICAgIG9wdGlvbnNFeHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICAgICAgICBnbG9iYWxPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgdGhlbWVPcHRpb25zRXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9ucy5kZWZhdWx0U2NhbGUgfHxcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgNS4wXHJcbiAgICAgIClcclxuICAgICksXHJcbiAgICAyXHJcbiAgKTtcclxuXHJcbiAgLy8gVXBkYXRlIGBoZWlnaHRgLCBgd2lkdGhgLCBhbmQgYHNjYWxlYCBpbmZvcm1hdGlvbiBpbiB0aGUgYGV4cG9ydGAgb3B0aW9uc1xyXG4gIGV4cG9ydE9wdGlvbnMuaGVpZ2h0ID0gaGVpZ2h0O1xyXG4gIGV4cG9ydE9wdGlvbnMud2lkdGggPSB3aWR0aDtcclxuICBleHBvcnRPcHRpb25zLnNjYWxlID0gc2NhbGU7XHJcblxyXG4gIC8vIEdldCByaWQgb2YgcG90ZW50aWFsIGBweGAgYW5kIGAlYFxyXG4gIGZvciAobGV0IHBhcmFtIG9mIFsnaGVpZ2h0JywgJ3dpZHRoJywgJ3NjYWxlJ10pIHtcclxuICAgIGlmICh0eXBlb2YgZXhwb3J0T3B0aW9uc1twYXJhbV0gPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbcGFyYW1dID0gK2V4cG9ydE9wdGlvbnNbcGFyYW1dLnJlcGxhY2UoL3B4fCUvZ2ksICcnKTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleGVjdXRpb24gb2YgY3VzdG9tIGxvZ2ljIG9wdGlvbnMsIGluY2x1ZGluZyBsb2FkaW5nIGByZXNvdXJjZXNgLFxyXG4gKiBgY3VzdG9tQ29kZWAsIGFuZCBgY2FsbGJhY2tgLiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBhbGxvd2VkLCBpdCBwcm9jZXNzZXNcclxuICogdGhlIGN1c3RvbSBsb2dpYyBvcHRpb25zIGFjY29yZGluZ2x5LiBJZiBjb2RlIGV4ZWN1dGlvbiBpcyBub3QgYWxsb3dlZCxcclxuICogaXQgZGlzYWJsZXMgdGhlIHVzYWdlIG9mIHJlc291cmNlcywgY3VzdG9tIGNvZGUgYW5kIGNhbGxiYWNrLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21Mb2dpY09wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiBgY3VzdG9tTG9naWNgIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiBjb2RlIGV4ZWN1dGlvblxyXG4gKiBpcyBub3QgYWxsb3dlZCBidXQgY3VzdG9tIGxvZ2ljIG9wdGlvbnMgYXJlIHN0aWxsIHByb3ZpZGVkLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUxvZ2ljKGN1c3RvbUxvZ2ljT3B0aW9ucykge1xyXG4gIC8vIEluIGNhc2Ugb2YgYWxsb3dpbmcgY29kZSBleGVjdXRpb25cclxuICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvbikge1xyXG4gICAgLy8gUHJvY2VzcyB0aGUgYHJlc291cmNlc2Agb3B0aW9uXHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBUcnkgdG8gaGFuZGxlIHJlc291cmNlc1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gX2hhbmRsZVJlc291cmNlcyhcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzLFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgdHJ1ZVxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nKDIsICdbY2hhcnRdIFRoZSBgcmVzb3VyY2VzYCBjYW5ub3QgYmUgbG9hZGVkLicpO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBudWxsO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFByb2Nlc3MgdGhlIGBjdXN0b21Db2RlYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGN1c3RvbSBjb2RlIGFuZCB3cmFwIGFyb3VuZCBpdCBpbiBhIHNlbGYgaW52b2tpbmcgZnVuY3Rpb25cclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBfaGFuZGxlQ3VzdG9tQ29kZShcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbY2hhcnRdIFRoZSBgY3VzdG9tQ29kZWAgY2Fubm90IGJlIGxvYWRlZC4nKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUHJvY2VzcyB0aGUgYGNhbGxiYWNrYCBvcHRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIFRyeSB0byBsb2FkIGNhbGxiYWNrIGZ1bmN0aW9uXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IF9oYW5kbGVDdXN0b21Db2RlKFxyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzLFxyXG4gICAgICAgIHRydWVcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1tjaGFydF0gVGhlIGBjYWxsYmFja2AgY2Fubm90IGJlIGxvYWRlZC4nKTtcclxuXHJcbiAgICAgIC8vIEluIGNhc2Ugb2YgYW4gZXJyb3IsIHNldCB0aGUgb3B0aW9uIHdpdGggbnVsbFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBudWxsO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHRoZSBgY3VzdG9tQ29kZWAgcHJlc2VudFxyXG4gICAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlKSkge1xyXG4gICAgICBsb2coMywgJ1tjaGFydF0gTm8gdmFsdWUgZm9yIHRoZSBgY3VzdG9tQ29kZWAgb3B0aW9uIGZvdW5kLicpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHRoZSBgY2FsbGJhY2tgIHByZXNlbnRcclxuICAgIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2spKSB7XHJcbiAgICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBjYWxsYmFja2Agb3B0aW9uIGZvdW5kLicpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZXJlIGlzIHRoZSBgcmVzb3VyY2VzYCBwcmVzZW50XHJcbiAgICBpZiAoW251bGwsIHVuZGVmaW5lZF0uaW5jbHVkZXMoY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcykpIHtcclxuICAgICAgbG9nKDMsICdbY2hhcnRdIE5vIHZhbHVlIGZvciB0aGUgYHJlc291cmNlc2Agb3B0aW9uIGZvdW5kLicpO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBJZiB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgZmxhZyBpcyBzZXQgdG8gZmFsc2UsIHdlIHNob3VsZCByZWZ1c2VcclxuICAgIC8vIHRoZSB1c2FnZSBvZiB0aGUgYGNhbGxiYWNrYCwgYHJlc291cmNlc2AsIGFuZCBgY3VzdG9tQ29kZWAgb3B0aW9ucy5cclxuICAgIC8vIEFkZGl0aW9uYWxseSwgdGhlIHdvcmtlciB3aWxsIHJlZnVzZSB0byBydW4gYXJiaXRyYXJ5IEphdmFTY3JpcHQuXHJcbiAgICBpZiAoXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlXHJcbiAgICApIHtcclxuICAgICAgLy8gUmVzZXQgYWxsIGN1c3RvbSBjb2RlIG9wdGlvbnNcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gbnVsbDtcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9IG51bGw7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gbnVsbDtcclxuXHJcbiAgICAgIC8vIFNlbmQgYSBtZXNzYWdlIHNheWluZyB0aGF0IHRoZSBleHBvcnRlciBkb2VzIG5vdCBzdXBwb3J0IHRoZXNlIHNldHRpbmdzXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW2NoYXJ0XSBUaGUgJ2NhbGxiYWNrJywgJ3Jlc291cmNlcycsIGFuZCAnY3VzdG9tQ29kZScgb3B0aW9ucyBoYXZlIGJlZW4gZGlzYWJsZWQgZm9yIHRoaXMgc2VydmVyLmAsXHJcbiAgICAgICAgNDAzXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogSGFuZGxlcyBhbmQgdmFsaWRhdGVzIHJlc291cmNlcyBmcm9tIHRoZSBgcmVzb3VyY2VzYCBvcHRpb24gZm9yIGV4cG9ydC5cclxuICpcclxuICogQGZ1bmN0aW9uIF9oYW5kbGVSZXNvdXJjZXNcclxuICpcclxuICogQHBhcmFtIHsoT2JqZWN0fHN0cmluZ3xudWxsKX0gW3Jlc291cmNlcz1udWxsXSAtIFRoZSByZXNvdXJjZXMgdG8gYmUgaGFuZGxlZC5cclxuICogQ2FuIGJlIGVpdGhlciBhIEpTT04gb2JqZWN0LCBzdHJpbmdpZmllZCBKU09OLCBhIHBhdGggdG8gYSBKU09OIGZpbGUsXHJcbiAqIG9yIG51bGwuIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGBudWxsYC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGxvYWRpbmdcclxuICogcmVzb3VyY2VzIGZyb20gZmlsZXMgaXMgYWxsb3dlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0NvZGVFeGVjdXRpb24gLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIGNvZGVcclxuICogZXhlY3V0aW9uIGlzIGFsbG93ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoT2JqZWN0fG51bGwpfSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgbnVsbCBpZiBubyB2YWxpZCByZXNvdXJjZXNcclxuICogYXJlIGZvdW5kLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZVJlc291cmNlcyhcclxuICByZXNvdXJjZXMgPSBudWxsLFxyXG4gIGFsbG93RmlsZVJlc291cmNlcyxcclxuICBhbGxvd0NvZGVFeGVjdXRpb25cclxuKSB7XHJcbiAgbGV0IGhhbmRsZWRSZXNvdXJjZXMgPSByZXNvdXJjZXM7XHJcblxyXG4gIC8vIElmIG5vIHJlc291cmNlcyBmb3VuZCwgdHJ5IHRvIGxvYWQgdGhlIGRlZmF1bHQgcmVzb3VyY2VzXHJcbiAgaWYgKCFoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICByZXNvdXJjZXMgPSAncmVzb3VyY2VzLmpzb24nO1xyXG4gIH1cclxuXHJcbiAgLy8gTGlzdCBvZiBhbGxvd2VkIHNlY3Rpb25zIGluIHRoZSByZXNvdXJjZXMgSlNPTlxyXG4gIGNvbnN0IGFsbG93ZWRQcm9wcyA9IFsnanMnLCAnY3NzJywgJ2ZpbGVzJ107XHJcblxyXG4gIC8vIEEgZmxhZyB0aGF0IGRlY2lkZXMgYmFzZWQgdG8gcmV0dXJuIHJlc291cmNlcyBvciBgbnVsbGBcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoXHJcbiAgICBhbGxvd0ZpbGVSZXNvdXJjZXMgJiZcclxuICAgIHR5cGVvZiByZXNvdXJjZXMgPT09ICdzdHJpbmcnICYmXHJcbiAgICByZXNvdXJjZXMuZW5kc1dpdGgoJy5qc29uJylcclxuICApIHtcclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgIHJlYWRGaWxlU3luYyhnZXRBYnNvbHV0ZVBhdGgocmVzb3VyY2VzKSwgJ3V0ZjgnKSxcclxuICAgICAgZmFsc2UsXHJcbiAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgKTtcclxuICB9IGVsc2Uge1xyXG4gICAgLy8gVHJ5IHRvIGdldCBKU09OXHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzID0gaXNBbGxvd2VkQ29uZmlnKHJlc291cmNlcywgZmFsc2UsIGFsbG93Q29kZUV4ZWN1dGlvbik7XHJcblxyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgZmlsZXMgc2VjdGlvblxyXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbHRlciBmcm9tIHVubmVjZXNzYXJ5IHByb3BlcnRpZXNcclxuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcclxuICAgIGlmICghYWxsb3dlZFByb3BzLmluY2x1ZGVzKHByb3BOYW1lKSkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlc1twcm9wTmFtZV07XHJcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICAgIGNvcnJlY3RSZXNvdXJjZXMgPSB0cnVlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIGFsbG93ZWQgcHJvcGVydGllcyBpcyBwcmVzZW50XHJcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICByZXR1cm4gbnVsbDtcclxuICB9XHJcblxyXG4gIC8vIEhhbmRsZSBmaWxlcyBzZWN0aW9uXHJcbiAgaWYgKGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgPSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLm1hcCgoaXRlbSkgPT4gaXRlbS50cmltKCkpO1xyXG4gICAgaWYgKCFoYW5kbGVkUmVzb3VyY2VzLmZpbGVzIHx8IGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMubGVuZ3RoIDw9IDApIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gcmVzb3VyY2VzXHJcbiAgcmV0dXJuIGhhbmRsZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIGN1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgaXQgc2FmZWx5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUNvZGVcclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tIGNvZGUgdG8gYmUgd3JhcHBlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBGbGFnIHRvIGFsbG93IGxvYWRpbmcgY29kZSBmcm9tIGEgZmlsZS5cclxuICogQHBhcmFtIHtib29sZWFufSBbaXNDYWxsYmFjaz1mYWxzZV0gLSBGbGFnIHRoYXQgaW5kaWNhdGVzIHRoZSByZXR1cm5lZCBjb2RlXHJcbiAqIG11c3QgYmUgaW4gYSBjYWxsYmFjayBmb3JtYXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoc3RyaW5nfG51bGwpfSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBudWxsIGlmIHdyYXBwaW5nIGZhaWxzLlxyXG4gKi9cclxuZnVuY3Rpb24gX2hhbmRsZUN1c3RvbUNvZGUoY3VzdG9tQ29kZSwgYWxsb3dGaWxlUmVzb3VyY2VzLCBpc0NhbGxiYWNrID0gZmFsc2UpIHtcclxuICBpZiAoY3VzdG9tQ29kZSAmJiB0eXBlb2YgY3VzdG9tQ29kZSA9PT0gJ3N0cmluZycpIHtcclxuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcclxuXHJcbiAgICBpZiAoY3VzdG9tQ29kZS5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgICAgLy8gTG9hZCBhIGZpbGUgaWYgdGhlIGZpbGUgcmVzb3VyY2VzIGFyZSBhbGxvd2VkXHJcbiAgICAgIHJldHVybiBhbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICAgICA/IF9oYW5kbGVDdXN0b21Db2RlKFxyXG4gICAgICAgICAgICByZWFkRmlsZVN5bmMoZ2V0QWJzb2x1dGVQYXRoKGN1c3RvbUNvZGUpLCAndXRmOCcpLFxyXG4gICAgICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXMsXHJcbiAgICAgICAgICAgIGlzQ2FsbGJhY2tcclxuICAgICAgICAgIClcclxuICAgICAgICA6IG51bGw7XHJcbiAgICB9IGVsc2UgaWYgKFxyXG4gICAgICAhaXNDYWxsYmFjayAmJlxyXG4gICAgICAoY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbigpJykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgpJykgfHxcclxuICAgICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpPT4nKSB8fFxyXG4gICAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCkgPT4nKSlcclxuICAgICkge1xyXG4gICAgICAvLyBUcmVhdCBhIGZ1bmN0aW9uIGFzIGEgc2VsZi1pbnZva2luZyBleHByZXNzaW9uXHJcbiAgICAgIHJldHVybiBgKCR7Y3VzdG9tQ29kZX0pKClgO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE9yIHJldHVybiBhcyBhIHN0cmluZ2lmaWVkIGNvZGVcclxuICAgIHJldHVybiBjdXN0b21Db2RlLnJlcGxhY2UoLzskLywgJycpO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGxvYWRpbmcgYW5kIHZhbGlkYXRpb24gb2YgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2BcclxuICogaW4gdGhlIGV4cG9ydCBvcHRpb25zLiBJZiB0aGUgb3B0aW9uIGlzIGEgc3RyaW5nIGFuZCByZWZlcmVuY2VzIGEgSlNPTiBmaWxlXHJcbiAqICh3aGVuIHRoZSBgYWxsb3dGaWxlUmVzb3VyY2VzYCBpcyBgdHJ1ZWApLCBpdCByZWFkcyBhbmQgcGFyc2VzIHRoZSBmaWxlLlxyXG4gKiBPdGhlcndpc2UsIGl0IGF0dGVtcHRzIHRvIHBhcnNlIHRoZSBzdHJpbmcgb3Igb2JqZWN0IGFzIEpTT04uIElmIGFueSBlcnJvcnNcclxuICogb2NjdXIgZHVyaW5nIHRoaXMgcHJvY2VzcywgdGhlIG9wdGlvbiBpcyBzZXQgdG8gYG51bGxgLiBJZiB0aGVyZSBpcyBhbiBlcnJvclxyXG4gKiBsb2FkaW5nIG9yIHBhcnNpbmcgdGhlIGBnbG9iYWxPcHRpb25zYCBvciBgdGhlbWVPcHRpb25zYCwgdGhlIGVycm9yIGlzIGxvZ2dlZFxyXG4gKiBhbmQgdGhlIG9wdGlvbiBpcyBzZXQgdG8gYG51bGxgLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2hhbmRsZUdsb2JhbEFuZFRoZW1lXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBleHBvcnRPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmcgYGV4cG9ydGBcclxuICogb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbUxvZ2ljT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGBjdXN0b21Mb2dpY2Agb3B0aW9ucy5cclxuICovXHJcbmZ1bmN0aW9uIF9oYW5kbGVHbG9iYWxBbmRUaGVtZShleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAvLyBHZXQgdGhlIGBhbGxvd0ZpbGVSZXNvdXJjZXNgIGFuZCBgYWxsb3dDb2RlRXhlY3V0aW9uYCBmbGFnc1xyXG4gIGNvbnN0IHsgYWxsb3dGaWxlUmVzb3VyY2VzLCBhbGxvd0NvZGVFeGVjdXRpb24gfSA9IGN1c3RvbUxvZ2ljT3B0aW9ucztcclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGBnbG9iYWxPcHRpb25zYCBhbmQgYHRoZW1lT3B0aW9uc2Agb3B0aW9uc1xyXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIG9wdGlvbiBleGlzdHNcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgLy8gQ2hlY2sgaWYgaXQgaXMgYSBzdHJpbmcgYW5kIGEgZmlsZSBuYW1lIHdpdGggdGhlIGAuanNvbmAgZXh0ZW5zaW9uXHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzICYmXHJcbiAgICAgICAgICB0eXBlb2YgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPT09ICdzdHJpbmcnICYmXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXS5lbmRzV2l0aCgnLmpzb24nKVxyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGZpbGUgY29udGVudCBjYW4gYmUgYSBjb25maWcsIGFuZCBzYXZlIGl0IGFzIGEgc3RyaW5nXHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQWxsb3dlZENvbmZpZyhcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGdldEFic29sdXRlUGF0aChleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSksICd1dGY4JyksXHJcbiAgICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHZhbHVlIGNhbiBiZSBhIGNvbmZpZywgYW5kIHNhdmUgaXQgYXMgYSBzdHJpbmdcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjaGFydF0gVGhlIFxcYCR7b3B0aW9uc05hbWV9XFxgIGNhbm5vdCBiZSBsb2FkZWQuYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgLy8gSW4gY2FzZSBvZiBhbiBlcnJvciwgc2V0IHRoZSBvcHRpb24gd2l0aCBudWxsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gbnVsbDtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlcmUgaXMgdGhlIGBnbG9iYWxPcHRpb25zYCBwcmVzZW50XHJcbiAgaWYgKFtudWxsLCB1bmRlZmluZWRdLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMuZ2xvYmFsT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGBnbG9iYWxPcHRpb25zYCBvcHRpb24gZm91bmQuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiB0aGVyZSBpcyB0aGUgYHRoZW1lT3B0aW9uc2AgcHJlc2VudFxyXG4gIGlmIChbbnVsbCwgdW5kZWZpbmVkXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnRoZW1lT3B0aW9ucykpIHtcclxuICAgIGxvZygzLCAnW2NoYXJ0XSBObyB2YWx1ZSBmb3IgdGhlIGB0aGVtZU9wdGlvbnNgIG9wdGlvbiBmb3VuZC4nKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBWYWxpZGF0ZXMgdGhlIHNpemUgb2YgdGhlIGRhdGEgZm9yIHRoZSBleHBvcnQgcHJvY2VzcyBhZ2FpbnN0IGEgZml4ZWQgbGltaXRcclxuICogb2YgMTAwTUIuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY2hlY2tEYXRhU2l6ZVxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW1hZ2VPcHRpb25zIC0gVGhlIGRhdGEgb2JqZWN0LCB3aGljaCBpbmNsdWRlcyBvcHRpb25zIGZyb21cclxuICogdGhlIGBleHBvcnRgIGFuZCBgY3VzdG9tTG9naWNgIHNlY3Rpb25zIGFuZCB3aWxsIGJlIHNlbnQgdG8gYSBQdXBwZXRlZXIgcGFnZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBzaXplIG9mIHRoZSBkYXRhIGZvclxyXG4gKiB0aGUgZXhwb3J0IHByb2Nlc3Mgb2JqZWN0IGV4Y2VlZHMgdGhlIDEwME1CIGxpbWl0LlxyXG4gKi9cclxuZnVuY3Rpb24gX2NoZWNrRGF0YVNpemUoaW1hZ2VPcHRpb25zKSB7XHJcbiAgLy8gU2V0IHRoZSBmaXhlZCBkYXRhIGxpbWl0ICgxMDBNQikgZm9yIHRoZSBkZXYtdG9vbHMgcHJvdG9jb2xcclxuICBjb25zdCBkYXRhTGltaXQgPSAxMDAgKiAxMDI0ICogMTAyNDtcclxuXHJcbiAgLy8gR2V0IHRoZSBzaXplIG9mIHRoZSBkYXRhXHJcbiAgY29uc3QgdG90YWxTaXplID0gQnVmZmVyLmJ5dGVMZW5ndGgoSlNPTi5zdHJpbmdpZnkoaW1hZ2VPcHRpb25zKSwgJ3V0Zi04Jyk7XHJcblxyXG4gIC8vIExvZyB0aGUgc2l6ZSBpbiBNQlxyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW2NoYXJ0XSBUaGUgY3VycmVudCB0b3RhbCBzaXplIG9mIHRoZSBkYXRhIGZvciB0aGUgZXhwb3J0IHByb2Nlc3MgaXMgYXJvdW5kICR7KFxyXG4gICAgICB0b3RhbFNpemUgL1xyXG4gICAgICAoMTAyNCAqIDEwMjQpXHJcbiAgICApLnRvRml4ZWQoMil9TUIuYFxyXG4gICk7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBzaXplIG9mIGRhdGEgYmVmb3JlIHBhc3NpbmcgdG8gYSBwYWdlXHJcbiAgaWYgKHRvdGFsU2l6ZSA+PSBkYXRhTGltaXQpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgYFtjaGFydF0gVGhlIGRhdGEgZm9yIHRoZSBleHBvcnQgcHJvY2VzcyBleGNlZWRzIDEwME1CIGxpbWl0LmBcclxuICAgICk7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIGdldEFsbG93Q29kZUV4ZWN1dGlvbixcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgbW9kdWxlIHByb3ZpZGVzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyBpbnRlcnZhbHNcclxuICogYW5kIHRpbWVvdXRzIGluIGEgY2VudHJhbGl6ZWQgbWFubmVyLiBJdCBtYWludGFpbnMgYSByZWdpc3RyeSBvZiBhbGwgYWN0aXZlXHJcbiAqIHRpbWVycyBhbmQgYWxsb3dzIGZvciB0aGVpciBlZmZpY2llbnQgY2xlYW51cCB3aGVuIG5lZWRlZCB0byBhdm9pZCBwb3RlbnRpYWxcclxuICogbWVtb3J5IGxlYWtzLCB1bmludGVuZGVkIGJlaGF2aW9yIG9yIGEgcHJvY2VzcyBmcm9tIGJlaW5nIHN0b3BwZWQuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuLy8gQXJyYXkgdGhhdCBjb250YWlucyBpZHMgb2YgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0c1xyXG5jb25zdCB0aW1lcklkcyA9IFtdO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgaWQgb2YgdGhlIGBzZXRJbnRlcnZhbGAgb3IgYHNldFRpbWVvdXRgIGFuZCB0byB0aGUgYHRpbWVySWRzYCBhcnJheS5cclxuICpcclxuICogQGZ1bmN0aW9uIGFkZFRpbWVyXHJcbiAqXHJcbiAqIEBwYXJhbSB7Tm9kZUpTLlRpbWVvdXR9IGlkIC0gSWQgb2YgYW4gaW50ZXJ2YWwgb3IgYSB0aW1lb3V0LlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGFkZFRpbWVyKGlkKSB7XHJcbiAgdGltZXJJZHMucHVzaChpZCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgYWxsIG9mIG9uZ29pbmcgaW50ZXJ2YWxzIGFuZCB0aW1lb3V0cyBieSBpZHMgZ2F0aGVyZWRcclxuICogaW4gdGhlIGB0aW1lcklkc2AgYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBjbGVhckFsbFRpbWVyc1xyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGNsZWFyQWxsVGltZXJzKCkge1xyXG4gIGxvZyg0LCBgW3RpbWVyXSBDbGVhcmluZyBhbGwgcmVnaXN0ZXJlZCBpbnRlcnZhbHMgYW5kIHRpbWVvdXRzLmApO1xyXG4gIGZvciAoY29uc3QgaWQgb2YgdGltZXJJZHMpIHtcclxuICAgIGNsZWFySW50ZXJ2YWwoaWQpO1xyXG4gICAgY2xlYXJUaW1lb3V0KGlkKTtcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBhZGRUaW1lcixcclxuICBjbGVhckFsbFRpbWVyc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWlkZGxld2FyZSBmdW5jdGlvbnMgZm9yIGxvZ2dpbmcgZXJyb3JzIHdpdGggc3RhY2sgdHJhY2VzXHJcbiAqIGFuZCBoYW5kbGluZyBlcnJvciByZXNwb25zZXMgaW4gYW4gRXhwcmVzcyBhcHBsaWNhdGlvbi5cclxuICovXHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlIGFuZCBoYW5kbGluZyBlcnJvciByZXNwb25zZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGxvZ0Vycm9yTWlkZGxld2FyZVxyXG4gKlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uIHdpdGhcclxuICogdGhlIHBhc3NlZCBlcnJvci5cclxuICovXHJcbmZ1bmN0aW9uIGxvZ0Vycm9yTWlkZGxld2FyZShlcnJvciwgcmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICAvLyBEaXNwbGF5IHRoZSBlcnJvciB3aXRoIHN0YWNrIGluIGEgY29ycmVjdCBmb3JtYXRcclxuICBsb2dXaXRoU3RhY2soMSwgZXJyb3IpO1xyXG5cclxuICAvLyBEZWxldGUgdGhlIHN0YWNrIGZvciB0aGUgZW52aXJvbm1lbnQgb3RoZXIgdGhhbiB0aGUgZGV2ZWxvcG1lbnRcclxuICBpZiAoZ2V0T3B0aW9ucygpLm90aGVyLm5vZGVFbnYgIT09ICdkZXZlbG9wbWVudCcpIHtcclxuICAgIGRlbGV0ZSBlcnJvci5zdGFjaztcclxuICB9XHJcblxyXG4gIC8vIENhbGwgdGhlIGByZXR1cm5FcnJvck1pZGRsZXdhcmVgIG1pZGRsZXdhcmVcclxuICByZXR1cm4gbmV4dChlcnJvcik7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciByZXR1cm5pbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByZXR1cm5FcnJvck1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICovXHJcbmZ1bmN0aW9uIHJldHVybkVycm9yTWlkZGxld2FyZShlcnJvciwgcmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpIHtcclxuICAvLyBHYXRoZXIgYWxsIHJlcXVpZWQgaW5mb3JtYXRpb24gZm9yIHRoZSByZXNwb25zZVxyXG4gIGNvbnN0IHsgbWVzc2FnZSwgc3RhY2sgfSA9IGVycm9yO1xyXG5cclxuICAvLyBVc2UgdGhlIGVycm9yJ3Mgc3RhdHVzIGNvZGUgb3IgdGhlIGRlZmF1bHQgNDAwXHJcbiAgY29uc3Qgc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGUgfHwgNDAwO1xyXG5cclxuICAvLyBTZXQgYW5kIHJldHVybiByZXNwb25zZVxyXG4gIHJlc3BvbnNlLnN0YXR1cyhzdGF0dXNDb2RlKS5qc29uKHsgc3RhdHVzQ29kZSwgbWVzc2FnZSwgc3RhY2sgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBlcnJvciBtaWRkbGV3YXJlcyB0byB0aGUgcGFzc2VkIGV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGVycm9yTWlkZGxld2FyZShhcHApIHtcclxuICAvLyBBZGQgbG9nIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKGxvZ0Vycm9yTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCBzZXQgc3RhdHVzIGFuZCByZXR1cm4gZXJyb3IgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UocmV0dXJuRXJyb3JNaWRkbGV3YXJlKTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWlkZGxld2FyZSBmdW5jdGlvbnMgZm9yIGNvbmZpZ3VyaW5nIGFuZCBlbmFibGluZyByYXRlXHJcbiAqIGxpbWl0aW5nIGluIGFuIEV4cHJlc3MgYXBwbGljYXRpb24uXHJcbiAqL1xyXG5cclxuaW1wb3J0IHJhdGVMaW1pdCBmcm9tICdleHByZXNzLXJhdGUtbGltaXQnO1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIGVuYWJsaW5nIHJhdGUgbGltaXRpbmcgb24gdGhlIHNwZWNpZmllZCBFeHByZXNzIGFwcC5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSByYXRlTGltaXRpbmdPcHRpb25zIC0gVGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogYHJhdGVMaW1pdGluZ2Agb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIGNvdWxkIG5vdCBjb25maWd1cmUgYW5kIHNldFxyXG4gKiB0aGUgcmF0ZSBsaW1pdGluZyBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gcmF0ZUxpbWl0aW5nTWlkZGxld2FyZShhcHAsIHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICB0cnkge1xyXG4gICAgLy8gQ2hlY2sgaWYgdGhlIHJhdGUgbGltaXRpbmcgaXMgZW5hYmxlZCBhbmQgdGhlIGFwcCBleGlzdHNcclxuICAgIGlmIChhcHAgJiYgcmF0ZUxpbWl0aW5nT3B0aW9ucy5lbmFibGUpIHtcclxuICAgICAgY29uc3QgbWVzc2FnZSA9XHJcbiAgICAgICAgJ1RvbyBtYW55IHJlcXVlc3RzLCB5b3UgaGF2ZSBiZWVuIHJhdGUgbGltaXRlZC4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4nO1xyXG5cclxuICAgICAgLy8gT3B0aW9ucyBmb3IgdGhlIHJhdGUgbGltaXRlclxyXG4gICAgICBjb25zdCByYXRlT3B0aW9ucyA9IHtcclxuICAgICAgICB3aW5kb3c6IHJhdGVMaW1pdGluZ09wdGlvbnMud2luZG93IHx8IDEsXHJcbiAgICAgICAgbWF4UmVxdWVzdHM6IHJhdGVMaW1pdGluZ09wdGlvbnMubWF4UmVxdWVzdHMgfHwgMzAsXHJcbiAgICAgICAgZGVsYXk6IHJhdGVMaW1pdGluZ09wdGlvbnMuZGVsYXkgfHwgMCxcclxuICAgICAgICB0cnVzdFByb3h5OiByYXRlTGltaXRpbmdPcHRpb25zLnRydXN0UHJveHkgfHwgZmFsc2UsXHJcbiAgICAgICAgc2tpcEtleTogcmF0ZUxpbWl0aW5nT3B0aW9ucy5za2lwS2V5IHx8IG51bGwsXHJcbiAgICAgICAgc2tpcFRva2VuOiByYXRlTGltaXRpbmdPcHRpb25zLnNraXBUb2tlbiB8fCBudWxsXHJcbiAgICAgIH07XHJcblxyXG4gICAgICAvLyBTZXQgaWYgYmVoaW5kIGEgcHJveHlcclxuICAgICAgaWYgKHJhdGVPcHRpb25zLnRydXN0UHJveHkpIHtcclxuICAgICAgICBhcHAuZW5hYmxlKCd0cnVzdCBwcm94eScpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBDcmVhdGUgYSBsaW1pdGVyXHJcbiAgICAgIGNvbnN0IGxpbWl0ZXIgPSByYXRlTGltaXQoe1xyXG4gICAgICAgIC8vIFRpbWUgZnJhbWUgZm9yIHdoaWNoIHJlcXVlc3RzIGFyZSBjaGVja2VkIGFuZCByZW1lbWJlcmVkXHJcbiAgICAgICAgd2luZG93TXM6IHJhdGVPcHRpb25zLndpbmRvdyAqIDYwICogMTAwMCxcclxuICAgICAgICAvLyBMaW1pdCBlYWNoIElQIHRvIDEwMCByZXF1ZXN0cyBwZXIgYHdpbmRvd01zYFxyXG4gICAgICAgIGxpbWl0OiByYXRlT3B0aW9ucy5tYXhSZXF1ZXN0cyxcclxuICAgICAgICAvLyBEaXNhYmxlIGRlbGF5aW5nLCBmdWxsIHNwZWVkIHVudGlsIHRoZSBtYXggbGltaXQgaXMgcmVhY2hlZFxyXG4gICAgICAgIGRlbGF5TXM6IHJhdGVPcHRpb25zLmRlbGF5LFxyXG4gICAgICAgIGhhbmRsZXI6IChyZXF1ZXN0LCByZXNwb25zZSkgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2UuZm9ybWF0KHtcclxuICAgICAgICAgICAganNvbjogKCkgPT4ge1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQoeyBtZXNzYWdlIH0pO1xyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICBkZWZhdWx0OiAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZChtZXNzYWdlKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSk7XHJcbiAgICAgICAgfSxcclxuICAgICAgICBza2lwOiAocmVxdWVzdCkgPT4ge1xyXG4gICAgICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcclxuICAgICAgICAgIGlmIChcclxuICAgICAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcEtleSAhPT0gbnVsbCAmJlxyXG4gICAgICAgICAgICByYXRlT3B0aW9ucy5za2lwVG9rZW4gIT09IG51bGwgJiZcclxuICAgICAgICAgICAgcmVxdWVzdC5xdWVyeS5rZXkgPT09IHJhdGVPcHRpb25zLnNraXBLZXkgJiZcclxuICAgICAgICAgICAgcmVxdWVzdC5xdWVyeS5hY2Nlc3NfdG9rZW4gPT09IHJhdGVPcHRpb25zLnNraXBUb2tlblxyXG4gICAgICAgICAgKSB7XHJcbiAgICAgICAgICAgIGxvZyg0LCAnW3JhdGUgbGltaXRpbmddIFNraXBwaW5nIHJhdGUgbGltaXRlci4nKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuXHJcbiAgICAgIC8vIFVzZSBhIGxpbWl0ZXIgYXMgYSBtaWRkbGV3YXJlXHJcbiAgICAgIGFwcC51c2UobGltaXRlcik7XHJcblxyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3JhdGUgbGltaXRpbmddIEVuYWJsZWQgcmF0ZSBsaW1pdGluZyB3aXRoICR7cmF0ZU9wdGlvbnMubWF4UmVxdWVzdHN9IHJlcXVlc3RzIHBlciAke3JhdGVPcHRpb25zLndpbmRvd30gbWludXRlIGZvciBlYWNoIElQLCB0cnVzdGluZyBwcm94eTogJHtyYXRlT3B0aW9ucy50cnVzdFByb3h5fS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tyYXRlIGxpbWl0aW5nXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBzZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9ucy4nLFxyXG4gICAgICA1MDBcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgUHJvdmlkZXMgbWlkZGxld2FyZSBmdW5jdGlvbnMgZm9yIHZhbGlkYXRpbmcgaW5jb21pbmcgSFRUUCByZXF1ZXN0c1xyXG4gKiBpbiBhbiBFeHByZXNzIGFwcGxpY2F0aW9uLiBUaGlzIG1vZHVsZSBlbnN1cmVzIHRoYXQgcmVxdWVzdHMgY29udGFpblxyXG4gKiBhcHByb3ByaWF0ZSBjb250ZW50IHR5cGVzIGFuZCB2YWxpZCByZXF1ZXN0IGJvZGllcywgaW5jbHVkaW5nIHByb3BlciBKU09OXHJcbiAqIHN0cnVjdHVyZXMgYW5kIGNoYXJ0IGRhdGEgZm9yIGV4cG9ydHMuIEl0IGNoZWNrcyBmb3IgcG90ZW50aWFsIGlzc3VlcyBzdWNoXHJcbiAqIGFzIG1pc3Npbmcgb3IgbWFsZm9ybWVkIGRhdGEsIHByaXZhdGUgcmFuZ2UgVVJMcyBpbiBTVkcgcGF5bG9hZHMsIGFuZCBhbGxvd3NcclxuICogZm9yIGZsZXhpYmxlIG9wdGlvbnMgdmFsaWRhdGlvbi4gVGhlIG1pZGRsZXdhcmUgbG9ncyBkZXRhaWxlZCBpbmZvcm1hdGlvblxyXG4gKiBhbmQgaGFuZGxlcyBlcnJvcnMgcmVsYXRlZCB0byBpbmNvcnJlY3QgcGF5bG9hZHMsIGNoYXJ0IGRhdGEsIGFuZCBwcml2YXRlIFVSTFxyXG4gKiB1c2FnZS5cclxuICovXHJcblxyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBbGxvd0NvZGVFeGVjdXRpb24gfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGlzQWxsb3dlZENvbmZpZyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGlzT2JqZWN0RW1wdHksIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciB2YWxpZGF0aW5nIHRoZSBjb250ZW50LXR5cGUgaGVhZGVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY29udGVudFR5cGVNaWRkbGV3YXJlXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfSBUaGUgY2FsbCB0byB0aGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIGNvbnRlbnQtdHlwZVxyXG4gKiBpcyBub3QgY29ycmVjdC5cclxuICovXHJcbmZ1bmN0aW9uIGNvbnRlbnRUeXBlTWlkZGxld2FyZShyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIGNvbnRlbnQgdHlwZSBoZWFkZXJcclxuICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gcmVxdWVzdC5oZWFkZXJzWydjb250ZW50LXR5cGUnXSB8fCAnJztcclxuXHJcbiAgICAvLyBBbGxvdyBvbmx5IEpTT04sIFVSTC1lbmNvZGVkIGFuZCBmb3JtIGRhdGEgd2l0aG91dCBmaWxlcyB0eXBlcyBvZiBkYXRhXHJcbiAgICBpZiAoXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24vanNvbicpICYmXHJcbiAgICAgICFjb250ZW50VHlwZS5pbmNsdWRlcygnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJykgJiZcclxuICAgICAgIWNvbnRlbnRUeXBlLmluY2x1ZGVzKCdtdWx0aXBhcnQvZm9ybS1kYXRhJylcclxuICAgICkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1t2YWxpZGF0aW9uXSBDb250ZW50LVR5cGUgbXVzdCBiZSBhcHBsaWNhdGlvbi9qc29uLCBhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQsIG9yIG11bHRpcGFydC9mb3JtLWRhdGEuJyxcclxuICAgICAgICA0MTVcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDYWxsIHRoZSBgcmVxdWVzdEJvZHlNaWRkbGV3YXJlYCBtaWRkbGV3YXJlXHJcbiAgICByZXR1cm4gbmV4dCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgdmFsaWRhdGluZyB0aGUgcmVxdWVzdCdzIGJvZHkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiByZXF1ZXN0Qm9keU1pZGRsZXdhcmVcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9IFRoZSBjYWxsIHRvIHRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpZiB0aGUgYm9keSBpcyBub3QgY29ycmVjdC5cclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBgRXhwb3J0RXJyb3JgIGlmIHRoZSBjaGFydCBkYXRhIGZyb20gdGhlIGJvZHlcclxuICogaXMgbm90IGNvcnJlY3QuXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gYEV4cG9ydEVycm9yYCBpbiBjYXNlIG9mIHRoZSBwcml2YXRlIHJhbmdlXHJcbiAqIHVybCBlcnJvci5cclxuICovXHJcbmZ1bmN0aW9uIHJlcXVlc3RCb2R5TWlkZGxld2FyZShyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBHZXQgdGhlIHJlcXVlc3QgYm9keVxyXG4gICAgY29uc3QgYm9keSA9IHJlcXVlc3QuYm9keTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSB1bmlxdWUgSUQgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgcmVxdWVzdElkID0gdXVpZCgpO1xyXG5cclxuICAgIC8vIFRocm93IGFuIGVycm9yIGlmIHRoZXJlIGlzIG5vIGNvcnJlY3QgYm9keVxyXG4gICAgaWYgKCFib2R5IHx8IGlzT2JqZWN0RW1wdHkoYm9keSkpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgZnJvbSAke1xyXG4gICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fCByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcGF5bG9hZCBpcyBlbXB0eS5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFt2YWxpZGF0aW9uXSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gVGhlIHJlcXVlc3QgYm9keSBpcyByZXF1aXJlZC4gUGxlYXNlIGVuc3VyZSB0aGF0IHlvdXIgQ29udGVudC1UeXBlIGhlYWRlciBpcyBjb3JyZWN0LiBBY2NlcHRlZCB0eXBlcyBhcmUgJ2FwcGxpY2F0aW9uL2pzb24nIGFuZCAnbXVsdGlwYXJ0L2Zvcm0tZGF0YScuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBvcHRpb24gZm9yIHRoZSBzZXJ2ZXJcclxuICAgIGNvbnN0IGFsbG93Q29kZUV4ZWN1dGlvbiA9IGdldEFsbG93Q29kZUV4ZWN1dGlvbigpO1xyXG5cclxuICAgIC8vIEZpbmQgYSBjb3JyZWN0IGNoYXJ0IG9wdGlvbnNcclxuICAgIGNvbnN0IGluc3RyID0gaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAvLyBVc2Ugb25lIG9mIHRoZSBiZWxvd1xyXG4gICAgICBib2R5Lmluc3RyIHx8IGJvZHkub3B0aW9ucyB8fCBib2R5LmluZmlsZSB8fCBib2R5LmRhdGEsXHJcbiAgICAgIC8vIFN0cmluZ2lmeSBvcHRpb25zXHJcbiAgICAgIHRydWUsXHJcbiAgICAgIC8vIEFsbG93IG9yIGRpc2FsbG93IGZ1bmN0aW9uc1xyXG4gICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICk7XHJcblxyXG4gICAgLy8gVGhyb3cgYW4gZXJyb3IgaWYgdGhlcmUgaXMgbm8gY29ycmVjdCBjaGFydCBkYXRhXHJcbiAgICBpZiAoaW5zdHIgPT09IG51bGwgJiYgIWJvZHkuc3ZnKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSByZXF1ZXN0IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFJlY2VpdmVkIHBheWxvYWQgaXMgbWlzc2luZyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm9yIGV4cG9ydDogJHtKU09OLnN0cmluZ2lmeShib2R5KX0uYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIE5vIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3VuZC4gRW5zdXJlIHRoYXQgeW91IGFyZSB1c2luZyBlaXRoZXIgYXBwbGljYXRpb24vanNvbiBvciBtdWx0aXBhcnQvZm9ybS1kYXRhIGhlYWRlcnMuIElmIHNlbmRpbmcgSlNPTiwgbWFrZSBzdXJlIHRoZSBjaGFydCBkYXRhIGlzIGluIHRoZSAnaW5maWxlJywgJ29wdGlvbnMnLCBvciAnZGF0YScgYXR0cmlidXRlLiBJZiBzZW5kaW5nIFNWRywgZW5zdXJlIGl0IGlzIGluIHRoZSAnc3ZnJyBhdHRyaWJ1dGUuYCxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBUaHJvdyBhbiBlcnJvciBpZiB0ZXN0IG9mIHhsaW5rOmhyZWYgZWxlbWVudHMgZnJvbSBwYXlsb2FkJ3MgU1ZHIGZhaWxzXHJcbiAgICBpZiAoYm9keS5zdmcgJiYgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChib2R5LnN2ZykpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIGBbdmFsaWRhdGlvbl0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFNWRyBwb3RlbnRpYWxseSBjb250YWluIGF0IGxlYXN0IG9uZSBmb3JiaWRkZW4gVVJMIGluICd4bGluazpocmVmJyBlbGVtZW50LiBQbGVhc2UgcmV2aWV3IHRoZSBTVkcgY29udGVudCBhbmQgZW5zdXJlIHRoYXQgYWxsIHJlZmVyZW5jZWQgVVJMcyBjb21wbHkgd2l0aCBzZWN1cml0eSBwb2xpY2llcy5gLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgcmVxdWVzdCBvcHRpb25zIGFuZCBzdG9yZSBwYXJzZWQgc3RydWN0dXJlIGluIHRoZSByZXF1ZXN0XHJcbiAgICByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnMgPSB7XHJcbiAgICAgIC8vIFNldCB0aGUgY3JlYXRlZCBJRCBhcyBhIGByZXF1ZXN0SWRgIHByb3BlcnR5IGluIHRoZSBvcHRpb25zXHJcbiAgICAgIHJlcXVlc3RJZCxcclxuICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgaW5zdHIsXHJcbiAgICAgICAgc3ZnOiBib2R5LnN2ZyxcclxuICAgICAgICBvdXRmaWxlOlxyXG4gICAgICAgICAgYm9keS5vdXRmaWxlIHx8XHJcbiAgICAgICAgICBgJHtyZXF1ZXN0LnBhcmFtcy5maWxlbmFtZSB8fCAnY2hhcnQnfS4ke2JvZHkudHlwZSB8fCAncG5nJ31gLFxyXG4gICAgICAgIHR5cGU6IGJvZHkudHlwZSxcclxuICAgICAgICBjb25zdHI6IGJvZHkuY29uc3RyLFxyXG4gICAgICAgIGI2NDogYm9keS5iNjQsXHJcbiAgICAgICAgbm9Eb3dubG9hZDogYm9keS5ub0Rvd25sb2FkLFxyXG4gICAgICAgIGhlaWdodDogYm9keS5oZWlnaHQsXHJcbiAgICAgICAgd2lkdGg6IGJvZHkud2lkdGgsXHJcbiAgICAgICAgc2NhbGU6IGJvZHkuc2NhbGUsXHJcbiAgICAgICAgZ2xvYmFsT3B0aW9uczogaXNBbGxvd2VkQ29uZmlnKFxyXG4gICAgICAgICAgYm9keS5nbG9iYWxPcHRpb25zLFxyXG4gICAgICAgICAgdHJ1ZSxcclxuICAgICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICAgICksXHJcbiAgICAgICAgdGhlbWVPcHRpb25zOiBpc0FsbG93ZWRDb25maWcoXHJcbiAgICAgICAgICBib2R5LnRoZW1lT3B0aW9ucyxcclxuICAgICAgICAgIHRydWUsXHJcbiAgICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgICApXHJcbiAgICAgIH0sXHJcbiAgICAgIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gICAgICAgIGFsbG93RmlsZVJlc291cmNlczogZmFsc2UsXHJcbiAgICAgICAgY3VzdG9tQ29kZTogYm9keS5jdXN0b21Db2RlLFxyXG4gICAgICAgIGNhbGxiYWNrOiBib2R5LmNhbGxiYWNrLFxyXG4gICAgICAgIHJlc291cmNlczogaXNBbGxvd2VkQ29uZmlnKGJvZHkucmVzb3VyY2VzLCB0cnVlLCBhbGxvd0NvZGVFeGVjdXRpb24pXHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgbmV4dCBtaWRkbGV3YXJlXHJcbiAgICByZXR1cm4gbmV4dCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlcyB0byB0aGUgcGFzc2VkIGV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIHZhbGlkYXRpb25NaWRkbGV3YXJlKGFwcCkge1xyXG4gIC8vIEFkZCBjb250ZW50IHR5cGUgdmFsaWRhdGlvbiBtaWRkbGV3YXJlXHJcbiAgYXBwLnBvc3QoWycvJywgJy86ZmlsZW5hbWUnXSwgY29udGVudFR5cGVNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHJlcXVlc3QgYm9keSByZXF1ZXN0IHZhbGlkYXRpb24gbWlkZGxld2FyZVxyXG4gIGFwcC5wb3N0KFsnLycsICcvOmZpbGVuYW1lJ10sIHJlcXVlc3RCb2R5TWlkZGxld2FyZSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgdGhlIGV4cG9ydCByb3V0ZXMgYW5kIGxvZ2ljIGZvciBoYW5kbGluZyBjaGFydCBleHBvcnRcclxuICogcmVxdWVzdHMgaW4gYW4gRXhwcmVzcyBzZXJ2ZXIuIFRoaXMgbW9kdWxlIHByb2Nlc3NlcyBpbmNvbWluZyByZXF1ZXN0c1xyXG4gKiB0byBleHBvcnQgY2hhcnRzIGluIHZhcmlvdXMgZm9ybWF0cyAoZS5nLiBKUEVHLCBQTkcsIFBERiwgU1ZHKS4gSXQgaW50ZWdyYXRlc1xyXG4gKiB3aXRoIEhpZ2hjaGFydHMnIGNvcmUgZnVuY3Rpb25hbGl0aWVzIGFuZCBzdXBwb3J0cyBib3RoIGltbWVkaWF0ZSBkb3dubG9hZFxyXG4gKiByZXNwb25zZXMgYW5kIEJhc2U2NC1lbmNvZGVkIGNvbnRlbnQgcmV0dXJucy4gVGhlIGNvZGUgYWxzbyBmZWF0dXJlc1xyXG4gKiBiZW5jaG1hcmtpbmcgZm9yIHBlcmZvcm1hbmNlIG1vbml0b3JpbmcuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsgc3RhcnRFeHBvcnQgfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGdldEJhc2U2NCwgbWVhc3VyZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFJldmVyc2VkIE1JTUUgdHlwZXNcclxuY29uc3QgcmV2ZXJzZWRNaW1lID0ge1xyXG4gIHBuZzogJ2ltYWdlL3BuZycsXHJcbiAganBlZzogJ2ltYWdlL2pwZWcnLFxyXG4gIGdpZjogJ2ltYWdlL2dpZicsXHJcbiAgcGRmOiAnYXBwbGljYXRpb24vcGRmJyxcclxuICBzdmc6ICdpbWFnZS9zdmcreG1sJ1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4cG9ydCByZXF1ZXN0cyBmcm9tIHRoZSBjbGllbnQuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gcmVxdWVzdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAqIGlzIGNvbXBsZXRlLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gcmVxdWVzdEV4cG9ydChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBTdGFydCBjb3VudGluZyB0aW1lIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHJlcXVlc3RDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBJbiBjYXNlIHRoZSBjb25uZWN0aW9uIGlzIGNsb3NlZCwgZm9yY2UgdG8gYWJvcnQgZnVydGhlciBhY3Rpb25zXHJcbiAgICBsZXQgY29ubmVjdGlvbkFib3J0ZWQgPSBmYWxzZTtcclxuICAgIHJlcXVlc3Quc29ja2V0Lm9uKCdjbG9zZScsIChoYWRFcnJvcnMpID0+IHtcclxuICAgICAgaWYgKGhhZEVycm9ycykge1xyXG4gICAgICAgIGNvbm5lY3Rpb25BYm9ydGVkID0gdHJ1ZTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBvcHRpb25zIHByZXZpb3VzbHkgdmFsaWRhdGVkIGluIHRoZSB2YWxpZGF0aW9uIG1pZGRsZXdhcmVcclxuICAgIGNvbnN0IG9wdGlvbnMgPSByZXF1ZXN0LnZhbGlkYXRlZE9wdGlvbnM7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZXF1ZXN0IGlkXHJcbiAgICBjb25zdCByZXF1ZXN0SWQgPSBvcHRpb25zLnJlcXVlc3RJZDtcclxuXHJcbiAgICAvLyBJbmZvIGFib3V0IGFuIGluY29taW5nIHJlcXVlc3Qgd2l0aCBjb3JyZWN0IGRhdGFcclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3QuYCk7XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChvcHRpb25zLCAoZXJyb3IsIGRhdGEpID0+IHtcclxuICAgICAgLy8gUmVtb3ZlIHRoZSBjbG9zZSBldmVudCBmcm9tIHRoZSBzb2NrZXRcclxuICAgICAgcmVxdWVzdC5zb2NrZXQucmVtb3ZlQWxsTGlzdGVuZXJzKCdjbG9zZScpO1xyXG5cclxuICAgICAgLy8gSWYgdGhlIGNvbm5lY3Rpb24gd2FzIGNsb3NlZCwgZG8gbm90aGluZ1xyXG4gICAgICBpZiAoY29ubmVjdGlvbkFib3J0ZWQpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFJlcXVlc3QgWyR7cmVxdWVzdElkfV0gLSBUaGUgY2xpZW50IGNsb3NlZCB0aGUgY29ubmVjdGlvbiBiZWZvcmUgdGhlIGNoYXJ0IGZpbmlzaGVkIHByb2Nlc3NpbmcuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBlcnJvciwgbG9nIGl0IGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBkYXRhIGlzIG1pc3NpbmcsIGxvZyB0aGUgbWVzc2FnZSBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoIWRhdGEgfHwgIWRhdGEucmVzdWx0KSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgIGBbZXhwb3J0XSBSZXF1ZXN0IFske3JlcXVlc3RJZH1dIC0gUmVxdWVzdCBmcm9tICR7XHJcbiAgICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHxcclxuICAgICAgICAgICAgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICAgIH0gd2FzIGluY29ycmVjdC4gUmVjZWl2ZWQgcmVzdWx0IGlzICR7ZGF0YS5yZXN1bHR9LmBcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFVuZXhwZWN0ZWQgcmV0dXJuIG9mIHRoZSBleHBvcnQgcmVzdWx0IGZyb20gdGhlIGNoYXJ0IGdlbmVyYXRpb24uIFBsZWFzZSBjaGVjayB5b3VyIHJlcXVlc3QgZGF0YS5gLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gUmV0dXJuIHRoZSByZXN1bHQgaW4gYW4gYXBwcm9wcmlhdGUgZm9ybWF0XHJcbiAgICAgIGlmIChkYXRhLnJlc3VsdCkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gUmVxdWVzdCBbJHtyZXF1ZXN0SWR9XSAtIFRoZSB3aG9sZSBleHBvcnRpbmcgcHJvY2VzcyB0b29rICR7cmVxdWVzdENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBgdHlwZWAsIGBiNjRgLCBgbm9Eb3dubG9hZGAsIGFuZCBgb3V0ZmlsZWAgZnJvbSBvcHRpb25zXHJcbiAgICAgICAgY29uc3QgeyB0eXBlLCBiNjQsIG5vRG93bmxvYWQsIG91dGZpbGUgfSA9IGRhdGEub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgICAgIC8vIElmIG9ubHkgQmFzZTY0IGlzIHJlcXVpcmVkLCByZXR1cm4gaXRcclxuICAgICAgICBpZiAoYjY0KSB7XHJcbiAgICAgICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChnZXRCYXNlNjQoZGF0YS5yZXN1bHQsIHR5cGUpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFNldCBjb3JyZWN0IGNvbnRlbnQgdHlwZVxyXG4gICAgICAgIHJlc3BvbnNlLmhlYWRlcignQ29udGVudC1UeXBlJywgcmV2ZXJzZWRNaW1lW3R5cGVdIHx8ICdpbWFnZS9wbmcnKTtcclxuXHJcbiAgICAgICAgLy8gRGVjaWRlIHdoZXRoZXIgdG8gZG93bmxvYWQgb3Igbm90IGNoYXJ0IGZpbGVcclxuICAgICAgICBpZiAoIW5vRG93bmxvYWQpIHtcclxuICAgICAgICAgIHJlc3BvbnNlLmF0dGFjaG1lbnQob3V0ZmlsZSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBJZiBTVkcsIHJldHVybiBwbGFpbiBjb250ZW50LCBvdGhlcndpc2UgYSBiNjQgc3RyaW5nIGZyb20gYSBidWZmZXJcclxuICAgICAgICByZXR1cm4gdHlwZSA9PT0gJ3N2ZydcclxuICAgICAgICAgID8gcmVzcG9uc2Uuc2VuZChkYXRhLnJlc3VsdClcclxuICAgICAgICAgIDogcmVzcG9uc2Uuc2VuZChCdWZmZXIuZnJvbShkYXRhLnJlc3VsdCwgJ2Jhc2U2NCcpKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBuZXh0KGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgZXhwb3J0YCByb3V0ZXMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBleHBvcnRSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBleHBvcnRSb3V0ZXMoYXBwKSB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnLycgLSBBIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIGF0IHRoZSByb290XHJcbiAgICogZW5kcG9pbnQuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy8nLCByZXF1ZXN0RXhwb3J0KTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAnLzpmaWxlbmFtZScgLSBBIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIHdpdGhcclxuICAgKiBhIHNwZWNpZmllZCBmaWxlbmFtZSBwYXJhbWV0ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy86ZmlsZW5hbWUnLCByZXF1ZXN0RXhwb3J0KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNSwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qKlxyXG4gKiBAb3ZlcnZpZXcgRGVmaW5lcyBhbiBFeHByZXNzIHJvdXRlIGZvciBzZXJ2ZXIgaGVhbHRoIG1vbml0b3JpbmcsIGluY2x1ZGluZ1xyXG4gKiB1cHRpbWUsIHN1Y2Nlc3MgcmF0ZXMsIGFuZCBvdGhlciBzZXJ2ZXIgc3RhdGlzdGljcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IGdldEhjVmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZ2V0UG9vbFN0YXRzLCBnZXRQb29sSW5mb0pTT04gfSBmcm9tICcuLi8uLi9wb29sLmpzJztcclxuaW1wb3J0IHsgYWRkVGltZXIgfSBmcm9tICcuLi8uLi90aW1lci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0TmV3RGF0ZVRpbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vLyBTZXQgdGhlIHN0YXJ0IGRhdGUgb2YgdGhlIHNlcnZlclxyXG5jb25zdCBzZXJ2ZXJTdGFydFRpbWUgPSBuZXcgRGF0ZSgpO1xyXG5cclxuLy8gR2V0IHRoZSBgcGFja2FnZS5qc29uYCBjb250ZW50XHJcbmNvbnN0IHBhY2thZ2VGaWxlID0gSlNPTi5wYXJzZShcclxuICByZWFkRmlsZVN5bmMoam9pbihfX2Rpcm5hbWUsICdwYWNrYWdlLmpzb24nKSwgJ3V0ZjgnKVxyXG4pO1xyXG5cclxuLy8gQW4gYXJyYXkgZm9yIHN1Y2Nlc3MgcmF0ZSByYXRpb3NcclxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XHJcblxyXG4vLyBSZWNvcmQgZXZlcnkgbWludXRlXHJcbmNvbnN0IHJlY29yZEludGVydmFsID0gNjAgKiAxMDAwO1xyXG5cclxuLy8gMzAgbWludXRlc1xyXG5jb25zdCB3aW5kb3dTaXplID0gMzA7XHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgYHN1Y2Nlc3NSYXRlc2BcclxuICogYXJyYXkuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfY2FsY3VsYXRlTW92aW5nQXZlcmFnZVxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBBIG1vdmluZyBhdmVyYWdlIGZvciBzdWNjZXNzIHJhdGlvIG9mIHRoZSBzZXJ2ZXIgZXhwb3J0cy5cclxuICovXHJcbmZ1bmN0aW9uIF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCkge1xyXG4gIHJldHVybiBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIHRoZSBpbnRlcnZhbCByZXNwb25zaWJsZSBmb3IgY2FsY3VsYXRpbmcgY3VycmVudCBzdWNjZXNzIHJhdGUgcmF0aW9cclxuICogYW5kIGNvbGxlY3RzIHJlY29yZHMgdG8gdGhlIGBzdWNjZXNzUmF0ZXNgIGFycmF5LlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX3N0YXJ0U3VjY2Vzc1JhdGVcclxuICpcclxuICogQHJldHVybnMge05vZGVKUy5UaW1lb3V0fSBJZCBvZiBhbiBpbnRlcnZhbC5cclxuICovXHJcbmZ1bmN0aW9uIF9zdGFydFN1Y2Nlc3NSYXRlKCkge1xyXG4gIHJldHVybiBzZXRJbnRlcnZhbCgoKSA9PiB7XHJcbiAgICBjb25zdCBzdGF0cyA9IGdldFBvb2xTdGF0cygpO1xyXG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID1cclxuICAgICAgc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCA9PT0gMFxyXG4gICAgICAgID8gMVxyXG4gICAgICAgIDogKHN0YXRzLmV4cG9ydHNQZXJmb3JtZWQgLyBzdGF0cy5leHBvcnRzQXR0ZW1wdGVkKSAqIDEwMDtcclxuXHJcbiAgICBzdWNjZXNzUmF0ZXMucHVzaChzdWNjZXNzUmF0aW8pO1xyXG4gICAgaWYgKHN1Y2Nlc3NSYXRlcy5sZW5ndGggPiB3aW5kb3dTaXplKSB7XHJcbiAgICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xyXG4gICAgfVxyXG4gIH0sIHJlY29yZEludGVydmFsKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGBoZWFsdGhgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIGhlYWx0aFJvdXRlc1xyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGhlYWx0aFJvdXRlcyhhcHApIHtcclxuICAvLyBTdGFydCBwcm9jZXNzaW5nIHN1Y2Nlc3MgcmF0ZSByYXRpbyBpbnRlcnZhbCBhbmQgc2F2ZSBpdHMgaWQgdG8gdGhlIGFycmF5XHJcbiAgLy8gZm9yIHRoZSBncmFjZWZ1bCBjbGVhcmluZyBvbiBzaHV0ZG93biB3aXRoIGluamVjdGVkIGBhZGRUaW1lcmAgZnVudGlvblxyXG4gIGFkZFRpbWVyKF9zdGFydFN1Y2Nlc3NSYXRlKCkpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBHRVQgJy9oZWFsdGgnIC0gQSByb3V0ZSBmb3IgZ2V0dGluZyB0aGUgYmFzaWMgc3RhdHMgb2YgdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAuZ2V0KCcvaGVhbHRoJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1toZWFsdGhdIFJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgICAgY29uc3Qgc3RhdHMgPSBnZXRQb29sU3RhdHMoKTtcclxuICAgICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgICAgY29uc3QgbW92aW5nQXZlcmFnZSA9IF9jYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgICAvLyBTZW5kIHRoZSBzZXJ2ZXIncyBzdGF0aXN0aWNzXHJcbiAgICAgIHJlc3BvbnNlLnNlbmQoe1xyXG4gICAgICAgIC8vIFN0YXR1cyBhbmQgdGltZXNcclxuICAgICAgICBzdGF0dXM6ICdPSycsXHJcbiAgICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcclxuICAgICAgICB1cHRpbWU6IGAke01hdGguZmxvb3IoKGdldE5ld0RhdGVUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MCl9IG1pbnV0ZXNgLFxyXG5cclxuICAgICAgICAvLyBWZXJzaW9uc1xyXG4gICAgICAgIHNlcnZlclZlcnNpb246IHBhY2thZ2VGaWxlLnZlcnNpb24sXHJcbiAgICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IGdldEhjVmVyc2lvbigpLFxyXG5cclxuICAgICAgICAvLyBFeHBvcnRzXHJcbiAgICAgICAgYXZlcmFnZUV4cG9ydFRpbWU6IHN0YXRzLnRpbWVTcGVudEF2ZXJhZ2UsXHJcbiAgICAgICAgYXR0ZW1wdGVkRXhwb3J0czogc3RhdHMuZXhwb3J0c0F0dGVtcHRlZCxcclxuICAgICAgICBwZXJmb3JtZWRFeHBvcnRzOiBzdGF0cy5leHBvcnRzUGVyZm9ybWVkLFxyXG4gICAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmV4cG9ydHNEcm9wcGVkLFxyXG4gICAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMuZXhwb3J0c1BlcmZvcm1lZCAvIHN0YXRzLmV4cG9ydHNBdHRlbXB0ZWQpICogMTAwLFxyXG5cclxuICAgICAgICAvLyBQb29sXHJcbiAgICAgICAgcG9vbDogZ2V0UG9vbEluZm9KU09OKCksXHJcblxyXG4gICAgICAgIC8vIE1vdmluZyBhdmVyYWdlXHJcbiAgICAgICAgcGVyaW9kLFxyXG4gICAgICAgIG1vdmluZ0F2ZXJhZ2UsXHJcbiAgICAgICAgbWVzc2FnZTpcclxuICAgICAgICAgIGlzTmFOKG1vdmluZ0F2ZXJhZ2UpIHx8ICFzdWNjZXNzUmF0ZXMubGVuZ3RoXHJcbiAgICAgICAgICAgID8gJ1RvbyBlYXJseSB0byByZXBvcnQuIE5vIGV4cG9ydHMgbWFkZSB5ZXQuIFBsZWFzZSBjaGVjayBiYWNrIHNvb24uJ1xyXG4gICAgICAgICAgICA6IGBMYXN0ICR7cGVyaW9kfSBtaW51dGVzIGhhZCBhIHN1Y2Nlc3MgcmF0ZSBvZiAke21vdmluZ0F2ZXJhZ2UudG9GaXhlZCgyKX0lLmAsXHJcblxyXG4gICAgICAgIC8vIFNWRyBhbmQgSlNPTiBleHBvcnRzXHJcbiAgICAgICAgc3ZnRXhwb3J0czogc3RhdHMuZXhwb3J0c0Zyb21TdmcsXHJcbiAgICAgICAganNvbkV4cG9ydHM6IHN0YXRzLmV4cG9ydHNGcm9tT3B0aW9ucyxcclxuICAgICAgICBzdmdFeHBvcnRzQXR0ZW1wdHM6IHN0YXRzLmV4cG9ydHNGcm9tU3ZnQXR0ZW1wdHMsXHJcbiAgICAgICAganNvbkV4cG9ydHNBdHRlbXB0czogc3RhdHMuZXhwb3J0c0Zyb21PcHRpb25zQXR0ZW1wdHNcclxuICAgICAgfSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3Igc2VydmluZyB0aGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyXHJcbiAqIHdoZW4gZW5hYmxlZC5cclxuICovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIGB1aWAgcm91dGVzLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdWlSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB1aVJvdXRlcyhhcHApIHtcclxuICAvLyBBZGQgdGhlIFVJIGVuZHBvaW50IG9ubHkgaWYgcmVxdWlyZWRcclxuICBpZiAoZ2V0T3B0aW9ucygpLnVpLmVuYWJsZSkge1xyXG4gICAgLyoqXHJcbiAgICAgKiBBZGRzIHRoZSBHRVQgJy8nIC0gQSByb3V0ZSBmb3IgYSBVSSB3aGVuIGVuYWJsZWQgb24gdGhlIGV4cG9ydCBzZXJ2ZXIuXHJcbiAgICAgKi9cclxuICAgIGFwcC5nZXQoZ2V0T3B0aW9ucygpLnVpLnJvdXRlIHx8ICcvJywgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKDQsICdbdWldIFJldHVybmluZyBVSSBmb3IgdGhlIGV4cG9ydC4nKTtcclxuXHJcbiAgICAgICAgcmVzcG9uc2Uuc2VuZEZpbGUoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnLCAnaW5kZXguaHRtbCcpLCB7XHJcbiAgICAgICAgICBhY2NlcHRSYW5nZXM6IGZhbHNlXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgcmV0dXJuIG5leHQoZXJyb3IpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IERlZmluZXMgYW4gRXhwcmVzcyByb3V0ZSBmb3IgdXBkYXRpbmcgdGhlIEhpZ2hjaGFydHMgdmVyc2lvblxyXG4gKiBvbiB0aGUgc2VydmVyLCB3aXRoIGF1dGhlbnRpY2F0aW9uIGFuZCB2YWxpZGF0aW9uLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IGdldEhjVmVyc2lvbiwgdXBkYXRlSGNWZXJzaW9uIH0gZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi4vLi4vZW52cy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBgdmVyc2lvbl9jaGFuZ2VgIHJvdXRlcy5cclxuICpcclxuICogQGZ1bmN0aW9uIHZlcnNpb25DaGFuZ2VSb3V0ZXNcclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiB2ZXJzaW9uQ2hhbmdlUm91dGVzKGFwcCkge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgJy92ZXJzaW9uX2NoYW5nZS86bmV3VmVyc2lvbicgLSBBIHJvdXRlIGZvciBjaGFuZ2luZ1xyXG4gICAqIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gb24gdGhlIHNlcnZlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnL3ZlcnNpb25fY2hhbmdlLzpuZXdWZXJzaW9uJywgYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1t2ZXJzaW9uXSBDaGFuZ2luZyBIaWdoY2hhcnRzIHZlcnNpb24uJyk7XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIHRva2VuIGRpcmVjdGx5IGZyb20gZW52c1xyXG4gICAgICBjb25zdCBhZG1pblRva2VuID0gZW52cy5ISUdIQ0hBUlRTX0FETUlOX1RPS0VOO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIGV4aXN0ZW5jZSBvZiB0aGUgdG9rZW5cclxuICAgICAgaWYgKCFhZG1pblRva2VuIHx8ICFhZG1pblRva2VuLmxlbmd0aCkge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdbdmVyc2lvbl0gVGhlIHNlcnZlciBpcyBub3QgY29uZmlndXJlZCB0byBwZXJmb3JtIHJ1bi10aW1lIHZlcnNpb24gY2hhbmdlczogYEhJR0hDSEFSVFNfQURNSU5fVE9LRU5gIGlzIG5vdCBzZXQuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdG9rZW4gZnJvbSB0aGUgaGMtYXV0aCBoZWFkZXJcclxuICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXHJcbiAgICAgIGlmICghdG9rZW4gfHwgdG9rZW4gIT09IGFkbWluVG9rZW4pIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW3ZlcnNpb25dIEludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgIDQwMVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgbmV3IHZlcnNpb24gZnJvbSB0aGUgcGFyYW1zXHJcbiAgICAgIGNvbnN0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG5cclxuICAgICAgLy8gVXBkYXRlIHZlcnNpb25cclxuICAgICAgaWYgKG5ld1ZlcnNpb24pIHtcclxuICAgICAgICB0cnkge1xyXG4gICAgICAgICAgYXdhaXQgdXBkYXRlSGNWZXJzaW9uKG5ld1ZlcnNpb24pO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAgIGBbdmVyc2lvbl0gVmVyc2lvbiBjaGFuZ2U6ICR7ZXJyb3IubWVzc2FnZX1gLFxyXG4gICAgICAgICAgICA0MDBcclxuICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gU3VjY2Vzc1xyXG4gICAgICAgIHJlc3BvbnNlLnN0YXR1cygyMDApLnNlbmQoe1xyXG4gICAgICAgICAgc3RhdHVzQ29kZTogMjAwLFxyXG4gICAgICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IGdldEhjVmVyc2lvbigpLFxyXG4gICAgICAgICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIEhpZ2hjaGFydHMgdG8gdmVyc2lvbjogJHtuZXdWZXJzaW9ufS5gXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gTm8gdmVyc2lvbiBzcGVjaWZpZWRcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1t2ZXJzaW9uXSBObyBuZXcgdmVyc2lvbiBzdXBwbGllZC4nLCA0MDApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbmV4dChlcnJvcik7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEEgbW9kdWxlIHRoYXQgc2V0cyB1cCBhbmQgbWFuYWdlcyBIVFRQIGFuZCBIVFRQUyBzZXJ2ZXJzXHJcbiAqIGZvciB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyLiBJdCBoYW5kbGVzIHNlcnZlciBpbml0aWFsaXphdGlvbixcclxuICogY29uZmlndXJhdGlvbiwgZXJyb3IgaGFuZGxpbmcsIG1pZGRsZXdhcmVzIHNldHVwLCByb3V0ZSBkZWZpbml0aW9uLCBhbmQgcmF0ZVxyXG4gKiBsaW1pdGluZy4gVGhlIG1vZHVsZSBleHBvcnRzIGZ1bmN0aW9ucyB0byBzdGFydCwgc3RvcCwgYW5kIG1hbmFnZSBzZXJ2ZXJcclxuICogaW5zdGFuY2VzLCBhcyB3ZWxsIGFzIHV0aWxpdHkgZnVuY3Rpb25zIGZvciBkZWZpbmluZyByb3V0ZXMgYW5kIGF0dGFjaGluZ1xyXG4gKiBtaWRkbGV3YXJlcy5cclxuICovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBjb3JzIGZyb20gJ2NvcnMnO1xyXG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcbmltcG9ydCBtdWx0ZXIgZnJvbSAnbXVsdGVyJztcclxuXHJcbmltcG9ydCB7IHVwZGF0ZU9wdGlvbnMgfSBmcm9tICcuLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSwgZ2V0QWJzb2x1dGVQYXRoIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IGVycm9yTWlkZGxld2FyZSBmcm9tICcuL21pZGRsZXdhcmVzL2Vycm9yLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdGluZ01pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy9yYXRlTGltaXRpbmcuanMnO1xyXG5pbXBvcnQgdmFsaWRhdGlvbk1pZGRsZXdhcmUgZnJvbSAnLi9taWRkbGV3YXJlcy92YWxpZGF0aW9uLmpzJztcclxuXHJcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcclxuaW1wb3J0IGhlYWx0aFJvdXRlcyBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xyXG5pbXBvcnQgdmVyc2lvbkNoYW5nZVJvdXRlcyBmcm9tICcuL3JvdXRlcy92ZXJzaW9uQ2hhbmdlLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcclxuY29uc3QgYWN0aXZlU2VydmVycyA9IG5ldyBNYXAoKTtcclxuXHJcbi8vIENyZWF0ZSBleHByZXNzIGFwcFxyXG5jb25zdCBhcHAgPSBleHByZXNzKCk7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIEhUVFAgYW5kL29yIEhUVFBTIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi5cclxuICogVGhlIGBzZXJ2ZXJPcHRpb25zYCBvYmplY3QgY29udGFpbnMgc2VydmVyLXJlbGF0ZWQgcHJvcGVydGllcyAocmVmZXJcclxuICogdG8gdGhlIGBzZXJ2ZXJgIHNlY3Rpb24gaW4gdGhlIGAuL2xpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgZGV0YWlscykuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gc3RhcnRTZXJ2ZXJcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlck9wdGlvbnMgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBUaGlzIG9iamVjdCBtYXkgaW5jbHVkZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIHRoZSBgc2VydmVyYFxyXG4gKiBvcHRpb25zLiBJZiB0aGUgb3B0aW9ucyBhcmUgcGFydGlhbCwgbWlzc2luZyB2YWx1ZXMgd2lsbCBkZWZhdWx0XHJcbiAqIHRvIHRoZSBjdXJyZW50IGdsb2JhbCBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGVpdGhlclxyXG4gKiBub3QgZW5hYmxlZCBvciBubyB2YWxpZCBFeHByZXNzIGFwcCBpcyBmb3VuZCwgc2lnbmFsaW5nIHRoZSBlbmQgb2YgdGhlXHJcbiAqIGZ1bmN0aW9uJ3MgZXhlY3V0aW9uLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIGBFeHBvcnRFcnJvcmAgaWYgdGhlIHNlcnZlciBjYW5ub3RcclxuICogYmUgY29uZmlndXJlZCBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFNlcnZlcihzZXJ2ZXJPcHRpb25zKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICAgIGNvbnN0IG9wdGlvbnMgPSB1cGRhdGVPcHRpb25zKHtcclxuICAgICAgc2VydmVyOiBzZXJ2ZXJPcHRpb25zXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBVc2UgdmFsaWRhdGVkIG9wdGlvbnNcclxuICAgIHNlcnZlck9wdGlvbnMgPSBvcHRpb25zLnNlcnZlcjtcclxuXHJcbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXHJcbiAgICBpZiAoIXNlcnZlck9wdGlvbnMuZW5hYmxlIHx8ICFhcHApIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbc2VydmVyXSBTZXJ2ZXIgY2Fubm90IGJlIHN0YXJ0ZWQgKG5vdCBlbmFibGVkIG9yIG5vIGNvcnJlY3QgRXhwcmVzcyBhcHAgZm91bmQpLicsXHJcbiAgICAgICAgNTAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVG9vIGJpZyBsaW1pdHMgbGVhZCB0byB0aW1lb3V0cyBpbiB0aGUgZXhwb3J0IHByb2Nlc3Mgd2hlblxyXG4gICAgLy8gdGhlIHJhc3Rlcml6YXRpb24gdGltZW91dCBpcyBzZXQgdG9vIGxvd1xyXG4gICAgY29uc3QgdXBsb2FkTGltaXRCeXRlcyA9IHNlcnZlck9wdGlvbnMudXBsb2FkTGltaXQgKiAxMDI0ICogMTAyNDtcclxuXHJcbiAgICAvLyBNZW1vcnkgc3RvcmFnZSBmb3IgbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHN0b3JhZ2UgPSBtdWx0ZXIubWVtb3J5U3RvcmFnZSgpO1xyXG5cclxuICAgIC8vIEVuYWJsZSBwYXJzaW5nIG9mIGZvcm0gZGF0YSAoZmlsZXMpIHdpdGggbXVsdGVyIHBhY2thZ2VcclxuICAgIGNvbnN0IHVwbG9hZCA9IG11bHRlcih7XHJcbiAgICAgIHN0b3JhZ2UsXHJcbiAgICAgIGxpbWl0czoge1xyXG4gICAgICAgIGZpZWxkU2l6ZTogdXBsb2FkTGltaXRCeXRlc1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXHJcbiAgICBhcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XHJcblxyXG4gICAgLy8gRW5hYmxlIENPUlMgc3VwcG9ydFxyXG4gICAgYXBwLnVzZShcclxuICAgICAgY29ycyh7XHJcbiAgICAgICAgbWV0aG9kczogWydQT1NUJywgJ0dFVCcsICdPUFRJT05TJ11cclxuICAgICAgfSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0dGluZyBhIGxvdCBvZiBgUmFuZ2VOb3RTYXRpc2ZpYWJsZUVycm9yYCBleGNlcHRpb25zIChldmVuIHRob3VnaCB0aGlzXHJcbiAgICAvLyBpcyBhIGRlcHJlY2F0ZWQgb3B0aW9ucywgbGV0J3MgdHJ5IHRvIHNldCBpdCB0byBmYWxzZSlcclxuICAgIGFwcC51c2UoKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgIHJlc3BvbnNlLnNldCgnQWNjZXB0LVJhbmdlcycsICdub25lJyk7XHJcbiAgICAgIG5leHQoKTtcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgSlNPTiBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLmpzb24oe1xyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlciBmb3IgVVJMLWVuY29kZWQgZm9ybSBkYXRhXHJcbiAgICBhcHAudXNlKFxyXG4gICAgICBleHByZXNzLnVybGVuY29kZWQoe1xyXG4gICAgICAgIGV4dGVuZGVkOiB0cnVlLFxyXG4gICAgICAgIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzXHJcbiAgICAgIH0pXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xyXG4gICAgYXBwLnVzZSh1cGxvYWQubm9uZSgpKTtcclxuXHJcbiAgICAvLyBTZXQgdXAgc3RhdGljIGZvbGRlcidzIHJvdXRlXHJcbiAgICBhcHAudXNlKGV4cHJlc3Muc3RhdGljKGpvaW4oX19kaXJuYW1lLCAncHVibGljJykpKTtcclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyT3B0aW9ucy5zc2wuZm9yY2UpIHtcclxuICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFApXHJcbiAgICAgIGNvbnN0IGh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApO1xyXG5cclxuICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICBfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIC8vIExpc3RlblxyXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJPcHRpb25zLnBvcnQsIHNlcnZlck9wdGlvbnMuaG9zdCwgKCkgPT4ge1xyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlck9wdGlvbnMucG9ydCwgaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlck9wdGlvbnMuaG9zdH06JHtzZXJ2ZXJPcHRpb25zLnBvcnR9LmBcclxuICAgICAgICApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgICBpZiAoc2VydmVyT3B0aW9ucy5zc2wuZW5hYmxlKSB7XHJcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cclxuICAgICAgbGV0IGtleSwgY2VydDtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XHJcbiAgICAgICAga2V5ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmtleScpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wgY2VydGlmaWNhdGVcclxuICAgICAgICBjZXJ0ID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgam9pbihnZXRBYnNvbHV0ZVBhdGgoc2VydmVyT3B0aW9ucy5zc2wuY2VydFBhdGgpLCAnc2VydmVyLmNydCcpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFVuYWJsZSB0byBsb2FkIGtleS9jZXJ0aWZpY2F0ZSBmcm9tIHRoZSAnJHtzZXJ2ZXJPcHRpb25zLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChrZXkgJiYgY2VydCkge1xyXG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcclxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xyXG5cclxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgICAgX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAvLyBMaXN0ZW5cclxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyT3B0aW9ucy5zc2wucG9ydCwgc2VydmVyT3B0aW9ucy5ob3N0LCAoKSA9PiB7XHJcbiAgICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUFMgc2VydmVyXHJcbiAgICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJPcHRpb25zLnNzbC5wb3J0LCBodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAzLFxyXG4gICAgICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQUyBzZXJ2ZXIgb24gJHtzZXJ2ZXJPcHRpb25zLmhvc3R9OiR7c2VydmVyT3B0aW9ucy5zc2wucG9ydH0uYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgICByYXRlTGltaXRpbmdNaWRkbGV3YXJlKGFwcCwgc2VydmVyT3B0aW9ucy5yYXRlTGltaXRpbmcpO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgdmFsaWRhdGlvbiBoYW5kbGVyXHJcbiAgICB2YWxpZGF0aW9uTWlkZGxld2FyZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGV4cG9ydFJvdXRlcyhhcHApO1xyXG4gICAgaGVhbHRoUm91dGVzKGFwcCk7XHJcbiAgICB1aVJvdXRlcyhhcHApO1xyXG4gICAgdmVyc2lvbkNoYW5nZVJvdXRlcyhhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCB0aGUgY2VudHJhbGl6ZWQgZXJyb3IgaGFuZGxlclxyXG4gICAgZXJyb3JNaWRkbGV3YXJlKGFwcCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tzZXJ2ZXJdIENvdWxkIG5vdCBjb25maWd1cmUgYW5kIHN0YXJ0IHRoZSBzZXJ2ZXIuJyxcclxuICAgICAgNTAwXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbG9zZXMgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gY2xvc2VTZXJ2ZXJzKCkge1xyXG4gIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBzZXJ2ZXJzIHdvcmtpbmdcclxuICBpZiAoYWN0aXZlU2VydmVycy5zaXplID4gMCkge1xyXG4gICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zaW5nIGFsbCBzZXJ2ZXJzLmApO1xyXG5cclxuICAgIC8vIENsb3NlIGVhY2ggb25lIG9mIHNlcnZlcnNcclxuICAgIGZvciAoY29uc3QgW3BvcnQsIHNlcnZlcl0gb2YgYWN0aXZlU2VydmVycykge1xyXG4gICAgICBzZXJ2ZXIuY2xvc2UoKCkgPT4ge1xyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuZGVsZXRlKHBvcnQpO1xyXG4gICAgICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2VkIHNlcnZlciBvbiBwb3J0OiAke3BvcnR9LmApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0U2VydmVyc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7QXJyYXk8T2JqZWN0Pn0gU2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2VydmVycygpIHtcclxuICByZXR1cm4gYWN0aXZlU2VydmVycztcclxufVxyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldEV4cHJlc3NcclxuICpcclxuICogQHJldHVybnMge0V4cHJlc3N9IFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldEV4cHJlc3MoKSB7XHJcbiAgcmV0dXJuIGV4cHJlc3M7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gZ2V0QXBwXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtFeHByZXNzfSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXBwKCkge1xyXG4gIHJldHVybiBhcHA7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQGZ1bmN0aW9uIGVuYWJsZVJhdGVMaW1pdGluZ1xyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmF0ZUxpbWl0aW5nT3B0aW9ucyAtIFRoZSBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nXHJcbiAqIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIFRoaXMgb2JqZWN0IG1heSBpbmNsdWRlIGEgcGFydGlhbCBvciBjb21wbGV0ZSBzZXRcclxuICogb2YgdGhlIGByYXRlTGltaXRpbmdgIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nIHZhbHVlc1xyXG4gKiB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZW5hYmxlUmF0ZUxpbWl0aW5nKHJhdGVMaW1pdGluZ09wdGlvbnMpIHtcclxuICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgc2VydmVyOiB7XHJcbiAgICAgIHJhdGVMaW1pdGluZzogcmF0ZUxpbWl0aW5nT3B0aW9uc1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBTZXQgdGhlIHJhdGUgbGltaXRpbmcgb3B0aW9uc1xyXG4gIHJhdGVMaW1pdGluZ01pZGRsZXdhcmUoYXBwLCBvcHRpb25zLnNlcnZlci5yYXRlTGltaXRpbmdPcHRpb25zKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFwcGx5IG1pZGRsZXdhcmUocykgdG8gYSBzcGVjaWZpYyBwYXRoLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gdXNlXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb24ocykgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB1c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpIHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggR0VUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQGZ1bmN0aW9uIGdldFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKSB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIFBPU1QgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gcG9zdFxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9uKHMpIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gcG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcykge1xyXG4gIGFwcC5wb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEF0dGFjaCBlcnJvciBoYW5kbGVycyB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnNcclxuICpcclxuICogQHBhcmFtIHsoaHR0cC5TZXJ2ZXJ8aHR0cHMuU2VydmVyKX0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxyXG4gKi9cclxuZnVuY3Rpb24gX2F0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoc2VydmVyKSB7XHJcbiAgc2VydmVyLm9uKCdjbGllbnRFcnJvcicsIChlcnJvciwgc29ja2V0KSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDEsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9LCBkZXN0cm95aW5nIHNvY2tldC5gXHJcbiAgICApO1xyXG4gICAgc29ja2V0LmRlc3Ryb3koKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignY29ubmVjdGlvbicsIChzb2NrZXQpID0+IHtcclxuICAgIHNvY2tldC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU29ja2V0IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGNsb3NlU2VydmVycyxcclxuICBnZXRTZXJ2ZXJzLFxyXG4gIGdldEV4cHJlc3MsXHJcbiAgZ2V0QXBwLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IEhhbmRsZXMgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIEhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlciwgZW5zdXJpbmdcclxuICogcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzIHN1Y2ggYXMgYnJvd3NlciwgcGFnZXMsIHNlcnZlcnMsIGFuZCB0aW1lcnMuXHJcbiAqL1xyXG5cclxuaW1wb3J0IHsga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBjbGVhckFsbFRpbWVycyB9IGZyb20gJy4vdGltZXIuanMnO1xyXG5cclxuaW1wb3J0IHsgY2xvc2VTZXJ2ZXJzIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBjbGVhbnVwIG9wZXJhdGlvbnMgdG8gZW5zdXJlIGEgZ3JhY2VmdWwgc2h1dGRvd24gb2YgdGhlIHByb2Nlc3MuXHJcbiAqIFRoaXMgaW5jbHVkZXMgY2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgdGltZW91dHMvaW50ZXJ2YWxzLCBjbG9zaW5nIGFjdGl2ZVxyXG4gKiBzZXJ2ZXJzLCB0ZXJtaW5hdGluZyByZXNvdXJjZXMgKHBhZ2VzKSBvZiB0aGUgcG9vbCwgcG9vbCBpdHNlbGYsIGFuZCBjbG9zaW5nXHJcbiAqIHRoZSBicm93c2VyLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb24gc2h1dGRvd25DbGVhblVwXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbZXhpdENvZGU9MF0gLSBUaGUgZXhpdCBjb2RlIHRvIHVzZSB3aXRoIGBwcm9jZXNzLmV4aXQoKWAuXHJcbiAqIFRoZSBkZWZhdWx0IHZhbHVlIGlzIGAwYC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93bkNsZWFuVXAoZXhpdENvZGUgPSAwKSB7XHJcbiAgLy8gQXdhaXQgZnJlZWluZyBhbGwgcmVzb3VyY2VzXHJcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcclxuICAgIC8vIENsZWFyIGFsbCBvbmdvaW5nIGludGVydmFsc1xyXG4gICAgY2xlYXJBbGxUaW1lcnMoKSxcclxuXHJcbiAgICAvLyBHZXQgYXZhaWxhYmxlIHNlcnZlciBpbnN0YW5jZXMgKEhUVFAvSFRUUFMpIGFuZCBjbG9zZSB0aGVtXHJcbiAgICBjbG9zZVNlcnZlcnMoKSxcclxuXHJcbiAgICAvLyBDbG9zZSBhbiBhY3RpdmUgcG9vbCBhbG9uZyB3aXRoIGl0cyB3b3JrZXJzIGFuZCB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gICAga2lsbFBvb2woKVxyXG4gIF0pO1xyXG5cclxuICAvLyBFeGl0IHByb2Nlc3Mgd2l0aCBhIGNvcnJlY3QgY29kZVxyXG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaHV0ZG93bkNsZWFuVXBcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjUsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFRoaXMgY29yZSBtb2R1bGUgaW5pdGlhbGl6ZXMgYW5kIG1hbmFnZXMgdGhlIEhpZ2hjaGFydHMgRXhwb3J0XHJcbiAqIFNlcnZlci4gVGhlIG1haW4gYGluaXRFeHBvcnRgIGZ1bmN0aW9uIGhhbmRsZXMgbG9nZ2luZywgc2NyaXB0IGNhY2hpbmcsXHJcbiAqIHJlc291cmNlIHBvb2xpbmcsIGJyb3dzZXIgc3RhcnR1cCwgYW5kIGVuc3VyZXMgZ3JhY2VmdWwgcHJvY2VzcyBjbGVhbnVwXHJcbiAqIG9uIGV4aXQuIEFkZGl0aW9uYWxseSwgaXQgcHJvdmlkZXMgQVBJIGZ1bmN0aW9ucyBmb3IgdXNpbmcgaXQgYXMgYSBOb2RlLmpzXHJcbiAqIG1vZHVsZSwgb2ZmZXJpbmcgZnVuY3Rpb25hbGl0aWVzIGZvciBwcm9jZXNzaW5nIG9wdGlvbnMsIGNvbmZpZ3VyaW5nXHJcbiAqIGFuZCBwZXJmb3JtaW5nIGV4cG9ydHMsIGFuZCBzZXR0aW5nIHVwIHNlcnZlci5cclxuICovXHJcblxyXG5pbXBvcnQgJ2NvbG9ycyc7XHJcblxyXG5pbXBvcnQgeyBjaGVja0NhY2hlIH0gZnJvbSAnLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7XHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvblxyXG59IGZyb20gJy4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCB1cGRhdGVPcHRpb25zLCBtYXBUb05ld09wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBpbml0TG9nZ2luZyxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZyxcclxuICBlbmFibGVGaWxlTG9nZ2luZyxcclxuICBzZXRMb2dMZXZlbFxyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2h1dGRvd25DbGVhblVwIH0gZnJvbSAnLi9yZXNvdXJjZVJlbGVhc2UuanMnO1xyXG5cclxuaW1wb3J0IHNlcnZlciBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiB0aGUgY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHJlc291cmNlIHBvb2wgb2NjdXIgZHVyaW5nIHRoaXNcclxuICogc3RhZ2UuXHJcbiAqXHJcbiAqIFRoaXMgZnVuY3Rpb24gbXVzdCBiZSBjYWxsZWQgYmVmb3JlIGF0dGVtcHRpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXRcclxuICogdXAgYSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBhc3luY1xyXG4gKiBAZnVuY3Rpb24gaW5pdEV4cG9ydFxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gaW5pdE9wdGlvbnMgLSBUaGUgYGluaXRPcHRpb25zYCBvYmplY3QsIHdoaWNoIG1heVxyXG4gKiBiZSBhIHBhcnRpYWwgb3IgY29tcGxldGUgc2V0IG9mIG9wdGlvbnMuIElmIHRoZSBvcHRpb25zIGFyZSBwYXJ0aWFsLCBtaXNzaW5nXHJcbiAqIHZhbHVlcyB3aWxsIGRlZmF1bHQgdG8gdGhlIGN1cnJlbnQgZ2xvYmFsIGNvbmZpZ3VyYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5pdEV4cG9ydChpbml0T3B0aW9ucykge1xyXG4gIC8vIEluaXQgYW5kIHVwZGF0ZSB0aGUgaW5zdGFuY2Ugb3B0aW9ucyBvYmplY3RcclxuICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyhpbml0T3B0aW9ucyk7XHJcblxyXG4gIC8vIFNldCB0aGUgYGFsbG93Q29kZUV4ZWN1dGlvbmAgcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24pO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgdGhlIGN1cnJlbnQgc3RhdHVzIG9mIGNhY2hlXHJcbiAgYXdhaXQgY2hlY2tDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMsIG9wdGlvbnMuc2VydmVyLnByb3h5KTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgcG9vbFxyXG4gIGF3YWl0IGluaXRQb29sKG9wdGlvbnMucG9vbCwgb3B0aW9ucy5wdXBwZXRlZXIuYXJncyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXHJcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nLFxyXG4gKiBhbmQgJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvbiBfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnNcclxuICovXHJcbmZ1bmN0aW9uIF9hdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdJTlQnXHJcbiAgcHJvY2Vzcy5vbignU0lHSU5UJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR1RFUk0nXHJcbiAgcHJvY2Vzcy5vbignU0lHVEVSTScsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFtwcm9jZXNzXSBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdIVVAnXHJcbiAgcHJvY2Vzcy5vbignU0lHSFVQJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgW3Byb2Nlc3NdIFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ3VuY2F1Z2h0RXhjZXB0aW9uJ1xyXG4gIHByb2Nlc3Mub24oJ3VuY2F1Z2h0RXhjZXB0aW9uJywgYXN5bmMgKGVycm9yLCBuYW1lKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbcHJvY2Vzc10gVGhlICR7bmFtZX0gZXJyb3IuYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoMSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICAvLyBTZXJ2ZXJcclxuICAuLi5zZXJ2ZXIsXHJcblxyXG4gIC8vIE9wdGlvbnNcclxuICBnZXRPcHRpb25zLFxyXG4gIHVwZGF0ZU9wdGlvbnMsXHJcbiAgbWFwVG9OZXdPcHRpb25zLFxyXG5cclxuICAvLyBFeHBvcnRpbmdcclxuICBpbml0RXhwb3J0LFxyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBiYXRjaEV4cG9ydCxcclxuICBzdGFydEV4cG9ydCxcclxuXHJcbiAgLy8gUmVsZWFzZVxyXG4gIGtpbGxQb29sLFxyXG4gIHNodXRkb3duQ2xlYW5VcCxcclxuXHJcbiAgLy8gTG9nc1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgc2V0TG9nTGV2ZWw6IGZ1bmN0aW9uIChsZXZlbCkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBsb2dnaW5nOiB7XHJcbiAgICAgICAgbGV2ZWxcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgZnVuY3Rpb25cclxuICAgIHNldExvZ0xldmVsKG9wdGlvbnMubG9nZ2luZy5sZXZlbCk7XHJcbiAgfSxcclxuICBlbmFibGVDb25zb2xlTG9nZ2luZzogZnVuY3Rpb24gKHRvQ29uc29sZSkge1xyXG4gICAgLy8gVXBkYXRlIHRoZSBpbnN0YW5jZSBvcHRpb25zIG9iamVjdFxyXG4gICAgY29uc3Qgb3B0aW9ucyA9IHVwZGF0ZU9wdGlvbnMoe1xyXG4gICAgICBsb2dnaW5nOiB7XHJcbiAgICAgICAgdG9Db25zb2xlXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICBlbmFibGVDb25zb2xlTG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcudG9Db25zb2xlKTtcclxuICB9LFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nOiBmdW5jdGlvbiAoZGVzdCwgZmlsZSwgdG9GaWxlKSB7XHJcbiAgICAvLyBVcGRhdGUgdGhlIGluc3RhbmNlIG9wdGlvbnMgb2JqZWN0XHJcbiAgICBjb25zdCBvcHRpb25zID0gdXBkYXRlT3B0aW9ucyh7XHJcbiAgICAgIGxvZ2dpbmc6IHtcclxuICAgICAgICBkZXN0LFxyXG4gICAgICAgIGZpbGUsXHJcbiAgICAgICAgdG9GaWxlXHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICBlbmFibGVGaWxlTG9nZ2luZyhcclxuICAgICAgb3B0aW9ucy5sb2dnaW5nLmRlc3QsXHJcbiAgICAgIG9wdGlvbnMubG9nZ2luZy5maWxlLFxyXG4gICAgICBvcHRpb25zLmxvZ2dpbmcudG9GaWxlXHJcbiAgICApO1xyXG4gIH1cclxufTtcclxuIl0sIm5hbWVzIjpbImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJhcmdzIiwidmFsdWUiLCJ0eXBlcyIsImVudkxpbmsiLCJjbGlOYW1lIiwiZGVzY3JpcHRpb24iLCJwcm9tcHRPcHRpb25zIiwidHlwZSIsInNlcGFyYXRvciIsImhpZ2hjaGFydHMiLCJ2ZXJzaW9uIiwiY2RuVXJsIiwiZm9yY2VGZXRjaCIsImNhY2hlUGF0aCIsImNvcmVTY3JpcHRzIiwiaW5zdHJ1Y3Rpb25zIiwibW9kdWxlU2NyaXB0cyIsImluZGljYXRvclNjcmlwdHMiLCJjdXN0b21TY3JpcHRzIiwiZXhwb3J0IiwiaW5maWxlIiwiaW5zdHIiLCJvcHRpb25zIiwic3ZnIiwiYmF0Y2giLCJvdXRmaWxlIiwiaGludCIsImNob2ljZXMiLCJjb25zdHIiLCJiNjQiLCJub0Rvd25sb2FkIiwiaGVpZ2h0Iiwid2lkdGgiLCJzY2FsZSIsImRlZmF1bHRIZWlnaHQiLCJkZWZhdWx0V2lkdGgiLCJkZWZhdWx0U2NhbGUiLCJtaW4iLCJtYXgiLCJnbG9iYWxPcHRpb25zIiwidGhlbWVPcHRpb25zIiwicmFzdGVyaXphdGlvblRpbWVvdXQiLCJjdXN0b21Mb2dpYyIsImFsbG93Q29kZUV4ZWN1dGlvbiIsImFsbG93RmlsZVJlc291cmNlcyIsImN1c3RvbUNvZGUiLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiaG9zdCIsInBvcnQiLCJ1cGxvYWRMaW1pdCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwibG9nZ2luZyIsImxldmVsIiwicm91bmQiLCJmaWxlIiwiZGVzdCIsInRvQ29uc29sZSIsInRvRmlsZSIsInVpIiwicm91dGUiLCJvdGhlciIsIm5vZGVFbnYiLCJsaXN0ZW5Ub1Byb2Nlc3NFeGl0cyIsIm5vTG9nbyIsImhhcmRSZXNldFBhZ2UiLCJicm93c2VyU2hlbGxNb2RlIiwiZGVidWciLCJoZWFkbGVzcyIsImRldnRvb2xzIiwibGlzdGVuVG9Db25zb2xlIiwiZHVtcGlvIiwic2xvd01vIiwiZGVidWdnaW5nUG9ydCIsImRvdGVudiIsImNvbmZpZyIsInYiLCJhcnJheSIsImZpbHRlckFycmF5IiwieiIsInN0cmluZyIsInRyYW5zZm9ybSIsInNwbGl0IiwibWFwIiwidHJpbSIsImZpbHRlciIsImluY2x1ZGVzIiwibGVuZ3RoIiwidW5kZWZpbmVkIiwiYm9vbGVhbiIsImVudW0iLCJ2YWx1ZXMiLCJyZWZpbmUiLCJtZXNzYWdlIiwicG9zaXRpdmVOdW0iLCJpc05hTiIsInBhcnNlRmxvYXQiLCJub25OZWdhdGl2ZU51bSIsIkNvbmZpZyIsIm9iamVjdCIsIlBVUFBFVEVFUl9BUkdTIiwiSElHSENIQVJUU19WRVJTSU9OIiwidGVzdCIsIkhJR0hDSEFSVFNfQ0ROX1VSTCIsInN0YXJ0c1dpdGgiLCJISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIIiwiSElHSENIQVJUU19DQUNIRV9QQVRIIiwiSElHSENIQVJUU19BRE1JTl9UT0tFTiIsIkhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTIiwiSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUyIsIkhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFMiLCJISUdIQ0hBUlRTX0NVU1RPTV9TQ1JJUFRTIiwiRVhQT1JUX0lORklMRSIsIkVYUE9SVF9JTlNUUiIsIkVYUE9SVF9PUFRJT05TIiwiRVhQT1JUX1NWRyIsIkVYUE9SVF9CQVRDSCIsIkVYUE9SVF9PVVRGSUxFIiwiRVhQT1JUX1RZUEUiLCJFWFBPUlRfQ09OU1RSIiwiRVhQT1JUX0I2NCIsIkVYUE9SVF9OT19ET1dOTE9BRCIsIkVYUE9SVF9IRUlHSFQiLCJFWFBPUlRfV0lEVEgiLCJFWFBPUlRfU0NBTEUiLCJFWFBPUlRfREVGQVVMVF9IRUlHSFQiLCJFWFBPUlRfREVGQVVMVF9XSURUSCIsIkVYUE9SVF9ERUZBVUxUX1NDQUxFIiwiRVhQT1JUX0dMT0JBTF9PUFRJT05TIiwiRVhQT1JUX1RIRU1FX09QVElPTlMiLCJFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTIiwiQ1VTVE9NX0xPR0lDX0NVU1RPTV9DT0RFIiwiQ1VTVE9NX0xPR0lDX0NBTExCQUNLIiwiQ1VTVE9NX0xPR0lDX1JFU09VUkNFUyIsIkNVU1RPTV9MT0dJQ19MT0FEX0NPTkZJRyIsIkNVU1RPTV9MT0dJQ19DUkVBVEVfQ09ORklHIiwiU0VSVkVSX0VOQUJMRSIsIlNFUlZFUl9IT1NUIiwiU0VSVkVSX1BPUlQiLCJTRVJWRVJfVVBMT0FEX0xJTUlUIiwiU0VSVkVSX0JFTkNITUFSS0lORyIsIlNFUlZFUl9QUk9YWV9IT1NUIiwiU0VSVkVSX1BST1hZX1BPUlQiLCJTRVJWRVJfUFJPWFlfVElNRU9VVCIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4iLCJTRVJWRVJfU1NMX0VOQUJMRSIsIlNFUlZFUl9TU0xfRk9SQ0UiLCJTRVJWRVJfU1NMX1BPUlQiLCJTRVJWRVJfU1NMX0NFUlRfUEFUSCIsIlBPT0xfTUlOX1dPUktFUlMiLCJQT09MX01BWF9XT1JLRVJTIiwiUE9PTF9XT1JLX0xJTUlUIiwiUE9PTF9BQ1FVSVJFX1RJTUVPVVQiLCJQT09MX0NSRUFURV9USU1FT1VUIiwiUE9PTF9ERVNUUk9ZX1RJTUVPVVQiLCJQT09MX0lETEVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1JFVFJZX0lOVEVSVkFMIiwiUE9PTF9SRUFQRVJfSU5URVJWQUwiLCJQT09MX0JFTkNITUFSS0lORyIsIkxPR0dJTkdfTEVWRUwiLCJMT0dHSU5HX0ZJTEUiLCJMT0dHSU5HX0RFU1QiLCJMT0dHSU5HX1RPX0NPTlNPTEUiLCJMT0dHSU5HX1RPX0ZJTEUiLCJVSV9FTkFCTEUiLCJVSV9ST1VURSIsIk9USEVSX05PREVfRU5WIiwiT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMiLCJPVEhFUl9OT19MT0dPIiwiT1RIRVJfSEFSRF9SRVNFVF9QQUdFIiwiT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFIiwiREVCVUdfRU5BQkxFIiwiREVCVUdfSEVBRExFU1MiLCJERUJVR19ERVZUT09MUyIsIkRFQlVHX0xJU1RFTl9UT19DT05TT0xFIiwiREVCVUdfRFVNUElPIiwiREVCVUdfU0xPV19NTyIsIkRFQlVHX0RFQlVHR0lOR19QT1JUIiwiZW52cyIsInBhcnRpYWwiLCJwYXJzZSIsInByb2Nlc3MiLCJlbnYiLCJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInRhZ05hbWUiLCJ0b1VwcGVyQ2FzZSIsInNyYyIsImJhc2VVUkkiLCJkZWVwQ29weSIsIm9iakFyciIsIm9iakFyckNvcHkiLCJBcnJheSIsImlzQXJyYXkiLCJrZXkiLCJPYmplY3QiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJnZXRBYnNvbHV0ZVBhdGgiLCJwYXRoIiwiaXNBYnNvbHV0ZSIsIm5vcm1hbGl6ZSIsInJlc29sdmUiLCJnZXRCYXNlNjQiLCJpbnB1dCIsIkJ1ZmZlciIsImZyb20iLCJ0b1N0cmluZyIsImdldE5ld0RhdGUiLCJEYXRlIiwiZ2V0TmV3RGF0ZVRpbWUiLCJnZXRUaW1lIiwiaXNPYmplY3QiLCJpdGVtIiwiaXNPYmplY3RFbXB0eSIsImtleXMiLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwic29tZSIsInBhdHRlcm4iLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwiaHJ0aW1lIiwiYmlnaW50IiwiTnVtYmVyIiwicm91bmROdW1iZXIiLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwiTWF0aCIsInBvdyIsImNvbG9ycyIsInBhdGhDcmVhdGVkIiwicGF0aFRvTG9nIiwibGV2ZWxzRGVzYyIsInRpdGxlIiwiY29sb3IiLCJsb2ciLCJuZXdMZXZlbCIsInRleHRzIiwicHJlZml4IiwiX2xvZ1RvRmlsZSIsImNvbnNvbGUiLCJhcHBseSIsImNvbmNhdCIsImxvZ1dpdGhTdGFjayIsImVycm9yIiwiY3VzdG9tTWVzc2FnZSIsIm1haW5NZXNzYWdlIiwic3RhY2tNZXNzYWdlIiwic3RhY2siLCJwdXNoIiwic2hpZnQiLCJpbml0TG9nZ2luZyIsImxvZ2dpbmdPcHRpb25zIiwic2V0TG9nTGV2ZWwiLCJlbmFibGVDb25zb2xlTG9nZ2luZyIsImVuYWJsZUZpbGVMb2dnaW5nIiwiaXNJbnRlZ2VyIiwiZXhpc3RzU3luYyIsIm1rZGlyU3luYyIsImpvaW4iLCJhcHBlbmRGaWxlIiwiX2luaXRPcHRpb25zIiwibmVzdGVkUHJvcHMiLCJfY3JlYXRlTmVzdGVkUHJvcHMiLCJhYnNvbHV0ZVByb3BzIiwiX2NyZWF0ZUFic29sdXRlUHJvcHMiLCJnZXRPcHRpb25zIiwiZ2V0Q29weSIsInVwZGF0ZU9wdGlvbnMiLCJuZXdPcHRpb25zIiwiX21lcmdlT3B0aW9ucyIsIm1hcFRvTmV3T3B0aW9ucyIsIm9sZE9wdGlvbnMiLCJlbnRyaWVzIiwicHJvcGVydGllc0NoYWluIiwicmVkdWNlIiwib2JqIiwicHJvcCIsImluZGV4IiwiaXNBbGxvd2VkQ29uZmlnIiwiYWxsb3dGdW5jdGlvbnMiLCJvYmplY3RDb25maWciLCJldmFsIiwiSlNPTiIsInN0cmluZ2lmaWVkT3B0aW9ucyIsIl9vcHRpb25zU3RyaW5naWZ5IiwicGFyc2VkT3B0aW9ucyIsIl8iLCJuYW1lIiwib3JpZ2luYWxPcHRpb25zIiwic3RyaW5naWZ5RnVuY3Rpb25zIiwic3RyaW5naWZ5IiwiZW5kc1dpdGgiLCJyZXBsYWNlQWxsIiwiRXJyb3IiLCJwcm9wQ2hhaW4iLCJmb3JFYWNoIiwiZW50cnkiLCJzdWJzdHJpbmciLCJhc3luYyIsImdldCIsInVybCIsInJlcXVlc3RPcHRpb25zIiwiUHJvbWlzZSIsInJlamVjdCIsIl9nZXRQcm90b2NvbE1vZHVsZSIsInJlc3BvbnNlIiwicmVzcG9uc2VEYXRhIiwib24iLCJjaHVuayIsInRleHQiLCJodHRwcyIsImh0dHAiLCJFeHBvcnRFcnJvciIsImNvbnN0cnVjdG9yIiwic3RhdHVzQ29kZSIsInN1cGVyIiwidGhpcyIsInNldEVycm9yIiwiY2FjaGUiLCJhY3RpdmVNYW5pZmVzdCIsInNvdXJjZXMiLCJoY1ZlcnNpb24iLCJjaGVja0NhY2hlIiwiaGlnaGNoYXJ0c09wdGlvbnMiLCJzZXJ2ZXJQcm94eU9wdGlvbnMiLCJmZXRjaGVkTW9kdWxlcyIsImdldENhY2hlUGF0aCIsIm1hbmlmZXN0UGF0aCIsInNvdXJjZVBhdGgiLCJyZWN1cnNpdmUiLCJfdXBkYXRlQ2FjaGUiLCJyZXF1ZXN0VXBkYXRlIiwibWFuaWZlc3QiLCJyZWFkRmlsZVN5bmMiLCJtb2R1bGVzIiwibW9kdWxlTWFwIiwibSIsIm51bWJlck9mTW9kdWxlcyIsIm1vZHVsZU5hbWUiLCJfZXh0cmFjdEhjVmVyc2lvbiIsIl9zYXZlQ29uZmlnVG9NYW5pZmVzdCIsImdldEhjVmVyc2lvbiIsInVwZGF0ZUhjVmVyc2lvbiIsIm5ld1ZlcnNpb24iLCJ3cml0ZUZpbGVTeW5jIiwiX2NvbmZpZ3VyZVJlcXVlc3QiLCJhbGwiLCJjcyIsIl9mZXRjaFNjcmlwdCIsIm1zIiwiaXMiLCJzY3JpcHQiLCJzaG91bGRUaHJvd0Vycm9yIiwiX2V4dHJhY3RNb2R1bGVOYW1lIiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiYWdlbnQiLCJIdHRwc1Byb3h5QWdlbnQiLCJjYWNoZVNvdXJjZXMiLCJpbmRleE9mIiwicmVwbGFjZSIsInNjcmlwdFBhdGgiLCJzZXR1cEhpZ2hjaGFydHMiLCJIaWdoY2hhcnRzIiwiYW5pbU9iamVjdCIsImR1cmF0aW9uIiwiY3JlYXRlQ2hhcnQiLCJleHBvcnRPcHRpb25zIiwiY3VzdG9tTG9naWNPcHRpb25zIiwic2V0T3B0aW9ucyIsIm1lcmdlIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJpc1JlbmRlckNvbXBsZXRlIiwiQ2hhcnQiLCJwcm9jZWVkIiwidXNlck9wdGlvbnMiLCJjYiIsImV4cG9ydGluZyIsImVuYWJsZWQiLCJwbG90T3B0aW9ucyIsInNlcmllcyIsImxhYmVsIiwidG9vbHRpcCIsImFuaW1hdGlvbiIsIm9uSGlnaGNoYXJ0c1JlbmRlciIsImFkZEV2ZW50IiwiU2VyaWVzIiwiY2hhcnQiLCJhZGRpdGlvbmFsT3B0aW9ucyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImltYWdlcyIsInF1ZXJ5U2VsZWN0b3JBbGwiLCJyYWNlIiwiaW1hZ2UiLCJjb21wbGV0ZSIsIm5hdHVyYWxIZWlnaHQiLCJhZGRFdmVudExpc3RlbmVyIiwib25jZSIsInNldFRpbWVvdXQiLCJkZWZhdWx0T3B0aW9ucyIsInBhZ2VUZW1wbGF0ZSIsImJyb3dzZXIiLCJjcmVhdGVCcm93c2VyIiwicHVwcGV0ZWVyQXJncyIsImVuYWJsZWREZWJ1ZyIsImRlYnVnT3B0aW9ucyIsImxhdW5jaE9wdGlvbnMiLCJ1c2VyRGF0YURpciIsImhhbmRsZVNJR0lOVCIsImhhbmRsZVNJR1RFUk0iLCJoYW5kbGVTSUdIVVAiLCJ3YWl0Rm9ySW5pdGlhbFBhZ2UiLCJkZWZhdWx0Vmlld3BvcnQiLCJ0cnlDb3VudCIsIm9wZW5Ccm93c2VyIiwibGF1bmNoIiwiY2xvc2VCcm93c2VyIiwiY29ubmVjdGVkIiwiY2xvc2UiLCJuZXdQYWdlIiwicG9vbFJlc291cmNlIiwicGFnZSIsInNldENhY2hlRW5hYmxlZCIsIl9zZXRQYWdlQ29udGVudCIsIl9zZXRQYWdlRXZlbnRzIiwiaXNDbG9zZWQiLCJjbGVhclBhZ2UiLCJoYXJkUmVzZXQiLCJnb3RvIiwid2FpdFVudGlsIiwiZXZhbHVhdGUiLCJib2R5IiwiaW5uZXJIVE1MIiwiaWQiLCJ3b3JrQ291bnQiLCJhZGRQYWdlUmVzb3VyY2VzIiwiaW5qZWN0ZWRSZXNvdXJjZXMiLCJpbmplY3RlZEpzIiwianMiLCJjb250ZW50IiwiZmlsZXMiLCJpc0xvY2FsIiwianNSZXNvdXJjZSIsImFkZFNjcmlwdFRhZyIsImluamVjdGVkQ3NzIiwiY3NzIiwiY3NzSW1wb3J0cyIsIm1hdGNoIiwiY3NzSW1wb3J0UGF0aCIsImNzc1Jlc291cmNlIiwiYWRkU3R5bGVUYWciLCJjbGVhclBhZ2VSZXNvdXJjZXMiLCJyZXNvdXJjZSIsImRpc3Bvc2UiLCJvbGRDaGFydHMiLCJjaGFydHMiLCJvbGRDaGFydCIsImRlc3Ryb3kiLCJzY3JpcHRzVG9SZW1vdmUiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsInN0eWxlc1RvUmVtb3ZlIiwibGlua3NUb1JlbW92ZSIsImVsZW1lbnQiLCJyZW1vdmUiLCJzZXRDb250ZW50IiwiY3NzVGVtcGxhdGUiLCJzdmdUZW1wbGF0ZSIsInB1cHBldGVlckV4cG9ydCIsImlzU1ZHIiwic2l6ZSIsIl9nZXRDaGFydFNpemUiLCJ4IiwieSIsIl9nZXRDbGlwUmVnaW9uIiwidmlld3BvcnRIZWlnaHQiLCJhYnMiLCJjZWlsIiwiY2hhcnRIZWlnaHQiLCJ2aWV3cG9ydFdpZHRoIiwiY2hhcnRXaWR0aCIsInJlc3VsdCIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJfY3JlYXRlU1ZHIiwiX2NyZWF0ZUltYWdlIiwiX2NyZWF0ZVBERiIsIiRldmFsIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidHJ1bmMiLCJzdmdFbGVtZW50IiwicXVlcnlTZWxlY3RvciIsImJhc2VWYWwiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJvdXRlckhUTUwiLCJjbGlwIiwic2NyZWVuc2hvdCIsImVuY29kaW5nIiwiZnVsbFBhZ2UiLCJvcHRpbWl6ZUZvclNwZWVkIiwiY2FwdHVyZUJleW9uZFZpZXdwb3J0IiwicXVhbGl0eSIsIm9taXRCYWNrZ3JvdW5kIiwiX3Jlc29sdmUiLCJlbXVsYXRlTWVkaWFUeXBlIiwicGRmIiwicG9vbFN0YXRzIiwiZXhwb3J0c0F0dGVtcHRlZCIsImV4cG9ydHNQZXJmb3JtZWQiLCJleHBvcnRzRHJvcHBlZCIsImV4cG9ydHNGcm9tU3ZnIiwiZXhwb3J0c0Zyb21PcHRpb25zIiwiZXhwb3J0c0Zyb21TdmdBdHRlbXB0cyIsImV4cG9ydHNGcm9tT3B0aW9uc0F0dGVtcHRzIiwidGltZVNwZW50IiwidGltZVNwZW50QXZlcmFnZSIsImluaXRQb29sIiwicG9vbE9wdGlvbnMiLCJQb29sIiwiX2ZhY3RvcnkiLCJhY3F1aXJlVGltZW91dE1pbGxpcyIsImNyZWF0ZVRpbWVvdXRNaWxsaXMiLCJkZXN0cm95VGltZW91dE1pbGxpcyIsImlkbGVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpcyIsInJlYXBJbnRlcnZhbE1pbGxpcyIsInByb3BhZ2F0ZUNyZWF0ZUVycm9yIiwiY2xlYXJTdGF0dXMiLCJfZXZlbnRJZCIsImluaXRpYWxSZXNvdXJjZXMiLCJpIiwiYWNxdWlyZSIsInByb21pc2UiLCJyZWxlYXNlIiwia2lsbFBvb2wiLCJ3b3JrZXIiLCJ1c2VkIiwiZGVzdHJveWVkIiwicG9zdFdvcmsiLCJ3b3JrZXJIYW5kbGUiLCJfZ2V0UG9vbEluZm8iLCJhY3F1aXJlQ291bnRlciIsInJlcXVlc3RJZCIsImV4cG9ydENvdW50ZXIiLCJleHBvcnRSZXN1bHQiLCJnZXRQb29sU3RhdHMiLCJnZXRQb29sSW5mb0pTT04iLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwibnVtRnJlZSIsImFsbENyZWF0ZWQiLCJwZW5kaW5nQWNxdWlyZXMiLCJudW1QZW5kaW5nQWNxdWlyZXMiLCJwZW5kaW5nQ3JlYXRlcyIsIm51bVBlbmRpbmdDcmVhdGVzIiwicGVuZGluZ1ZhbGlkYXRpb25zIiwibnVtUGVuZGluZ1ZhbGlkYXRpb25zIiwicGVuZGluZ0Rlc3Ryb3lzIiwiYWJzb2x1dGVBbGwiLCJjcmVhdGUiLCJ1dWlkIiwicmFuZG9tIiwic3RhcnREYXRlIiwidmFsaWRhdGUiLCJtYWluRnJhbWUiLCJkZXRhY2hlZCIsInJlbW92ZUFsbExpc3RlbmVycyIsInNhbml0aXplIiwiSlNET00iLCJET01QdXJpZnkiLCJBRERfVEFHUyIsInNpbmdsZUV4cG9ydCIsInN0YXJ0RXhwb3J0IiwiZGF0YSIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiYmF0Y2hSZXN1bHRzIiwiYWxsU2V0dGxlZCIsInJlYXNvbiIsImltYWdlT3B0aW9ucyIsImVuZENhbGxiYWNrIiwiZmlsZUNvbnRlbnQiLCJfZXhwb3J0RnJvbVN2ZyIsIl9leHBvcnRGcm9tT3B0aW9ucyIsImdldEFsbG93Q29kZUV4ZWN1dGlvbiIsInNldEFsbG93Q29kZUV4ZWN1dGlvbiIsImlucHV0VG9FeHBvcnQiLCJfcHJlcGFyZUV4cG9ydCIsIl9maXhDb25zdHIiLCJfZml4VHlwZSIsIl9maXhPdXRmaWxlIiwiX2hhbmRsZUN1c3RvbUxvZ2ljIiwiX2hhbmRsZUdsb2JhbEFuZFRoZW1lIiwiX2hhbmRsZVNpemUiLCJfY2hlY2tEYXRhU2l6ZSIsImZpeGVkQ29uc3RyIiwidG9Mb3dlckNhc2UiLCJtaW1lVHlwZXMiLCJmb3JtYXRzIiwib3V0VHlwZSIsInBvcCIsImZpbmQiLCJ0Iiwib3B0aW9uc0NoYXJ0Iiwib3B0aW9uc0V4cG9ydGluZyIsImdsb2JhbE9wdGlvbnNDaGFydCIsImdsb2JhbE9wdGlvbnNFeHBvcnRpbmciLCJ0aGVtZU9wdGlvbnNDaGFydCIsInRoZW1lT3B0aW9uc0V4cG9ydGluZyIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJfaGFuZGxlUmVzb3VyY2VzIiwiX2hhbmRsZUN1c3RvbUNvZGUiLCJoYW5kbGVkUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiY29ycmVjdFJlc291cmNlcyIsInByb3BOYW1lIiwiaXNDYWxsYmFjayIsIm9wdGlvbnNOYW1lIiwidG90YWxTaXplIiwiYnl0ZUxlbmd0aCIsInRvRml4ZWQiLCJ0aW1lcklkcyIsImFkZFRpbWVyIiwiY2xlYXJBbGxUaW1lcnMiLCJjbGVhckludGVydmFsIiwiY2xlYXJUaW1lb3V0IiwibG9nRXJyb3JNaWRkbGV3YXJlIiwicmVxdWVzdCIsIm5leHQiLCJyZXR1cm5FcnJvck1pZGRsZXdhcmUiLCJzdGF0dXMiLCJqc29uIiwiZXJyb3JNaWRkbGV3YXJlIiwiYXBwIiwidXNlIiwicmF0ZUxpbWl0aW5nTWlkZGxld2FyZSIsInJhdGVMaW1pdGluZ09wdGlvbnMiLCJyYXRlT3B0aW9ucyIsImxpbWl0ZXIiLCJyYXRlTGltaXQiLCJ3aW5kb3dNcyIsImxpbWl0IiwiZGVsYXlNcyIsImhhbmRsZXIiLCJmb3JtYXQiLCJzZW5kIiwiZGVmYXVsdCIsInNraXAiLCJxdWVyeSIsImFjY2Vzc190b2tlbiIsImNvbnRlbnRUeXBlTWlkZGxld2FyZSIsImNvbnRlbnRUeXBlIiwiaGVhZGVycyIsInJlcXVlc3RCb2R5TWlkZGxld2FyZSIsImNvbm5lY3Rpb24iLCJyZW1vdGVBZGRyZXNzIiwidmFsaWRhdGVkT3B0aW9ucyIsInBhcmFtcyIsImZpbGVuYW1lIiwidmFsaWRhdGlvbk1pZGRsZXdhcmUiLCJwb3N0IiwicmV2ZXJzZWRNaW1lIiwicG5nIiwianBlZyIsImdpZiIsInJlcXVlc3RFeHBvcnQiLCJyZXF1ZXN0Q291bnRlciIsImNvbm5lY3Rpb25BYm9ydGVkIiwic29ja2V0IiwiaGFkRXJyb3JzIiwiaGVhZGVyIiwiYXR0YWNobWVudCIsImV4cG9ydFJvdXRlcyIsInNlcnZlclN0YXJ0VGltZSIsInBhY2thZ2VGaWxlIiwic3VjY2Vzc1JhdGVzIiwicmVjb3JkSW50ZXJ2YWwiLCJ3aW5kb3dTaXplIiwiX2NhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2UiLCJhIiwiYiIsIl9zdGFydFN1Y2Nlc3NSYXRlIiwic2V0SW50ZXJ2YWwiLCJzdGF0cyIsInN1Y2Nlc3NSYXRpbyIsImhlYWx0aFJvdXRlcyIsInBlcmlvZCIsIm1vdmluZ0F2ZXJhZ2UiLCJib290VGltZSIsInVwdGltZSIsImZsb29yIiwic2VydmVyVmVyc2lvbiIsImhpZ2hjaGFydHNWZXJzaW9uIiwiYXZlcmFnZUV4cG9ydFRpbWUiLCJhdHRlbXB0ZWRFeHBvcnRzIiwicGVyZm9ybWVkRXhwb3J0cyIsImZhaWxlZEV4cG9ydHMiLCJzdWNlc3NSYXRpbyIsInN2Z0V4cG9ydHMiLCJqc29uRXhwb3J0cyIsInN2Z0V4cG9ydHNBdHRlbXB0cyIsImpzb25FeHBvcnRzQXR0ZW1wdHMiLCJ1aVJvdXRlcyIsInNlbmRGaWxlIiwiYWNjZXB0UmFuZ2VzIiwidmVyc2lvbkNoYW5nZVJvdXRlcyIsImFkbWluVG9rZW4iLCJ0b2tlbiIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJPcHRpb25zIiwidXBsb2FkTGltaXRCeXRlcyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRTaXplIiwiZGlzYWJsZSIsImNvcnMiLCJtZXRob2RzIiwic2V0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsInN0YXRpYyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJfYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyIsImxpc3RlbiIsImNlcnQiLCJodHRwc1NlcnZlciIsImNsb3NlU2VydmVycyIsImRlbGV0ZSIsImdldFNlcnZlcnMiLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwiZW5hYmxlUmF0ZUxpbWl0aW5nIiwibWlkZGxld2FyZXMiLCJzaHV0ZG93bkNsZWFuVXAiLCJleGl0Q29kZSIsImV4aXQiLCJpbml0RXhwb3J0IiwiaW5pdE9wdGlvbnMiLCJfYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMiLCJjb2RlIl0sIm1hcHBpbmdzIjoid2xCQWlDQSxNQUFNQSxjQUFnQixDQUNwQkMsVUFBVyxDQUNUQyxLQUFNLENBQ0pDLE1BQU8sQ0FDTCxtQ0FDQSxrQkFDQSwwQ0FDQSwyQkFDQSxrQ0FDQSxrQ0FDQSx3Q0FDQSwyQ0FDQSxx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 diff --git a/dist/index.esm.js b/dist/index.esm.js index c42623e5..361e3794 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -1,2 +1,2 @@ -import"colors";import{readFileSync,existsSync,mkdirSync,appendFile,writeFileSync}from"fs";import{isAbsolute,normalize,resolve,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import{fileURLToPath}from"url";import dotenv from"dotenv";import{z}from"zod";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const __dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e}`}function fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function getAbsolutePath(e){return isAbsolute(e)?normalize(e):resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}function wrapAround(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?wrapAround(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"",logging.file=t||"")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}},nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),globalOptions=_initOptions(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty array.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}async function fetch(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}setStatus(e){return this.statusCode=e,this}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkAndUpdateCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=extractVersion(cache.sources))}await _saveConfigToManifest(e,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHighchartsVersion(){return cache.hcVersion}async function updateHighchartsVersion(e){const t=updateOptions({highcharts:{version:e}});await checkAndUpdateCache(t.highcharts,t.server.proxy)}function extractVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _fetchAndProcessScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await fetch(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}async function _saveConfigToManifest(e,t={}){const o={version:e.version,modules:t};cache.activeManifest=o,log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _fetchScripts(e,t,o,r,n){let i;const s=r.host,a=r.port;if(s&&a)try{i=new HttpsProxyAgent({host:s,port:a})}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}const l=i?{agent:i,timeout:r.timeout}:{},c=[...e.map((e=>_fetchAndProcessScript(`${e}`,l,n,!0))),...t.map((e=>_fetchAndProcessScript(`${e}`,l,n))),...o.map((e=>_fetchAndProcessScript(`${e}`,l)))];return(await Promise.all(c)).join(";\n")}async function _updateCache(e,t,o){const r="latest"===e.version?null:`${e.version}`,n=e.cdnUrl||cache.cdnUrl;try{const i={};return log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`),cache.sources=await _fetchScripts([...e.coreScripts.map((e=>r?`${n}/${r}/${e}`:`${n}/${e}`))],[...e.moduleScripts.map((e=>"map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`)),...e.indicatorScripts.map((e=>r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`))],e.customScripts,t,i),cache.hcVersion=extractVersion(cache.sources),writeFileSync(o,cache.sources),i}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=new Function(`return ${e.globalOptions}`)(),p=n(!1,l,a,s),u=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a),c&&r(c),Highcharts[e.constr]("container",p,u);const g=o();for(const e in g)"function"!=typeof g[e]&&delete g[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const template=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){let n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(template,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=n?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(t.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);const r=getNewDateTime();log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const n=measureTime(),i=await puppeteerExport(t.page,e.export,e.customLogic);if(i instanceof Error)throw"Rasterization timeout"===i.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===i.name||"Rasterization timeout"===i.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(i):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${n()}ms.`).setError(i);e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${n()}ms.`),pool.release(t);const s=getNewDateTime()-r;return poolStats.timeSpent+=s,poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${s}ms.`),{result:i,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.options=null,t.export.instr=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.options=null,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.type=fixType(t.type,t.outfile),t.outfile=fixOutfile(t.type,t.outfile),t.constr=fixConstr(t.constr),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o,o.allowCodeExecution),_handleGlobalAndTheme(t,o.allowFileResources,o.allowCodeExecution),e.export={...t,..._findChartSize(t)},_checkDataSize({export:t,customLogic:o}),postWork(e)}function _findChartSize(e){const{chart:t,exporting:o}=isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2),l={height:e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,width:e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,scale:a};for(let[e,t]of Object.entries(l))l[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return l}function _handleCustomLogic(e,t){if(t){if("string"==typeof e.resources)e.resources=_handleResources(e.resources,e.allowFileResources,!0);else if(!e.resources)try{e.resources=_handleResources(readFileSync(getAbsolutePath("resources.json"),"utf8"),e.allowFileResources,!0)}catch(e){log(2,"[chart] Unable to load the default `resources.json` file.")}try{e.customCode=wrapAround(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=wrapAround(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){const r=["js","css","files"];let n=e,i=!1;if(t&&e.endsWith(".json"))try{n=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o)}catch{return null}else n=isAllowedConfig(e,!1,o),n&&!t&&delete n.files;for(const e in n)r.includes(e)?i||(i=!0):delete n[e];return i?(n.files&&(n.files=n.files.map((e=>e.trim())),(!n.files||n.files.length<=0)&&delete n.files),n):null}function _handleGlobalAndTheme(e,t,o){["globalOptions","themeOptions"].forEach((r=>{try{e[r]&&(t&&"string"==typeof e[r]&&e[r].endsWith(".json")?e[r]=isAllowedConfig(readFileSync(getAbsolutePath(e[r]),"utf8"),!0,o):e[r]=isAllowedConfig(e[r],!0,o))}catch(t){logWithStack(2,t,`[chart] The \`${r}\` cannot be loaded.`),e[r]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}function _checkDataSize(e){const t=Buffer.byteLength(JSON.stringify(e),"utf-8");if(log(3,`[chart] The current total size of the data for the export process is around ${(t/1048576).toFixed(2)}MB.`),t>=104857600)throw new ExportError("[chart] The data for the export process exceeds 100MB limit.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`[validation] Request [${r}] - 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`[validation] Request [${r}] - 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHighchartsVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: `HIGHCHARTS_ADMIN_TOKEN` is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHighchartsVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHighchartsVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkAndUpdateCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; +import"colors";import{existsSync,mkdirSync,appendFile,readFileSync,writeFileSync}from"fs";import{normalize,resolve,isAbsolute,join}from"path";import{HttpsProxyAgent}from"https-proxy-agent";import dotenv from"dotenv";import{z}from"zod";import{fileURLToPath}from"url";import http from"http";import https from"https";import{Pool}from"tarn";import{v4}from"uuid";import puppeteer from"puppeteer";import DOMPurify from"dompurify";import{JSDOM}from"jsdom";import cors from"cors";import express from"express";import multer from"multer";import rateLimit from"express-rate-limit";const defaultConfig={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],types:["string[]"],envLink:"PUPPETEER_ARGS",cliName:"puppeteerArgs",description:"Array of Puppeteer arguments",promptOptions:{type:"list",separator:";"}}},highcharts:{version:{value:"latest",types:["string"],envLink:"HIGHCHARTS_VERSION",description:"Highcharts version",promptOptions:{type:"text"}},cdnUrl:{value:"https://code.highcharts.com",types:["string"],envLink:"HIGHCHARTS_CDN_URL",description:"CDN URL for Highcharts scripts",promptOptions:{type:"text"}},forceFetch:{value:!1,types:["boolean"],envLink:"HIGHCHARTS_FORCE_FETCH",description:"Flag to refetch scripts after each server rerun",promptOptions:{type:"toggle"}},cachePath:{value:".cache",types:["string"],envLink:"HIGHCHARTS_CACHE_PATH",description:"Directory path for cached Highcharts scripts",promptOptions:{type:"text"}},coreScripts:{value:["highcharts","highcharts-more","highcharts-3d"],types:["string[]"],envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"Highcharts core scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},moduleScripts:{value:["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","overlapping-datalabels","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"],types:["string[]"],envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"Highcharts module scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},indicatorScripts:{value:["indicators-all"],types:["string[]"],envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"Highcharts indicator scripts to fetch",promptOptions:{type:"multiselect",instructions:"Space: Select specific, A: Select all, Enter: Confirm"}},customScripts:{value:["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"],types:["string[]"],envLink:"HIGHCHARTS_CUSTOM_SCRIPTS",description:"Additional custom scripts or dependencies to fetch",promptOptions:{type:"list",separator:";"}}},export:{infile:{value:null,types:["string","null"],envLink:"EXPORT_INFILE",description:"Input filename with type, formatted correctly as JSON or SVG",promptOptions:{type:"text"}},instr:{value:null,types:["Object","string","null"],envLink:"EXPORT_INSTR",description:"Overrides the `infile` with JSON, stringified JSON, or SVG input",promptOptions:{type:"text"}},options:{value:null,types:["Object","string","null"],envLink:"EXPORT_OPTIONS",description:"Alias for the `instr` option",promptOptions:{type:"text"}},svg:{value:null,types:["string","null"],envLink:"EXPORT_SVG",description:"SVG string representation of the chart to render",promptOptions:{type:"text"}},batch:{value:null,types:["string","null"],envLink:"EXPORT_BATCH",description:'Batch job string with input/output pairs: "in=out;in=out;..."',promptOptions:{type:"text"}},outfile:{value:null,types:["string","null"],envLink:"EXPORT_OUTFILE",description:"Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option",promptOptions:{type:"text"}},type:{value:"png",types:["string"],envLink:"EXPORT_TYPE",description:"File export format. Can be jpeg, png, pdf, or svg",promptOptions:{type:"select",hint:"Default: png",choices:["png","jpeg","pdf","svg"]}},constr:{value:"chart",types:["string"],envLink:"EXPORT_CONSTR",description:"Chart constructor. Can be chart, stockChart, mapChart, or ganttChart",promptOptions:{type:"select",hint:"Default: chart",choices:["chart","stockChart","mapChart","ganttChart"]}},b64:{value:!1,types:["boolean"],envLink:"EXPORT_B64",description:"Whether or not to the chart should be received in Base64 format instead of binary",promptOptions:{type:"toggle"}},noDownload:{value:!1,types:["boolean"],envLink:"EXPORT_NO_DOWNLOAD",description:"Whether or not to include or exclude attachment headers in the response",promptOptions:{type:"toggle"}},height:{value:null,types:["number","null"],envLink:"EXPORT_HEIGHT",description:"Height of the exported chart, overrides chart settings",promptOptions:{type:"number"}},width:{value:null,types:["number","null"],envLink:"EXPORT_WIDTH",description:"Width of the exported chart, overrides chart settings",promptOptions:{type:"number"}},scale:{value:null,types:["number","null"],envLink:"EXPORT_SCALE",description:"Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0",promptOptions:{type:"number"}},defaultHeight:{value:400,types:["number"],envLink:"EXPORT_DEFAULT_HEIGHT",description:"Default height of the exported chart if not set",promptOptions:{type:"number"}},defaultWidth:{value:600,types:["number"],envLink:"EXPORT_DEFAULT_WIDTH",description:"Default width of the exported chart if not set",promptOptions:{type:"number"}},defaultScale:{value:1,types:["number"],envLink:"EXPORT_DEFAULT_SCALE",description:"Default scale of the exported chart if not set. Ranges from 0.1 to 5.0",promptOptions:{type:"number",min:.1,max:5}},globalOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_GLOBAL_OPTIONS",description:"JSON, stringified JSON or filename with global options for Highcharts.setOptions",promptOptions:{type:"text"}},themeOptions:{value:null,types:["Object","string","null"],envLink:"EXPORT_THEME_OPTIONS",description:"JSON, stringified JSON or filename with theme options for Highcharts.setOptions",promptOptions:{type:"text"}},rasterizationTimeout:{value:1500,types:["number"],envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"Milliseconds to wait for webpage rendering",promptOptions:{type:"number"}}},customLogic:{allowCodeExecution:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Allows or disallows execution of arbitrary code during exporting",promptOptions:{type:"toggle"}},allowFileResources:{value:!1,types:["boolean"],envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Allows or disallows injection of filesystem resources (disabled in server mode)",promptOptions:{type:"toggle"}},customCode:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CUSTOM_CODE",description:"Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename",promptOptions:{type:"text"}},callback:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CALLBACK",description:"JavaScript code to run during construction. Can be a function or a .js filename",promptOptions:{type:"text"}},resources:{value:null,types:["Object","string","null"],envLink:"CUSTOM_LOGIC_RESOURCES",description:"Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections",promptOptions:{type:"text"}},loadConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_LOAD_CONFIG",legacyName:"fromFile",description:"File with a pre-defined configuration to use",promptOptions:{type:"text"}},createConfig:{value:null,types:["string","null"],envLink:"CUSTOM_LOGIC_CREATE_CONFIG",description:"Prompt-based option setting, saved to a provided config file",promptOptions:{type:"text"}}},server:{enable:{value:!1,types:["boolean"],envLink:"SERVER_ENABLE",cliName:"enableServer",description:"Starts the server when true",promptOptions:{type:"toggle"}},host:{value:"0.0.0.0",types:["string"],envLink:"SERVER_HOST",description:"Hostname of the server",promptOptions:{type:"text"}},port:{value:7801,types:["number"],envLink:"SERVER_PORT",description:"Port number for the server",promptOptions:{type:"number"}},uploadLimit:{value:3,types:["number"],envLink:"SERVER_UPLOAD_LIMIT",description:"Maximum request body size in MB",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Displays or not action durations in milliseconds during server requests",promptOptions:{type:"toggle"}},proxy:{host:{value:null,types:["string","null"],envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"Host of the proxy server, if applicable",promptOptions:{type:"text"}},port:{value:null,types:["number","null"],envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"Port of the proxy server, if applicable",promptOptions:{type:"number"}},timeout:{value:5e3,types:["number"],envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"Timeout in milliseconds for the proxy server, if applicable",promptOptions:{type:"number"}}},rateLimiting:{enable:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables or disables rate limiting on the server",promptOptions:{type:"toggle"}},maxRequests:{value:10,types:["number"],envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"Maximum number of requests allowed per minute",promptOptions:{type:"number"}},window:{value:1,types:["number"],envLink:"SERVER_RATE_LIMITING_WINDOW",description:"Time window in minutes for rate limiting",promptOptions:{type:"number"}},delay:{value:0,types:["number"],envLink:"SERVER_RATE_LIMITING_DELAY",description:"Delay duration between successive requests before reaching the limit",promptOptions:{type:"number"}},trustProxy:{value:!1,types:["boolean"],envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set to true if the server is behind a load balancer",promptOptions:{type:"toggle"}},skipKey:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Key to bypass the rate limiter, used with `skipToken`",promptOptions:{type:"text"}},skipToken:{value:null,types:["string","null"],envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Token to bypass the rate limiter, used with `skipKey`",promptOptions:{type:"text"}}},ssl:{enable:{value:!1,types:["boolean"],envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables SSL protocol",promptOptions:{type:"toggle"}},force:{value:!1,types:["boolean"],envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"Forces the server to use HTTPS only when true",promptOptions:{type:"toggle"}},port:{value:443,types:["number"],envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"Port for the SSL server",promptOptions:{type:"number"}},certPath:{value:null,types:["string","null"],envLink:"SERVER_SSL_CERT_PATH",cliName:"sslCertPath",legacyName:"sslPath",description:"Path to the SSL certificate/key file",promptOptions:{type:"text"}}}},pool:{minWorkers:{value:4,types:["number"],envLink:"POOL_MIN_WORKERS",description:"Minimum and initial number of pool workers to spawn",promptOptions:{type:"number"}},maxWorkers:{value:8,types:["number"],envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"Maximum number of pool workers to spawn",promptOptions:{type:"number"}},workLimit:{value:40,types:["number"],envLink:"POOL_WORK_LIMIT",description:"Number of tasks a worker can handle before restarting",promptOptions:{type:"number"}},acquireTimeout:{value:5e3,types:["number"],envLink:"POOL_ACQUIRE_TIMEOUT",description:"Timeout in milliseconds for acquiring a resource",promptOptions:{type:"number"}},createTimeout:{value:5e3,types:["number"],envLink:"POOL_CREATE_TIMEOUT",description:"Timeout in milliseconds for creating a resource",promptOptions:{type:"number"}},destroyTimeout:{value:5e3,types:["number"],envLink:"POOL_DESTROY_TIMEOUT",description:"Timeout in milliseconds for destroying a resource",promptOptions:{type:"number"}},idleTimeout:{value:3e4,types:["number"],envLink:"POOL_IDLE_TIMEOUT",description:"Timeout in milliseconds for destroying idle resources",promptOptions:{type:"number"}},createRetryInterval:{value:200,types:["number"],envLink:"POOL_CREATE_RETRY_INTERVAL",description:"Interval in milliseconds before retrying resource creation on failure",promptOptions:{type:"number"}},reaperInterval:{value:1e3,types:["number"],envLink:"POOL_REAPER_INTERVAL",description:"Interval in milliseconds to check and destroy idle resources",promptOptions:{type:"number"}},benchmarking:{value:!1,types:["boolean"],envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Shows statistics for the pool of resources",promptOptions:{type:"toggle"}}},logging:{level:{value:4,types:["number"],envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"Logging verbosity level",promptOptions:{type:"number",round:0,min:0,max:5}},file:{value:"highcharts-export-server.log",types:["string"],envLink:"LOGGING_FILE",cliName:"logFile",description:"Log file name. Requires `logToFile` and `logDest` to be set",promptOptions:{type:"text"}},dest:{value:"log",types:["string"],envLink:"LOGGING_DEST",cliName:"logDest",description:"Path to store log files. Requires `logToFile` to be set",promptOptions:{type:"text"}},toConsole:{value:!0,types:["boolean"],envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables console logging",promptOptions:{type:"toggle"}},toFile:{value:!0,types:["boolean"],envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables logging to a file",promptOptions:{type:"toggle"}}},ui:{enable:{value:!1,types:["boolean"],envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the UI for the export server",promptOptions:{type:"toggle"}},route:{value:"/",types:["string"],envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route for the UI",promptOptions:{type:"text"}}},other:{nodeEnv:{value:"production",types:["string"],envLink:"OTHER_NODE_ENV",description:"The Node.js environment type",promptOptions:{type:"text"}},listenToProcessExits:{value:!0,types:["boolean"],envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Whether or not to attach process.exit handlers",promptOptions:{type:"toggle"}},noLogo:{value:!1,types:["boolean"],envLink:"OTHER_NO_LOGO",description:"Display or skip printing the logo on startup",promptOptions:{type:"toggle"}},hardResetPage:{value:!1,types:["boolean"],envLink:"OTHER_HARD_RESET_PAGE",description:"Whether or not to reset the page content entirely",promptOptions:{type:"toggle"}},browserShellMode:{value:!0,types:["boolean"],envLink:"OTHER_BROWSER_SHELL_MODE",description:"Whether or not to set the browser to run in shell mode",promptOptions:{type:"toggle"}}},debug:{enable:{value:!1,types:["boolean"],envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser",promptOptions:{type:"toggle"}},headless:{value:!1,types:["boolean"],envLink:"DEBUG_HEADLESS",description:"Whether or not to set the browser to run in headless mode during debugging",promptOptions:{type:"toggle"}},devtools:{value:!1,types:["boolean"],envLink:"DEBUG_DEVTOOLS",description:"Enables or disables DevTools in headful mode",promptOptions:{type:"toggle"}},listenToConsole:{value:!1,types:["boolean"],envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Enables or disables listening to console messages from the browser",promptOptions:{type:"toggle"}},dumpio:{value:!1,types:["boolean"],envLink:"DEBUG_DUMPIO",description:"Redirects or not browser stdout and stderr to process.stdout and process.stderr",promptOptions:{type:"toggle"}},slowMo:{value:0,types:["number"],envLink:"DEBUG_SLOW_MO",description:"Delays Puppeteer operations by the specified milliseconds",promptOptions:{type:"number"}},debuggingPort:{value:9222,types:["number"],envLink:"DEBUG_DEBUGGING_PORT",description:"Port used for debugging",promptOptions:{type:"number"}}}};dotenv.config();const v={array:e=>z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),boolean:()=>z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),enum:e=>z.enum([...e,""]).transform((e=>""!==e?e:void 0)),string:()=>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)),positiveNum:()=>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)),nonNegativeNum:()=>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))},Config=z.object({PUPPETEER_ARGS:v.string(),HIGHCHARTS_VERSION: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: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_FORCE_FETCH:v.boolean(),HIGHCHARTS_CACHE_PATH:v.string(),HIGHCHARTS_ADMIN_TOKEN:v.string(),HIGHCHARTS_CORE_SCRIPTS:v.array(defaultConfig.highcharts.coreScripts.value),HIGHCHARTS_MODULE_SCRIPTS:v.array(defaultConfig.highcharts.moduleScripts.value),HIGHCHARTS_INDICATOR_SCRIPTS:v.array(defaultConfig.highcharts.indicatorScripts.value),HIGHCHARTS_CUSTOM_SCRIPTS:v.array(defaultConfig.highcharts.customScripts.value),EXPORT_INFILE:v.string(),EXPORT_INSTR:v.string(),EXPORT_OPTIONS:v.string(),EXPORT_SVG:v.string(),EXPORT_BATCH:v.string(),EXPORT_OUTFILE:v.string(),EXPORT_TYPE:v.enum(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:v.enum(["chart","stockChart","mapChart","ganttChart"]),EXPORT_B64:v.boolean(),EXPORT_NO_DOWNLOAD:v.boolean(),EXPORT_HEIGHT:v.positiveNum(),EXPORT_WIDTH:v.positiveNum(),EXPORT_SCALE:v.positiveNum(),EXPORT_DEFAULT_HEIGHT:v.positiveNum(),EXPORT_DEFAULT_WIDTH:v.positiveNum(),EXPORT_DEFAULT_SCALE:v.positiveNum(),EXPORT_GLOBAL_OPTIONS:v.string(),EXPORT_THEME_OPTIONS:v.string(),EXPORT_RASTERIZATION_TIMEOUT:v.nonNegativeNum(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:v.boolean(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:v.boolean(),CUSTOM_LOGIC_CUSTOM_CODE:v.string(),CUSTOM_LOGIC_CALLBACK:v.string(),CUSTOM_LOGIC_RESOURCES:v.string(),CUSTOM_LOGIC_LOAD_CONFIG:v.string(),CUSTOM_LOGIC_CREATE_CONFIG:v.string(),SERVER_ENABLE:v.boolean(),SERVER_HOST:v.string(),SERVER_PORT:v.positiveNum(),SERVER_UPLOAD_LIMIT:v.positiveNum(),SERVER_BENCHMARKING:v.boolean(),SERVER_PROXY_HOST:v.string(),SERVER_PROXY_PORT:v.positiveNum(),SERVER_PROXY_TIMEOUT:v.nonNegativeNum(),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(),SERVER_SSL_ENABLE:v.boolean(),SERVER_SSL_FORCE:v.boolean(),SERVER_SSL_PORT:v.positiveNum(),SERVER_SSL_CERT_PATH:v.string(),POOL_MIN_WORKERS:v.nonNegativeNum(),POOL_MAX_WORKERS:v.nonNegativeNum(),POOL_WORK_LIMIT:v.positiveNum(),POOL_ACQUIRE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_TIMEOUT:v.nonNegativeNum(),POOL_DESTROY_TIMEOUT:v.nonNegativeNum(),POOL_IDLE_TIMEOUT:v.nonNegativeNum(),POOL_CREATE_RETRY_INTERVAL:v.nonNegativeNum(),POOL_REAPER_INTERVAL:v.nonNegativeNum(),POOL_BENCHMARKING:v.boolean(),LOGGING_LEVEL: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:v.string(),LOGGING_DEST:v.string(),LOGGING_TO_CONSOLE:v.boolean(),LOGGING_TO_FILE:v.boolean(),UI_ENABLE:v.boolean(),UI_ROUTE:v.string(),OTHER_NODE_ENV:v.enum(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:v.boolean(),OTHER_NO_LOGO:v.boolean(),OTHER_HARD_RESET_PAGE:v.boolean(),OTHER_BROWSER_SHELL_MODE:v.boolean(),DEBUG_ENABLE:v.boolean(),DEBUG_HEADLESS:v.boolean(),DEBUG_DEVTOOLS:v.boolean(),DEBUG_LISTEN_TO_CONSOLE:v.boolean(),DEBUG_DUMPIO:v.boolean(),DEBUG_SLOW_MO:v.nonNegativeNum(),DEBUG_DEBUGGING_PORT:v.positiveNum()}),envs=Config.partial().parse(process.env),__dirname=fileURLToPath(new URL("../.",import.meta.url));function deepCopy(e){if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=deepCopy(e[o]));return t}function getAbsolutePath(e){return isAbsolute(e)?normalize(e):resolve(e)}function getBase64(e,t){return"pdf"===t||"svg"==t?Buffer.from(e,"utf8").toString("base64"):e}function getNewDate(){return(new Date).toString().split("(")[0].trim()}function getNewDateTime(){return(new Date).getTime()}function isObject(e){return"[object Object]"===Object.prototype.toString.call(e)}function isObjectEmpty(e){return"object"==typeof e&&!Array.isArray(e)&&null!==e&&0===Object.keys(e).length}function isPrivateRangeUrlFound(e){return[/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)))}function measureTime(){const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6}function roundNumber(e,t=1){const o=Math.pow(10,t||0);return Math.round(+e*o)/o}const colors=["red","yellow","blue","gray","green"],logging={toConsole:!0,toFile:!1,pathCreated:!1,pathToLog:"",levelsDesc:[{title:"error",color:colors[0]},{title:"warning",color:colors[1]},{title:"notice",color:colors[2]},{title:"verbose",color:colors[3]},{title:"benchmark",color:colors[4]}]};function log(...e){const[t,...o]=e,{levelsDesc:r,level:n}=logging;if(5!==t&&(0===t||t>n||n>r.length))return;const i=`${getNewDate()} [${r[t-1].title}] -`;logging.toFile&&_logToFile(o,i),logging.toConsole&&console.log.apply(void 0,[i.toString()[logging.levelsDesc[t-1].color]].concat(o))}function logWithStack(e,t,o){const r=o||t&&t.message||"",{level:n,levelsDesc:i}=logging;if(0===e||e>n||n>i.length)return;const s=`${getNewDate()} [${i[e-1].title}] -`,a=t&&t.stack,l=[r];a&&l.push("\n",a),logging.toFile&&_logToFile(l,s),logging.toConsole&&console.log.apply(void 0,[s.toString()[logging.levelsDesc[e-1].color]].concat([l.shift()[colors[e-1]],...l]))}function initLogging(e){const{level:t,dest:o,file:r,toConsole:n,toFile:i}=e;logging.pathCreated=!1,logging.pathToLog="",setLogLevel(t),enableConsoleLogging(n),enableFileLogging(o,r,i)}function setLogLevel(e){Number.isInteger(e)&&e>=0&&e<=logging.levelsDesc.length&&(logging.level=e)}function enableConsoleLogging(e){logging.toConsole=!!e}function enableFileLogging(e,t,o){logging.toFile=!!o,logging.toFile&&(logging.dest=e||"log",logging.file=t||"highcharts-export-server.log")}function _logToFile(e,t){logging.pathCreated||(!existsSync(getAbsolutePath(logging.dest))&&mkdirSync(getAbsolutePath(logging.dest)),logging.pathToLog=getAbsolutePath(join(logging.dest,logging.file)),logging.pathCreated=!0),appendFile(logging.pathToLog,[t].concat(e).join(" ")+"\n",(e=>{e&&logging.toFile&&logging.pathCreated&&(logging.toFile=!1,logging.pathCreated=!1,logWithStack(2,e,"[logger] Unable to write to log file."))}))}const globalOptions=_initOptions(defaultConfig),nestedProps=_createNestedProps(defaultConfig),absoluteProps=_createAbsoluteProps(defaultConfig);function getOptions(e=!0){return e?deepCopy(globalOptions):globalOptions}function updateOptions(e,t=!1){return _mergeOptions(getOptions(t),e)}function mapToNewOptions(e){const t={};if(isObject(e))for(const[o,r]of Object.entries(e)){const e=nestedProps[o]?nestedProps[o].split("."):[];e.reduce(((t,o,n)=>t[o]=e.length-1===n?r:t[o]||{}),t)}else log(2,"[config] No correct object with options was provided. Returning an empty object.");return t}function isAllowedConfig(config,toString=!1,allowFunctions=!1){try{if(!isObject(config)&&"string"!=typeof config)return null;const objectConfig="string"==typeof config?allowFunctions?eval(`(${config})`):JSON.parse(config):config,stringifiedOptions=_optionsStringify(objectConfig,allowFunctions,!1),parsedOptions=allowFunctions?JSON.parse(_optionsStringify(objectConfig,allowFunctions,!0),((_,value)=>"string"==typeof value&&value.startsWith("function")?eval(`(${value})`):value)):JSON.parse(stringifiedOptions);return toString?stringifiedOptions:parsedOptions}catch(e){return null}}function _initOptions(e){const t={};for(const[o,r]of Object.entries(e))Object.prototype.hasOwnProperty.call(r,"value")?void 0!==envs[r.envLink]&&null!==envs[r.envLink]?t[o]=envs[r.envLink]:t[o]=r.value:t[o]=_initOptions(r);return t}function _mergeOptions(e,t){if(isObject(e)&&isObject(t))for(const[o,r]of Object.entries(t))e[o]=isObject(r)&&!absoluteProps.includes(o)&&void 0!==e[o]?_mergeOptions(e[o],r):void 0!==r?r:e[o]||null;return e}function _optionsStringify(e,t,o){return JSON.stringify(e,((e,r)=>{if("string"==typeof r&&(r=r.trim()),"function"==typeof r||"string"==typeof r&&r.startsWith("function")&&r.endsWith("}")){if(t)return o?`"EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN"`:`EXP_FUN${(r+"").replaceAll(/\s+/g," ")}EXP_FUN`;throw new Error}return r})).replaceAll(o?/\\"EXP_FUN|EXP_FUN\\"/g:/"EXP_FUN|EXP_FUN"/g,"")}function _createNestedProps(e,t={},o=""){return Object.keys(e).forEach((r=>{const n=e[r];void 0===n.value?_createNestedProps(n,t,`${o}.${r}`):(t[n.cliName||r]=`${o}.${r}`.substring(1),void 0!==n.legacyName&&(t[n.legacyName]=`${o}.${r}`.substring(1)))})),t}function _createAbsoluteProps(e,t=[]){return Object.keys(e).forEach((o=>{const r=e[o];void 0===r.types?_createAbsoluteProps(r,t):r.types.includes("Object")&&t.push(o)})),t}async function get$1(e,t={}){return new Promise(((o,r)=>{_getProtocolModule(e).get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}function _getProtocolModule(e){return e.startsWith("https")?https:http}class ExportError extends Error{constructor(e,t){super(),this.message=e,this.stackMessage=e,t&&(this.statusCode=t)}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 cache={cdnUrl:"https://code.highcharts.com",activeManifest:{},sources:"",hcVersion:""};async function checkCache(e,t){try{let o;const r=getCachePath(),n=join(r,"manifest.json"),i=join(r,"sources.js");if(!existsSync(r)&&mkdirSync(r,{recursive:!0}),!existsSync(n)||e.forceFetch)log(3,"[cache] Fetching and caching Highcharts dependencies."),o=await _updateCache(e,t,i);else{let r=!1;const s=JSON.parse(readFileSync(n),"utf8");if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{coreScripts:a,moduleScripts:l,indicatorScripts:c}=e,p=a.length+l.length+c.length;s.version!==e.version?(log(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(s.modules||{}).length!==p?(log(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(l||[]).some((e=>{if(!s.modules[e])return log(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await _updateCache(e,t,i):(log(3,"[cache] Dependency cache is up to date, proceeding."),cache.sources=readFileSync(i,"utf8"),o=s.modules,cache.hcVersion=_extractHcVersion(cache.sources))}await _saveConfigToManifest(e.version,o)}catch(e){throw new ExportError("[cache] Could not configure cache and create or update the config manifest.",500).setError(e)}}function getHcVersion(){return cache.hcVersion}async function updateHcVersion(e){const t=updateOptions({highcharts:{version:e}});await checkCache(t.highcharts,t.server.proxy)}function getCachePath(){return getAbsolutePath(getOptions().highcharts.cachePath)}async function _saveConfigToManifest(e,t={}){cache.activeManifest={version:e,modules:t},log(3,"[cache] Writing a new manifest.");try{writeFileSync(join(getCachePath(),"manifest.json"),JSON.stringify(cache.activeManifest),"utf8")}catch(e){throw new ExportError("[cache] Error writing the cache manifest.",500).setError(e)}}async function _updateCache(e,t,o){try{const r="latest"===e.version?null:`${e.version}`;log(3,`[cache] Updating cache version to Highcharts: ${r||"latest"}.`);const n=e.cdnUrl||cache.cdnUrl,i=_configureRequest(t),s={};return cache.sources=(await Promise.all([...e.coreScripts.map((e=>_fetchScript(r?`${n}/${r}/${e}`:`${n}/${e}`,i,s,!0))),...e.moduleScripts.map((e=>_fetchScript("map"===e?r?`${n}/maps/${r}/modules/${e}`:`${n}/maps/modules/${e}`:r?`${n}/${r}/modules/${e}`:`${n}/modules/${e}`,i,s))),...e.indicatorScripts.map((e=>_fetchScript(r?`${n}/stock/${r}/indicators/${e}`:`${n}/stock/indicators/${e}`,i,s))),...e.customScripts.map((e=>_fetchScript(`${e}`,i)))])).join(";\n"),cache.hcVersion=_extractHcVersion(cache.sources),writeFileSync(o,cache.sources),s}catch(e){throw new ExportError("[cache] Unable to update the local Highcharts cache.",500).setError(e)}}async function _fetchScript(e,t,o,r=!1){e.endsWith(".js")&&(e=e.substring(0,e.length-3)),log(4,`[cache] Fetching script - ${e}.js`);const n=await get$1(`${e}.js`,t);if(200===n.statusCode&&"string"==typeof n.text){if(o){o[_extractModuleName(e)]=1}return n.text}if(r)throw new ExportError(`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`,404).setError(n);log(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`)}function _configureRequest(e){const t=e.host,o=e.port;if(t&&o)try{return{agent:new HttpsProxyAgent({host:t,port:o}),timeout:e.timeout}}catch(e){throw new ExportError("[cache] Could not create a Proxy Agent.",500).setError(e)}return{}}function _extractHcVersion(e){return e.substring(0,e.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim()}function _extractModuleName(e){return e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")}function setupHighcharts(){Highcharts.animObject=function(){return{duration:0}}}async function createChart(e,t){const{getOptions:o,setOptions:r,merge:n,wrap:i}=Highcharts;Highcharts.setOptionsObj=n(!1,{},o()),window.isRenderComplete=!1,i(Highcharts.Chart.prototype,"init",(function(e,t,o){((t=n(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,o])})),i(Highcharts.Series.prototype,"init",(function(e,t,o){e.apply(this,[t,o])}));const s={chart:{animation:!1,height:e.height,width:e.width},exporting:{enabled:!1}},a=new Function(`return ${e.instr}`)(),l=new Function(`return ${e.themeOptions}`)(),c=n(!1,l,a,s),p=t.callback?new Function(`return ${t.callback}`)():null;t.customCode&&new Function("options",t.customCode)(a);const u=new Function(`return ${e.globalOptions}`)();u&&r(u),Highcharts[e.constr]("container",c,p);const g=Array.from(document.querySelectorAll(".highcharts-container image"));await Promise.race([Promise.all(g.map((e=>e.complete&&0!==e.naturalHeight?Promise.resolve():new Promise((t=>e.addEventListener("load",t,{once:!0})))))),new Promise((e=>setTimeout(e,2e3)))]);const d=o();for(const e in d)"function"!=typeof d[e]&&delete d[e];r(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const pageTemplate=readFileSync(join(__dirname,"templates","template.html"),"utf8");let browser=null;async function createBrowser(e){const{debug:t,other:o}=getOptions(),{enable:r,...n}=t,i={headless:!o.browserShellMode||"shell",userDataDir:"tmp",args:e||[],handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...r&&n};if(!browser){let e=0;const t=async()=>{try{log(3,`[browser] Attempting to launch and get a browser instance (try ${++e}).`),browser=await puppeteer.launch(i)}catch(o){if(logWithStack(1,o,"[browser] Failed to launch a browser instance."),!(e<25))throw o;log(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await t()}};try{await t(),"shell"===i.headless&&log(3,"[browser] Launched browser in shell mode."),r&&log(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ExportError("[browser] Maximum retries to open a browser instance reached.",500).setError(e)}if(!browser)throw new ExportError("[browser] Cannot find a browser to open.",500)}return browser}async function closeBrowser(){browser&&browser.connected&&await browser.close(),browser=null,log(4,"[browser] Closed the browser.")}async function newPage(e){if(!browser||!browser.connected)throw new ExportError("[browser] Browser is not yet connected.",500);if(e.page=await browser.newPage(),await e.page.setCacheEnabled(!1),await _setPageContent(e.page),_setPageEvents(e.page),!e.page||e.page.isClosed())throw new ExportError("[browser] The page is invalid or closed.",400)}async function clearPage(e,t=!1){try{if(e.page&&!e.page.isClosed())return t?(await e.page.goto("about:blank",{waitUntil:"domcontentloaded"}),await _setPageContent(e.page)):await e.page.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(t){logWithStack(2,t,`[pool] Pool resource [${e.id}] - Content of the page could not be cleared.`),e.workCount=getOptions().pool.workLimit+1}return!1}async function addPageResources(e,t){const o=[],r=t.resources;if(r){const n=[];if(r.js&&n.push({content:r.js}),r.files)for(const e of r.files){const t=!e.startsWith("http");n.push(t?{content:readFileSync(getAbsolutePath(e),"utf8")}:{url:e})}for(const t of n)try{o.push(await e.addScriptTag(t))}catch(e){logWithStack(2,e,"[browser] The JS resource cannot be loaded.")}n.length=0;const i=[];if(r.css){const n=r.css.match(/@import\s*([^;]*);/g);if(n)for(let e of n)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?i.push({url:e}):t.allowFileResources&&i.push({path:getAbsolutePath(e)}));i.push({content:r.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of i)try{o.push(await e.addStyleTag(t))}catch(e){logWithStack(2,e,"[browser] The CSS resource cannot be loaded.")}i.length=0}}return o}async function clearPageResources(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"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))}catch(e){logWithStack(2,e,"[browser] Could not clear page's resources.")}}async function _setPageContent(e){await e.setContent(pageTemplate,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:join(getCachePath(),"sources.js")}),await e.evaluate(setupHighcharts)}function _setPageEvents(e){const{debug:t}=getOptions();e.on("pageerror",(async()=>{e.isClosed()})),t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}))}var cssTemplate=()=>"\n\nhtml, body {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\n#table-div, #sliders, #datatable, #controls, .ld-row {\n display: none;\n height: 0;\n}\n\n#chart-container {\n box-sizing: border-box;\n margin: 0;\n overflow: auto;\n font-size: 0;\n}\n\n#chart-container > figure, div {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n",svgTemplate=e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`;async function puppeteerExport(e,t,o){const r=[];try{let n=!1;if(t.svg){if(log(4,"[export] Treating as SVG input."),"svg"===t.type)return t.svg;n=!0,await e.setContent(svgTemplate(t.svg),{waitUntil:"domcontentloaded"})}else log(4,"[export] Treating as JSON config."),await e.evaluate(createChart,t,o);r.push(...await addPageResources(e,o));const i=await _getChartSize(e,n,t.scale),{x:s,y:a}=await _getClipRegion(e),l=Math.abs(Math.ceil(i.chartHeight||t.height)),c=Math.abs(Math.ceil(i.chartWidth||t.width));let p;switch(await e.setViewport({height:l,width:c,deviceScaleFactor:n?1:parseFloat(t.scale)}),t.type){case"svg":p=await _createSVG(e);break;case"png":case"jpeg":p=await _createImage(e,t.type,{width:c,height:l,x:s,y:a},t.rasterizationTimeout);break;case"pdf":p=await _createPDF(e,l,c,t.rasterizationTimeout);break;default:throw new ExportError(`[export] Unsupported output format: ${t.type}.`,400)}return await clearPageResources(e,r),p}catch(t){return await clearPageResources(e,r),t}}async function _getClipRegion(e){return e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:n}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(n>1?n:500)}}))}async function _getChartSize(e,t,o){return t?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),o=t.height.baseVal.value*e,r=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:o,chartWidth:r}}),parseFloat(o)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}}))}async function _createSVG(e){return e.$eval("#container svg:first-of-type",(e=>e.outerHTML))}async function _createImage(e,t,o,r){return Promise.race([e.screenshot({type:t,clip:o,encoding:"base64",fullPage:!1,optimizeForSpeed:!0,captureBeyondViewport:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ExportError("Rasterization timeout",408))),r||1500)))])}async function _createPDF(e,t,o,r){return await e.emulateMediaType("screen"),e.pdf({height:t+1,width:o,encoding:"base64",timeout:r||1500})}let pool=null;const poolStats={exportsAttempted:0,exportsPerformed:0,exportsDropped:0,exportsFromSvg:0,exportsFromOptions:0,exportsFromSvgAttempts:0,exportsFromOptionsAttempts:0,timeSpent:0,timeSpentAverage:0};async function initPool(e,t){await createBrowser(t);try{if(log(3,`[pool] Initializing pool with workers: min ${e.minWorkers}, max ${e.maxWorkers}.`),pool)return void log(4,"[pool] Already initialized, please kill it before creating a new one.");e.minWorkers>e.maxWorkers&&(e.minWorkers=e.maxWorkers),pool=new Pool({..._factory(e),min:e.minWorkers,max:e.maxWorkers,acquireTimeoutMillis:e.acquireTimeout,createTimeoutMillis:e.createTimeout,destroyTimeoutMillis:e.destroyTimeout,idleTimeoutMillis:e.idleTimeout,createRetryIntervalMillis:e.createRetryInterval,reapIntervalMillis:e.reaperInterval,propagateCreateError:!1}),pool.on("release",(async e=>{const t=await clearPage(e,!1);log(4,`[pool] Pool resource [${e.id}] - Releasing a worker. Clear page status: ${t}.`)})),pool.on("destroySuccess",((e,t)=>{log(4,`[pool] Pool resource [${t.id}] - Destroyed a worker successfully.`),t.page=null}));const t=[];for(let o=0;o{pool.release(e)})),log(3,"[pool] The pool is ready"+(t.length?` with ${t.length} initial resources waiting.`:"."))}catch(e){throw new ExportError("[pool] Could not configure and create the pool of workers.",500).setError(e)}}async function killPool(){if(log(3,"[pool] Killing pool with all workers and closing browser."),pool){for(const e of pool.used)pool.release(e.resource);pool.destroyed||(await pool.destroy(),log(4,"[pool] Destroyed the pool of resources.")),pool=null}await closeBrowser()}async function postWork(e){let t;try{if(log(4,"[pool] Work received, starting to process."),++poolStats.exportsAttempted,e.pool.benchmarking&&_getPoolInfo(),!pool)throw new ExportError("[pool] Work received, but pool has not been started.",500);const o=measureTime();try{log(4,"[pool] Acquiring a worker handle."),t=await pool.acquire().promise,e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Acquiring a worker handle took ${o()}ms.`)}catch(t){throw new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered when acquiring an available entry: ${o()}ms.`,400).setError(t)}if(log(4,"[pool] Acquired a worker handle."),!t.page)throw t.workCount=e.pool.workLimit+1,new ExportError("[pool] Resolved worker page is invalid: the pool setup is wonky.",400);log(4,`[pool] Pool resource [${t.id}] - Starting work on this pool entry.`);const r=measureTime(),n=await puppeteerExport(t.page,e.export,e.customLogic);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(t.workCount=e.pool.workLimit+1,t.page=null),"TimeoutError"===n.name||"Rasterization timeout"===n.message?new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`).setError(n):new ExportError(`[pool] ${e.requestId?`Request [${e.requestId}] - `:""}Error encountered during export: ${r()}ms.`).setError(n);return e.server.benchmarking&&log(5,"[benchmark] "+(e.requestId?`Request [${e.requestId}] - `:""),`Exporting a chart sucessfully took ${r()}ms.`),pool.release(t),poolStats.timeSpent+=r(),poolStats.timeSpentAverage=poolStats.timeSpent/++poolStats.exportsPerformed,log(4,`[pool] Work completed in ${r()}ms.`),{result:n,options:e}}catch(e){throw++poolStats.exportsDropped,t&&pool.release(t),e}}function getPoolStats(){return poolStats}function getPoolInfoJSON(){return{min:pool.min,max:pool.max,used:pool.numUsed(),available:pool.numFree(),allCreated:pool.numUsed()+pool.numFree(),pendingAcquires:pool.numPendingAcquires(),pendingCreates:pool.numPendingCreates(),pendingValidations:pool.numPendingValidations(),pendingDestroys:pool.pendingDestroys.length,absoluteAll:pool.numUsed()+pool.numFree()+pool.numPendingAcquires()+pool.numPendingCreates()+pool.numPendingValidations()+pool.pendingDestroys.length}}function _getPoolInfo(){const{min:e,max:t,used:o,available:r,allCreated:n,pendingAcquires:i,pendingCreates:s,pendingValidations:a,pendingDestroys:l,absoluteAll:c}=getPoolInfoJSON();log(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),log(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),log(5,`[pool] The number of used resources: ${o}.`),log(5,`[pool] The number of free resources: ${r}.`),log(5,`[pool] The number of all created (used and free) resources: ${n}.`),log(5,`[pool] The number of resources waiting to be acquired: ${i}.`),log(5,`[pool] The number of resources waiting to be created: ${s}.`),log(5,`[pool] The number of resources waiting to be validated: ${a}.`),log(5,`[pool] The number of resources waiting to be destroyed: ${l}.`),log(5,`[pool] The number of all resources: ${c}.`)}function _factory(e){return{create:async()=>{const t={id:v4(),workCount:Math.round(Math.random()*(e.workLimit/2))};try{const e=getNewDateTime();return await newPage(t),log(3,`[pool] Pool resource [${t.id}] - Successfully created a worker, took ${getNewDateTime()-e}ms.`),t}catch(e){throw log(3,`[pool] Pool resource [${t.id}] - Error encountered when creating a new page.`),e}},validate:async t=>t.page?t.page.isClosed()?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page is closed or invalid).`),!1):t.page.mainFrame().detached?(log(3,`[pool] Pool resource [${t.id}] - Validation failed (page's frame is detached).`),!1):!(e.workLimit&&++t.workCount>e.workLimit)||(log(3,`[pool] Pool resource [${t.id}] - Validation failed (exceeded the ${e.workLimit} works per resource limit).`),!1):(log(3,`[pool] Pool resource [${t.id}] - Validation failed (no valid page is found).`),!1),destroy:async e=>{if(log(3,`[pool] Pool resource [${e.id}] - Destroying a worker.`),e.page&&!e.page.isClosed())try{e.page.removeAllListeners("pageerror"),e.page.removeAllListeners("console"),e.page.removeAllListeners("framedetached"),await e.page.close()}catch(t){throw log(3,`[pool] Pool resource [${e.id}] - Page could not be closed upon destroying.`),t}}}}function sanitize(e){const t=new JSDOM("").window;return DOMPurify(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}let allowCodeExecution=!1;async function singleExport(e){if(!e||!e.export)throw new ExportError("[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.",400);await startExport({export:e.export,customLogic:e.customLogic},(async(e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r||`chart.${n}`,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}await killPool()}))}async function batchExport(e){if(!(e&&e.export&&e.export.batch))throw new ExportError("[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.",400);{const t=[];for(let o of e.export.batch.split(";")||[])o=o.split("="),2===o.length?t.push(startExport({export:{...e.export,infile:o[0],outfile:o[1]},customLogic:e.customLogic},((e,t)=>{if(e)throw e;const{b64:o,outfile:r,type:n}=t.options.export;try{o?writeFileSync(`${r.split(".").shift()||"chart"}.txt`,getBase64(t.result,n)):writeFileSync(r,"svg"!==n?Buffer.from(t.result,"base64"):t.result)}catch(e){throw new ExportError("[chart] Error while saving a chart.",500).setError(e)}}))):log(2,"[chart] No correct pair found for the batch export.");const o=await Promise.allSettled(t);await killPool(),o.forEach(((e,t)=>{e.reason&&logWithStack(1,e.reason,`[chart] Batch export number ${t+1} could not be correctly completed.`)}))}}async function startExport(e,t){try{if(!isObject(e))throw new ExportError("[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.",400);const o=updateOptions({export:e.export,customLogic:e.customLogic},!0),r=o.export;if(log(4,"[chart] Starting the exporting process."),null!==r.infile){let e;log(4,"[chart] Attempting to export from a file input.");try{e=readFileSync(getAbsolutePath(r.infile),"utf8")}catch(e){throw new ExportError("[chart] Error loading content from a file input.",400).setError(e)}if(r.infile.endsWith(".svg"))r.svg=e;else{if(!r.infile.endsWith(".json"))throw new ExportError("[chart] Incorrect value of the `infile` option.",400);r.instr=e}}if(null!==r.svg){log(4,"[chart] Attempting to export from an SVG input."),++getPoolStats().exportsFromSvgAttempts;const e=await _exportFromSvg(sanitize(r.svg),o);return++getPoolStats().exportsFromSvg,t(null,e)}if(null!==r.instr||null!==r.options){log(4,"[chart] Attempting to export from options input."),++getPoolStats().exportsFromOptionsAttempts;const e=await _exportFromOptions(r.instr||r.options,o);return++getPoolStats().exportsFromOptions,t(null,e)}return t(new ExportError("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.",400))}catch(e){return t(e)}}function getAllowCodeExecution(){return allowCodeExecution}function setAllowCodeExecution(e){allowCodeExecution=e}async function _exportFromSvg(e,t){if("string"==typeof e&&(e.indexOf("=0||e.indexOf("=0))return log(4,"[chart] Parsing input as SVG."),t.export.svg=e,t.export.options=null,t.export.instr=null,_prepareExport(t);throw new ExportError("[chart] Not a correct SVG input.",400)}async function _exportFromOptions(e,t){log(4,"[chart] Parsing input from options.");const o=isAllowedConfig(e,!0,t.customLogic.allowCodeExecution);if(null===o||"string"!=typeof o||!o.startsWith("{")||!o.endsWith("}"))throw new ExportError("[chart] Invalid configuration provided - Only options 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` options set to true.",403);return t.export.instr=o,t.export.options=null,t.export.svg=null,_prepareExport(t)}async function _prepareExport(e){const{export:t,customLogic:o}=e;return t.constr=_fixConstr(t.constr),t.type=_fixType(t.type,t.outfile),t.outfile=_fixOutfile(t.type,t.outfile),log(3,`[chart] The custom logic is ${o.allowCodeExecution?"allowed":"disallowed"}.`),_handleCustomLogic(o),_handleGlobalAndTheme(t,o),_handleSize(t),_checkDataSize({export:t,customLogic:o}),postWork(e)}function _fixConstr(e){try{const t=`${e.toLowerCase().replace("chart","")}Chart`;return"Chart"===t&&t.toLowerCase(),["chart","stockChart","mapChart","ganttChart"].includes(t)?t:"chart"}catch{return"chart"}}function _fixOutfile(e,t){return`${getAbsolutePath(t||"chart").split(".").shift()}.${e||"png"}`}function _fixType(e,t=null){const o={"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"},r=Object.values(o);if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return o[e]||r.find((t=>t===e))||"png"}function _handleSize(e){const{chart:t,exporting:o}=isAllowedConfig(e.instr)||!1,{chart:r,exporting:n}=isAllowedConfig(e.globalOptions)||!1,{chart:i,exporting:s}=isAllowedConfig(e.themeOptions)||!1,a=e.height||o?.sourceHeight||t?.height||n?.sourceHeight||r?.height||s?.sourceHeight||i?.height||e.defaultHeight||400,l=e.width||o?.sourceWidth||t?.width||n?.sourceWidth||r?.width||s?.sourceWidth||i?.width||e.defaultWidth||600,c=roundNumber(Math.max(.1,Math.min(e.scale||o?.scale||n?.scale||s?.scale||e.defaultScale||1,5)),2);e.height=a,e.width=l,e.scale=c;for(let t of["height","width","scale"])"string"==typeof e[t]&&(e[t]=+e[t].replace(/px|%/gi,""))}function _handleCustomLogic(e){if(e.allowCodeExecution){try{e.resources=_handleResources(e.resources,e.allowFileResources,!0)}catch(t){log(2,"[chart] The `resources` cannot be loaded."),e.resources=null}try{e.customCode=_handleCustomCode(e.customCode,e.allowFileResources)}catch(t){logWithStack(2,t,"[chart] The `customCode` cannot be loaded."),e.customCode=null}try{e.callback=_handleCustomCode(e.callback,e.allowFileResources,!0)}catch(t){logWithStack(2,t,"[chart] The `callback` cannot be loaded."),e.callback=null}[null,void 0].includes(e.customCode)&&log(3,"[chart] No value for the `customCode` option found."),[null,void 0].includes(e.callback)&&log(3,"[chart] No value for the `callback` option found."),[null,void 0].includes(e.resources)&&log(3,"[chart] No value for the `resources` option found.")}else if(e.callback||e.resources||e.customCode)throw e.callback=null,e.resources=null,e.customCode=null,new ExportError("[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.",403)}function _handleResources(e=null,t,o){let r=e;r||(e="resources.json");const n=["js","css","files"];let i=!1;t&&"string"==typeof e&&e.endsWith(".json")?r=isAllowedConfig(readFileSync(getAbsolutePath(e),"utf8"),!1,o):(r=isAllowedConfig(e,!1,o),r&&!t&&delete r.files);for(const e in r)n.includes(e)?i||(i=!0):delete r[e];return i?(r.files&&(r.files=r.files.map((e=>e.trim())),(!r.files||r.files.length<=0)&&delete r.files),r):null}function _handleCustomCode(e,t,o=!1){if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?t?_handleCustomCode(readFileSync(getAbsolutePath(e),"utf8"),t,o):null:!o&&(e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>"))?`(${e})()`:e.replace(/;$/,"")}function _handleGlobalAndTheme(e,t){const{allowFileResources:o,allowCodeExecution:r}=t;["globalOptions","themeOptions"].forEach((t=>{try{e[t]&&(o&&"string"==typeof e[t]&&e[t].endsWith(".json")?e[t]=isAllowedConfig(readFileSync(getAbsolutePath(e[t]),"utf8"),!0,r):e[t]=isAllowedConfig(e[t],!0,r))}catch(o){logWithStack(2,o,`[chart] The \`${t}\` cannot be loaded.`),e[t]=null}})),[null,void 0].includes(e.globalOptions)&&log(3,"[chart] No value for the `globalOptions` option found."),[null,void 0].includes(e.themeOptions)&&log(3,"[chart] No value for the `themeOptions` option found.")}function _checkDataSize(e){const t=Buffer.byteLength(JSON.stringify(e),"utf-8");if(log(3,`[chart] The current total size of the data for the export process is around ${(t/1048576).toFixed(2)}MB.`),t>=104857600)throw new ExportError("[chart] The data for the export process exceeds 100MB limit.")}const timerIds=[];function addTimer(e){timerIds.push(e)}function clearAllTimers(){log(4,"[timer] Clearing all registered intervals and timeouts.");for(const e of timerIds)clearInterval(e),clearTimeout(e)}function logErrorMiddleware(e,t,o,r){return logWithStack(1,e),"development"!==getOptions().other.nodeEnv&&delete e.stack,r(e)}function returnErrorMiddleware(e,t,o,r){const{message:n,stack:i}=e,s=e.statusCode||400;o.status(s).json({statusCode:s,message:n,stack:i})}function errorMiddleware(e){e.use(logErrorMiddleware),e.use(returnErrorMiddleware)}function rateLimitingMiddleware(e,t){try{if(e&&t.enable){const o="Too many requests, you have been rate limited. Please try again later.",r={window:t.window||1,maxRequests:t.maxRequests||30,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||null,skipToken:t.skipToken||null};r.trustProxy&&e.enable("trust proxy");const n=rateLimit({windowMs:60*r.window*1e3,limit:r.maxRequests,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>null!==r.skipKey&&null!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(log(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(n),log(3,`[rate limiting] Enabled rate limiting with ${r.maxRequests} requests per ${r.window} minute for each IP, trusting proxy: ${r.trustProxy}.`)}}catch(e){throw new ExportError("[rate limiting] Could not configure and set the rate limiting options.",500).setError(e)}}function contentTypeMiddleware(e,t,o){try{const t=e.headers["content-type"]||"";if(!t.includes("application/json")&&!t.includes("application/x-www-form-urlencoded")&&!t.includes("multipart/form-data"))throw new ExportError("[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.",415);return o()}catch(e){return o(e)}}function requestBodyMiddleware(e,t,o){try{const t=e.body,r=v4();if(!t||isObjectEmpty(t))throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is empty.`),new ExportError(`[validation] Request [${r}] - The request body is required. Please ensure that your Content-Type header is correct. Accepted types are 'application/json' and 'multipart/form-data'.`,400);const n=getAllowCodeExecution(),i=isAllowedConfig(t.instr||t.options||t.infile||t.data,!0,n);if(null===i&&!t.svg)throw log(2,`[validation] Request [${r}] - The request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(t)}.`),new ExportError(`[validation] Request [${r}] - 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);if(t.svg&&isPrivateRangeUrlFound(t.svg))throw new ExportError(`[validation] Request [${r}] - 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);return e.validatedOptions={requestId:r,export:{instr:i,svg:t.svg,outfile:t.outfile||`${e.params.filename||"chart"}.${t.type||"png"}`,type:t.type,constr:t.constr,b64:t.b64,noDownload:t.noDownload,height:t.height,width:t.width,scale:t.scale,globalOptions:isAllowedConfig(t.globalOptions,!0,n),themeOptions:isAllowedConfig(t.themeOptions,!0,n)},customLogic:{allowCodeExecution:n,allowFileResources:!1,customCode:t.customCode,callback:t.callback,resources:isAllowedConfig(t.resources,!0,n)}},o()}catch(e){return o(e)}}function validationMiddleware(e){e.post(["/","/:filename"],contentTypeMiddleware),e.post(["/","/:filename"],requestBodyMiddleware)}const reversedMime={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};async function requestExport(e,t,o){try{const o=measureTime();let r=!1;e.socket.on("close",(e=>{e&&(r=!0)}));const n=e.validatedOptions,i=n.requestId;log(4,`[export] Request [${i}] - Got an incoming HTTP request.`),await startExport(n,((n,s)=>{if(e.socket.removeAllListeners("close"),r)log(3,`[export] Request [${i}] - The client closed the connection before the chart finished processing.`);else{if(n)throw n;if(!s||!s.result)throw log(2,`[export] Request [${i}] - Request from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Received result is ${s.result}.`),new ExportError(`[export] Request [${i}] - Unexpected return of the export result from the chart generation. Please check your request data.`,400);if(s.result){log(3,`[export] Request [${i}] - The whole exporting process took ${o()}ms.`);const{type:e,b64:r,noDownload:n,outfile:a}=s.options.export;return r?t.send(getBase64(s.result,e)):(t.header("Content-Type",reversedMime[e]||"image/png"),n||t.attachment(a),"svg"===e?t.send(s.result):t.send(Buffer.from(s.result,"base64")))}}}))}catch(e){return o(e)}}function exportRoutes(e){e.post("/",requestExport),e.post("/:filename",requestExport)}const serverStartTime=new Date,packageFile=JSON.parse(readFileSync(join(__dirname,"package.json"),"utf8")),successRates=[],recordInterval=6e4,windowSize=30;function _calculateMovingAverage(){return successRates.reduce(((e,t)=>e+t),0)/successRates.length}function _startSuccessRate(){return setInterval((()=>{const e=getPoolStats(),t=0===e.exportsAttempted?1:e.exportsPerformed/e.exportsAttempted*100;successRates.push(t),successRates.length>windowSize&&successRates.shift()}),recordInterval)}function healthRoutes(e){addTimer(_startSuccessRate()),e.get("/health",((e,t,o)=>{try{log(4,"[health] Returning server health.");const e=getPoolStats(),o=successRates.length,r=_calculateMovingAverage();t.send({status:"OK",bootTime:serverStartTime,uptime:`${Math.floor((getNewDateTime()-serverStartTime.getTime())/1e3/60)} minutes`,serverVersion:packageFile.version,highchartsVersion:getHcVersion(),averageExportTime:e.timeSpentAverage,attemptedExports:e.exportsAttempted,performedExports:e.exportsPerformed,failedExports:e.exportsDropped,sucessRatio:e.exportsPerformed/e.exportsAttempted*100,pool:getPoolInfoJSON(),period:o,movingAverage:r,message:isNaN(r)||!successRates.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${r.toFixed(2)}%.`,svgExports:e.exportsFromSvg,jsonExports:e.exportsFromOptions,svgExportsAttempts:e.exportsFromSvgAttempts,jsonExportsAttempts:e.exportsFromOptionsAttempts})}catch(e){return o(e)}}))}function uiRoutes(e){getOptions().ui.enable&&e.get(getOptions().ui.route||"/",((e,t,o)=>{try{log(4,"[ui] Returning UI for the export."),t.sendFile(join(__dirname,"public","index.html"),{acceptRanges:!1})}catch(e){return o(e)}}))}function versionChangeRoutes(e){e.post("/version_change/:newVersion",(async(e,t,o)=>{try{log(4,"[version] Changing Highcharts version.");const o=envs.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)throw new ExportError("[version] The server is not configured to perform run-time version changes: `HIGHCHARTS_ADMIN_TOKEN` is not set.",401);const r=e.get("hc-auth");if(!r||r!==o)throw new ExportError("[version] Invalid or missing token: Set the token in the hc-auth header.",401);const n=e.params.newVersion;if(!n)throw new ExportError("[version] No new version supplied.",400);try{await updateHcVersion(n)}catch(e){throw new ExportError(`[version] Version change: ${e.message}`,400).setError(e)}t.status(200).send({statusCode:200,highchartsVersion:getHcVersion(),message:`Successfully updated Highcharts to version: ${n}.`})}catch(e){return o(e)}}))}const activeServers=new Map,app=express();async function startServer(e){try{const t=updateOptions({server:e});if(!(e=t.server).enable||!app)throw new ExportError("[server] Server cannot be started (not enabled or no correct Express app found).",500);const o=1024*e.uploadLimit*1024,r=multer.memoryStorage(),n=multer({storage:r,limits:{fieldSize:o}});if(app.disable("x-powered-by"),app.use(cors({methods:["POST","GET","OPTIONS"]})),app.use(((e,t,o)=>{t.set("Accept-Ranges","none"),o()})),app.use(express.json({limit:o})),app.use(express.urlencoded({extended:!0,limit:o})),app.use(n.none()),app.use(express.static(join(__dirname,"public"))),!e.ssl.force){const t=http.createServer(app);_attachServerErrorHandlers(t),t.listen(e.port,e.host,(()=>{activeServers.set(e.port,t),log(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}))}if(e.ssl.enable){let t,o;try{t=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.key"),"utf8"),o=readFileSync(join(getAbsolutePath(e.ssl.certPath),"server.crt"),"utf8")}catch(t){log(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&o){const r=https.createServer({key:t,cert:o},app);_attachServerErrorHandlers(r),r.listen(e.ssl.port,e.host,(()=>{activeServers.set(e.ssl.port,r),log(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}))}}rateLimitingMiddleware(app,e.rateLimiting),validationMiddleware(app),exportRoutes(app),healthRoutes(app),uiRoutes(app),versionChangeRoutes(app),errorMiddleware(app)}catch(e){throw new ExportError("[server] Could not configure and start the server.",500).setError(e)}}function closeServers(){if(activeServers.size>0){log(4,"[server] Closing all servers.");for(const[e,t]of activeServers)t.close((()=>{activeServers.delete(e),log(4,`[server] Closed server on port: ${e}.`)}))}}function getServers(){return activeServers}function getExpress(){return express}function getApp(){return app}function enableRateLimiting(e){const t=updateOptions({server:{rateLimiting:e}});rateLimitingMiddleware(app,t.server.rateLimitingOptions)}function use(e,...t){app.use(e,...t)}function get(e,...t){app.get(e,...t)}function post(e,...t){app.post(e,...t)}function _attachServerErrorHandlers(e){e.on("clientError",((e,t)=>{logWithStack(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{logWithStack(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{logWithStack(1,e,`[server] Socket error: ${e.message}`)}))}))}var server={startServer:startServer,closeServers:closeServers,getServers:getServers,getExpress:getExpress,getApp:getApp,enableRateLimiting:enableRateLimiting,use:use,get:get,post:post};async function shutdownCleanUp(e=0){await Promise.allSettled([clearAllTimers(),closeServers(),killPool()]),process.exit(e)}async function initExport(e){const t=updateOptions(e);setAllowCodeExecution(t.customLogic.allowCodeExecution),initLogging(t.logging),t.other.listenToProcessExits&&_attachProcessExitListeners(),await checkCache(t.highcharts,t.server.proxy),await initPool(t.pool,t.puppeteer.args)}function _attachProcessExitListeners(){log(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{log(4,`[process] Process exited with code: ${e}.`)})),process.on("SIGINT",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGTERM",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("SIGHUP",(async(e,t)=>{log(4,`[process] The ${e} event with code: ${t}.`),await shutdownCleanUp()})),process.on("uncaughtException",(async(e,t)=>{logWithStack(1,e,`[process] The ${t} error.`),await shutdownCleanUp(1)}))}var index={...server,getOptions:getOptions,updateOptions:updateOptions,mapToNewOptions:mapToNewOptions,initExport:initExport,singleExport:singleExport,batchExport:batchExport,startExport:startExport,killPool:killPool,shutdownCleanUp:shutdownCleanUp,log:log,logWithStack:logWithStack,setLogLevel:function(e){setLogLevel(updateOptions({logging:{level:e}}).logging.level)},enableConsoleLogging:function(e){enableConsoleLogging(updateOptions({logging:{toConsole:e}}).logging.toConsole)},enableFileLogging:function(e,t,o){const r=updateOptions({logging:{dest:e,file:t,toFile:o}});enableFileLogging(r.logging.dest,r.logging.file,r.logging.toFile)}};export{index as default,initExport}; //# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index b8893d8d..489fd04a 100644 --- a/dist/index.esm.js.map +++ b/dist/index.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/utils.js","../lib/logger.js","../lib/schemas/config.js","../lib/envs.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { isAbsolute, normalize, resolve } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Adjusts the constructor name by transforming and normalizing it based\r\n * on common chart types.\r\n *\r\n * @function fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be fixed.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nexport function fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Fixes the outfile based on provided type.\r\n *\r\n * @function fixOutfile\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 outfile.\r\n */\r\nexport function fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type}`;\r\n}\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @function fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type.\r\n */\r\nexport function fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function isAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? normalize(path) : resolve(path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} 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 function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\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 function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\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 otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @function wrapAround\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nexport function wrapAround(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? wrapAround(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || '';\r\n logging.file = file || '';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array.} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * Provides default configurations that support environment variables, CLI\r\n * arguments, and interactive prompts for customization of options and features.\r\n * Additionally, it maps legacy options to modern structures, generates nested\r\n * argument mappings, and displays CLI usage information.\r\n */\r\n\r\n/**\r\n * The configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option\r\n * - Data types for validation\r\n * - Names of corresponding environment variables\r\n * - Descriptions of each property\r\n * - Information used for prompts in interactive configuration\r\n * - [Optional] Corresponding CLI argument names for CLI usage\r\n * - [Optional] Legacy names from the previous PhantomJS-based server\r\n */\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\n// Properties nesting level of all options\r\nexport const nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nexport const absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * can be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array.} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array.} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n defaultConfig,\r\n nestedProps,\r\n absoluteProps\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 { defaultConfig } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 Manages configuration for the Highcharts Export Server by loading\r\n * and merging options from multiple sources, such as default settings,\r\n * environment variables, user-provided options, and command-line arguments.\r\n * Ensures the global options are up-to-date with the highest priority values.\r\n * Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { log, logWithStack } from './logger.js';\r\nimport { envs } from './envs.js';\r\nimport { __dirname, deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport { absoluteProps, nestedProps, defaultConfig } from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getCopy=true] - Specifies whether to return a copied\r\n * object of the global options (`true`) or a reference to the global options\r\n * object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} A copy of the global options object, or a reference\r\n * to the global options object.\r\n */\r\nexport function getOptions(getCopy = true) {\r\n return getCopy ? deepCopy(globalOptions) : globalOptions;\r\n}\r\n\r\n/**\r\n * Updates a copy of the global options object or a reference to the global\r\n * options object, based on the `getCopy` flag.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(getOptions(getCopy), newOptions);\r\n}\r\n\r\n/**\r\n * Updates the global options with values provided through the CLI, keeping\r\n * the principle of options load priority. This function accepts a `cliArgs`\r\n * array containing arguments from the CLI, which will be validated and applied\r\n * if provided.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n * 2. Values from the command line interface (CLI).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array.} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Update global options with the values from the `configOptions`\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(nestedProps, cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions`\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return reference to the global options\r\n return getOptions(false);\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS) to a new format\r\n * (Puppeteer). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping\r\n * (`nestedProps`). The new format is used for Puppeteer, while the old format\r\n * was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in `nestedProps` or an empty object if the provided\r\n * `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty array.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If true, functions are preserved. Otherwise, when\r\n * a function is found, null is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is true, and null\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return null if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return null if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo, version, and license information.\r\n *\r\n * @function printLicense\r\n */\r\nexport function printLicense() {\r\n // Print the logo and version information\r\n printVersion();\r\n\r\n // Print the license information\r\n console.log(\r\n 'This software requires a valid Highcharts license for commercial use.\\n'\r\n .yellow,\r\n '\\nFor a full list of CLI options, type:',\r\n '\\nhighcharts-export-server --help\\n'.green,\r\n '\\nIf you do not have a license, one can be obtained here:',\r\n '\\nhttps://shop.highsoft.com/\\n'.green,\r\n '\\nTo customize your installation, please refer to the README file at:',\r\n '\\nhttps://github.com/highcharts/node-export-server#readme\\n'.green\r\n );\r\n}\r\n\r\n/**\r\n * Prints usage information for CLI arguments, displaying available options\r\n * and their descriptions. It can list properties recursively if categories\r\n * contain nested options.\r\n *\r\n * @function printUsage\r\n */\r\nexport function printUsage() {\r\n // Display README and general usage information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n-----------------------',\r\n `\\nFor more detailed information, visit the README file at: ${'https://github.com/highcharts/node-export-server#readme'.green}.\\n`\r\n );\r\n\r\n // Iterate through each category in the `defaultConfig` and display usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n console.log(`${category.toUpperCase()}`.bold.red);\r\n _cycleCategories(defaultConfig[category]);\r\n console.log('');\r\n });\r\n}\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo or text with the version\r\n * information.\r\n *\r\n * @function printVersion\r\n *\r\n * @param {boolean} [noLogo=false] - If true, only prints text with the version\r\n * information, without the logo. The default value is `false`.\r\n */\r\nexport function printVersion(noLogo = false) {\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'), 'utf8')\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Highcharts Export Server v${packageVersion}`);\r\n } else {\r\n // Print the logo\r\n console.log(\r\n readFileSync(join(__dirname, 'msg', 'startup.msg'), 'utf8').toString()\r\n .bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The priority order for setting values is:\r\n *\r\n * 1. Values from the `./lib/schemas/config.js` file (defaults).\r\n * 2. Values from environment variables (specified in the `.env` file).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a section in the options\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport function _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string\r\n * with the option to preserve functions. In order for a function\r\n * to be preserved, it needs to follow the format `function (...) {...}`.\r\n * Such a function can also be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to true, functions are saved\r\n * as strings. The `allowFunctions` must be set to true as well for this to take\r\n * an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nexport function _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If value is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If allowFunctions is set to true, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array.} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Get the allow flags for the custom logic check\r\n const { allowCodeExecution, allowFileResources } = getOptions().customLogic;\r\n\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array.} nestedProps - An array of nesting level for all\r\n * options.\r\n * @param {Array.} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(nestedProps, cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively traverses the options object to print the usage information\r\n * for each option category and individual option.\r\n *\r\n * @function _cycleCategories\r\n *\r\n * @param {Object} options - The options object containing CLI options. It may\r\n * include nested categories and individual options.\r\n */\r\nfunction _cycleCategories(options) {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If the current entry is a category and not a leaf option, recurse into it\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n _cycleCategories(option);\r\n } else {\r\n // Prepare description\r\n const descName = ` --${option.cliName || name}`;\r\n\r\n // Get the value\r\n let optionValue = option.value;\r\n\r\n // Prepare value for option that is not null and is array of strings\r\n if (optionValue !== null && option.types.includes('string[]')) {\r\n optionValue =\r\n '[' + optionValue.map((item) => `'${item}'`).join(', ') + ']';\r\n }\r\n\r\n // Prepare value for option that is not null and is a string\r\n if (optionValue !== null && option.types.includes('string')) {\r\n optionValue = `'${optionValue}'`;\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName.green,\r\n `${('<' + option.types.join('|') + '>').yellow}`,\r\n `${String(optionValue).bold}`.blue,\r\n `- ${option.description}.`\r\n );\r\n }\r\n }\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig,\r\n printLicense,\r\n printUsage,\r\n printVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function fetch\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n fetch,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets or updates the HTTP status code for the error.\r\n *\r\n * @param {number} statusCode - The HTTP status code to assign to the error.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setStatus(statusCode) {\r\n this.statusCode = statusCode;\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n this.error = error;\r\n\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkAndUpdateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkAndUpdateCache(\r\n highchartsOptions,\r\n serverProxyOptions\r\n) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\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(highchartsOptions, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHighchartsVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHighchartsVersion() {\r\n return cache.hcVersion;\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 * @async\r\n * @function updateHighchartsVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHighchartsVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function extractVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport function extractVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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 * @function extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nexport function extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchAndProcessScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchAndProcessScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which Highcharts\r\n * modules have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(highchartsOptions, fetchedModules = {}) {\r\n const newManifest = {\r\n version: highchartsOptions.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(getCachePath(), 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches Highcharts `scripts` and `customScripts` from the given CDNs.\r\n *\r\n * @async\r\n * @function _fetchScripts\r\n *\r\n * @param {Array.} coreScripts - Highcharts core scripts to fetch.\r\n * @param {Array.} moduleScripts - Highcharts modules to fetch.\r\n * @param {Array.} customScripts - Custom script paths to fetch (full\r\n * URLs).\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} A Promise that resolves to the fetched scripts\r\n * content joined.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * setting an HTTP Agent for proxy.\r\n */\r\nasync function _fetchScripts(\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n) {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\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: serverProxyOptions.timeout\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n _fetchAndProcessScript(`${script}`, requestOptions)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n try {\r\n const fetchedModules = {};\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n cache.sources = await _fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${c}` : `${cdnUrl}/${c}`\r\n )\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/maps/modules/${m}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${m}`\r\n : `${cdnUrl}/modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${i}`\r\n : `${cdnUrl}/stock/indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n serverProxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = extractVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getHighchartsVersion,\r\n updateHighchartsVersion,\r\n extractVersion,\r\n extractModuleName,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.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\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that resources are correctly managed and can handle failures during\r\n * operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst template = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\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\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\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 500\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to true when page\r\n * is correctly cleared and false when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = customLogicOptions.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') ? false : true;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(getAbsolutePath(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, `[browser] 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n path: getAbsolutePath(cssImportPath)\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\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: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer Page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body 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\r\n // of exports. 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 // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array.} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n // Save the start time\r\n const workStart = getNewDateTime();\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE:\r\n // 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:\r\n // 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 // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.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 `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\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 `[pool] ${\r\n options.requestId ? `Request [${options.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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 = getNewDateTime();\r\n const exportTime = workEnd - workStart;\r\n\r\n poolStats.timeSpent += exportTime;\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\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 ++poolStats.exportsDropped;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function getPoolInfo\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`,\r\n * and `destroy` functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfo,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions to prepare for the exporting charts\r\n * into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport {\r\n fixConstr,\r\n fixOutfile,\r\n fixType,\r\n getAbsolutePath,\r\n getBase64,\r\n isObject,\r\n roundNumber,\r\n wrapAround\r\n} from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `imageOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} imageOptions - The `imageOptions` object, which should\r\n * include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(imageOptions, endCallback) {\r\n try {\r\n // Check if provided options are in an object\r\n if (!isObject(imageOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: imageOptions.export,\r\n customLogic: imageOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.options = null;\r\n options.export.instr = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.options = null;\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = fixOutfile(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = fixConstr(exportOptions.constr);\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the custom logic options (`customCode`, `callback`, `resources`)\r\n _handleCustomLogic(customLogicOptions, customLogicOptions.allowCodeExecution);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(\r\n exportOptions,\r\n customLogicOptions.allowFileResources,\r\n customLogicOptions.allowCodeExecution\r\n );\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n options.export = {\r\n ...exportOptions,\r\n ..._findChartSize(exportOptions)\r\n };\r\n\r\n // Check if the image options object does not exceed the size limit\r\n _checkDataSize({ export: exportOptions, customLogic: customLogicOptions });\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Calculates the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _findChartSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n *\r\n * @returns {Object} The object containing calculated `height`, `width`\r\n * and `scale` values for the chart export.\r\n */\r\nfunction _findChartSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `scale` value:\r\n // - It cannot be lower than 0.1\r\n // - It cannot be higher than 5.0\r\n // - It must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Gather `height`, `width` and `scale` information in one object\r\n const size = { height, width, scale };\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\r\n // Return the size object\r\n return size;\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions, allowCodeExecution) {\r\n // In case of allowing code execution\r\n if (allowCodeExecution) {\r\n // Process the `resources` option\r\n if (typeof customLogicOptions.resources === 'string') {\r\n // Custom stringified resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } else if (!customLogicOptions.resources) {\r\n try {\r\n // Load the default one\r\n customLogicOptions.resources = _handleResources(\r\n readFileSync(getAbsolutePath('resources.json'), 'utf8'),\r\n customLogicOptions.allowFileResources,\r\n true\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 // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = wrapAround(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // List of allowed sections in the resources JSON\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 = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } catch {\r\n return null;\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is true), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to null. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to null.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n */\r\nfunction _handleGlobalAndTheme(\r\n exportOptions,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\n/**\r\n * Validates the size of the data for the export process against a fixed limit\r\n * of 100MB.\r\n *\r\n * @function _checkDataSize\r\n *\r\n * @param {Object} imageOptions - The data object, which includes options from\r\n * the `export` and `customLogic` sections and will be sent to a Puppeteer page.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the size of the data for\r\n * the export process object exceeds the 100MB limit.\r\n */\r\nfunction _checkDataSize(imageOptions) {\r\n // Set the fixed data limit (100MB) for the dev-tools protocol\r\n const dataLimit = 100 * 1024 * 1024;\r\n\r\n // Get the size of the data\r\n const totalSize = Buffer.byteLength(JSON.stringify(imageOptions), 'utf-8');\r\n\r\n // Log the size in MB\r\n log(\r\n 3,\r\n `[chart] The current total size of the data for the export process 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 before passing to a page\r\n if (totalSize >= dataLimit) {\r\n throw new ExportError(\r\n `[chart] The data for the export process exceeds 100MB limit.`\r\n );\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed. This can be useful\r\n * in applications where proper resource management and clean shutdown of timers\r\n * are critical to avoid memory leaks or unintended behavior.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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 *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const options = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = options.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(options, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHighchartsVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHighchartsVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHighchartsVersion, updateHighchartsVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] Invalid or missing token: Set the token in the hc-auth header.',\r\n 401\r\n );\r\n }\r\n\r\n // Get the new version from the params\r\n const newVersion = request.params.newVersion;\r\n\r\n // Update version\r\n if (newVersion) {\r\n try {\r\n await updateHighchartsVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHighchartsVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middleware setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 = readFileSync(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = readFileSync(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array.} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 Core module for initializing and managing the Highcharts Export\r\n * Server. Provides functionalities for configuring exports, setting up server\r\n * operations, logging, scripts caching, resource pooling, and graceful process\r\n * cleanup.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM'\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","fixConstr","constr","fixedConstr","toLowerCase","replace","includes","fixOutfile","type","outfile","getAbsolutePath","split","shift","fixType","mimeTypes","formats","values","outType","pop","find","t","path","isAbsolute","normalize","resolve","getBase64","input","Buffer","from","toString","getNewDate","Date","trim","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","length","isPrivateRangeUrlFound","some","pattern","test","measureTime","start","process","hrtime","bigint","Number","roundNumber","value","precision","multiplier","Math","pow","round","wrapAround","customCode","allowFileResources","isCallback","endsWith","readFileSync","startsWith","colors","logging","toConsole","toFile","pathCreated","pathToLog","levelsDesc","title","color","log","args","newLevel","texts","level","prefix","_logToFile","console","apply","undefined","concat","logWithStack","error","customMessage","mainMessage","message","stackMessage","stack","push","initLogging","loggingOptions","dest","file","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","join","appendFile","defaultConfig","puppeteer","types","envLink","cliName","description","promptOptions","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","hint","choices","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","config","propChain","forEach","entry","substring","dotenv","v","array","filterArray","z","string","transform","map","filter","boolean","enum","refine","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","env","_initOptions","getOptions","getCopy","updateOptions","newOptions","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","replaceAll","Error","async","fetch","requestOptions","Promise","reject","_getProtocolModule","get","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setStatus","setError","cache","activeManifest","sources","hcVersion","checkAndUpdateCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","modules","moduleMap","m","numberOfModules","moduleName","extractVersion","_saveConfigToManifest","getHighchartsVersion","updateHighchartsVersion","newVersion","cacheSources","indexOf","extractModuleName","scriptPath","_fetchAndProcessScript","script","shouldThrowError","newManifest","writeFileSync","_fetchScripts","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","c","i","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","defaultOptions","template","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","setTimeout","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","document","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","style","zoom","margin","x","y","_getClipRegion","viewportHeight","abs","ceil","viewportWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","outerHTML","clip","race","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","getPoolInfo","acquireCounter","requestId","workStart","exportCounter","exportTime","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","imageOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_handleCustomLogic","_handleGlobalAndTheme","_findChartSize","_checkDataSize","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","allowedProps","handledResources","correctResources","propName","optionsName","totalSize","byteLength","toFixed","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"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KA,GAASpB,QAAQE,QAAUF,QAAQG,cACrCH,QAAQE,QAAS,EACjBF,QAAQG,aAAc,EACtBgB,aAAa,EAAGC,EAAO,yCACxB,GAGP,CCjPO,MAAMmB,cAAgB,CAC3BC,UAAW,CACT/B,KAAM,CACJvB,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFuD,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbtG,KAAM,OACNuG,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACP9D,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbtG,KAAM,SAGV0G,OAAQ,CACN/D,MAAO,8BACPuD,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbtG,KAAM,SAGV2G,WAAY,CACVhE,OAAO,EACPuD,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbtG,KAAM,WAGV4G,UAAW,CACTjE,MAAO,SACPuD,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbtG,KAAM,SAGV6G,YAAa,CACXlE,MAAO,CAAC,aAAc,kBAAmB,iBACzCuD,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbtG,KAAM,cACN8G,aAAc,0DAGlBC,cAAe,CACbpE,MAAO,CACL,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,eACA,yBACA,SACA,eACA,YACA,k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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/utils.js","../lib/logger.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../templates/svgExport/css.js","../templates/svgExport/svgExport.js","../lib/export.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/timer.js","../lib/server/middlewares/error.js","../lib/server/middlewares/rateLimiting.js","../lib/server/middlewares/validation.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/routes/ui.js","../lib/server/routes/versionChange.js","../lib/server/server.js","../lib/resourceRelease.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Configuration management module for the Highcharts Export Server.\r\n * It provides a default configuration object with predefined default values,\r\n * descriptions, and characteristics for each option used in the Export Server.\r\n */\r\n\r\n/**\r\n * The default configuration object containing all available options, organized\r\n * by sections.\r\n *\r\n * This object includes:\r\n * - Default values for each option.\r\n * - Data types for validation.\r\n * - Names of corresponding environment variables.\r\n * - Descriptions of each property.\r\n * - Information used for prompts in interactive configuration.\r\n * - [Optional] Corresponding CLI argument names for CLI usage.\r\n * - [Optional] Legacy names from the previous PhantomJS-based server.\r\n */\r\nconst defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n types: ['string[]'],\r\n envLink: 'PUPPETEER_ARGS',\r\n cliName: 'puppeteerArgs',\r\n description: 'Array of Puppeteer arguments',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'Highcharts version',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n cdnUrl: {\r\n value: 'https://code.highcharts.com',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'CDN URL for Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n forceFetch: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description: 'Flag to refetch scripts after each server rerun',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n types: ['string'],\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description: 'Directory path for cached Highcharts scripts',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n coreScripts: {\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'Highcharts core scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n moduleScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'Highcharts module scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n indicatorScripts: {\r\n value: ['indicators-all'],\r\n types: ['string[]'],\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'Highcharts indicator scripts to fetch',\r\n promptOptions: {\r\n type: 'multiselect',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm'\r\n }\r\n },\r\n customScripts: {\r\n value: [\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 types: ['string[]'],\r\n envLink: 'HIGHCHARTS_CUSTOM_SCRIPTS',\r\n description: 'Additional custom scripts or dependencies to fetch',\r\n promptOptions: {\r\n type: 'list',\r\n separator: ';'\r\n }\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_INFILE',\r\n description:\r\n 'Input filename with type, formatted correctly as JSON or SVG',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n instr: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_INSTR',\r\n description:\r\n 'Overrides the `infile` with JSON, stringified JSON, or SVG input',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n options: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_OPTIONS',\r\n description: 'Alias for the `instr` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n svg: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_SVG',\r\n description: 'SVG string representation of the chart to render',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n batch: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_BATCH',\r\n description:\r\n 'Batch job string with input/output pairs: \"in=out;in=out;...\"',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n outfile: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'EXPORT_OUTFILE',\r\n description:\r\n 'Output filename with type. Can be jpeg, png, pdf, or svg and ignores `type` option',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n type: {\r\n value: 'png',\r\n types: ['string'],\r\n envLink: 'EXPORT_TYPE',\r\n description: 'File export format. Can be jpeg, png, pdf, or svg',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: png',\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n }\r\n },\r\n constr: {\r\n value: 'chart',\r\n types: ['string'],\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'Chart constructor. Can be chart, stockChart, mapChart, or ganttChart',\r\n promptOptions: {\r\n type: 'select',\r\n hint: 'Default: chart',\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n }\r\n },\r\n b64: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_B64',\r\n description:\r\n 'Whether or not to the chart should be received in Base64 format instead of binary',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noDownload: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'EXPORT_NO_DOWNLOAD',\r\n description:\r\n 'Whether or not to include or exclude attachment headers in the response',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n height: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_HEIGHT',\r\n description: 'Height of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n width: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_WIDTH',\r\n description: 'Width of the exported chart, overrides chart settings',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n scale: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'EXPORT_SCALE',\r\n description:\r\n 'Scale of the exported chart, overrides chart settings. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description: 'Default height of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description: 'Default width of the exported chart if not set',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n defaultScale: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'Default scale of the exported chart if not set. Ranges from 0.1 to 5.0',\r\n promptOptions: {\r\n type: 'number',\r\n min: 0.1,\r\n max: 5\r\n }\r\n },\r\n globalOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_GLOBAL_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with global options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n themeOptions: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'EXPORT_THEME_OPTIONS',\r\n description:\r\n 'JSON, stringified JSON or filename with theme options for Highcharts.setOptions',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n types: ['number'],\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description: 'Milliseconds to wait for webpage rendering',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Allows or disallows execution of arbitrary code during exporting',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n allowFileResources: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Allows or disallows injection of filesystem resources (disabled in server mode)',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n customCode: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CUSTOM_CODE',\r\n description:\r\n 'Custom code to execute before chart initialization. Can be a function, code wrapped in a function, or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n callback: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CALLBACK',\r\n description:\r\n 'JavaScript code to run during construction. Can be a function or a .js filename',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n resources: {\r\n value: null,\r\n types: ['Object', 'string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_RESOURCES',\r\n description:\r\n 'Additional resources as JSON, stringified JSON, or filename, containing files, js, and css sections',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n loadConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_LOAD_CONFIG',\r\n legacyName: 'fromFile',\r\n description: 'File with a pre-defined configuration to use',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n createConfig: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'CUSTOM_LOGIC_CREATE_CONFIG',\r\n description:\r\n 'Prompt-based option setting, saved to a provided config file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n server: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description: 'Starts the server when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n types: ['string'],\r\n envLink: 'SERVER_HOST',\r\n description: 'Hostname of the server',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: 7801,\r\n types: ['number'],\r\n envLink: 'SERVER_PORT',\r\n description: 'Port number for the server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n uploadLimit: {\r\n value: 3,\r\n types: ['number'],\r\n envLink: 'SERVER_UPLOAD_LIMIT',\r\n description: 'Maximum request body size in MB',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Displays or not action durations in milliseconds during server requests',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n proxy: {\r\n host: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'Host of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n port: {\r\n value: null,\r\n types: ['number', 'null'],\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'Port of the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n timeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description:\r\n 'Timeout in milliseconds for the proxy server, if applicable',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables or disables rate limiting on the server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n maxRequests: {\r\n value: 10,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'Maximum number of requests allowed per minute',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n window: {\r\n value: 1,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'Time window in minutes for rate limiting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n delay: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'Delay duration between successive requests before reaching the limit',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n trustProxy: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set to true if the server is behind a load balancer',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n skipKey: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description: 'Key to bypass the rate limiter, used with `skipToken`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n skipToken: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description: 'Token to bypass the rate limiter, used with `skipKey`',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables SSL protocol',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n force: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description: 'Forces the server to use HTTPS only when true',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n port: {\r\n value: 443,\r\n types: ['number'],\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'Port for the SSL server',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n certPath: {\r\n value: null,\r\n types: ['string', 'null'],\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n cliName: 'sslCertPath',\r\n legacyName: 'sslPath',\r\n description: 'Path to the SSL certificate/key file',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'Minimum and initial number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n types: ['number'],\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'Maximum number of pool workers to spawn',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n workLimit: {\r\n value: 40,\r\n types: ['number'],\r\n envLink: 'POOL_WORK_LIMIT',\r\n description: 'Number of tasks a worker can handle before restarting',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description: 'Timeout in milliseconds for acquiring a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description: 'Timeout in milliseconds for creating a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n types: ['number'],\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying a resource',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n types: ['number'],\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description: 'Timeout in milliseconds for destroying idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n types: ['number'],\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'Interval in milliseconds before retrying resource creation on failure',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n types: ['number'],\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'Interval in milliseconds to check and destroy idle resources',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n benchmarking: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description: 'Shows statistics for the pool of resources',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n types: ['number'],\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'Logging verbosity level',\r\n promptOptions: {\r\n type: 'number',\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n }\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n types: ['string'],\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'Log file name. Requires `logToFile` and `logDest` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n dest: {\r\n value: 'log',\r\n types: ['string'],\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description: 'Path to store log files. Requires `logToFile` to be set',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n toConsole: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables console logging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n toFile: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description: 'Enables or disables logging to a file',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description: 'Enables or disables the UI for the export server',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n route: {\r\n value: '/',\r\n types: ['string'],\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description: 'The endpoint route for the UI',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n types: ['string'],\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The Node.js environment type',\r\n promptOptions: {\r\n type: 'text'\r\n }\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Whether or not to attach process.exit handlers',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n noLogo: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_NO_LOGO',\r\n description: 'Display or skip printing the logo on startup',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n hardResetPage: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Whether or not to reset the page content entirely',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n browserShellMode: {\r\n value: true,\r\n types: ['boolean'],\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Whether or not to set the browser to run in shell mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n headless: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Whether or not to set the browser to run in headless mode during debugging',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n devtools: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description: 'Enables or disables DevTools in headful mode',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n listenToConsole: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Enables or disables listening to console messages from the browser',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n dumpio: {\r\n value: false,\r\n types: ['boolean'],\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects or not browser stdout and stderr to process.stdout and process.stderr',\r\n promptOptions: {\r\n type: 'toggle'\r\n }\r\n },\r\n slowMo: {\r\n value: 0,\r\n types: ['number'],\r\n envLink: 'DEBUG_SLOW_MO',\r\n description: 'Delays Puppeteer operations by the specified milliseconds',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n types: ['number'],\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Port used for debugging',\r\n promptOptions: {\r\n type: 'number'\r\n }\r\n }\r\n }\r\n};\r\n\r\nexport default defaultConfig;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This file is responsible for parsing the environment variables\r\n * with the 'zod' library. The parsed environment variables are then exported\r\n * to be used in the application as `envs`. We should not use the `process.env`\r\n * directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\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 defaultConfig from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_ARGS: v.string(),\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_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(defaultConfig.highcharts.coreScripts.value),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(\r\n defaultConfig.highcharts.moduleScripts.value\r\n ),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(\r\n defaultConfig.highcharts.indicatorScripts.value\r\n ),\r\n HIGHCHARTS_CUSTOM_SCRIPTS: v.array(\r\n defaultConfig.highcharts.customScripts.value\r\n ),\r\n\r\n // export\r\n EXPORT_INFILE: v.string(),\r\n EXPORT_INSTR: v.string(),\r\n EXPORT_OPTIONS: v.string(),\r\n EXPORT_SVG: v.string(),\r\n EXPORT_BATCH: v.string(),\r\n EXPORT_OUTFILE: v.string(),\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_B64: v.boolean(),\r\n EXPORT_NO_DOWNLOAD: v.boolean(),\r\n EXPORT_HEIGHT: v.positiveNum(),\r\n EXPORT_WIDTH: v.positiveNum(),\r\n EXPORT_SCALE: v.positiveNum(),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_GLOBAL_OPTIONS: v.string(),\r\n EXPORT_THEME_OPTIONS: v.string(),\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 CUSTOM_LOGIC_CUSTOM_CODE: v.string(),\r\n CUSTOM_LOGIC_CALLBACK: v.string(),\r\n CUSTOM_LOGIC_RESOURCES: v.string(),\r\n CUSTOM_LOGIC_LOAD_CONFIG: v.string(),\r\n CUSTOM_LOGIC_CREATE_CONFIG: v.string(),\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_UPLOAD_LIMIT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n 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\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-2025, 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 The Highcharts Export Server utility module provides\r\n * a comprehensive set of helper functions and constants designed to streamline\r\n * and enhance various operations required for Highcharts export tasks.\r\n */\r\n\r\nimport { isAbsolute, normalize, resolve } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The directory path\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 * @function clearText\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. The default value\r\n * is the '/\\s\\s+/g' RegExp.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters. The default value is the ' ' string.\r\n *\r\n * @returns {string} The cleared and standardized text.\r\n */\r\nexport function clearText(text, rule = /\\s\\s+/g, replacer = ' ') {\r\n return text.replaceAll(rule, replacer).trim();\r\n}\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @function deepCopy\r\n *\r\n * @param {(Object|Array)} objArr - 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 function deepCopy(objArr) {\r\n // If the `objArr` is null or not of the `object` type, return it\r\n if (objArr === null || typeof objArr !== 'object') {\r\n return objArr;\r\n }\r\n\r\n // Prepare either a new array or a new object\r\n const objArrCopy = Array.isArray(objArr) ? [] : {};\r\n\r\n // Recursively copy each property\r\n for (const key in objArr) {\r\n if (Object.prototype.hasOwnProperty.call(objArr, key)) {\r\n objArrCopy[key] = deepCopy(objArr[key]);\r\n }\r\n }\r\n\r\n // Return the copied object\r\n return objArrCopy;\r\n}\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 * @async\r\n * @function expBackoff\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number. The default value\r\n * is `0`.\r\n * @param {...unknown} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} A Promise that resolves to the result\r\n * of the function 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 async function expBackoff(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 repeat, 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\r\n /// TO DO: Correct\r\n // // Information about the resource timeout\r\n // log(\r\n // 3,\r\n // `[utils] Waited ${delayInMs}ms until next call for the resource of 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 * Checks if the given path is relative or absolute and returns the corrected,\r\n * absolute path.\r\n *\r\n * @function getAbsolutePath\r\n *\r\n * @param {string} path - The path to be checked on.\r\n *\r\n * @returns {string} The absolute path.\r\n */\r\nexport function getAbsolutePath(path) {\r\n return isAbsolute(path) ? normalize(path) : resolve(path);\r\n}\r\n\r\n/**\r\n * Converts input data to a Base64 string based on the export type.\r\n *\r\n * @function getBase64\r\n *\r\n * @param {string} input - The input to be transformed to Base64 format.\r\n * @param {string} type - The original export type.\r\n *\r\n * @returns {string} The Base64 string representation of the input.\r\n */\r\nexport function getBase64(input, type) {\r\n // For pdf and svg types the input must be transformed to Base64 from a buffer\r\n if (type === 'pdf' || type == 'svg') {\r\n return Buffer.from(input, 'utf8').toString('base64');\r\n }\r\n\r\n // For png and jpeg input is already a Base64 string\r\n return input;\r\n}\r\n\r\n/**\r\n * Returns stringified date without the GMT text information.\r\n *\r\n * @function getNewDate\r\n */\r\nexport function getNewDate() {\r\n // Get rid of the GMT text information\r\n return new Date().toString().split('(')[0].trim();\r\n}\r\n\r\n/**\r\n * Returns the stored time value in milliseconds.\r\n *\r\n * @function getNewDateTime\r\n */\r\nexport function getNewDateTime() {\r\n return new Date().getTime();\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @function isObject\r\n *\r\n * @param {unknown} item - The item to be checked.\r\n *\r\n * @returns {boolean} Returns `true` if the item is an object, `false`\r\n * otherwise.\r\n */\r\nexport function isObject(item) {\r\n return Object.prototype.toString.call(item) === '[object Object]';\r\n}\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @function isObjectEmpty\r\n *\r\n * @param {Object} item - The object to be checked.\r\n *\r\n * @returns {boolean} Returns `true` if the item is an empty object, `false`\r\n * otherwise.\r\n */\r\nexport function isObjectEmpty(item) {\r\n return (\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\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @function isPrivateRangeUrlFound\r\n *\r\n * @param {string} item - The string to be checked for a private IP range URL.\r\n *\r\n * @returns {boolean} Returns `true` if a private IP range URL is found, `false`\r\n * otherwise.\r\n */\r\nexport function 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 * Utility to measure elapsed time using the Node.js `process.hrtime()` method.\r\n *\r\n * @function measureTime\r\n *\r\n * @returns {Function} A function to calculate the elapsed time in milliseconds.\r\n */\r\nexport function measureTime() {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @function roundNumber\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 function 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 * @function toBoolean\r\n *\r\n * @param {unknown} 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 function toBoolean(item) {\r\n return ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n}\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n deepCopy,\r\n expBackoff,\r\n getAbsolutePath,\r\n getBase64,\r\n getNewDate,\r\n getNewDateTime,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n measureTime,\r\n roundNumber,\r\n toBoolean\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module for managing logging functionality with customizable\r\n * log levels, console and file logging options, and error handling support.\r\n * The module also ensures that file-based logs are stored in a structured\r\n * directory, creating the necessary paths automatically if they do not exist.\r\n */\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getAbsolutePath, getNewDate } from './utils.js';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nconst logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Full path to the log file\r\n pathToLog: '',\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};\r\n\r\n/**\r\n * Logs a message with a specified log level. Accepts a variable number\r\n * of arguments. The arguments after the `level` are passed to `console.log`\r\n * and/or used to construct and append messages to a log file.\r\n *\r\n * @function log\r\n *\r\n * @param {...unknown} args - An array of arguments where the first is the log\r\n * level and the remaining are strings used to build the log message.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function 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 the log level is within a correct range or is it 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n _logToFile(texts, prefix);\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Logs an error message along with its stack trace. Optionally, a custom\r\n * message can be provided.\r\n *\r\n * @function logWithStack\r\n *\r\n * @param {number} newLevel - The log level.\r\n * @param {Error} error - The error object containing the stack trace.\r\n * @param {string} customMessage - An optional custom message to be included\r\n * in the log alongside the error.\r\n *\r\n * @returns {void} Exits the function execution if attempting to log at a level\r\n * higher than allowed.\r\n */\r\nexport function logWithStack(newLevel, error, customMessage) {\r\n // Get the main message\r\n const mainMessage = customMessage || (error && error.message) || '';\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if the 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 // Create a message's prefix\r\n const prefix = `${getNewDate()} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Add the whole stack message\r\n const stackMessage = error && error.stack;\r\n\r\n // Combine custom message or error message with error stack message, if exists\r\n const texts = [mainMessage];\r\n if (stackMessage) {\r\n texts.push('\\n', stackMessage);\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 // 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 texts.shift()[colors[newLevel - 1]],\r\n ...texts\r\n ])\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @function initLogging\r\n *\r\n * @param {Object} loggingOptions - The configuration object containing\r\n * `logging` options.\r\n */\r\nexport function initLogging(loggingOptions) {\r\n // Get options from the `loggingOptions` object\r\n const { level, dest, file, toConsole, toFile } = loggingOptions;\r\n\r\n // Reset flags to the default values\r\n logging.pathCreated = false;\r\n logging.pathToLog = '';\r\n\r\n // Set the logging level\r\n setLogLevel(level);\r\n\r\n // Set the console logging\r\n enableConsoleLogging(toConsole);\r\n\r\n // Set the file logging\r\n enableFileLogging(dest, file, toFile);\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 * @function setLogLevel\r\n *\r\n * @param {number} level - The log level to be set.\r\n */\r\nexport function setLogLevel(level) {\r\n if (\r\n Number.isInteger(level) &&\r\n level >= 0 &&\r\n level <= logging.levelsDesc.length\r\n ) {\r\n // Update the module logging's `level` option\r\n logging.level = level;\r\n }\r\n}\r\n\r\n/**\r\n * Enables console logging.\r\n *\r\n * @function enableConsoleLogging\r\n *\r\n * @param {boolean} toConsole - The flag for setting the logging to the console.\r\n */\r\nexport function enableConsoleLogging(toConsole) {\r\n // Update the module logging's `toConsole` option\r\n logging.toConsole = !!toConsole;\r\n}\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file name.\r\n *\r\n * @function enableFileLogging\r\n *\r\n * @param {string} dest - The destination path where the log file should\r\n * be saved.\r\n * @param {string} file - The name of the log file.\r\n * @param {boolean} toFile - A flag indicating whether logging should\r\n * be directed to a file.\r\n */\r\nexport function enableFileLogging(dest, file, toFile) {\r\n // Update the module logging's `toFile` option\r\n logging.toFile = !!toFile;\r\n\r\n // Set the `dest` and `file` options only if the file logging is enabled\r\n if (logging.toFile) {\r\n logging.dest = dest || 'log';\r\n logging.file = file || 'highcharts-export-server.log';\r\n }\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\r\n * the content, including an optional prefix, to the specified log file.\r\n *\r\n * @function _logToFile\r\n *\r\n * @param {Array} 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\nfunction _logToFile(texts, prefix) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(getAbsolutePath(logging.dest)) &&\r\n mkdirSync(getAbsolutePath(logging.dest));\r\n\r\n // Create the full path\r\n logging.pathToLog = getAbsolutePath(join(logging.dest, logging.file));\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.pathToLog,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error && logging.toFile && logging.pathCreated) {\r\n logging.toFile = false;\r\n logging.pathCreated = false;\r\n logWithStack(2, error, `[logger] Unable to write to log file.`);\r\n }\r\n }\r\n );\r\n}\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n setLogLevel,\r\n enableConsoleLogging,\r\n enableFileLogging\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module manages configuration for the Highcharts Export Server\r\n * by loading and merging options from multiple sources, such as the default\r\n * settings, environment variables, user-provided options, and command-line\r\n * arguments. Ensures the global options are up-to-date with the highest\r\n * priority values. Provides functions for accessing and updating configuration.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\n\r\nimport { envs } from './envs.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { deepCopy, getAbsolutePath, isObject } from './utils.js';\r\n\r\nimport defaultConfig from './schemas/config.js';\r\n\r\n// Sets the global options with initial values from the default config\r\nconst globalOptions = _initOptions(defaultConfig);\r\n\r\n// Properties nesting level of all options\r\nconst nestedProps = _createNestedProps(defaultConfig);\r\n\r\n// Properties names that should not be recursively merged\r\nconst absoluteProps = _createAbsoluteProps(defaultConfig);\r\n\r\n/**\r\n * Retrieves a copy of the global options object or an original global options\r\n * object, based on the `getCopy` flag.\r\n *\r\n * @function getOptions\r\n *\r\n * @param {boolean} [getCopy=true] - Specifies whether to return a copied\r\n * object of the global options (`true`) or a reference to the global options\r\n * object (`false`). The default value is `true`.\r\n *\r\n * @returns {Object} A copy of the global options object, or a reference\r\n * to the global options object.\r\n */\r\nexport function getOptions(getCopy = true) {\r\n // Return a copy or an original global options object\r\n return getCopy ? deepCopy(globalOptions) : globalOptions;\r\n}\r\n\r\n/**\r\n * Updates and returns the global options object or a copy of the global options\r\n * object, based on the `getCopy` flag.\r\n *\r\n * @function updateOptions\r\n *\r\n * @param {Object} newOptions - An object containing the new options to be\r\n * merged into the global options.\r\n * @param {boolean} [getCopy=false] - Determines whether to merge the new\r\n * options into a copy of the global options object (`true`) or directly into\r\n * the global options object (`false`). The default value is `false`.\r\n *\r\n * @returns {Object} The updated options object, either the modified global\r\n * options or a modified copy, based on the value of `getCopy`.\r\n */\r\nexport function updateOptions(newOptions, getCopy = false) {\r\n // Merge new options to the global options or its copy and return the result\r\n return _mergeOptions(getOptions(getCopy), newOptions);\r\n}\r\n\r\n/**\r\n * Updates and returns the global options object with values provided through\r\n * the CLI, keeping the principle of options load priority. The function accepts\r\n * a `cliArgs` array containing arguments from the CLI, which will be validated\r\n * and applied if provided.\r\n *\r\n * The function prioritizes values in the following order:\r\n *\r\n * 1. Values from the command line interface (CLI).\r\n * 2. Values from a custom JSON file (loaded by the `--loadConfig` option).\r\n *\r\n * @function setCliOptions\r\n *\r\n * @param {Array} cliArgs - An array of command line arguments used\r\n * for additional configuration.\r\n *\r\n * @returns {Object} The updated global options object, reflecting the merged\r\n * configuration from sources provided through the CLI.\r\n */\r\nexport function setCliOptions(cliArgs) {\r\n // Only for the CLI usage\r\n if (cliArgs && Array.isArray(cliArgs) && cliArgs.length) {\r\n // Get options from the custom JSON loaded via the `--loadConfig`\r\n const configOptions = _loadConfigFile(cliArgs);\r\n\r\n // Update global options with the values from the `configOptions` object\r\n updateOptions(configOptions);\r\n\r\n // Get options from the CLI\r\n const cliOptions = _pairArgumentValue(cliArgs);\r\n\r\n // Update global options with the values from the `cliOptions` object\r\n updateOptions(cliOptions);\r\n }\r\n\r\n // Return reference to the global options\r\n return getOptions(false);\r\n}\r\n\r\n/**\r\n * Maps old-structured configuration options (PhantomJS-based) to a new format\r\n * (Puppeteer-based). This function converts flat, old-structured options into\r\n * a new, nested configuration format based on a predefined mapping provided\r\n * in the `nestedProps` object. The new format is used for Puppeteer, while\r\n * the old format was used for PhantomJS.\r\n *\r\n * @function mapToNewOptions\r\n *\r\n * @param {Object} oldOptions - The old, flat configuration options\r\n * to be converted.\r\n *\r\n * @returns {Object} A new object containing options structured according\r\n * to the mapping defined in the `nestedProps` object or an empty object\r\n * if the provided `oldOptions` is not a correct object.\r\n */\r\nexport function mapToNewOptions(oldOptions) {\r\n // An object for the new structured options\r\n const newOptions = {};\r\n\r\n // Check if provided value is a correct object\r\n if (isObject(oldOptions)) {\r\n // Iterate over each key-value pair in the old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n // If there is a nested mapping, split it into a properties chain\r\n const propertiesChain = nestedProps[key]\r\n ? nestedProps[key].split('.')\r\n : [];\r\n\r\n // If it is the last property in the chain, assign the value, otherwise,\r\n // create or reuse the nested object\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 } else {\r\n log(\r\n 2,\r\n '[config] No correct object with options was provided. Returning an empty object.'\r\n );\r\n }\r\n\r\n // Return the new, structured options object\r\n return newOptions;\r\n}\r\n\r\n/**\r\n * Validates, parses, and checks if the provided config is allowed set\r\n * of options.\r\n *\r\n * @function isAllowedConfig\r\n *\r\n * @param {unknown} config - The config to be validated and parsed as a set\r\n * of options. Must be either an object or a string.\r\n * @param {boolean} [toString=false] - Whether to return a stringified version\r\n * of the parsed config. The default value is `false`.\r\n * @param {boolean} [allowFunctions=false] - Whether to allow functions\r\n * in the parsed config. If `true`, functions are preserved. Otherwise, when\r\n * a function is found, `null` is returned. The default value is `false`.\r\n *\r\n * @returns {(Object|string|null)} Returns a parsed set of options object,\r\n * a stringified set of options object if the `toString` is `true`, and `null`\r\n * if the config is not a valid set of options or parsing fails.\r\n */\r\nexport function isAllowedConfig(\r\n config,\r\n toString = false,\r\n allowFunctions = false\r\n) {\r\n try {\r\n // Accept only objects and strings\r\n if (!isObject(config) && typeof config !== 'string') {\r\n // Return `null` if any other type\r\n return null;\r\n }\r\n\r\n // Get the object representation of the original config\r\n const objectConfig =\r\n typeof config === 'string'\r\n ? allowFunctions\r\n ? eval(`(${config})`)\r\n : JSON.parse(config)\r\n : config;\r\n\r\n // Preserve or remove potential functions based on the `allowFunctions` flag\r\n const stringifiedOptions = _optionsStringify(\r\n objectConfig,\r\n allowFunctions,\r\n false\r\n );\r\n\r\n // Parse the config to check if it is valid set of options\r\n const parsedOptions = allowFunctions\r\n ? JSON.parse(\r\n _optionsStringify(objectConfig, allowFunctions, true),\r\n (_, value) =>\r\n typeof value === 'string' && value.startsWith('function')\r\n ? eval(`(${value})`)\r\n : value\r\n )\r\n : JSON.parse(stringifiedOptions);\r\n\r\n // Return stringified or object options based on the `toString` flag\r\n return toString ? stringifiedOptions : parsedOptions;\r\n } catch (error) {\r\n // Return `null` if parsing fails\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Initializes and returns the global options object based on the provided\r\n * configuration, setting values from nested properties recursively.\r\n *\r\n * The function prioritizes values in the following order:\r\n *\r\n * 1. Values from environment variables (specified in the `.env` file).\r\n * 2. Values from the `./lib/schemas/config.js` file (defaults).\r\n *\r\n * @function _initOptions\r\n *\r\n * @param {Object} config - The configuration object used for initializing\r\n * the global options. It should include nested properties with a `value`\r\n * and an `envLink` for linking to environment variables.\r\n *\r\n * @returns {Object} The initialized global options object, populated with\r\n * values based on the provided configuration and the established priority\r\n * order.\r\n */\r\nfunction _initOptions(config) {\r\n // Init the object for options\r\n const options = {};\r\n\r\n // Start initializing the `options` object recursively\r\n for (const [name, item] of Object.entries(config)) {\r\n if (Object.prototype.hasOwnProperty.call(item, 'value')) {\r\n // Set the correct value based on the established priority order\r\n if (envs[item.envLink] !== undefined && envs[item.envLink] !== null) {\r\n // The environment variables value\r\n options[name] = envs[item.envLink];\r\n } else {\r\n // The value from the config file\r\n options[name] = item.value;\r\n }\r\n } else {\r\n // Create a category of options in the `options` object\r\n options[name] = _initOptions(item);\r\n }\r\n }\r\n\r\n // Return the created `options` object\r\n return options;\r\n}\r\n\r\n/**\r\n * Recursively merges two sets of configuration options, taking into account\r\n * properties specified in the `absoluteProps` array that require absolute\r\n * merging. The `originalOptions` object will be extended with options from\r\n * the `newOptions` object.\r\n *\r\n * @function _mergeOptions\r\n *\r\n * @param {Object} originalOptions - The original configuration options object\r\n * to be extended.\r\n * @param {Object} newOptions - The new configuration options object to merge.\r\n *\r\n * @returns {Object} The extended `originalOptions` object.\r\n */\r\nfunction _mergeOptions(originalOptions, newOptions) {\r\n // Check if the `originalOptions` and `newOptions` are correct objects\r\n if (isObject(originalOptions) && isObject(newOptions)) {\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n originalOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n originalOptions[key] !== undefined\r\n ? _mergeOptions(originalOptions[key], value)\r\n : value !== undefined\r\n ? value\r\n : originalOptions[key] || null;\r\n }\r\n }\r\n\r\n // Return the original (modified or not) options\r\n return originalOptions;\r\n}\r\n\r\n/**\r\n * Converts the provided options object to a JSON string with the option\r\n * to preserve functions. In order for a function to be preserved, it needs\r\n * to follow the format `function (...) {...}`. Such a function can also\r\n * be stringified.\r\n *\r\n * @function _optionsStringify\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. Otherwise an error is thrown.\r\n * @param {boolean} stringifyFunctions - If set to `true`, functions are saved\r\n * as strings. The `allowFunctions` must be set to `true` as well for this\r\n * to take an effect.\r\n *\r\n * @returns {string} The JSON-formatted string representing the options.\r\n *\r\n * @throws {Error} Throws an `Error` when functions are not allowed but are\r\n * found in provided options object.\r\n */\r\nfunction _optionsStringify(options, allowFunctions, stringifyFunctions) {\r\n const replacerCallback = (_, value) => {\r\n // Trim string values\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n }\r\n\r\n // If `value` is a function or stringified function\r\n if (\r\n typeof value === 'function' ||\r\n (typeof value === 'string' &&\r\n value.startsWith('function') &&\r\n value.endsWith('}'))\r\n ) {\r\n // If the `allowFunctions` is set to `true`, preserve functions\r\n if (allowFunctions) {\r\n // Based on the `stringifyFunctions` options, set function values\r\n return stringifyFunctions\r\n ? // As stringified functions\r\n `\"EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN\"`\r\n : // As functions\r\n `EXP_FUN${(value + '').replaceAll(/\\s+/g, ' ')}EXP_FUN`;\r\n } else {\r\n // Throw an error otherwise\r\n throw new Error();\r\n }\r\n }\r\n\r\n // In all other cases, simply return the value\r\n return 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 stringifyFunctions ? /\\\\\"EXP_FUN|EXP_FUN\\\\\"/g : /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n}\r\n\r\n/**\r\n * Loads additional configuration from a specified file provided via\r\n * the `--loadConfig` option in the command-line arguments.\r\n *\r\n * @function _loadConfigFile\r\n *\r\n * @param {Array} cliArgs - Command-line arguments to search\r\n * for the `--loadConfig` option and the corresponding file path.\r\n *\r\n * @returns {Object} The additional configuration loaded from the specified\r\n * file, or an empty object if the file is not found, invalid, or an error\r\n * occurs.\r\n */\r\nfunction _loadConfigFile(cliArgs) {\r\n // Get the allow flags for the custom logic check\r\n const { allowCodeExecution, allowFileResources } = getOptions().customLogic;\r\n\r\n // Check if the `--loadConfig` option was used\r\n const configIndex = cliArgs.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Get the `--loadConfig` option value\r\n const configFileName = configIndex > -1 && cliArgs[configIndex + 1];\r\n\r\n // Check if the `--loadConfig` is present and has a correct value\r\n if (configFileName && allowFileResources) {\r\n try {\r\n // Load an optional custom JSON config file\r\n return isAllowedConfig(\r\n readFileSync(getAbsolutePath(configFileName), 'utf8'),\r\n false,\r\n allowCodeExecution\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 ${configFileName} 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 * Parses command-line arguments and pairs each argument with its corresponding\r\n * option in the configuration. The values are structured into a nested options\r\n * object, based on predefined mappings in the `nestedProps` object.\r\n *\r\n * @function _pairArgumentValue\r\n *\r\n * @param {Array} cliArgs - An array of command-line arguments\r\n * containing options and their associated values.\r\n *\r\n * @returns {Object} An updated options object where each option from\r\n * the command-line is paired with its value, structured into nested objects\r\n * as defined.\r\n */\r\nfunction _pairArgumentValue(cliArgs) {\r\n // An empty object to collect and structurize data from the args\r\n const cliOptions = {};\r\n\r\n // Cycle through all CLI args and filter them\r\n for (let i = 0; i < cliArgs.length; i++) {\r\n const option = cliArgs[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedProps[option]\r\n ? nestedProps[option].split('.')\r\n : [];\r\n\r\n // Create options object with values from CLI for later parsing and merging\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n const value = cliArgs[++i];\r\n if (!value) {\r\n log(\r\n 2,\r\n `[config] Missing value for the CLI '--${option}' argument. Using the default value.`\r\n );\r\n }\r\n obj[prop] = value || null;\r\n } else if (obj[prop] === undefined) {\r\n obj[prop] = {};\r\n }\r\n return obj[prop];\r\n }, cliOptions);\r\n }\r\n\r\n // Return parsed CLI options\r\n return cliOptions;\r\n}\r\n\r\n/**\r\n * Recursively generates a mapping of nested argument chains from a nested\r\n * config object. This function traverses a nested object and creates a mapping\r\n * where each key is an argument name (either from `cliName`, `legacyName`,\r\n * or the original key) and each value is a string representing the chain\r\n * of nested properties leading to that argument.\r\n *\r\n * @function _createNestedProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Object} [nestedProps={}] - The accumulator object for storing\r\n * the resulting arguments chains. The default value is an empty object.\r\n * @param {string} [propChain=''] - The current chain of nested properties,\r\n * used internally during recursion. The default value is an empty string.\r\n *\r\n * @returns {Object} An object mapping argument names to their corresponding\r\n * nested property chains.\r\n */\r\nfunction _createNestedProps(config, nestedProps = {}, propChain = '') {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.value === 'undefined') {\r\n // Recurse into deeper levels of nested arguments\r\n _createNestedProps(entry, nestedProps, `${propChain}.${key}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedProps[entry.cliName || key] = `${propChain}.${key}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedProps[entry.legacyName] = `${propChain}.${key}`.substring(1);\r\n }\r\n }\r\n });\r\n\r\n // Return the object with nested argument chains\r\n return nestedProps;\r\n}\r\n\r\n/**\r\n * Recursively gathers the names of properties from a configuration object that\r\n * should be treated as absolute properties. These properties have values that\r\n * are objects and do not contain further nested depth when merging an object\r\n * containing these options.\r\n *\r\n * @function _createAbsoluteProps\r\n *\r\n * @param {Object} config - The configuration object.\r\n * @param {Array} [absoluteProps=[]] - An array to collect the names\r\n * of absolute properties. The default value is an empty array.\r\n *\r\n * @returns {Array} An array containing the names of absolute\r\n * properties.\r\n */\r\nfunction _createAbsoluteProps(config, absoluteProps = []) {\r\n Object.keys(config).forEach((key) => {\r\n // Get the specific section\r\n const entry = config[key];\r\n\r\n // Check if there is still more depth to traverse\r\n if (typeof entry.types === 'undefined') {\r\n // Recurse into deeper levels\r\n _createAbsoluteProps(entry, absoluteProps);\r\n } else {\r\n // If the option can be an object, save its type in the array\r\n if (entry.types.includes('Object')) {\r\n absoluteProps.push(key);\r\n }\r\n }\r\n });\r\n\r\n // Return the array with the names of absolute properties\r\n return absoluteProps;\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n updateOptions,\r\n setCliOptions,\r\n mapToNewOptions,\r\n isAllowedConfig\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 HTTP utility module for fetching and posting data. Supports both\r\n * HTTP and HTTPS protocols, providing methods to make GET and POST requests\r\n * with customizable options. Includes protocol determination based on URL\r\n * and augments response objects with a 'text' property for easier data access.\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Sends a GET request to the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @async\r\n * @function get\r\n *\r\n * @param {string} url - The URL to get data from.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function get(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n // Decide on the protocol\r\n _getProtocolModule(url)\r\n .get(url, requestOptions, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n if (!responseData) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n\r\n // Get the full result and resolve the request\r\n response.text = responseData;\r\n resolve(response);\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 * @async\r\n * @function post\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 * The default value is an empty object.\r\n * @param {Object} [requestOptions={}] - Options for the HTTP/HTTPS request.\r\n * The default value is an empty object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the HTTP/HTTPS response\r\n * object with added 'text' property or rejecting with an error.\r\n */\r\nexport async function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\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 // Decide on the protocol\r\n const request = _getProtocolModule(url)\r\n .request(url, options, (response) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received\r\n response.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received\r\n response.on('end', () => {\r\n try {\r\n // Get the full result and resolve the request\r\n response.text = responseData;\r\n resolve(response);\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 request.write(data);\r\n request.end();\r\n });\r\n}\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @function _getProtocolModule\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\nfunction _getProtocolModule(url) {\r\n return url.startsWith('https') ? https : http;\r\n}\r\n\r\nexport default {\r\n get,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 * A custom error class for handling export-related errors. Extends the native\r\n * `Error` class to include additional properties like status code and stack\r\n * trace details.\r\n */\r\nclass ExportError extends Error {\r\n /**\r\n * Creates an instance of the `ExportError`.\r\n *\r\n * @param {string} message - The error message to be displayed.\r\n * @param {number} statusCode - Optional HTTP status code associated\r\n * with the error (e.g., 400, 500).\r\n */\r\n constructor(message, statusCode) {\r\n super();\r\n\r\n // Set the `message` and `stackMessage` with provided message\r\n this.message = message;\r\n this.stackMessage = message;\r\n\r\n // Set the `statusCode` if provided\r\n if (statusCode) {\r\n this.statusCode = statusCode;\r\n }\r\n }\r\n\r\n /**\r\n * Sets additional error details based on an existing error object.\r\n *\r\n * @param {Error} error - An error object containing details to populate\r\n * the `ExportError` instance.\r\n *\r\n * @returns {ExportError} The updated instance of the `ExportError` class.\r\n */\r\n setError(error) {\r\n // Save the provided error\r\n this.error = error;\r\n\r\n // Set the error's name if present\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n\r\n // Set the error's status code if present\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n\r\n // Set the error's stack and stack's message if present\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\r\n\r\n // Return updated `ExportError` instance\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-2025, 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 The cache manager is responsible for handling and managing\r\n * the Highcharts library along with its dependencies. It ensures that these\r\n * resources are stored and retrieved efficiently to optimize performance\r\n * and reduce redundant network requests. The cache is stored in the `.cache`\r\n * directory by default, which serves as a dedicated folder for keeping cached\r\n * files.\r\n */\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions, updateOptions } from './config.js';\r\nimport { get } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The initial cache template\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 * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @async\r\n * @function checkCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n */\r\nexport async function checkCache(highchartsOptions, serverProxyOptions) {\r\n try {\r\n let fetchedModules;\r\n\r\n // Get the cache path\r\n const cachePath = getCachePath();\r\n\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, { recursive: true });\r\n\r\n // Fetch all the scripts either if the `manifest.json` does not exist\r\n // or if the `forceFetch` option is enabled\r\n if (!existsSync(manifestPath) || highchartsOptions.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n\r\n // The initial cache update\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath), 'utf8');\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 // Get the actual number of scripts to be fetched\r\n const { coreScripts, moduleScripts, indicatorScripts } =\r\n highchartsOptions;\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 !== highchartsOptions.version) {\r\n // Check the 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 (\r\n Object.keys(manifest.modules || {}).length !== numberOfModules\r\n ) {\r\n // Check the number of modules\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 // Update cache if needed\r\n if (requestUpdate) {\r\n fetchedModules = await _updateCache(\r\n highchartsOptions,\r\n serverProxyOptions,\r\n sourcePath\r\n );\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 // Extract and save version of currently used Highcharts\r\n cache.hcVersion = _extractHcVersion(cache.sources);\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(highchartsOptions.version, fetchedModules);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not configure cache and create or update the config manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Gets the version of Highcharts from the cache.\r\n *\r\n * @function getHcVersion\r\n *\r\n * @returns {string} The cached Highcharts version.\r\n */\r\nexport function getHcVersion() {\r\n return cache.hcVersion;\r\n}\r\n\r\n/**\r\n * Updates the Highcharts version in the applied configuration and checks\r\n * the cache for the scripts of a new version.\r\n *\r\n * @async\r\n * @function updateHcVersion\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n */\r\nexport async function updateHcVersion(newVersion) {\r\n // Update to the new version\r\n const options = updateOptions({\r\n highcharts: {\r\n version: newVersion\r\n }\r\n });\r\n\r\n // Check if cache needs to be updated\r\n await checkCache(options.highcharts, options.server.proxy);\r\n}\r\n\r\n/**\r\n * Retrieves the current cache object.\r\n *\r\n * @function getCache\r\n *\r\n * @returns {Object} The cache object containing various cached data.\r\n */\r\nexport function getCache() {\r\n return cache;\r\n}\r\n\r\n/**\r\n * Gets the cache path for Highcharts.\r\n *\r\n * @function getCachePath\r\n *\r\n * @returns {string} The absolute path to the cache directory for Highcharts.\r\n */\r\nexport function getCachePath() {\r\n return getAbsolutePath(getOptions().highcharts.cachePath, 'utf8'); // #562\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 * @async\r\n * @function _saveConfigToManifest\r\n *\r\n * @param {number} version - The currently used Highcharts version.\r\n * @param {Object} [fetchedModules={}] - An object which tracks which modules\r\n * have been fetched. The default value is an empty object.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs while\r\n * writing the cache manifest.\r\n */\r\nasync function _saveConfigToManifest(version, fetchedModules = {}) {\r\n // Update cache object with the current modules\r\n cache.activeManifest = {\r\n version,\r\n modules: fetchedModules\r\n };\r\n\r\n log(3, '[cache] Writing a new manifest.');\r\n try {\r\n writeFileSync(\r\n join(getCachePath(), 'manifest.json'),\r\n JSON.stringify(cache.activeManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Error writing the cache manifest.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts content and information,\r\n * and used Highcharts version.\r\n *\r\n * @async\r\n * @function _updateCache\r\n *\r\n * @param {Object} highchartsOptions - The configuration object containing\r\n * `highcharts` options.\r\n * @param {Object} serverProxyOptions - The configuration object containing\r\n * `server.proxy` options.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise that resolves 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\nasync function _updateCache(highchartsOptions, serverProxyOptions, sourcePath) {\r\n try {\r\n // Get Highcharts version for scripts\r\n const hcVersion =\r\n highchartsOptions.version === 'latest'\r\n ? null\r\n : `${highchartsOptions.version}`;\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n // Get the CDN url for scripts\r\n const cdnUrl = highchartsOptions.cdnUrl || cache.cdnUrl;\r\n\r\n // Prepare options for a request\r\n const requestOptions = _configureRequest(serverProxyOptions);\r\n\r\n // An object to record which scripts are fetched\r\n const fetchedModules = {};\r\n\r\n // Join all fetched scripts and save in the manifest's sources\r\n cache.sources = (\r\n await Promise.all([\r\n // Highcharts core scripts fetch\r\n ...highchartsOptions.coreScripts.map((cs) =>\r\n _fetchScript(\r\n hcVersion ? `${cdnUrl}/${hcVersion}/${cs}` : `${cdnUrl}/${cs}`,\r\n requestOptions,\r\n fetchedModules,\r\n true\r\n )\r\n ),\r\n // Highcharts module scripts fetch\r\n ...highchartsOptions.moduleScripts.map((ms) =>\r\n _fetchScript(\r\n ms === 'map'\r\n ? hcVersion\r\n ? `${cdnUrl}/maps/${hcVersion}/modules/${ms}`\r\n : `${cdnUrl}/maps/modules/${ms}`\r\n : hcVersion\r\n ? `${cdnUrl}/${hcVersion}/modules/${ms}`\r\n : `${cdnUrl}/modules/${ms}`,\r\n requestOptions,\r\n fetchedModules\r\n )\r\n ),\r\n // Highcharts indicator scripts fetch\r\n ...highchartsOptions.indicatorScripts.map((is) =>\r\n _fetchScript(\r\n hcVersion\r\n ? `${cdnUrl}/stock/${hcVersion}/indicators/${is}`\r\n : `${cdnUrl}/stock/indicators/${is}`,\r\n requestOptions,\r\n fetchedModules\r\n )\r\n ),\r\n // Custom scripts fetch\r\n ...highchartsOptions.customScripts.map((cs) =>\r\n _fetchScript(`${cs}`, requestOptions)\r\n )\r\n ])\r\n ).join(';\\n');\r\n\r\n // Extract and save version of currently used Highcharts\r\n cache.hcVersion = _extractHcVersion(cache.sources);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n\r\n // Return the fetched modules\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 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Fetches a single script and updates the `fetchedModules` accordingly.\r\n *\r\n * @async\r\n * @function _fetchScript\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional requests options.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\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. The default\r\n * value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem\r\n * with fetching the script.\r\n */\r\nasync function _fetchScript(\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // Fetch the script\r\n const response = await get(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = _extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n return response.text;\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 ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`,\r\n 404\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Configures a proxy agent for outgoing HTTP requests based on the provided\r\n * `server.proxy` options. If a valid `host` and `port` are specified, it tries\r\n * to create an `HttpsProxyAgent`. If the creation fails, an `ExportError`\r\n * is thrown. If no proxy is configured, an empty object is returned.\r\n *\r\n * @function _configureRequest\r\n *\r\n * @param {Object} serverProxyOptions- The configuration object containing\r\n * `server.proxy` options.\r\n *\r\n * @returns {Object} The request options, including the proxy agent if created,\r\n * or an empty object if no proxy configuration is provided.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the proxy agent creation\r\n * fails.\r\n */\r\nfunction _configureRequest(serverProxyOptions) {\r\n // Get the `host` and `port` of the proxy\r\n const proxyHost = serverProxyOptions.host;\r\n const proxyPort = serverProxyOptions.port;\r\n\r\n // Try to create a proxy agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n // Create the agent\r\n const proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: proxyPort\r\n });\r\n\r\n // Add the agent to the request's options\r\n return {\r\n agent: proxyAgent,\r\n timeout: serverProxyOptions.timeout\r\n };\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Could not create a Proxy Agent.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n\r\n // Return an empty object when no proxy agent is created\r\n return {};\r\n}\r\n\r\n/**\r\n * Extracts Highcharts version from the cache's sources string.\r\n *\r\n * @function _extractHcVersion\r\n *\r\n * @param {Object} cacheSources - The cache sources object.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nfunction _extractHcVersion(cacheSources) {\r\n return cacheSources\r\n .substring(0, cacheSources.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` property.\r\n *\r\n * @function _extractModuleName\r\n *\r\n * @param {string} scriptPath - The path of the script from which the module\r\n * name will be extracted.\r\n *\r\n * @returns {string} The extracted module name.\r\n */\r\nfunction _extractModuleName(scriptPath) {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n}\r\n\r\nexport default {\r\n checkCache,\r\n getHcVersion,\r\n updateHcVersion,\r\n getCache,\r\n getCachePath\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides methods for initializing Highcharts with customized\r\n * animation settings and triggering the creation of Highcharts charts with\r\n * export-specific configurations in the page context. Supports dynamic option\r\n * merging, custom logic injection, and control over rendering behaviors. Used\r\n * by the Puppeteer page.\r\n */\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the `Highcharts.animObject` function. Called when initing the page.\r\n *\r\n * @function setupHighcharts\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 Highcharts chart on a page.\r\n *\r\n * @async\r\n * @function createChart\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n */\r\nexport async function createChart(exportOptions, customLogicOptions) {\r\n // Get required functions\r\n const { getOptions, setOptions, merge, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential `setOptions` usages in order\r\n // to prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\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 the `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 the `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 // Some mandatory additional `chart` and `exporting` options\r\n const additionalOptions = {\r\n chart: {\r\n // By default animation is disabled\r\n animation: false,\r\n // Get the right size values\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n },\r\n exporting: {\r\n // No need for the exporting button\r\n enabled: false\r\n }\r\n };\r\n\r\n // Get the input to export from the `instr` option\r\n const userOptions = new Function(`return ${exportOptions.instr}`)();\r\n\r\n // Get the `themeOptions` option\r\n const themeOptions = new Function(`return ${exportOptions.themeOptions}`)();\r\n\r\n // Merge the following options objects to create final options\r\n const finalOptions = merge(\r\n false,\r\n themeOptions,\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n additionalOptions\r\n );\r\n\r\n // Prepare the `callback` option\r\n const finalCallback = customLogicOptions.callback\r\n ? new Function(`return ${customLogicOptions.callback}`)()\r\n : null;\r\n\r\n // Trigger the `customCode` option\r\n if (customLogicOptions.customCode) {\r\n new Function('options', customLogicOptions.customCode)(userOptions);\r\n }\r\n\r\n // Get the `globalOptions` option\r\n const globalOptions = new Function(`return ${exportOptions.globalOptions}`)();\r\n\r\n // Set the global options if exist\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n // Call the chart creation\r\n Highcharts[exportOptions.constr]('container', finalOptions, finalCallback);\r\n\r\n // Get all images from within the chart\r\n const images = Array.from(\r\n document.querySelectorAll('.highcharts-container image')\r\n );\r\n\r\n // Wait for all images for 2 seconds\r\n await Promise.race([\r\n Promise.all(\r\n images.map((image) =>\r\n image.complete && image.naturalHeight !== 0\r\n ? Promise.resolve()\r\n : new Promise((resolve) =>\r\n image.addEventListener('load', resolve, { once: true })\r\n )\r\n )\r\n ),\r\n // Proceed further even if images did not load\r\n new Promise((resolve) => setTimeout(resolve, 2000))\r\n ]);\r\n\r\n // Get the current global options\r\n const defaultOptions = getOptions();\r\n\r\n // Clear it just in case (e.g. the `setOptions` was used in the `customCode`)\r\n for (const prop in defaultOptions) {\r\n if (typeof defaultOptions[prop] !== 'function') {\r\n delete defaultOptions[prop];\r\n }\r\n }\r\n\r\n // Set the default options back\r\n setOptions(Highcharts.setOptionsObj);\r\n\r\n // Empty the custom global options object\r\n Highcharts.setOptionsObj = {};\r\n}\r\n\r\nexport default {\r\n setupHighcharts,\r\n createChart\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions for managing Puppeteer browser\r\n * instance, creating and clearing pages, injecting custom JS and CSS resources,\r\n * and setting up Highcharts for server-side rendering. The module ensures\r\n * that the browser and pages are correctly managed and can handle failures\r\n * during operations like launching the browser or creating new pages.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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, getAbsolutePath } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for pages\r\nconst pageTemplate = readFileSync(\r\n join(__dirname, 'templates', 'template.html'),\r\n 'utf8'\r\n);\r\n\r\n// To save the browser\r\nlet browser = null;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @function getBrowser\r\n *\r\n * @returns {Object} The Puppeteer browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been created.\r\n */\r\nexport function getBrowser() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.', 500);\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 * @async\r\n * @function createBrowser\r\n *\r\n * @param {Array} puppeteerArgs - Additional arguments for Puppeteer\r\n * browser's launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to the created Puppeteer\r\n * browser instance.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if max retries to open\r\n * a browser instance are reached, or if no browser instance is found after\r\n * retries.\r\n */\r\nexport async function createBrowser(puppeteerArgs) {\r\n // Get `debug` and `other` options\r\n const { debug, other } = getOptions();\r\n\r\n // Get the `debug` options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n // Launch options for the browser instance\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: 'tmp',\r\n args: puppeteerArgs || [],\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n // A counter for the browser's launch retries\r\n let tryCount = 0;\r\n const openBrowser = async () => {\r\n try {\r\n log(\r\n 3,\r\n `[browser] Attempting to launch and get a browser instance (try ${++tryCount}).`\r\n );\r\n\r\n // Launch the browser\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n\r\n // Wait for a 4 seconds before trying again\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await openBrowser();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n // Try to open a browser\r\n await openBrowser();\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 500\r\n ).setError(error);\r\n }\r\n\r\n // No correct browser\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.', 500);\r\n }\r\n }\r\n\r\n // Return a browser instance\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 * @async\r\n * @function closeBrowser\r\n */\r\nexport async function closeBrowser() {\r\n // Close the browser when connected\r\n if (browser && browser.connected) {\r\n await browser.close();\r\n }\r\n browser = null;\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 * The function creates a new page, disables caching, sets content using\r\n * the `_setPageContent()`, and returns the created Puppeteer page.\r\n *\r\n * @async\r\n * @function newPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains `id`,\r\n * `workCount`, and `page`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if no valid browser\r\n * has been connected or if a page is invalid or closed.\r\n */\r\nexport async function newPage(poolResource) {\r\n // Error in case of no connected browser\r\n if (!browser || !browser.connected) {\r\n throw new ExportError(`[browser] Browser is not yet connected.`, 500);\r\n }\r\n\r\n // Create a page\r\n poolResource.page = await browser.newPage();\r\n\r\n // Disable cache\r\n await poolResource.page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await _setPageContent(poolResource.page);\r\n\r\n // Set page events\r\n _setPageEvents(poolResource.page);\r\n\r\n // Check if the page is correctly created\r\n if (!poolResource.page || poolResource.page.isClosed()) {\r\n throw new ExportError('[browser] The page is invalid or closed.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer page based on the specified mode. Logs\r\n * thrown error if clearing of a page's content fails.\r\n *\r\n * @async\r\n * @function clearPage\r\n *\r\n * @param {Object} poolResource - The pool resource that contains page and id.\r\n * @param {boolean} [hardReset=false] - 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. The default value is `false`.\r\n *\r\n * @returns {Promise} A Promise that resolves to `true` when page\r\n * is correctly cleared and `false` when it is not.\r\n */\r\nexport async function clearPage(poolResource, hardReset = false) {\r\n try {\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to `about:blank`\r\n await poolResource.page.goto('about:blank', {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n\r\n // Set the content and and scripts again\r\n await _setPageContent(poolResource.page);\r\n } else {\r\n // Clear body content\r\n await poolResource.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 `[pool] Pool resource [${poolResource.id}] - Content of the page could not be cleared.`\r\n );\r\n\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n poolResource.workCount = getOptions().pool.workLimit + 1;\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 * @async\r\n * @function addPageResources\r\n *\r\n * @param {Object} page - The Puppeteer page object to which resources will\r\n * be added.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise>} A Promise that resolves to an array\r\n * of injected resources.\r\n */\r\nexport async function addPageResources(page, customLogicOptions) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use the content of the `resources`\r\n const resources = customLogicOptions.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') ? false : true;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(getAbsolutePath(file), 'utf8')\r\n }\r\n : {\r\n url: file\r\n }\r\n );\r\n }\r\n }\r\n\r\n // The actual injection of collected scripts\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, `[browser] 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 const 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 (customLogicOptions.allowFileResources) {\r\n injectedCss.push({\r\n path: getAbsolutePath(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 // The actual injection of collected CSS\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] The CSS resource cannot be loaded.`\r\n );\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` and `addStyleTag`.\r\n * Removes injected resources and resets CSS and script tags on the page.\r\n * Additionally, it destroys previously existing charts.\r\n *\r\n * @async\r\n * @function clearPageResources\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, when doing SVG 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.\r\n *\r\n * @async\r\n * @function _setPageContent\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the content\r\n * is being set.\r\n */\r\nasync function _setPageContent(page) {\r\n // Set the initial page content\r\n await page.setContent(pageTemplate, { waitUntil: 'domcontentloaded' });\r\n\r\n // Add all registered Higcharts scripts, quite demanding\r\n await page.addScriptTag({ path: join(getCachePath(), 'sources.js') });\r\n\r\n // Set the initial `animObject` for Highcharts\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events (like `pageerror` and `console`) for a Puppeteer page in order\r\n * to catch and display errors and console logs from the window context.\r\n *\r\n * @function _setPageEvents\r\n *\r\n * @param {Object} page - The Puppeteer page object to which the listeners\r\n * are being set.\r\n */\r\nfunction _setPageEvents(page) {\r\n // Get `debug` options\r\n const { debug } = getOptions();\r\n\r\n // Set the `pageerror` listener\r\n page.on('pageerror', async () => {\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\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\r\nexport default {\r\n getBrowser,\r\n createBrowser,\r\n closeBrowser,\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-2025, 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 * The CSS to be used on the exported page.\r\n *\r\n * @returns {string} The CSS configuration.\r\n */\r\nexport default () => `\r\n\r\nhtml, body {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n}\r\n\r\n#table-div, #sliders, #datatable, #controls, .ld-row {\r\n display: none;\r\n height: 0;\r\n}\r\n\r\n#chart-container {\r\n box-sizing: border-box;\r\n margin: 0;\r\n overflow: auto;\r\n font-size: 0;\r\n}\r\n\r\n#chart-container > figure, div {\r\n margin-top: 0 !important;\r\n margin-bottom: 0 !important;\r\n}\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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\n/**\r\n * The SVG template to use when loading SVG content to be exported.\r\n *\r\n * @param {string} svg - The SVG input content to be exported.\r\n *\r\n * @returns {string} The SVG template.\r\n */\r\nexport default (svg) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${svg}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module handles chart export functionality using Puppeteer.\r\n * It supports exporting charts as SVG, PNG, JPEG, and PDF formats. The module\r\n * manages page resources, sets up the export environment, and processes chart\r\n * configurations or SVG inputs for rendering. Exports to a chart from a page\r\n * using Puppeteer.\r\n */\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { createChart } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from '../templates/svgExport/svgExport.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @async\r\n * @function puppeteerExport\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @returns {Promise<(string|Buffer|ExportError)>} A Promise that resolves\r\n * to the exported data or rejecting with an `ExportError`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if export to an unsupported\r\n * output format occurs.\r\n */\r\nexport async function puppeteerExport(page, exportOptions, customLogicOptions) {\r\n // Injected resources array (additional JS and CSS)\r\n const injectedResources = [];\r\n\r\n try {\r\n let isSVG = false;\r\n\r\n // Decide on the export method\r\n if (exportOptions.svg) {\r\n log(4, '[export] Treating as SVG input.');\r\n\r\n // If the `type` is also SVG, return the input\r\n if (exportOptions.type === 'svg') {\r\n return exportOptions.svg;\r\n }\r\n\r\n // Mark as SVG export for the later size corrections\r\n isSVG = true;\r\n\r\n // SVG export\r\n await page.setContent(svgTemplate(exportOptions.svg), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n log(4, '[export] Treating as JSON config.');\r\n\r\n // Options export\r\n await page.evaluate(createChart, exportOptions, customLogicOptions);\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.push(\r\n ...(await addPageResources(page, customLogicOptions))\r\n );\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = await _getChartSize(page, isSVG, exportOptions.scale);\r\n\r\n // Get the clip region for the page\r\n const { x, y } = await _getClipRegion(page);\r\n\r\n // Set final `height` for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n\r\n // Set final `width` for viewport\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\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 result;\r\n // Rasterization process\r\n switch (exportOptions.type) {\r\n case 'svg':\r\n result = await _createSVG(page);\r\n break;\r\n case 'png':\r\n case 'jpeg':\r\n result = await _createImage(\r\n page,\r\n exportOptions.type,\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 break;\r\n case 'pdf':\r\n result = await _createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n exportOptions.rasterizationTimeout\r\n );\r\n break;\r\n default:\r\n throw new ExportError(\r\n `[export] Unsupported output format: ${exportOptions.type}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return result;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element\r\n * with the 'chart-container' id.\r\n *\r\n * @async\r\n * @function _getClipRegion\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * `x`, `y`, `width`, and `height` properties.\r\n */\r\nasync function _getClipRegion(page) {\r\n return 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/**\r\n * Retrieves the real chart dimensions from a Puppeteer page. The function\r\n * behaves differently based on whether the export type is SVG or another\r\n * format.\r\n *\r\n * @async\r\n * @function _getChartSize\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {boolean} isSVG - Determines whether the chart being processed\r\n * is an SVG or another format.\r\n * @param {number} scale - The scale factor to be applied to the chart\r\n * dimensions.\r\n *\r\n * @returns {Promise} A Promise that resolves to an object containing\r\n * the actual height and width of the chart after scaling.\r\n */\r\nasync function _getChartSize(page, isSVG, scale) {\r\n // Trigger appropriate function based on the `isSvg` flag to get chart size\r\n return 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 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(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\r\n // of exports. 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\r\n/**\r\n * Creates an SVG by evaluating the `outerHTML` of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @async\r\n * @function _createSVG\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} A Promise that resolves to the SVG string.\r\n */\r\nasync function _createSVG(page) {\r\n return page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\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 * @async\r\n * @function _createImage\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\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} A Promise that resolves to the image buffer\r\n * or rejecting with an `ExportError` for timeout.\r\n */\r\nasync function _createImage(page, type, clip, rasterizationTimeout) {\r\n return Promise.race([\r\n page.screenshot({\r\n type,\r\n clip,\r\n encoding: 'base64',\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n captureBeyondViewport: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n // Always render on a transparent page if the expected type format is PNG\r\n omitBackground: type == 'png' // #447, #463\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout', 408)),\r\n rasterizationTimeout || 1500\r\n )\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 * @async\r\n * @function _createPDF\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 {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} A Promise that resolves to the PDF buffer.\r\n */\r\nasync function _createPDF(page, height, width, rasterizationTimeout) {\r\n await page.emulateMediaType('screen');\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: 'base64',\r\n timeout: rasterizationTimeout || 1500\r\n });\r\n}\r\n\r\nexport default {\r\n puppeteerExport\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides a worker pool implementation for managing\r\n * the browser instance and pages, specifically designed for use with\r\n * the Highcharts Export Server. It optimizes resources usage and performance\r\n * by maintaining a pool of workers that can handle concurrent export tasks\r\n * using Puppeteer.\r\n */\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { createBrowser, closeBrowser, newPage, clearPage } from './browser.js';\r\nimport { puppeteerExport } from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getNewDateTime, measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = null;\r\n\r\n// Pool statistics\r\nconst poolStats = {\r\n exportsAttempted: 0,\r\n exportsPerformed: 0,\r\n exportsDropped: 0,\r\n exportsFromSvg: 0,\r\n exportsFromOptions: 0,\r\n exportsFromSvgAttempts: 0,\r\n exportsFromOptionsAttempts: 0,\r\n timeSpent: 0,\r\n timeSpentAverage: 0\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 * @async\r\n * @function initPool\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n * @param {Array} puppeteerArgs - Additional arguments for Puppeteer\r\n * launch.\r\n *\r\n * @returns {Promise} A Promise that resolves to ending the function\r\n * execution when an already initialized pool of resources is found.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not create the pool\r\n * of workers.\r\n */\r\nexport async function initPool(poolOptions, puppeteerArgs) {\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(puppeteerArgs);\r\n\r\n try {\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolOptions.minWorkers}, max ${poolOptions.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n return;\r\n }\r\n\r\n // Keep an eye on a correct min and max workers number\r\n if (poolOptions.minWorkers > poolOptions.maxWorkers) {\r\n poolOptions.minWorkers = poolOptions.maxWorkers;\r\n }\r\n\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the `create`, `validate`, and `destroy` functions\r\n ..._factory(poolOptions),\r\n min: poolOptions.minWorkers,\r\n max: poolOptions.maxWorkers,\r\n acquireTimeoutMillis: poolOptions.acquireTimeout,\r\n createTimeoutMillis: poolOptions.createTimeout,\r\n destroyTimeoutMillis: poolOptions.destroyTimeout,\r\n idleTimeoutMillis: poolOptions.idleTimeout,\r\n createRetryIntervalMillis: poolOptions.createRetryInterval,\r\n reapIntervalMillis: poolOptions.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 clearStatus = await clearPage(resource, false);\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Releasing a worker. Clear page status: ${clearStatus}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (_eventId, resource) => {\r\n log(\r\n 4,\r\n `[pool] Pool resource [${resource.id}] - Destroyed a worker successfully.`\r\n );\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 < poolOptions.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 configure and create the pool of workers.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n\r\n/**\r\n * Terminates all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @async\r\n * @function killPool\r\n *\r\n * @returns {Promise} A Promise that resolves once all workers are\r\n * terminated, the pool is destroyed, and the browser is successfully 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, '[pool] Destroyed the pool of resources.');\r\n }\r\n pool = null;\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 * @async\r\n * @function postWork\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to the export result\r\n * and options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if an error occurs during\r\n * the export process.\r\n */\r\nexport async function postWork(options) {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n // An export attempt counted\r\n ++poolStats.exportsAttempted;\r\n\r\n // Display the pool information if needed\r\n if (options.pool.benchmarking) {\r\n _getPoolInfo();\r\n }\r\n\r\n // Throw an error in case of lacking the pool instance\r\n if (!pool) {\r\n throw new ExportError(\r\n '[pool] Work received, but pool has not been started.',\r\n 500\r\n );\r\n }\r\n\r\n // The acquire counter\r\n const acquireCounter = measureTime();\r\n\r\n // Try to acquire the worker along with the id, works count and page\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n\r\n // Acquire a pool resource\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Acquiring a worker handle took ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered when acquiring an available entry: ${acquireCounter()}ms.`,\r\n 400\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n throw new ExportError(\r\n '[pool] Resolved worker page is invalid: the pool setup is wonky.',\r\n 400\r\n );\r\n }\r\n\r\n log(\r\n 4,\r\n `[pool] Pool resource [${workerHandle.id}] - Starting work on this pool entry.`\r\n );\r\n\r\n // Start measuring export time\r\n const exportCounter = measureTime();\r\n\r\n // Perform an export on a puppeteer level\r\n const exportResult = await puppeteerExport(\r\n workerHandle.page,\r\n options.export,\r\n options.customLogic\r\n );\r\n\r\n // Check if it's an error\r\n if (exportResult instanceof Error) {\r\n // NOTE:\r\n // 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 // 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 (exportResult.message === 'Rasterization timeout') {\r\n // Set the `workLimit` to exceeded in order to recreate the resource\r\n workerHandle.workCount = options.pool.workLimit + 1;\r\n workerHandle.page = null;\r\n }\r\n\r\n if (\r\n exportResult.name === 'TimeoutError' ||\r\n exportResult.message === 'Rasterization timeout'\r\n ) {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.`\r\n ).setError(exportResult);\r\n } else {\r\n throw new ExportError(\r\n `[pool] ${\r\n options.requestId ? `Request [${options.requestId}] - ` : ''\r\n }Error encountered during export: ${exportCounter()}ms.`\r\n ).setError(exportResult);\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 `[benchmark] ${options.requestId ? `Request [${options.requestId}] - ` : ''}`,\r\n `Exporting a chart sucessfully took ${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 // Update statistics\r\n poolStats.timeSpent += exportCounter();\r\n poolStats.timeSpentAverage =\r\n poolStats.timeSpent / ++poolStats.exportsPerformed;\r\n\r\n log(4, `[pool] Work completed in ${exportCounter()}ms.`);\r\n\r\n // Otherwise return an object with the result and options\r\n return {\r\n result: exportResult,\r\n options\r\n };\r\n } catch (error) {\r\n ++poolStats.exportsDropped;\r\n\r\n // Try to release the worker, if it exists\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @function getPool\r\n *\r\n * @returns {(Object|null)} The current pool instance if initialized, or `null`\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Gets the statistic of a pool instace about exports.\r\n *\r\n * @function getPoolStats\r\n *\r\n * @returns {Object} The current pool statistics.\r\n */\r\nexport function getPoolStats() {\r\n return poolStats;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @function getPoolInfoJSON\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport function getPoolInfoJSON() {\r\n return {\r\n min: pool.min,\r\n max: pool.max,\r\n used: pool.numUsed(),\r\n available: pool.numFree(),\r\n allCreated: pool.numUsed() + pool.numFree(),\r\n pendingAcquires: pool.numPendingAcquires(),\r\n pendingCreates: pool.numPendingCreates(),\r\n pendingValidations: pool.numPendingValidations(),\r\n pendingDestroys: pool.pendingDestroys.length,\r\n absoluteAll:\r\n pool.numUsed() +\r\n pool.numFree() +\r\n pool.numPendingAcquires() +\r\n pool.numPendingCreates() +\r\n pool.numPendingValidations() +\r\n pool.pendingDestroys.length\r\n };\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\n * @function _getPoolInfo\r\n */\r\nfunction _getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n used,\r\n available,\r\n allCreated,\r\n pendingAcquires,\r\n pendingCreates,\r\n pendingValidations,\r\n pendingDestroys,\r\n absoluteAll\r\n } = 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 used resources: ${used}.`);\r\n log(5, `[pool] The number of free resources: ${available}.`);\r\n log(\r\n 5,\r\n `[pool] The number of all created (used and free) resources: ${allCreated}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be acquired: ${pendingAcquires}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be created: ${pendingCreates}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be validated: ${pendingValidations}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources waiting to be destroyed: ${pendingDestroys}.`\r\n );\r\n log(5, `[pool] The number of all resources: ${absoluteAll}.`);\r\n}\r\n\r\n/**\r\n * Factory function that returns an object with `create`, `validate`, `destroy`\r\n * functions for the pool instance.\r\n *\r\n * @function _factory\r\n *\r\n * @param {Object} poolOptions - The configuration object containing `pool`\r\n * options.\r\n */\r\nfunction _factory(poolOptions) {\r\n return {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @async\r\n * @function create\r\n *\r\n * @returns {Promise} A Promise that resolves to an object\r\n * containing the worker ID, a reference to the browser page, and initial\r\n * work count.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is an error during\r\n * the creation of the new page.\r\n */\r\n create: async () => {\r\n // Init the resource with unique id and work count\r\n const poolResource = {\r\n id: uuid(),\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolOptions.workLimit / 2))\r\n };\r\n\r\n try {\r\n // Start measuring a page creation time\r\n const startDate = getNewDateTime();\r\n\r\n // Create a new page\r\n await newPage(poolResource);\r\n\r\n // Measure the time of full creation and configuration of a page\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Successfully created a worker, took ${\r\n getNewDateTime() - startDate\r\n }ms.`\r\n );\r\n\r\n // Return ready pool resource\r\n return poolResource;\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Error encountered when creating a new page.`\r\n );\r\n throw error;\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 * @async\r\n * @function validate\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {Promise} A Promise that resolves to true if the worker\r\n * is valid and within the work limit; otherwise, to false.\r\n */\r\n validate: async (poolResource) => {\r\n // NOTE:\r\n // 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 //\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\r\n // Check if the `page` is valid\r\n if (!poolResource.page) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (no valid page is found).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `page` is closed\r\n if (poolResource.page.isClosed()) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page is closed or invalid).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `mainFrame` is detached\r\n if (poolResource.page.mainFrame().detached) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (page's frame is detached).`\r\n );\r\n return false;\r\n }\r\n\r\n // Check if the `workLimit` is exceeded\r\n if (\r\n poolOptions.workLimit &&\r\n ++poolResource.workCount > poolOptions.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Validation failed (exceeded the ${poolOptions.workLimit} works per resource limit).`\r\n );\r\n return false;\r\n }\r\n\r\n // The `poolResource` is validated\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 * @async\r\n * @function destroy\r\n *\r\n * @param {Object} poolResource - The handle to the worker, containing\r\n * the worker's ID, a reference to the browser page, and work count.\r\n */\r\n destroy: async (poolResource) => {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Destroying a worker.`\r\n );\r\n\r\n if (poolResource.page && !poolResource.page.isClosed()) {\r\n try {\r\n // Remove all attached event listeners from the resource\r\n poolResource.page.removeAllListeners('pageerror');\r\n poolResource.page.removeAllListeners('console');\r\n poolResource.page.removeAllListeners('framedetached');\r\n\r\n // We need to wait around for this\r\n await poolResource.page.close();\r\n } catch (error) {\r\n log(\r\n 3,\r\n `[pool] Pool resource [${poolResource.id}] - Page could not be closed upon destroying.`\r\n );\r\n throw error;\r\n }\r\n }\r\n }\r\n };\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolStats,\r\n getPoolInfoJSON\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 DOMPurify from 'dompurify';\r\nimport { JSDOM } from 'jsdom';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing \r\n * tags and any content within them.\r\n *\r\n * @function sanitize\r\n *\r\n * @param {string} input - The HTML string to be sanitized.\r\n *\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n // Get the virtual DOM\r\n const window = new JSDOM('').window;\r\n\r\n // Create a purifying instance\r\n const purify = DOMPurify(window);\r\n\r\n // Return sanitized input, allowing for the `foreignObject` elements\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default {\r\n sanitize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides functions that prepare for the exporting\r\n * charts into various image output formats such as JPEG, PNG, PDF, and SVGs.\r\n * It supports single and batch export operations and allows customization\r\n * through options passed from configurations or APIs.\r\n */\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { isAllowedConfig, updateOptions } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { getPoolStats, killPool, postWork } from './pool.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport { getAbsolutePath, getBase64, isObject, roundNumber } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The global flag for the code execution permission\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts a single export process based on the specified options and saves\r\n * the resulting image to the provided output file.\r\n *\r\n * @async\r\n * @function singleExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. The object must contain at least one\r\n * of the following `export` properties: `infile`, `instr`, `options`, or `svg`\r\n * to generate a valid image.\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 async function singleExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export) {\r\n // Perform an export\r\n await startExport(\r\n { export: options.export, customLogic: options.customLogic },\r\n async (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(data.result, 'base64') : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\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 } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide one of the following options: `infile`, `instr`, `options`, or `svg` to generate a valid image.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on information\r\n * provided in the `batch` option. The `batch` is a string in the following\r\n * format: \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\". Results\r\n * are saved to the specified output files.\r\n *\r\n * @async\r\n * @function batchExport\r\n *\r\n * @param {Object} options - The `options` object, which should include settings\r\n * from the `export` and `customLogic` sections. It can be a partial or complete\r\n * set of options from these sections. It must contain the `batch` option from\r\n * the `export` section to generate valid images.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * processes are 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 async function batchExport(options) {\r\n // Check if the export makes sense\r\n if (options && options.export && options.export.batch) {\r\n // An array for collecting batch exports\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 export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n },\r\n customLogic: options.customLogic\r\n },\r\n (error, data) => {\r\n // Exit process when error exists\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Get the `b64`, `outfile`, and `type` for a chart\r\n const { b64, outfile, type } = data.options.export;\r\n\r\n // Save the result\r\n try {\r\n if (b64) {\r\n // As a Base64 string to a txt file\r\n writeFileSync(\r\n `${outfile.split('.').shift() || 'chart'}.txt`,\r\n getBase64(data.result, type)\r\n );\r\n } else {\r\n // As a correct image format\r\n writeFileSync(\r\n outfile,\r\n type !== 'svg'\r\n ? Buffer.from(data.result, 'base64')\r\n : data.result\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error while saving a chart.',\r\n 500\r\n ).setError(error);\r\n }\r\n }\r\n )\r\n );\r\n } else {\r\n log(2, '[chart] No correct pair found for the batch export.');\r\n }\r\n }\r\n\r\n // Await all exports are done\r\n const batchResults = await Promise.allSettled(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n\r\n // Log errors if found\r\n batchResults.forEach((result, index) => {\r\n // Log the error with stack about the specific batch export\r\n if (result.reason) {\r\n logWithStack(\r\n 1,\r\n result.reason,\r\n `[chart] Batch export number ${index + 1} could not be correctly completed.`\r\n );\r\n }\r\n });\r\n } else {\r\n throw new ExportError(\r\n '[chart] No expected `export` options were found. Please provide the `batch` option to generate valid images.',\r\n 400\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Starts an export process. The `imageOptions` parameter is an object that\r\n * should include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If partial\r\n * options are provided, missing values will be merged with the current global\r\n * options.\r\n *\r\n * The `endCallback` function is invoked upon the completion of the export,\r\n * either successfully or with an error. The `error` object is provided\r\n * as the first argument, and the `data` object is the second, containing\r\n * the Base64 representation of the chart in the `result` property\r\n * and the complete set of options in the `options` property.\r\n *\r\n * @async\r\n * @function startExport\r\n *\r\n * @param {Object} imageOptions - The `imageOptions` object, which should\r\n * include settings from the `export` and `customLogic` sections. It can\r\n * be a partial or complete set of options from these sections. If the provided\r\n * options are partial, missing values will be merged with the current global\r\n * options.\r\n * @param {Function} endCallback - The callback function to be invoked upon\r\n * finalizing the export process or upon encountering an error. The first\r\n * argument is the `error` object, and the second argument is the `data` object,\r\n * which includes the Base64 representation of the chart in the `result`\r\n * property and the full set of options in the `options` property.\r\n *\r\n * @returns {Promise} This function does not return a value directly.\r\n * Instead, it communicates results via the `endCallback`.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is a problem with\r\n * processing input of any type. The error is passed into the `endCallback`\r\n * function and processed there.\r\n */\r\nexport async function startExport(imageOptions, endCallback) {\r\n try {\r\n // Check if provided options are in an object\r\n if (!isObject(imageOptions)) {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the provided `imageOptions`. Needs to be an object.',\r\n 400\r\n );\r\n }\r\n\r\n // Merge additional options to the copy of the instance options\r\n const options = updateOptions(\r\n {\r\n export: imageOptions.export,\r\n customLogic: imageOptions.customLogic\r\n },\r\n true\r\n );\r\n\r\n // Get the `export` options\r\n const exportOptions = options.export;\r\n\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Export using options from the file as an input\r\n if (exportOptions.infile !== null) {\r\n log(4, '[chart] Attempting to export from a file input.');\r\n\r\n let fileContent;\r\n try {\r\n // Try to read the file to get the string representation\r\n fileContent = readFileSync(\r\n getAbsolutePath(exportOptions.infile),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error loading content from a file input.',\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Check the file's extension\r\n if (exportOptions.infile.endsWith('.svg')) {\r\n // Set to the `svg` option\r\n exportOptions.svg = fileContent;\r\n } else if (exportOptions.infile.endsWith('.json')) {\r\n // Set to the `instr` option\r\n exportOptions.instr = fileContent;\r\n } else {\r\n throw new ExportError(\r\n '[chart] Incorrect value of the `infile` option.',\r\n 400\r\n );\r\n }\r\n }\r\n\r\n // Export using SVG as an input\r\n if (exportOptions.svg !== null) {\r\n log(4, '[chart] Attempting to export from an SVG input.');\r\n\r\n // SVG exports attempts counter\r\n ++getPoolStats().exportsFromSvgAttempts;\r\n\r\n // Export from an SVG string\r\n const result = await _exportFromSvg(\r\n sanitize(exportOptions.svg), // #209\r\n options\r\n );\r\n\r\n // SVG exports counter\r\n ++getPoolStats().exportsFromSvg;\r\n\r\n // Pass SVG export result to the end callback\r\n return endCallback(null, result);\r\n }\r\n\r\n // Export using options as an input\r\n if (exportOptions.instr !== null || exportOptions.options !== null) {\r\n log(4, '[chart] Attempting to export from options input.');\r\n\r\n // Options exports attempts counter\r\n ++getPoolStats().exportsFromOptionsAttempts;\r\n\r\n // Export from options\r\n const result = await _exportFromOptions(\r\n exportOptions.instr || exportOptions.options,\r\n options\r\n );\r\n\r\n // Options exports counter\r\n ++getPoolStats().exportsFromOptions;\r\n\r\n // Pass options export result to the end callback\r\n return endCallback(null, result);\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 400\r\n )\r\n );\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n}\r\n\r\n/**\r\n * Retrieves and returns the current status of the code execution permission.\r\n *\r\n * @function getAllowCodeExecution\r\n *\r\n * @returns {boolean} The value of the global `allowCodeExecution` option.\r\n */\r\nexport function getAllowCodeExecution() {\r\n return allowCodeExecution;\r\n}\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @function setAllowCodeExecution\r\n *\r\n * @param {boolean} value - The boolean value to be assigned to the global\r\n * `allowCodeExecution` option.\r\n */\r\nexport function setAllowCodeExecution(value) {\r\n allowCodeExecution = value;\r\n}\r\n\r\n/**\r\n * Exports from an SVG based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromSvg\r\n *\r\n * @param {string} inputToExport - The SVG based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct SVG\r\n * input.\r\n */\r\nasync function _exportFromSvg(inputToExport, options) {\r\n // Check if it is SVG\r\n if (\r\n typeof inputToExport === 'string' &&\r\n (inputToExport.indexOf('= 0 || inputToExport.indexOf('= 0)\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n\r\n // Set the export input as SVG\r\n options.export.svg = inputToExport;\r\n\r\n // Reset the rest of the export input options\r\n options.export.options = null;\r\n options.export.instr = null;\r\n\r\n // Call the function with an SVG string as an export input\r\n return _prepareExport(options);\r\n } else {\r\n throw new ExportError('[chart] Not a correct SVG input.', 400);\r\n }\r\n}\r\n\r\n/**\r\n * Exports from an options based input with the provided options.\r\n *\r\n * @async\r\n * @function _exportFromOptions\r\n *\r\n * @param {string} inputToExport - The options based input to be exported.\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if there is not a correct\r\n * chart options input.\r\n */\r\nasync function _exportFromOptions(inputToExport, options) {\r\n log(4, '[chart] Parsing input from options.');\r\n\r\n // Try to check, validate and parse to stringified options\r\n const stringifiedOptions = isAllowedConfig(\r\n inputToExport,\r\n true,\r\n options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Check if a correct stringified options\r\n if (\r\n stringifiedOptions === null ||\r\n typeof stringifiedOptions !== 'string' ||\r\n !stringifiedOptions.startsWith('{') ||\r\n !stringifiedOptions.endsWith('}')\r\n ) {\r\n throw new ExportError(\r\n '[chart] Invalid configuration provided - Only options 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` options set to true.',\r\n 403\r\n );\r\n }\r\n\r\n // Set the export input as a stringified chart options\r\n options.export.instr = stringifiedOptions;\r\n\r\n // Reset the rest of the export input options\r\n options.export.options = null;\r\n options.export.svg = null;\r\n\r\n // Call the function with a stringified chart options\r\n return _prepareExport(options);\r\n}\r\n\r\n/**\r\n * Function for finalizing options and configurations before export.\r\n *\r\n * @async\r\n * @function _prepareExport\r\n *\r\n * @param {Object} options - The configuration object containing complete set\r\n * of options.\r\n *\r\n * @returns {Promise} A Promise that resolves to a result of the export\r\n * process.\r\n */\r\nasync function _prepareExport(options) {\r\n // Get the `export` and `customLogic` options\r\n const { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n // Prepare the `constr` option\r\n exportOptions.constr = _fixConstr(exportOptions.constr);\r\n\r\n // Prepare the `type` option\r\n exportOptions.type = _fixType(exportOptions.type, exportOptions.outfile);\r\n\r\n // Prepare the `outfile` option\r\n exportOptions.outfile = _fixOutfile(\r\n exportOptions.type,\r\n exportOptions.outfile\r\n );\r\n\r\n // Notify about the custom logic usage status\r\n log(\r\n 3,\r\n `[chart] The custom logic is ${customLogicOptions.allowCodeExecution ? 'allowed' : 'disallowed'}.`\r\n );\r\n\r\n // Prepare the `customCode`, `callback`, and `resources` options\r\n _handleCustomLogic(customLogicOptions);\r\n\r\n // Prepare the `globalOptions` and `themeOptions` options\r\n _handleGlobalAndTheme(exportOptions, customLogicOptions);\r\n\r\n // Prepare the `height`, `width`, and `scale` options\r\n _handleSize(exportOptions);\r\n\r\n // Check if the image options object does not exceed the size limit\r\n _checkDataSize({ export: exportOptions, customLogic: customLogicOptions });\r\n\r\n // Post the work to the pool\r\n return postWork(options);\r\n}\r\n\r\n/**\r\n * Handles adjusting the constructor name by transforming and normalizing\r\n * it based on common chart types.\r\n *\r\n * @function _fixConstr\r\n *\r\n * @param {string} constr - The original constructor name to be adjusted.\r\n *\r\n * @returns {string} The corrected constructor name, or 'chart' if the input\r\n * is not recognized.\r\n */\r\nfunction _fixConstr(constr) {\r\n try {\r\n // Fix the constructor by lowering casing\r\n const fixedConstr = `${constr.toLowerCase().replace('chart', '')}Chart`;\r\n\r\n // Handle the case where the result is just 'Chart'\r\n if (fixedConstr === 'Chart') {\r\n fixedConstr.toLowerCase();\r\n }\r\n\r\n // Return the corrected constructor, otherwise default to 'chart'\r\n return ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(\r\n fixedConstr\r\n )\r\n ? fixedConstr\r\n : 'chart';\r\n } catch {\r\n // Default to 'chart' in case of any error\r\n return 'chart';\r\n }\r\n}\r\n\r\n/**\r\n * Handles fixing the outfile based on provided type.\r\n *\r\n * @function _fixOutfile\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 outfile, or 'chart.png' if the input\r\n * is not recognized.\r\n */\r\nfunction _fixOutfile(type, outfile) {\r\n // Get the file name from the `outfile` option\r\n const fileName = getAbsolutePath(outfile || 'chart')\r\n .split('.')\r\n .shift();\r\n\r\n // Return a correct outfile\r\n return `${fileName}.${type || 'png'}`;\r\n}\r\n\r\n/**\r\n * Handles fixing the export type based on MIME types and file extensions.\r\n *\r\n * @function _fixType\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} [outfile=null] - The file path or name. The default value\r\n * is `null`.\r\n *\r\n * @returns {string} The corrected export type, or 'png' if the input\r\n * is not recognized.\r\n */\r\nfunction _fixType(type, outfile = null) {\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 // Get formats\r\n const formats = Object.values(mimeTypes);\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 // Support the JPG type\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 * Handle calculating the `height`, `width` and `scale` for chart exports based\r\n * on the provided export options.\r\n *\r\n * The function prioritizes values in the following order:\r\n *\r\n * 1. The `height`, `width`, `scale` from the `exportOptions`.\r\n * 2. Options from the chart configuration (from `exporting` and `chart`).\r\n * 3. Options from the global options (from `exporting` and `chart`).\r\n * 4. Options from the theme options (from `exporting` and `chart` sections).\r\n * 5. Fallback default values (`height = 400`, `width = 600`, `scale = 1`).\r\n *\r\n * @function _handleSize\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n */\r\nfunction _handleSize(exportOptions) {\r\n // Check the `options` and `instr` for chart and exporting sections\r\n const { chart: optionsChart, exporting: optionsExporting } =\r\n isAllowedConfig(exportOptions.instr) || false;\r\n\r\n // Check the `globalOptions` for chart and exporting sections\r\n const { chart: globalOptionsChart, exporting: globalOptionsExporting } =\r\n isAllowedConfig(exportOptions.globalOptions) || false;\r\n\r\n // Check the `themeOptions` for chart and exporting sections\r\n const { chart: themeOptionsChart, exporting: themeOptionsExporting } =\r\n isAllowedConfig(exportOptions.themeOptions) || false;\r\n\r\n // Find the `height` value\r\n const height =\r\n exportOptions.height ||\r\n optionsExporting?.sourceHeight ||\r\n optionsChart?.height ||\r\n globalOptionsExporting?.sourceHeight ||\r\n globalOptionsChart?.height ||\r\n themeOptionsExporting?.sourceHeight ||\r\n themeOptionsChart?.height ||\r\n exportOptions.defaultHeight ||\r\n 400;\r\n\r\n // Find the `width` value\r\n const width =\r\n exportOptions.width ||\r\n optionsExporting?.sourceWidth ||\r\n optionsChart?.width ||\r\n globalOptionsExporting?.sourceWidth ||\r\n globalOptionsChart?.width ||\r\n themeOptionsExporting?.sourceWidth ||\r\n themeOptionsChart?.width ||\r\n exportOptions.defaultWidth ||\r\n 600;\r\n\r\n // Find the `scale` value:\r\n // - Cannot be lower than 0.1\r\n // - Cannot be higher than 5.0\r\n // - Must be rounded to 2 decimal places (e.g. 0.23234 -> 0.23)\r\n const scale = roundNumber(\r\n Math.max(\r\n 0.1,\r\n Math.min(\r\n exportOptions.scale ||\r\n optionsExporting?.scale ||\r\n globalOptionsExporting?.scale ||\r\n themeOptionsExporting?.scale ||\r\n exportOptions.defaultScale ||\r\n 1,\r\n 5.0\r\n )\r\n ),\r\n 2\r\n );\r\n\r\n // Update `height`, `width`, and `scale` information in the `export` options\r\n exportOptions.height = height;\r\n exportOptions.width = width;\r\n exportOptions.scale = scale;\r\n\r\n // Get rid of potential `px` and `%`\r\n for (let param of ['height', 'width', 'scale']) {\r\n if (typeof exportOptions[param] === 'string') {\r\n exportOptions[param] = +exportOptions[param].replace(/px|%/gi, '');\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles the execution of custom logic options, including loading `resources`,\r\n * `customCode`, and `callback`. If code execution is allowed, it processes\r\n * the custom logic options accordingly. If code execution is not allowed,\r\n * it disables the usage of resources, custom code and callback.\r\n *\r\n * @function _handleCustomLogic\r\n *\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if code execution\r\n * is not allowed but custom logic options are still provided.\r\n */\r\nfunction _handleCustomLogic(customLogicOptions) {\r\n // In case of allowing code execution\r\n if (customLogicOptions.allowCodeExecution) {\r\n // Process the `resources` option\r\n try {\r\n // Try to handle resources\r\n customLogicOptions.resources = _handleResources(\r\n customLogicOptions.resources,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n log(2, '[chart] The `resources` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.resources = null;\r\n }\r\n\r\n // Process the `customCode` option\r\n try {\r\n // Try to load custom code and wrap around it in a self invoking function\r\n customLogicOptions.customCode = _handleCustomCode(\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 // In case of an error, set the option with null\r\n customLogicOptions.customCode = null;\r\n }\r\n\r\n // Process the `callback` option\r\n try {\r\n // Try to load callback function\r\n customLogicOptions.callback = _handleCustomCode(\r\n customLogicOptions.callback,\r\n customLogicOptions.allowFileResources,\r\n true\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, '[chart] The `callback` cannot be loaded.');\r\n\r\n // In case of an error, set the option with null\r\n customLogicOptions.callback = null;\r\n }\r\n\r\n // Check if there is the `customCode` present\r\n if ([null, undefined].includes(customLogicOptions.customCode)) {\r\n log(3, '[chart] No value for the `customCode` option found.');\r\n }\r\n\r\n // Check if there is the `callback` present\r\n if ([null, undefined].includes(customLogicOptions.callback)) {\r\n log(3, '[chart] No value for the `callback` option found.');\r\n }\r\n\r\n // Check if there is the `resources` present\r\n if ([null, undefined].includes(customLogicOptions.resources)) {\r\n log(3, '[chart] No value for the `resources` option found.');\r\n }\r\n } else {\r\n // If the `allowCodeExecution` flag is set to false, we should refuse\r\n // the usage of the `callback`, `resources`, and `customCode` options.\r\n // Additionally, the worker will refuse to run arbitrary JavaScript.\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Reset all custom code options\r\n customLogicOptions.callback = null;\r\n customLogicOptions.resources = null;\r\n customLogicOptions.customCode = null;\r\n\r\n // Send a message saying that the exporter does not support these settings\r\n throw new ExportError(\r\n `[chart] The 'callback', 'resources', and 'customCode' options have been disabled for this server.`,\r\n 403\r\n );\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Handles and validates resources from the `resources` option for export.\r\n *\r\n * @function _handleResources\r\n *\r\n * @param {(Object|string|null)} [resources=null] - The resources to be handled.\r\n * Can be either a JSON object, stringified JSON, a path to a JSON file,\r\n * or null. The default value is `null`.\r\n * @param {boolean} allowFileResources - A flag indicating whether loading\r\n * resources from files is allowed.\r\n * @param {boolean} allowCodeExecution - A flag indicating whether code\r\n * execution is allowed.\r\n *\r\n * @returns {(Object|null)} The handled resources or null if no valid resources\r\n * are found.\r\n */\r\nfunction _handleResources(\r\n resources = null,\r\n allowFileResources,\r\n allowCodeExecution\r\n) {\r\n let handledResources = resources;\r\n\r\n // If no resources found, try to load the default resources\r\n if (!handledResources) {\r\n resources = 'resources.json';\r\n }\r\n\r\n // List of allowed sections in the resources JSON\r\n const allowedProps = ['js', 'css', 'files'];\r\n\r\n // A flag that decides based to return resources or `null`\r\n let correctResources = false;\r\n\r\n // Try to load resources from a file\r\n if (\r\n allowFileResources &&\r\n typeof resources === 'string' &&\r\n resources.endsWith('.json')\r\n ) {\r\n handledResources = isAllowedConfig(\r\n readFileSync(getAbsolutePath(resources), 'utf8'),\r\n false,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Try to get JSON\r\n handledResources = isAllowedConfig(resources, false, allowCodeExecution);\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 null;\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 * Handles custom code to execute it safely.\r\n *\r\n * @function _handleCustomCode\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 * @param {boolean} [isCallback=false] - Flag that indicates the returned code\r\n * must be in a callback format.\r\n *\r\n * @returns {(string|null)} The wrapped custom code or null if wrapping fails.\r\n */\r\nfunction _handleCustomCode(customCode, allowFileResources, isCallback = false) {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n // Load a file if the file resources are allowed\r\n return allowFileResources\r\n ? _handleCustomCode(\r\n readFileSync(getAbsolutePath(customCode), 'utf8'),\r\n allowFileResources,\r\n isCallback\r\n )\r\n : null;\r\n } else if (\r\n !isCallback &&\r\n (customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>'))\r\n ) {\r\n // Treat a function as a self-invoking expression\r\n return `(${customCode})()`;\r\n }\r\n\r\n // Or return as a stringified code\r\n return customCode.replace(/;$/, '');\r\n }\r\n}\r\n\r\n/**\r\n * Handles the loading and validation of the `globalOptions` and `themeOptions`\r\n * in the export options. If the option is a string and references a JSON file\r\n * (when the `allowFileResources` is `true`), it reads and parses the file.\r\n * Otherwise, it attempts to parse the string or object as JSON. If any errors\r\n * occur during this process, the option is set to `null`. If there is an error\r\n * loading or parsing the `globalOptions` or `themeOptions`, the error is logged\r\n * and the option is set to `null`.\r\n *\r\n * @function _handleGlobalAndTheme\r\n *\r\n * @param {Object} exportOptions - The configuration object containing `export`\r\n * options.\r\n * @param {Object} customLogicOptions - The configuration object containing\r\n * `customLogic` options.\r\n */\r\nfunction _handleGlobalAndTheme(exportOptions, customLogicOptions) {\r\n // Get the `allowFileResources` and `allowCodeExecution` flags\r\n const { allowFileResources, allowCodeExecution } = customLogicOptions;\r\n\r\n // Check the `globalOptions` and `themeOptions` options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n // Check if the option exists\r\n if (exportOptions[optionsName]) {\r\n // Check if it is a string and a file name with the `.json` extension\r\n if (\r\n allowFileResources &&\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n // Check if the file content can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n readFileSync(getAbsolutePath(exportOptions[optionsName]), 'utf8'),\r\n true,\r\n allowCodeExecution\r\n );\r\n } else {\r\n // Check if the value can be a config, and save it as a string\r\n exportOptions[optionsName] = isAllowedConfig(\r\n exportOptions[optionsName],\r\n true,\r\n allowCodeExecution\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] The \\`${optionsName}\\` cannot be loaded.`\r\n );\r\n\r\n // In case of an error, set the option with null\r\n exportOptions[optionsName] = null;\r\n }\r\n });\r\n\r\n // Check if there is the `globalOptions` present\r\n if ([null, undefined].includes(exportOptions.globalOptions)) {\r\n log(3, '[chart] No value for the `globalOptions` option found.');\r\n }\r\n\r\n // Check if there is the `themeOptions` present\r\n if ([null, undefined].includes(exportOptions.themeOptions)) {\r\n log(3, '[chart] No value for the `themeOptions` option found.');\r\n }\r\n}\r\n\r\n/**\r\n * Validates the size of the data for the export process against a fixed limit\r\n * of 100MB.\r\n *\r\n * @function _checkDataSize\r\n *\r\n * @param {Object} imageOptions - The data object, which includes options from\r\n * the `export` and `customLogic` sections and will be sent to a Puppeteer page.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the size of the data for\r\n * the export process object exceeds the 100MB limit.\r\n */\r\nfunction _checkDataSize(imageOptions) {\r\n // Set the fixed data limit (100MB) for the dev-tools protocol\r\n const dataLimit = 100 * 1024 * 1024;\r\n\r\n // Get the size of the data\r\n const totalSize = Buffer.byteLength(JSON.stringify(imageOptions), 'utf-8');\r\n\r\n // Log the size in MB\r\n log(\r\n 3,\r\n `[chart] The current total size of the data for the export process 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 before passing to a page\r\n if (totalSize >= dataLimit) {\r\n throw new ExportError(\r\n `[chart] The data for the export process exceeds 100MB limit.`\r\n );\r\n }\r\n}\r\n\r\nexport default {\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 This module provides utility functions for managing intervals\r\n * and timeouts in a centralized manner. It maintains a registry of all active\r\n * timers and allows for their efficient cleanup when needed to avoid potential\r\n * memory leaks, unintended behavior or a process from being stopped.\r\n */\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals and timeouts\r\nconst timerIds = [];\r\n\r\n/**\r\n * Adds id of the `setInterval` or `setTimeout` and to the `timerIds` array.\r\n *\r\n * @function addTimer\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval or a timeout.\r\n */\r\nexport function addTimer(id) {\r\n timerIds.push(id);\r\n}\r\n\r\n/**\r\n * Clears all of ongoing intervals and timeouts by ids gathered\r\n * in the `timerIds` array.\r\n *\r\n * @function clearAllTimers\r\n */\r\nexport function clearAllTimers() {\r\n log(4, `[timer] Clearing all registered intervals and timeouts.`);\r\n for (const id of timerIds) {\r\n clearInterval(id);\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\nexport default {\r\n addTimer,\r\n clearAllTimers\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for logging errors with stack traces\r\n * and handling error responses in an Express application.\r\n */\r\n\r\nimport { getOptions } from '../../config.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 * @function logErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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 {undefined} The call to the next middleware function with\r\n * the passed error.\r\n */\r\nfunction logErrorMiddleware(error, request, response, 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 (getOptions().other.nodeEnv !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the `returnErrorMiddleware` middleware\r\n return next(error);\r\n}\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @function returnErrorMiddleware\r\n *\r\n * @param {Error} error - The error object.\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\nfunction returnErrorMiddleware(error, request, response, next) {\r\n // Gather all requied information for the response\r\n const { message, stack } = error;\r\n\r\n // Use the error's status code or the default 400\r\n const statusCode = error.statusCode || 400;\r\n\r\n // Set and return response\r\n response.status(statusCode).json({ statusCode, message, stack });\r\n}\r\n\r\n/**\r\n * Adds the error middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function errorMiddleware(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-2025, 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 Provides middleware functions for configuring and enabling rate\r\n * limiting in an Express application.\r\n */\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if could not configure and set\r\n * the rate limiting options.\r\n */\r\nexport default function rateLimitingMiddleware(app, rateLimitingOptions) {\r\n try {\r\n // Check if the rate limiting is enabled and the app exists\r\n if (app && rateLimitingOptions.enable) {\r\n const message =\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 window: rateLimitingOptions.window || 1,\r\n maxRequests: rateLimitingOptions.maxRequests || 30,\r\n delay: rateLimitingOptions.delay || 0,\r\n trustProxy: rateLimitingOptions.trustProxy || false,\r\n skipKey: rateLimitingOptions.skipKey || null,\r\n skipToken: rateLimitingOptions.skipToken || null\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 // Time frame for which requests are checked and remembered\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per `windowMs`\r\n limit: rateOptions.maxRequests,\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 });\r\n },\r\n default: () => {\r\n response.status(429).send(message);\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 !== null &&\r\n rateOptions.skipToken !== null &&\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.maxRequests} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[rate limiting] Could not configure and set the rate limiting options.',\r\n 500\r\n ).setError(error);\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Provides middleware functions for validating incoming HTTP requests\r\n * in an Express application. This module ensures that requests contain\r\n * appropriate content types and valid request bodies, including proper JSON\r\n * structures and chart data for exports. It checks for potential issues such\r\n * as missing or malformed data, private range URLs in SVG payloads, and allows\r\n * for flexible options validation. The middleware logs detailed information\r\n * and handles errors related to incorrect payloads, chart data, and private URL\r\n * usage.\r\n */\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution } from '../../chart.js';\r\nimport { isAllowedConfig } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { isObjectEmpty, isPrivateRangeUrlFound } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Middleware for validating the content-type header.\r\n *\r\n * @function contentTypeMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the content-type\r\n * is not correct.\r\n */\r\nfunction contentTypeMiddleware(request, response, next) {\r\n try {\r\n // Get the content type header\r\n const contentType = request.headers['content-type'] || '';\r\n\r\n // Allow only JSON, URL-encoded and form data without files types of data\r\n if (\r\n !contentType.includes('application/json') &&\r\n !contentType.includes('application/x-www-form-urlencoded') &&\r\n !contentType.includes('multipart/form-data')\r\n ) {\r\n throw new ExportError(\r\n '[validation] Content-Type must be application/json, application/x-www-form-urlencoded, or multipart/form-data.',\r\n 415\r\n );\r\n }\r\n\r\n // Call the `requestBodyMiddleware` middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Middleware for validating the request's body.\r\n *\r\n * @function requestBodyMiddleware\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 {undefined} The call to the next middleware function.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the body is not correct.\r\n * @throws {ExportError} Throws an `ExportError` if the chart data from the body\r\n * is not correct.\r\n * @throws {ExportError} Throws an `ExportError` in case of the private range\r\n * url error.\r\n */\r\nfunction requestBodyMiddleware(request, response, next) {\r\n try {\r\n // Get the request body\r\n const body = request.body;\r\n\r\n // Create a unique ID for a request\r\n const requestId = uuid();\r\n\r\n // Throw an error if there is no correct body\r\n if (!body || isObjectEmpty(body)) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is empty.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the allowCodeExecution option for the server\r\n const allowCodeExecution = getAllowCodeExecution();\r\n\r\n // Find a correct chart options\r\n const instr = isAllowedConfig(\r\n // Use one of the below\r\n body.instr || body.options || body.infile || body.data,\r\n // Stringify options\r\n true,\r\n // Allow or disallow functions\r\n allowCodeExecution\r\n );\r\n\r\n // Throw an error if there is no correct chart data\r\n if (instr === null && !body.svg) {\r\n log(\r\n 2,\r\n `[validation] Request [${requestId}] - The request from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Received payload is missing correct chart data for export: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Throw an error if test of xlink:href elements from payload's SVG fails\r\n if (body.svg && isPrivateRangeUrlFound(body.svg)) {\r\n throw new ExportError(\r\n `[validation] Request [${requestId}] - 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 // Get the request options and store parsed structure in the request\r\n request.validatedOptions = {\r\n // Set the created ID as a `requestId` property in the options\r\n requestId,\r\n export: {\r\n instr,\r\n svg: body.svg,\r\n outfile:\r\n body.outfile ||\r\n `${request.params.filename || 'chart'}.${body.type || 'png'}`,\r\n type: body.type,\r\n constr: body.constr,\r\n b64: body.b64,\r\n noDownload: body.noDownload,\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale,\r\n globalOptions: isAllowedConfig(\r\n body.globalOptions,\r\n true,\r\n allowCodeExecution\r\n ),\r\n themeOptions: isAllowedConfig(\r\n body.themeOptions,\r\n true,\r\n allowCodeExecution\r\n )\r\n },\r\n customLogic: {\r\n allowCodeExecution,\r\n allowFileResources: false,\r\n customCode: body.customCode,\r\n callback: body.callback,\r\n resources: isAllowedConfig(body.resources, true, allowCodeExecution)\r\n }\r\n };\r\n\r\n // Call the next middleware\r\n return next();\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the validation middlewares to the passed express app instance.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function validationMiddleware(app) {\r\n // Add content type validation middleware\r\n app.post(['/', '/:filename'], contentTypeMiddleware);\r\n\r\n // Add request body request validation middleware\r\n app.post(['/', '/:filename'], requestBodyMiddleware);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines the export routes and logic for handling chart export\r\n * requests in an Express server. This module processes incoming requests\r\n * to export charts in various formats (e.g. JPEG, PNG, PDF, SVG). It integrates\r\n * with Highcharts' core functionalities and supports both immediate download\r\n * responses and Base64-encoded content returns. The code also features\r\n * benchmarking for performance monitoring.\r\n */\r\n\r\nimport { startExport } from '../../chart.js';\r\nimport { log } from '../../logger.js';\r\nimport { getBase64, measureTime } from '../../utils.js';\r\n\r\nimport ExportError from '../../errors/ExportError.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/**\r\n * Handles the export requests from the client.\r\n *\r\n * @async\r\n * @function requestExport\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\nasync function requestExport(request, response, next) {\r\n try {\r\n // Start counting time for a request\r\n const requestCounter = measureTime();\r\n\r\n // In case the connection is closed, force to abort further actions\r\n let connectionAborted = false;\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n // Get the options previously validated in the validation middleware\r\n const options = request.validatedOptions;\r\n\r\n // Get the request id\r\n const requestId = options.requestId;\r\n\r\n // Info about an incoming request with correct data\r\n log(4, `[export] Request [${requestId}] - Got an incoming HTTP request.`);\r\n\r\n // Start the export process\r\n await startExport(options, (error, data) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The client closed the connection before the chart finished processing.`\r\n );\r\n return;\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 (!data || !data.result) {\r\n log(\r\n 2,\r\n `[export] Request [${requestId}] - Request from ${\r\n request.headers['x-forwarded-for'] ||\r\n request.connection.remoteAddress\r\n } was incorrect. Received result is ${data.result}.`\r\n );\r\n\r\n throw new ExportError(\r\n `[export] Request [${requestId}] - Unexpected return of the export result from the chart generation. Please check your request data.`,\r\n 400\r\n );\r\n }\r\n\r\n // Return the result in an appropriate format\r\n if (data.result) {\r\n log(\r\n 3,\r\n `[export] Request [${requestId}] - The whole exporting process took ${requestCounter()}ms.`\r\n );\r\n\r\n // Get the `type`, `b64`, `noDownload`, and `outfile` from options\r\n const { type, b64, noDownload, outfile } = data.options.export;\r\n\r\n // If only Base64 is required, return it\r\n if (b64) {\r\n return response.send(getBase64(data.result, type));\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 (!noDownload) {\r\n response.attachment(outfile);\r\n }\r\n\r\n // If SVG, return plain content, otherwise a b64 string from a buffer\r\n return type === 'svg'\r\n ? response.send(data.result)\r\n : response.send(Buffer.from(data.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n}\r\n\r\n/**\r\n * Adds the `export` routes.\r\n *\r\n * @function exportRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function exportRoutes(app) {\r\n /**\r\n * Adds the POST '/' - A route for handling POST requests at the root\r\n * endpoint.\r\n */\r\n app.post('/', requestExport);\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', requestExport);\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for server health monitoring, including\r\n * uptime, success rates, and other server statistics.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport { getHcVersion } from '../../cache.js';\r\nimport { log } from '../../logger.js';\r\nimport { getPoolStats, getPoolInfoJSON } from '../../pool.js';\r\nimport { addTimer } from '../../timer.js';\r\nimport { __dirname, getNewDateTime } from '../../utils.js';\r\n\r\n// Set the start date of the server\r\nconst serverStartTime = new Date();\r\n\r\n// Get the `package.json` content\r\nconst packageFile = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'), 'utf8')\r\n);\r\n\r\n// An array for success rate ratios\r\nconst successRates = [];\r\n\r\n// Record every minute\r\nconst recordInterval = 60 * 1000;\r\n\r\n// 30 minutes\r\nconst windowSize = 30;\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the `successRates`\r\n * array.\r\n *\r\n * @function _calculateMovingAverage\r\n *\r\n * @returns {number} A moving average for success ratio of the server exports.\r\n */\r\nfunction _calculateMovingAverage() {\r\n return successRates.reduce((a, b) => a + b, 0) / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and collects records to the `successRates` array.\r\n *\r\n * @function _startSuccessRate\r\n *\r\n * @returns {NodeJS.Timeout} Id of an interval.\r\n */\r\nfunction _startSuccessRate() {\r\n return setInterval(() => {\r\n const stats = getPoolStats();\r\n const successRatio =\r\n stats.exportsAttempted === 0\r\n ? 1\r\n : (stats.exportsPerformed / stats.exportsAttempted) * 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/**\r\n * Adds the `health` routes.\r\n *\r\n * @function healthRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function healthRoutes(app) {\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 `addTimer` funtion\r\n addTimer(_startSuccessRate());\r\n\r\n /**\r\n * Adds the GET '/health' - A route for getting the basic stats of the server.\r\n */\r\n app.get('/health', (request, response, next) => {\r\n try {\r\n log(4, '[health] Returning server health.');\r\n\r\n const stats = getPoolStats();\r\n const period = successRates.length;\r\n const movingAverage = _calculateMovingAverage();\r\n\r\n // Send the server's statistics\r\n response.send({\r\n // Status and times\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime: `${Math.floor((getNewDateTime() - serverStartTime.getTime()) / 1000 / 60)} minutes`,\r\n\r\n // Versions\r\n serverVersion: packageFile.version,\r\n highchartsVersion: getHcVersion(),\r\n\r\n // Exports\r\n averageExportTime: stats.timeSpentAverage,\r\n attemptedExports: stats.exportsAttempted,\r\n performedExports: stats.exportsPerformed,\r\n failedExports: stats.exportsDropped,\r\n sucessRatio: (stats.exportsPerformed / stats.exportsAttempted) * 100,\r\n\r\n // Pool\r\n 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 and JSON exports\r\n svgExports: stats.exportsFromSvg,\r\n jsonExports: stats.exportsFromOptions,\r\n svgExportsAttempts: stats.exportsFromSvgAttempts,\r\n jsonExportsAttempts: stats.exportsFromOptionsAttempts\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for serving the UI for the export server\r\n * when enabled.\r\n */\r\n\r\nimport { join } from 'path';\r\n\r\nimport { getOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the `ui` routes.\r\n *\r\n * @function uiRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function uiRoutes(app) {\r\n // Add the UI endpoint only if required\r\n if (getOptions().ui.enable) {\r\n /**\r\n * Adds the GET '/' - A route for a UI when enabled on the export server.\r\n */\r\n app.get(getOptions().ui.route || '/', (request, response, next) => {\r\n try {\r\n log(4, '[ui] Returning UI for the export.');\r\n\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n }\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 Defines an Express route for updating the Highcharts version\r\n * on the server, with authentication and validation.\r\n */\r\n\r\nimport { getHcVersion, updateHcVersion } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\nimport { log } from '../../logger.js';\r\n\r\nimport ExportError from '../../errors/ExportError.js';\r\n\r\n/**\r\n * Adds the `version_change` routes.\r\n *\r\n * @function versionChangeRoutes\r\n *\r\n * @param {Express} app - The Express app instance.\r\n */\r\nexport default function versionChangeRoutes(app) {\r\n /**\r\n * Adds the POST '/version_change/:newVersion' - A route for changing\r\n * the Highcharts version on the server.\r\n */\r\n app.post('/version_change/:newVersion', async (request, response, next) => {\r\n try {\r\n log(4, '[version] Changing Highcharts version.');\r\n\r\n // Get the token directly from envs\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 ExportError(\r\n '[version] 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 // Get the token from the hc-auth header\r\n const token = request.get('hc-auth');\r\n\r\n // Check if the hc-auth header contain a correct token\r\n if (!token || token !== adminToken) {\r\n throw new ExportError(\r\n '[version] Invalid or missing token: Set the token in the hc-auth header.',\r\n 401\r\n );\r\n }\r\n\r\n // Get the new version from the params\r\n const newVersion = request.params.newVersion;\r\n\r\n // Update version\r\n if (newVersion) {\r\n try {\r\n await updateHcVersion(newVersion);\r\n } catch (error) {\r\n throw new ExportError(\r\n `[version] Version change: ${error.message}`,\r\n 400\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n highchartsVersion: getHcVersion(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new ExportError('[version] No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n return next(error);\r\n }\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2025, 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 A module that sets up and manages HTTP and HTTPS servers\r\n * for the Highcharts Export Server. It handles server initialization,\r\n * configuration, error handling, middlewares setup, route definition, and rate\r\n * limiting. The module exports functions to start, stop, and manage server\r\n * instances, as well as utility functions for defining routes and attaching\r\n * middlewares.\r\n */\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join } 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 { updateOptions } from '../config.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname, getAbsolutePath } from '../utils.js';\r\n\r\nimport errorMiddleware from './middlewares/error.js';\r\nimport rateLimitingMiddleware from './middlewares/rateLimiting.js';\r\nimport validationMiddleware from './middlewares/validation.js';\r\n\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoutes from './routes/health.js';\r\nimport uiRoutes from './routes/ui.js';\r\nimport versionChangeRoutes from './routes/versionChange.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/**\r\n * Starts an HTTP and/or HTTPS server based on the provided configuration.\r\n * The `serverOptions` object contains server-related properties (refer\r\n * to the `server` section in the `./lib/schemas/config.js` file for details).\r\n *\r\n * @async\r\n * @function startServer\r\n *\r\n * @param {Object} serverOptions - The configuration object containing `server`\r\n * options. This object may include a partial or complete set of the `server`\r\n * options. If the options are partial, missing values will default\r\n * to the current global configuration.\r\n *\r\n * @returns {Promise} A Promise that resolves when the server is either\r\n * not enabled or no valid Express app is found, signaling the end of the\r\n * function's execution.\r\n *\r\n * @throws {ExportError} Throws an `ExportError` if the server cannot\r\n * be configured and started.\r\n */\r\nexport async function startServer(serverOptions) {\r\n try {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: serverOptions\r\n });\r\n\r\n // Use validated options\r\n serverOptions = options.server;\r\n\r\n // Stop if not enabled\r\n if (!serverOptions.enable || !app) {\r\n throw new ExportError(\r\n '[server] Server cannot be started (not enabled or no correct Express app found).',\r\n 500\r\n );\r\n }\r\n\r\n // Too big limits lead to timeouts in the export process when\r\n // the rasterization timeout is set too low\r\n const uploadLimitBytes = serverOptions.uploadLimit * 1024 * 1024;\r\n\r\n // Memory storage for multer package\r\n const storage = multer.memoryStorage();\r\n\r\n // Enable parsing of form data (files) with multer package\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Disable the X-Powered-By header\r\n app.disable('x-powered-by');\r\n\r\n // Enable CORS support\r\n app.use(\r\n cors({\r\n methods: ['POST', 'GET', 'OPTIONS']\r\n })\r\n );\r\n\r\n // Getting a lot of `RangeNotSatisfiableError` exceptions (even though this\r\n // is a deprecated options, let's try to set it to false)\r\n app.use((request, response, next) => {\r\n response.set('Accept-Ranges', 'none');\r\n next();\r\n });\r\n\r\n // Enable body parser for JSON data\r\n app.use(\r\n express.json({\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Enable body parser for URL-encoded form data\r\n app.use(\r\n express.urlencoded({\r\n extended: true,\r\n limit: uploadLimitBytes\r\n })\r\n );\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Set up static folder's route\r\n app.use(express.static(join(__dirname, 'public')));\r\n\r\n // Listen HTTP server\r\n if (!serverOptions.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(serverOptions.port, serverOptions.host, () => {\r\n // Save the reference to HTTP server\r\n activeServers.set(serverOptions.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverOptions.host}:${serverOptions.port}.`\r\n );\r\n });\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverOptions.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 = readFileSync(\r\n join(getAbsolutePath(serverOptions.ssl.certPath), 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = readFileSync(\r\n join(getAbsolutePath(serverOptions.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 '${serverOptions.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(serverOptions.ssl.port, serverOptions.host, () => {\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverOptions.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverOptions.host}:${serverOptions.ssl.port}.`\r\n );\r\n });\r\n }\r\n }\r\n\r\n // Set up the rate limiter\r\n rateLimitingMiddleware(app, serverOptions.rateLimiting);\r\n\r\n // Set up the validation handler\r\n validationMiddleware(app);\r\n\r\n // Set up routes\r\n exportRoutes(app);\r\n healthRoutes(app);\r\n uiRoutes(app);\r\n versionChangeRoutes(app);\r\n\r\n // Set up the centralized error handler\r\n errorMiddleware(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.',\r\n 500\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\n * @function closeServers\r\n */\r\nexport function closeServers() {\r\n // Check if there are servers working\r\n if (activeServers.size > 0) {\r\n log(4, `[server] Closing all servers.`);\r\n\r\n // Close each one of 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/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @function getServers\r\n *\r\n * @returns {Array} Servers associated with Express app instance.\r\n */\r\nexport function getServers() {\r\n return activeServers;\r\n}\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @function getExpress\r\n *\r\n * @returns {Express} The Express instance.\r\n */\r\nexport function getExpress() {\r\n return express;\r\n}\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @function getApp\r\n *\r\n * @returns {Express} The Express app instance.\r\n */\r\nexport function getApp() {\r\n return app;\r\n}\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @function enableRateLimiting\r\n *\r\n * @param {Object} rateLimitingOptions - The configuration object containing\r\n * `rateLimiting` options. This object may include a partial or complete set\r\n * of the `rateLimiting` options. If the options are partial, missing values\r\n * will default to the current global configuration.\r\n */\r\nexport function enableRateLimiting(rateLimitingOptions) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n server: {\r\n rateLimiting: rateLimitingOptions\r\n }\r\n });\r\n\r\n // Set the rate limiting options\r\n rateLimitingMiddleware(app, options.server.rateLimitingOptions);\r\n}\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @function use\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function get\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function 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 * @function post\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware function(s) to be applied.\r\n */\r\nexport function post(path, ...middlewares) {\r\n app.post(path, ...middlewares);\r\n}\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @function _attachServerErrorHandlers\r\n *\r\n * @param {(http.Server|https.Server)} server - The HTTP/HTTPS server instance.\r\n */\r\nfunction _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\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n getExpress,\r\n getApp,\r\n enableRateLimiting,\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-2025, 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 Handles graceful shutdown of the Highcharts Export Server, ensuring\r\n * proper cleanup of resources such as browser, pages, servers, and timers.\r\n */\r\n\r\nimport { killPool } from './pool.js';\r\nimport { clearAllTimers } from './timer.js';\r\n\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Performs cleanup operations to ensure a graceful shutdown of the process.\r\n * This includes clearing all registered timeouts/intervals, closing active\r\n * servers, terminating resources (pages) of the pool, pool itself, and closing\r\n * the browser.\r\n *\r\n * @function shutdownCleanUp\r\n *\r\n * @param {number} [exitCode=0] - The exit code to use with `process.exit()`.\r\n * The default value is `0`.\r\n */\r\nexport async function shutdownCleanUp(exitCode = 0) {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllTimers(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close an active pool along with its workers and the browser instance\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-2025, 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 This core module initializes and manages the Highcharts Export\r\n * Server. The main `initExport` function handles logging, script caching,\r\n * resource pooling, browser startup, and ensures graceful process cleanup\r\n * on exit. Additionally, it provides API functions for using it as a Node.js\r\n * module, offering functionalities for processing options, configuring\r\n * and performing exports, and setting up server.\r\n */\r\n\r\nimport 'colors';\r\n\r\nimport { checkCache } from './cache.js';\r\nimport {\r\n batchExport,\r\n singleExport,\r\n startExport,\r\n setAllowCodeExecution\r\n} from './chart.js';\r\nimport { getOptions, updateOptions, mapToNewOptions } from './config.js';\r\nimport {\r\n log,\r\n logWithStack,\r\n initLogging,\r\n enableConsoleLogging,\r\n enableFileLogging,\r\n setLogLevel\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resourceRelease.js';\r\n\r\nimport server from './server/server.js';\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * the cache and sources, and initializing the resource pool occur during this\r\n * stage.\r\n *\r\n * This function must be called before attempting to export charts or set\r\n * up a server.\r\n *\r\n * @async\r\n * @function initExport\r\n *\r\n * @param {Object} initOptions - The `initOptions` object, which may\r\n * be a partial or complete set of options. If the options are partial, missing\r\n * values will default to the current global configuration.\r\n */\r\nexport async function initExport(initOptions) {\r\n // Init and update the instance options object\r\n const options = updateOptions(initOptions);\r\n\r\n // Set the `allowCodeExecution` per export module scope\r\n setAllowCodeExecution(options.customLogic.allowCodeExecution);\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 the current status of cache\r\n await checkCache(options.highcharts, options.server.proxy);\r\n\r\n // Init the pool\r\n await initPool(options.pool, options.puppeteer.args);\r\n}\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM',\r\n * and 'uncaughtException' events.\r\n *\r\n * @function _attachProcessExitListeners\r\n */\r\nfunction _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] 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, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `[process] The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp();\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `[process] The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n}\r\n\r\nexport default {\r\n // Server\r\n ...server,\r\n\r\n // Options\r\n getOptions,\r\n updateOptions,\r\n mapToNewOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Release\r\n killPool,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel: function (level) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n level\r\n }\r\n });\r\n\r\n // Call the function\r\n setLogLevel(options.logging.level);\r\n },\r\n enableConsoleLogging: function (toConsole) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n toConsole\r\n }\r\n });\r\n\r\n // Call the function\r\n enableConsoleLogging(options.logging.toConsole);\r\n },\r\n enableFileLogging: function (dest, file, toFile) {\r\n // Update the instance options object\r\n const options = updateOptions({\r\n logging: {\r\n dest,\r\n file,\r\n toFile\r\n }\r\n });\r\n\r\n // Call the function\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file,\r\n options.logging.toFile\r\n );\r\n }\r\n};\r\n"],"names":["defaultConfig","puppeteer","args","value","types","envLink","cliName","description","promptOptions","type","separator","highcharts","version","cdnUrl","forceFetch","cachePath","coreScripts","instructions","moduleScripts","indicatorScripts","customScripts","export","infile","instr","options","svg","batch","outfile","hint","choices","constr","b64","noDownload","height","width","scale","defaultHeight","defaultWidth","defaultScale","min","max","globalOptions","themeOptions","rasterizationTimeout","customLogic","allowCodeExecution","allowFileResources","customCode","callback","resources","loadConfig","legacyName","createConfig","server","enable","host","port","uploadLimit","benchmarking","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","logging","level","round","file","dest","toConsole","toFile","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","dotenv","config","v","array","filterArray","z","string","transform","split","map","trim","filter","includes","length","undefined","boolean","enum","values","refine","message","positiveNum","isNaN","parseFloat","nonNegativeNum","Config","object","PUPPETEER_ARGS","HIGHCHARTS_VERSION","test","HIGHCHARTS_CDN_URL","startsWith","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_CUSTOM_SCRIPTS","EXPORT_INFILE","EXPORT_INSTR","EXPORT_OPTIONS","EXPORT_SVG","EXPORT_BATCH","EXPORT_OUTFILE","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_B64","EXPORT_NO_DOWNLOAD","EXPORT_HEIGHT","EXPORT_WIDTH","EXPORT_SCALE","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_GLOBAL_OPTIONS","EXPORT_THEME_OPTIONS","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","CUSTOM_LOGIC_CUSTOM_CODE","CUSTOM_LOGIC_CALLBACK","CUSTOM_LOGIC_RESOURCES","CUSTOM_LOGIC_LOAD_CONFIG","CUSTOM_LOGIC_CREATE_CONFIG","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_UPLOAD_LIMIT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","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","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","envs","partial","parse","process","env","__dirname","fileURLToPath","URL","url","deepCopy","objArr","objArrCopy","Array","isArray","key","Object","prototype","hasOwnProperty","call","getAbsolutePath","path","isAbsolute","normalize","resolve","getBase64","input","Buffer","from","toString","getNewDate","Date","getNewDateTime","getTime","isObject","item","isObjectEmpty","keys","isPrivateRangeUrlFound","some","pattern","measureTime","start","hrtime","bigint","Number","roundNumber","precision","multiplier","Math","pow","colors","pathCreated","pathToLog","levelsDesc","title","color","log","newLevel","texts","prefix","_logToFile","console","apply","concat","logWithStack","error","customMessage","mainMessage","stackMessage","stack","push","shift","initLogging","loggingOptions","setLogLevel","enableConsoleLogging","enableFileLogging","isInteger","existsSync","mkdirSync","join","appendFile","_initOptions","nestedProps","_createNestedProps","absoluteProps","_createAbsoluteProps","getOptions","getCopy","updateOptions","newOptions","_mergeOptions","mapToNewOptions","oldOptions","entries","propertiesChain","reduce","obj","prop","index","isAllowedConfig","allowFunctions","objectConfig","eval","JSON","stringifiedOptions","_optionsStringify","parsedOptions","_","name","originalOptions","stringifyFunctions","stringify","endsWith","replaceAll","Error","propChain","forEach","entry","substring","async","get","requestOptions","Promise","reject","_getProtocolModule","response","responseData","on","chunk","text","https","http","ExportError","constructor","statusCode","super","this","setError","cache","activeManifest","sources","hcVersion","checkCache","highchartsOptions","serverProxyOptions","fetchedModules","getCachePath","manifestPath","sourcePath","recursive","_updateCache","requestUpdate","manifest","readFileSync","modules","moduleMap","m","numberOfModules","moduleName","_extractHcVersion","_saveConfigToManifest","getHcVersion","updateHcVersion","newVersion","writeFileSync","_configureRequest","all","cs","_fetchScript","ms","is","script","shouldThrowError","_extractModuleName","proxyHost","proxyPort","agent","HttpsProxyAgent","cacheSources","indexOf","replace","scriptPath","setupHighcharts","Highcharts","animObject","duration","createChart","exportOptions","customLogicOptions","setOptions","merge","wrap","setOptionsObj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","animation","onHighchartsRender","addEvent","Series","chart","additionalOptions","Function","finalOptions","finalCallback","images","document","querySelectorAll","race","image","complete","naturalHeight","addEventListener","once","setTimeout","defaultOptions","pageTemplate","browser","createBrowser","puppeteerArgs","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","openBrowser","launch","closeBrowser","connected","close","newPage","poolResource","page","setCacheEnabled","_setPageContent","_setPageEvents","isClosed","clearPage","hardReset","goto","waitUntil","evaluate","body","innerHTML","id","workCount","addPageResources","injectedResources","injectedJs","js","content","files","isLocal","jsResource","addScriptTag","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","clearPageResources","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","setContent","cssTemplate","svgTemplate","puppeteerExport","isSVG","size","_getChartSize","x","y","_getClipRegion","viewportHeight","abs","ceil","chartHeight","viewportWidth","chartWidth","result","setViewport","deviceScaleFactor","_createSVG","_createImage","_createPDF","$eval","getBoundingClientRect","trunc","svgElement","querySelector","baseVal","style","zoom","margin","outerHTML","clip","screenshot","encoding","fullPage","optimizeForSpeed","captureBeyondViewport","quality","omitBackground","_resolve","emulateMediaType","pdf","poolStats","exportsAttempted","exportsPerformed","exportsDropped","exportsFromSvg","exportsFromOptions","exportsFromSvgAttempts","exportsFromOptionsAttempts","timeSpent","timeSpentAverage","initPool","poolOptions","Pool","_factory","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","clearStatus","_eventId","initialResources","i","acquire","promise","release","killPool","worker","used","destroyed","postWork","workerHandle","_getPoolInfo","acquireCounter","requestId","exportCounter","exportResult","getPoolStats","getPoolInfoJSON","numUsed","available","numFree","allCreated","pendingAcquires","numPendingAcquires","pendingCreates","numPendingCreates","pendingValidations","numPendingValidations","pendingDestroys","absoluteAll","create","uuid","random","startDate","validate","mainFrame","detached","removeAllListeners","sanitize","JSDOM","DOMPurify","ADD_TAGS","singleExport","startExport","data","batchExport","batchFunctions","pair","batchResults","allSettled","reason","imageOptions","endCallback","fileContent","_exportFromSvg","_exportFromOptions","getAllowCodeExecution","setAllowCodeExecution","inputToExport","_prepareExport","_fixConstr","_fixType","_fixOutfile","_handleCustomLogic","_handleGlobalAndTheme","_handleSize","_checkDataSize","fixedConstr","toLowerCase","mimeTypes","formats","outType","pop","find","t","optionsChart","optionsExporting","globalOptionsChart","globalOptionsExporting","themeOptionsChart","themeOptionsExporting","sourceHeight","sourceWidth","param","_handleResources","_handleCustomCode","handledResources","allowedProps","correctResources","propName","isCallback","optionsName","totalSize","byteLength","toFixed","timerIds","addTimer","clearAllTimers","clearInterval","clearTimeout","logErrorMiddleware","request","next","returnErrorMiddleware","status","json","errorMiddleware","app","use","rateLimitingMiddleware","rateLimitingOptions","rateOptions","limiter","rateLimit","windowMs","limit","delayMs","handler","format","send","default","skip","query","access_token","contentTypeMiddleware","contentType","headers","requestBodyMiddleware","connection","remoteAddress","validatedOptions","params","filename","validationMiddleware","post","reversedMime","png","jpeg","gif","requestExport","requestCounter","connectionAborted","socket","hadErrors","header","attachment","exportRoutes","serverStartTime","packageFile","successRates","recordInterval","windowSize","_calculateMovingAverage","a","b","_startSuccessRate","setInterval","stats","successRatio","healthRoutes","period","movingAverage","bootTime","uptime","floor","serverVersion","highchartsVersion","averageExportTime","attemptedExports","performedExports","failedExports","sucessRatio","svgExports","jsonExports","svgExportsAttempts","jsonExportsAttempts","uiRoutes","sendFile","acceptRanges","versionChangeRoutes","adminToken","token","activeServers","Map","express","startServer","serverOptions","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","disable","cors","methods","set","urlencoded","extended","none","static","httpServer","createServer","_attachServerErrorHandlers","listen","cert","httpsServer","closeServers","delete","getServers","getExpress","getApp","enableRateLimiting","middlewares","shutdownCleanUp","exitCode","exit","initExport","initOptions","_attachProcessExitListeners","code"],"mappings":"0jBAiCA,MAAMA,cAAgB,CACpBC,UAAW,CACTC,KAAM,CACJC,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFC,MAAO,CAAC,YACRC,QAAS,iBACTC,QAAS,gBACTC,YAAa,+BACbC,cAAe,CACbC,KAAM,OACNC,UAAW,OAIjBC,WAAY,CACVC,QAAS,CACPT,MAAO,SACPC,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,qBACbC,cAAe,CACbC,KAAM,SAGVI,OAAQ,CACNV,MAAO,8BACPC,MAAO,CAAC,UACRC,QAAS,qBACTE,YAAa,iCACbC,cAAe,CACbC,KAAM,SAGVK,WAAY,CACVX,OAAO,EACPC,MAAO,CAAC,WACRC,QAAS,yBACTE,YAAa,kDACbC,cAAe,CACbC,KAAM,WAGVM,UAAW,CACTZ,MAAO,SACPC,MAAO,CAAC,UACRC,QAAS,wBACTE,YAAa,+CACbC,cAAe,CACbC,KAAM,SAGVO,YAAa,CACXb,MAAO,CAAC,aAAc,kBAAmB,iBACzCC,MAAO,CAAC,YACRC,QAAS,0BACTE,YAAa,mCACbC,cAAe,CACbC,KAAM,cACNQ,aAAc,0DAGlBC,cAAe,CACbf,MAAO,CACL,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,eACA,yBACA,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,UA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"} \ No newline at end of file From e354d579400b679e8b85ebe71ad3b035f074ffaf Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Fri, 14 Feb 2025 01:30:27 +0100 Subject: [PATCH 101/102] Updated the CHANGELOG.md file and bumped the version. --- CHANGELOG.md | 159 ++++++++++- package-lock.json | 703 +++++++++++++++++++++++++++++----------------- package.json | 10 +- 3 files changed, 606 insertions(+), 266 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6e8ac1f..2ce1bf4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,146 @@ +# 5.0.0 + +_Breaking Changes:_ + +- Renamed and refined from `mapToNewConfig` to `mapToNewOptions`. +- Renamed the `certPath` CLI equivalent from `certPath` to `sslCertPath`. +- The `setCliOptions` function (renamed from the `setOptions`) updates additional options only for CLI exports now and it is replaced by the `updateOptions` function. +- Removed following API functions: `initPool`, `setOptions`, `mapToNewConfig`, `manualConfig`, `printLogo`, `printUsage` (some removed due to refactoring). + +_New Features:_ + +- Introduced redefined `getOptions` and `updateOptions` functions to retrieve and update the original global options or a copy of global options, allowing flexibility in export scenarios. +- Added a new option called `uploadLimit` to control the maximum size of a request's payload body. +- Added the possibility to return a Base64 version of the chart using any export method (not only through requests). +- Added support for displaying CLI usage (`-h`, `--h`, `-help`, `--help`) and version information with license details (`-v`, `--v`). +- Introduced `validation` middleware to check `Content-Type` headers and validate request bodies. + +_Enhancements:_ + +- Completely redefined, refactored, and optimized options initialization, updating, processing, and management. +- Simplified and optimized the `./lib/schemas/config.js` module, to export only the default configuration object (`defaultConfig`). +- Gathered previously scattered meta-information about options into the `defaultConfig` object. +- Gathered and enhanced logic from multiple modules (mainly from the `./lib/schemas/config.js`) and placed it in the `./lib/config.js` module. +- Added missing environment variables for all options along with corresponding validators. +- Global options now initialize with default values from the `defaultConfig` and potential values from the `.env` file. +- Created a new internal function `_initOptions` (combined from `initOptions` and `updateDefaultConfig` functions) to initialize default values at startup. +- Adjusted the options loading sequence: `default config -> environment variables` at initialization, `custom JSON -> CLI arguments` when using `setCliOptions` (CLI exports only). +- The `getOptions` function can now return either a direct reference to the `globalOptions` or a copy (by setting the `getCopy` flag). +- The `updateOptions` function can now update and return either a direct reference to the `globalOptions` or a copy (by setting the `getCopy` flag). +- The `_mergeOptions` (renamed from the `mergeConfigOptions`) modifies the first object directly now and is used internally. +- Replaced the fixed `absoluteProps` array (previously in `./lib/schemas/config.js`) with dynamic generation via `_createAbsoluteProps` function. +- Enhanced the `isAllowedConfig` (renamed from the `isCorrectJSON`) and `_optionsStringify` functions to better handle stringified options in JSON. +- Moved options previously used only in the request-oriented export method to the `export` section (`svg`, `b64`, and `noDownload`). +- Updated default values for certain options in the `.env` file (`HIGHCHARTS_CDN_URL`, `HIGHCHARTS_CACHE_PATH`, `LOGGING_DEST`, `UI_ENABLE`, and `DEBUG_HEADLESS`). +- Enhanced the `_loadConfigFile` to check if a file can be loaded via `allowFileResources`, for internal use in the `setCliOptions` function. +- Removed the `initExportSettings` function (no longer required). +- Major refactor, overhaul, and optimization of the main export functions (`singleExport`, `batchExport`, and `startExport`). +- The main export functions, such as `batchExport`, `singleExport`, and `startExport`, now accept an object as a parameter, which should include settings from the `export` and `customLogic` sections instead of the full options object. +- Improved how options are passed, validated, updated, and processed within `startExport`. +- Allowed partial exports in batch export operations and corrected the corresponding logs. +- Introduced `_exportFromSvg` and `_exportFromOptions` to handle specific export scenarios with proper validation. +- Split and enhanced logic of the `doExport` into multiple functions and created `_prepareExport` to gather and calling all these functions responsible for different export preparations (listed below). +- Created `_fixConstr` for finding correct chart constructor. +- Created `_fixType` for finding correct type. +- Created `_fixOutfile` for finding correct outfile. +- Created `_handleCustomLogic` for handling `customLogic` options. +- Created `_handleResources` for handling custom logic `resources` option. +- Created `_handleGlobalAndTheme` for handling `globalOptions` and `themeOptions` options. +- Created `_handleSize` for handling the `height`, `width`, and `scale` options. +- Created `_checkDataSize` for handling the data size validation. +- Optimized `initExport`, utilizing `updateOptions` for global option updates. +- The `initExport` now have its options parameters set as optional, using global option values if not provided. +- Updated exported API functions for module usage. +- Adjusted imports to get functions from corresponding modules rather than `index.js`. +- Server functions are now directly exported (rather than within a `server` object) as API functions. +- The `logger` API functions that modify options now update global options. +- Added following API functions: `getOptions`, `updateOptions`, `mapToNewOptions`, `enableConsoleLogging`. +- Small corrections of the `_attachProcessExitListeners` (renamed from the `attachProcessExitListeners`). +- Refactored logic for initial configuration, startup, and HTTP/HTTPS server management. +- Optimized `startServer`, utilizing `updateOptions` for global option updates. +- The `startServer` now have its options parameters set as optional, using global option values if not provided. +- Optimized logic and statistics display in `health.js` router. +- Optimized logic and corrected the url (from `version/change` to `version_change`) in the `versionChange.js` router. +- Refactored `exportHandler` (to `requestExport`) by moving some logic to the `validaion` middleware and refactoring the rest. +- Removed unnecessary `doCallbacks` from the export router. +- Moved `server/error.js` and `server/rateLimiting.js` to `middlewares/`, optimizing error handling and rate limiting. +- The `nodeEnv` option is now obtained from `getOptions`, not directly from the environment variables in the `error.js` middleware. +- Refactored and optimized the entire logic for checking the cache and fetching scripts. +- Refactored and split the `_updateCache` function into smaller, more manageable parts. +- The `updateHcVersion` function now correctly updates the global options. +- The new `_configureRequest` function is responsible for setting the proxy agent. +- Passing `version` as the first argument to `_saveConfigToManifest`. +- Removed the `fetchAndProcessScript` and `fetchScripts` functions, replacing them with a single function, `_fetchScript`, for the same purpose. +- Renamed the `checkAndUpdateCache` function to `checkCache`, the `updateVersion` function to `updateHcVersion`, the `version` function to `getHcVersion`, the `saveConfigToManifest` function to `_saveConfigToManifest`, the `extractModuleName` function to `_extractModuleName`, the `extractVersion` function to `extractHcVersion`, and the `cdnURL` property to `cdnUrl` in the `cache.js` module. +- Named the default exported function of the `export.js` module, `puppeteerExport`. +- Optimized `puppeteerExport` by simplifying processing, reducing the size of data passed to the browser, and improving performance and readability. +- Merged the logic from `createSVG` and `setAsConfig` into the `puppeteerExport` function. +- Extracted `_getChartSize` from part of the `puppeteerExport` logic to determine the final exported image size. +- Simplified error handling by removing the `HttpError` class and using only the `ExportError` class throughout the code. +- Removed forced modification of pool worker numbers for batch exports. +- Optimized the `initPool` and `postWork` functions. +- Moved the `factory` object into a separate function, optimizing pool resource functions. +- Extended and corrected the `poolStats` object. +- The `newPage` function now accepts the `poolResource` object and throws errors if the `browser` or `page` is incorrect. +- The `clearPage` function now accepts the `poolResource` object (instead of `page`) and correctly sets `workCount` to an exceeding number in case of issues with clearing the page. +- The `addPageResources` function now accepts the `customLogicOptions` object instead of an object containing all options. +- Set the `launchOptions.userDataDir` property to 'tmp'. +- Secured the `launchOptions.args` by setting it to [] if `puppeteerArgs` is not found in the `createBrowser` function. +- Renamed the `get` function to `getBrowser`, the `create` function to `createBrowser`, and the `close` function to `closeBrowser`. +- Renamed `triggerExport` to `createChart`, optimizing the options passed and processed in the `highcharts.js` module. +- Improved the module's overall logic, optimizing logging functions and the initialization process. +- Added `pathToLog` to the module's logging options to remember the full path to the log file. +- Added the `enableConsoleLogging` function. +- Removed `listen` function and `listeners` array from the module's logging options. +- Created a new module, `prompts.js`, to manage enhanced and refactored logic related to creating and loading options from prompts, previously handled in `config.js`. +- Added the `allowCodeExecution` flag as the second argument in the `manualConfig` function to check whether a file is allowed to be loaded. +- Added `isAllowedConfig` check to the options loaded with the `manualConfig` function. +- Created a new module, `info.js`, to group togheter and enhance logic (functions like `printInfo` and `printUsage`) previously in `utils.js` related to processing and displaying information such as license, version, and option usage. +- Created a new internal function `_cycleCategories` from the `printUsage` logic. +- Minor corrections in the `fetch.js` module, including changing the `fetch` function to `get`. +- Renamed the `getProtocol` function to `_getProtocolModule` (for internal use). +- Renamed `intervals.js` to `timer.js`. +- Added functionality to manage both intervals and timeouts. +- The default `exitCode` value in the `shutdownCleanUp` function is now set to `0`. +- Revised all utility functions. +- Added or moved the following functions: `getAbsolutePath`, `getBase64`, `getNewDate`, `getNewDateTime`. +- Removed or moved the following functions: `fixType`, `handleResources`, `isCorrectJSON`, `optionsStringify`, `printLogo`, `printUsage`, `wrapAround`. +- Changed the notation of most functions from `() =>` to `function()` for consistency and readability. +- Corrected and optimized the data passed to each function. +- Redefined and corrected which functions and properties should be exported. +- Fixed and standardized property and function names and values. +- Optimized dependencies between modules. +- Reduced unnecessary exports of modules. +- Marked all internal functions with a `_` prefix. +- Improved logging and error messages. +- Improved error handling. +- Corrections in error status codes. +- Enhanced all files with improved JSDoc tags, descriptions, and comments, adding extra tags such as `@overview`, `@async`, and `@function`. +- Corrected function descriptions, parameter types, return values, and documented errors. +- Fixed all tests, samples, and scenario runners. +- Created, renamed, or removed various tests, samples, and scenario runners. +- Removed separate test runner scripts. +- Made a minor correction in the `build` script. +- Updated package versions. +- Corrected the description of options prioritization order in the `Configuration` section. +- Added explanations of overall option handling, management, and processing, along with descriptions for each export method (`Options Handling` section and subsections). +- Added, updated, corrected, or redefined descriptions and values of options in the following sections: `Default JSON Config`, `Environment Variables`, `Custom JSON Config`, `Command Line Arguments`, `HTTP Server POST Arguments`. +- Fixed an incorrect version change endpoint description in the `Switching Highcharts Version at Runtime` section. +- Corrected example and added description of the `Node.js Module` section. +- Refreshed, expanded, and completely redefined the API documentation. +- Added a note on help and version information (`Note About Version and Help Information` section). +- Added a note about path interpretation for properties requiring path settings (`Note About Paths` section). +- Corrected the `Note About Chart Size` section. +- Various other fixes, optimizations and minor stylistic and formatting improvements. + +_Fixes:_ + +- Fixed a recurring issue with images not loading by implementing a mechanism that waits for images for a certain period. +- Corrected values, types, and other data for each property in the `defaultConfig` object. +- Corrected the `createConfig` and `loadConfig` options to allow usage with or without the `.json` extension. +- Fixed the `uiEnabled` by enabling its usage in `ui.js` router. +- Fixed issues with relative paths when used as a Node.js module. + # 4.1.0 _Enhancements:_ @@ -12,7 +155,7 @@ _Enhancements:_ # 4.0.2 -_Hotfix_: +_Hotfixes:_ - Fixed missing `msg` and `public` folders for a bundle in `v4.0.1` on NPM. @@ -22,7 +165,7 @@ _Fixes:_ # 4.0.1 -_Hotfix_: +_Hotfixes:_ - Fixed missing `dist` folder for a bundle in `v4.0.0` on NPM. @@ -67,7 +210,7 @@ _Enhancements:_ - Made corrections for gracefully shutting down resources, including running servers, ongoing intervals, browser instance, created pages, and workers pool. - Updated `createImage` and `createPDF` functions with faster execution options including `optimizeForSpeed` and `quality`. - Set `waitUntil` to `'domcontentloaded'` for `setContent` and `goto` functions to improve performance. -- Replaced browser's deprecated `isConnected()` with the `connected` property. +- Replaced browser's deprecated `isConnected` function with the `connected` property. - Added information on all available pool resources. - Numerous minor improvements for performance and stability. - Moved the `listenToProcessExits` from the `pool` to the `other` section of the options. @@ -180,7 +323,7 @@ _Fixes:_ # 3.0.0 -_Fixes and enhancements:_ +_Enhancements and Fixes:_ - Replaced `PhantomJS` with `Puppeteer`. - Updated the config handling system to optionally load JSON files, and improved environment var loading. @@ -192,7 +335,7 @@ _Fixes and enhancements:_ - Lots of smaller bugfixes and tweaks. - Transitioned our public server (export.highcharts.com) from HTTP to HTTPS. -_New features:_ +_New Features:_ - Added `/health` route to server to display basic server information. - Added a UI served on `/` to perform exports from JSON configurations in browser. @@ -201,7 +344,7 @@ _New features:_ This version is not backwards compatible out of the box! -_Breaking changes:_ +_Breaking Changes:_ - Log destinations must now exist before starting file logging. - When running in server mode, the following options are now disabled by default: @@ -211,7 +354,7 @@ _Breaking changes:_ Disabled options can be enabled by adding the `--allowCodeExecution` flag when starting the server. Using this flag is not recommended, and should not be done unless the server is sandboxed and not reachable on the public internet. -_Changelog:_ +_Enhancements:_ - Added the `--allowCodeExecution` flag which is now required to be set when exporting pure JavaScript, using additional external resources, or using callback when running in server mode. - Removed the `mkdirp` dependency. @@ -367,7 +510,7 @@ _Changelog:_ # 1.0.11 - Fixed an issue with `themeOptions` when using CLI mode. -- Added `listenToProcessExits` option to `pool.init()`. +- Added `listenToProcessExits` option to `pool.init` function. - Exposed `listenToProcessExits` in CLI mode. - Fixed issue with `--callback` when the callback was a file. diff --git a/package-lock.json b/package-lock.json index 98d7665d..7714c43e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "highcharts-export-server", - "version": "4.1.0", + "version": "5.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "highcharts-export-server", - "version": "4.1.0", + "version": "5.0.0", "license": "MIT", "dependencies": { "colors": "1.4.0", @@ -19,10 +19,10 @@ "jsdom": "^26.0.0", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^24.1.1", + "puppeteer": "^24.2.0", "tarn": "^3.0.2", "uuid": "^11.0.5", - "zod": "^3.24.1" + "zod": "^3.24.2" }, "bin": { "highcharts-export-server": "bin/cli.js" @@ -38,8 +38,8 @@ "jest": "^29.7.0", "lint-staged": "^15.4.3", "nodemon": "^3.1.9", - "prettier": "^3.4.2", - "rollup": "^4.34.0" + "prettier": "^3.5.1", + "rollup": "^4.34.6" }, "engines": { "node": ">=18.12.0" @@ -93,9 +93,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", - "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, "license": "MIT", "engines": { @@ -103,22 +103,23 @@ } }, "node_modules/@babel/core": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", - "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.8.tgz", + "integrity": "sha512-l+lkXCHS6tQEc5oUpK28xBOZ6+HwaH7YwoYQbLFiYb4nS2/l1tKnZEtEWkD0GuiYdvArf9qBS0XlQGXzPMsNqQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", + "@babel/generator": "^7.26.8", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", "@babel/helpers": "^7.26.7", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.26.7", - "@babel/types": "^7.26.7", + "@babel/parser": "^7.26.8", + "@babel/template": "^7.26.8", + "@babel/traverse": "^7.26.8", + "@babel/types": "^7.26.8", + "@types/gensync": "^1.0.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -134,14 +135,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", - "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.8.tgz", + "integrity": "sha512-ef383X5++iZHWAXX0SXQR6ZyQhw/0KtTkrTz61WXRhFM6dhpHulO/RJz79L8S6ugZHJkOOkUrUdxgdF2YiPFnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.5", - "@babel/types": "^7.26.5", + "@babel/parser": "^7.26.8", + "@babel/types": "^7.26.8", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -253,13 +254,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", - "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", + "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.7" + "@babel/types": "^7.26.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -508,32 +509,32 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.8.tgz", + "integrity": "sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.8", + "@babel/types": "^7.26.8" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", - "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.8.tgz", + "integrity": "sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.5", - "@babel/parser": "^7.26.7", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7", + "@babel/generator": "^7.26.8", + "@babel/parser": "^7.26.8", + "@babel/template": "^7.26.8", + "@babel/types": "^7.26.8", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -552,9 +553,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", - "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", + "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", "dev": true, "license": "MIT", "dependencies": { @@ -1315,18 +1316,17 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.0.tgz", - "integrity": "sha512-bO61XnTuopsz9kvtfqhVbH6LTM1koxK0IlBR+yuVrM2LB7mk8+5o1w18l5zqd5cs8xlf+ntgambqRqGifMDjog==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.7.1.tgz", + "integrity": "sha512-MK7rtm8JjaxPN7Mf1JdZIZKPD2Z+W7osvrC1vjpvfOX1K0awDIHYbNi89f7eotp7eMUn2shWnt03HwVbriXtKQ==", "license": "Apache-2.0", "dependencies": { "debug": "^4.4.0", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", + "semver": "^7.7.0", + "tar-fs": "^3.0.8", "yargs": "^17.7.2" }, "bin": { @@ -1337,9 +1337,9 @@ } }, "node_modules/@puppeteer/browsers/node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1371,10 +1371,262 @@ } } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.6.tgz", + "integrity": "sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.6.tgz", + "integrity": "sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.6.tgz", + "integrity": "sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.6.tgz", + "integrity": "sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.6.tgz", + "integrity": "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.6.tgz", + "integrity": "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.6.tgz", + "integrity": "sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.6.tgz", + "integrity": "sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.6.tgz", + "integrity": "sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.6.tgz", + "integrity": "sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.6.tgz", + "integrity": "sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.6.tgz", + "integrity": "sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.6.tgz", + "integrity": "sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.6.tgz", + "integrity": "sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.6.tgz", + "integrity": "sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.6.tgz", + "integrity": "sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.6.tgz", + "integrity": "sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.6.tgz", + "integrity": "sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.0.tgz", - "integrity": "sha512-g2ASy1QwHP88y5KWvblUolJz9rN+i4ZOsYzkEwcNfaNooxNUXG+ON6F5xFo0NIItpHqxcdAyls05VXpBnludGw==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.6.tgz", + "integrity": "sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==", "cpu": [ "x64" ], @@ -1477,6 +1729,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/gensync": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.4.tgz", + "integrity": "sha512-C3YYeRQWp2fmq9OryX+FoDy8nXS6scQ7dPptD8LnFDAUNcKWJjXQKDNJD3HVm+kOUsXhTOkpi69vI4EuAr95bA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -1522,9 +1781,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", - "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", + "version": "22.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2054,9 +2313,9 @@ } }, "node_modules/bare-stream": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.4.tgz", - "integrity": "sha512-G6i3A74FjNq4nVrrSTUz5h3vgXzBJnjmWAVlBWaZETkgu+LgKd7AiyOml3EDJY1AHlIbBHKDXE+TUT53Ff8OaA==", + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", + "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", "license": "Apache-2.0", "optional": true, "dependencies": { @@ -2075,26 +2334,6 @@ } } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/basic-ftp": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", @@ -2223,30 +2462,6 @@ "node-int64": "^0.4.0" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -2302,9 +2517,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2350,9 +2565,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001696", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001696.tgz", - "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==", + "version": "1.0.30001699", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz", + "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==", "dev": true, "funding": [ { @@ -2436,13 +2651,13 @@ } }, "node_modules/chromium-bidi": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-1.1.0.tgz", - "integrity": "sha512-HislCEczCuamWm3+55Lig9XKmMF13K+BGKum9rwtDAzgUAHT4h5jNwhDmD4U20VoVUG8ujnv9UZ89qiIf5uF8w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-1.2.0.tgz", + "integrity": "sha512-XtdJ1GSN6S3l7tO7F77GhNsw0K367p0IsLYf2yZawCVAKKC3lUvDhPdMVrB2FNhmhfW43QGYbEX3Wg6q0maGwQ==", "license": "Apache-2.0", "dependencies": { - "mitt": "3.0.1", - "zod": "3.24.1" + "mitt": "^3.0.1", + "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" @@ -3018,9 +3233,9 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1380148", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1380148.tgz", - "integrity": "sha512-1CJABgqLxbYxVI+uJY/UDUHJtJ0KZTSjNYJYKqd9FRoXT33WDakDHNxRapMEgzeJ/C3rcs01+avshMnPmKQbvA==", + "version": "0.0.1402036", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1402036.tgz", + "integrity": "sha512-JwAYQgEvm3yD45CHB+RmF5kMbWtXBaOGwuxa87sZogHcLCv8c/IqnThaoQ1y60d7pXWjSKWQphPEc+1rAScVdg==", "license": "BSD-3-Clause" }, "node_modules/diff-sequences": { @@ -3085,9 +3300,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.90", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.90.tgz", - "integrity": "sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug==", + "version": "1.5.99", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.99.tgz", + "integrity": "sha512-77c/+fCyL2U+aOyqfIFi89wYLBeSTCs55xCZL0oFH0KjqsvSvyh6AdQ+UIl1vgpnQQE6g+/KK8hOIupH6VwPtg==", "dev": true, "license": "ISC" }, @@ -3285,13 +3500,16 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -4013,9 +4231,9 @@ "license": "ISC" }, "node_modules/for-each": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", - "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", "dependencies": { @@ -4067,6 +4285,21 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -4516,26 +4749,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4728,13 +4941,13 @@ } }, "node_modules/is-boolean-object": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", - "integrity": "sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", + "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" }, "engines": { @@ -5071,13 +5284,13 @@ } }, "node_modules/is-weakref": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.0.tgz", - "integrity": "sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -5144,9 +5357,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -5683,9 +5896,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -6339,9 +6552,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -6617,9 +6830,9 @@ } }, "node_modules/nodemon/node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -6681,9 +6894,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -7135,9 +7348,9 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, "license": "MIT", "engines": { @@ -7155,9 +7368,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", + "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", "dev": true, "license": "MIT", "bin": { @@ -7313,17 +7526,17 @@ } }, "node_modules/puppeteer": { - "version": "24.1.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.1.1.tgz", - "integrity": "sha512-fuhceZ5HZuDXVuaMIRxUuDHfCJLmK0pXh8FlzVQ0/+OApStevxZhU5kAVeYFOEqeCF5OoAyZjcWbdQK27xW/9A==", + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.2.0.tgz", + "integrity": "sha512-z8vv7zPEgrilIbOo3WNvM+2mXMnyM9f4z6zdrB88Fzeuo43Oupmjrzk3EpuvuCtyK0A7Lsllfx7Z+4BvEEGJcQ==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.7.0", - "chromium-bidi": "1.1.0", + "@puppeteer/browsers": "2.7.1", + "chromium-bidi": "1.2.0", "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1380148", - "puppeteer-core": "24.1.1", + "devtools-protocol": "0.0.1402036", + "puppeteer-core": "24.2.0", "typed-query-selector": "^2.12.0" }, "bin": { @@ -7334,15 +7547,15 @@ } }, "node_modules/puppeteer-core": { - "version": "24.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.1.1.tgz", - "integrity": "sha512-7FF3gq6bpIsbq3I8mfbodXh3DCzXagoz3l2eGv1cXooYU4g0P4mcHQVHuBD4iSZPXNg8WjzlP5kmRwK9UvwF0A==", + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.2.0.tgz", + "integrity": "sha512-e4A4/xqWdd4kcE6QVHYhJ+Qlx/+XpgjP4d8OwBx0DJoY/nkIRhSgYmKQnv7+XSs1ofBstalt+XPGrkaz4FoXOQ==", "license": "Apache-2.0", "dependencies": { - "@puppeteer/browsers": "2.7.0", - "chromium-bidi": "1.1.0", + "@puppeteer/browsers": "2.7.1", + "chromium-bidi": "1.2.0", "debug": "^4.4.0", - "devtools-protocol": "0.0.1380148", + "devtools-protocol": "0.0.1402036", "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, @@ -7676,9 +7889,9 @@ } }, "node_modules/rollup": { - "version": "4.34.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.0.tgz", - "integrity": "sha512-+4C/cgJ9w6sudisA0nZz0+O7lTP9a3CzNLsoDwaRumM8QHwghUsu6tqHXiTmNUp/rqNiM14++7dkzHDyCRs0Jg==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz", + "integrity": "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7692,25 +7905,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.0", - "@rollup/rollup-android-arm64": "4.34.0", - "@rollup/rollup-darwin-arm64": "4.34.0", - "@rollup/rollup-darwin-x64": "4.34.0", - "@rollup/rollup-freebsd-arm64": "4.34.0", - "@rollup/rollup-freebsd-x64": "4.34.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.0", - "@rollup/rollup-linux-arm-musleabihf": "4.34.0", - "@rollup/rollup-linux-arm64-gnu": "4.34.0", - "@rollup/rollup-linux-arm64-musl": "4.34.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.0", - "@rollup/rollup-linux-riscv64-gnu": "4.34.0", - "@rollup/rollup-linux-s390x-gnu": "4.34.0", - "@rollup/rollup-linux-x64-gnu": "4.34.0", - "@rollup/rollup-linux-x64-musl": "4.34.0", - "@rollup/rollup-win32-arm64-msvc": "4.34.0", - "@rollup/rollup-win32-ia32-msvc": "4.34.0", - "@rollup/rollup-win32-x64-msvc": "4.34.0", + "@rollup/rollup-android-arm-eabi": "4.34.6", + "@rollup/rollup-android-arm64": "4.34.6", + "@rollup/rollup-darwin-arm64": "4.34.6", + "@rollup/rollup-darwin-x64": "4.34.6", + "@rollup/rollup-freebsd-arm64": "4.34.6", + "@rollup/rollup-freebsd-x64": "4.34.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.6", + "@rollup/rollup-linux-arm-musleabihf": "4.34.6", + "@rollup/rollup-linux-arm64-gnu": "4.34.6", + "@rollup/rollup-linux-arm64-musl": "4.34.6", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.6", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.6", + "@rollup/rollup-linux-riscv64-gnu": "4.34.6", + "@rollup/rollup-linux-s390x-gnu": "4.34.6", + "@rollup/rollup-linux-x64-gnu": "4.34.6", + "@rollup/rollup-linux-x64-musl": "4.34.6", + "@rollup/rollup-win32-arm64-msvc": "4.34.6", + "@rollup/rollup-win32-ia32-msvc": "4.34.6", + "@rollup/rollup-win32-x64-msvc": "4.34.6", "fsevents": "~2.3.2" } }, @@ -8105,9 +8318,9 @@ } }, "node_modules/simple-update-notifier/node_modules/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -8181,9 +8394,9 @@ "license": "MIT" }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "license": "MIT", "dependencies": { "ip-address": "^9.0.5", @@ -8562,9 +8775,9 @@ } }, "node_modules/terser": { - "version": "5.37.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", - "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -8629,28 +8842,22 @@ "dev": true, "license": "MIT" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT" - }, "node_modules/tldts": { - "version": "6.1.76", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.76.tgz", - "integrity": "sha512-6U2ti64/nppsDxQs9hw8ephA3nO6nSQvVVfxwRw8wLQPFtLI1cFI1a1eP22g+LUP+1TA2pKKjUTwWB+K2coqmQ==", + "version": "6.1.77", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.77.tgz", + "integrity": "sha512-lBpoWgy+kYmuXWQ83+R7LlJCnsd9YW8DGpZSHhrMl4b8Ly/1vzOie3OdtmUJDkKxcgRGOehDu5btKkty+JEe+g==", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.76" + "tldts-core": "^6.1.77" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.76", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.76.tgz", - "integrity": "sha512-uzhJ02RaMzgQR3yPoeE65DrcHI6LoM4saUqXOt/b5hmb3+mc4YWpdSeAQqVqRUlQ14q8ZuLRWyBR1ictK1dzzg==", + "version": "6.1.77", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.77.tgz", + "integrity": "sha512-bCaqm24FPk8OgBkM0u/SrEWJgHnhBWYqeBo6yUmcZJDCHt/IfyWBb+14CXdGi4RInMv4v7eUAin15W0DoA+Ytg==", "license": "MIT" }, "node_modules/tmpl": { @@ -8693,9 +8900,9 @@ } }, "node_modules/tough-cookie": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz", - "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.1.tgz", + "integrity": "sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA==", "license": "BSD-3-Clause", "dependencies": { "tldts": "^6.1.32" @@ -8916,16 +9123,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "license": "MIT", - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -9107,9 +9304,9 @@ } }, "node_modules/whatwg-url": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", - "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz", + "integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==", "license": "MIT", "dependencies": { "tr46": "^5.0.0", @@ -9474,9 +9671,9 @@ } }, "node_modules/zod": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", - "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", + "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 54ca6142..d24cadc2 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "author": "Highsoft AS (http://www.highcharts.com/about)", "license": "MIT", "type": "module", - "version": "4.1.0", + "version": "5.0.0", "main": "./dist/index.esm.js", "repository": { "url": "https://github.com/highcharts/node-export-server", @@ -53,10 +53,10 @@ "jsdom": "^26.0.0", "multer": "^1.4.5-lts.1", "prompts": "^2.4.2", - "puppeteer": "^24.1.1", + "puppeteer": "^24.2.0", "tarn": "^3.0.2", "uuid": "^11.0.5", - "zod": "^3.24.1" + "zod": "^3.24.2" }, "devDependencies": { "@jest/globals": "^29.7.0", @@ -69,7 +69,7 @@ "jest": "^29.7.0", "lint-staged": "^15.4.3", "nodemon": "^3.1.9", - "prettier": "^3.4.2", - "rollup": "^4.34.0" + "prettier": "^3.5.1", + "rollup": "^4.34.6" } } From 220fefd2e7a8e7e8d342a98f10d5bc2d08a9c33a Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Fri, 14 Feb 2025 15:46:12 +0100 Subject: [PATCH 102/102] Cosmetic corrections. --- CHANGELOG.md | 4 ++-- README.md | 2 +- bin/cli.js | 2 +- lib/schemas/config.js | 2 +- lib/server/routes/ui.js | 4 ++-- public/index.html | 2 +- tests/cli/cliTestRunnerSingle.js | 2 +- tests/http/httpTestRunner.js | 2 +- tests/http/httpTestRunnerSingle.js | 2 +- tests/other/sideBySide.js | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ce1bf4b..7fff4d22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,7 +63,7 @@ _Enhancements:_ - Optimized logic and corrected the url (from `version/change` to `version_change`) in the `versionChange.js` router. - Refactored `exportHandler` (to `requestExport`) by moving some logic to the `validaion` middleware and refactoring the rest. - Removed unnecessary `doCallbacks` from the export router. -- Moved `server/error.js` and `server/rateLimiting.js` to `middlewares/`, optimizing error handling and rate limiting. +- Moved `./lib/server/error.js` and `./lib/server/rateLimiting.js` to `./lib/server/middlewares/`, optimizing error handling and rate limiting. - The `nodeEnv` option is now obtained from `getOptions`, not directly from the environment variables in the `error.js` middleware. - Refactored and optimized the entire logic for checking the cache and fetching scripts. - Refactored and split the `_updateCache` function into smaller, more manageable parts. @@ -235,7 +235,7 @@ _Enhancements:_ - Updated the `killPool` function. - The `uncaughtException` handler now kills the pool, browser, and terminates the process with exit code 1, when enabled. - The browser instance should be correctly closed now when an error occurs during pool creation. -- Corrected error handling and response sending in the `/change_hc_version.js` route. +- Corrected error handling and response sending in the `./lib/server/routes/change_hc_version.js` route. - Corrected the `handleResources` function. - Corrected samples, test scenarios, and test runners. - Bumped versions of most packages, with an updating deprecated `Puppeteer` from `v21.1.1` to latest. diff --git a/README.md b/README.md index fee6fc79..2fa44897 100644 --- a/README.md +++ b/README.md @@ -1026,7 +1026,7 @@ There are two main ways to debug code: - By adding a `debugger` statement within any client-side code (e.g., inside a `page.evaluate` callback). With the `--devtools` option set to **true**, the code execution will stop automatically. -- By running the export server with the `--inspect-brk=` flag, and adding a `debugger` statement within any server-side code. Subsequently, navigate to `chrome://inspect/`, input the server's IP address and port (e.g., `localhost:9229`) in the Configure section. Clicking 'inspect' initiates debugging of the server-side code. +- By running the Export Server with the `--inspect-brk=` flag, and adding a `debugger` statement within any server-side code. Subsequently, navigate to `chrome://inspect/`, input the server's IP address and port (e.g., `localhost:9229`) in the Configure section. Clicking 'inspect' initiates debugging of the server-side code. The `npm run start:debug` script from the `package.json` allows debugging code using both methods simultaneously. In this setup, client-side code is accessible from the devTools of a specific Puppeteer browser's page, while server-side code can be debugged from the devTools of `chrome://inspect/`. diff --git a/bin/cli.js b/bin/cli.js index f73c1f5b..099be3fc 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -145,5 +145,5 @@ async function start() { } } -// Start the export server process +// Start the Export Server process start(); diff --git a/lib/schemas/config.js b/lib/schemas/config.js index fa318f10..c8d1e677 100644 --- a/lib/schemas/config.js +++ b/lib/schemas/config.js @@ -856,7 +856,7 @@ const defaultConfig = { types: ['boolean'], envLink: 'UI_ENABLE', cliName: 'enableUi', - description: 'Enables or disables the UI for the export server', + description: 'Enables or disables the UI for the Export Server', promptOptions: { type: 'toggle' } diff --git a/lib/server/routes/ui.js b/lib/server/routes/ui.js index f4d45eff..3356f773 100644 --- a/lib/server/routes/ui.js +++ b/lib/server/routes/ui.js @@ -13,7 +13,7 @@ See LICENSE file in root for details. *******************************************************************************/ /** - * @overview Defines an Express route for serving the UI for the export server + * @overview Defines an Express route for serving the UI for the Export Server * when enabled. */ @@ -34,7 +34,7 @@ export default function uiRoutes(app) { // Add the UI endpoint only if required if (getOptions().ui.enable) { /** - * Adds the GET '/' - A route for a UI when enabled on the export server. + * Adds the GET '/' - A route for a UI when enabled on the Export Server. */ app.get(getOptions().ui.route || '/', (request, response, next) => { try { diff --git a/public/index.html b/public/index.html index 75866695..8113211d 100644 --- a/public/index.html +++ b/public/index.html @@ -49,7 +49,7 @@

Highcharts Export Server

This page allows you to experiment with different options for the - export server. If you use the public Export Server at + Export Server. If you use the public Export Server at https://export.highcharts.com diff --git a/tests/cli/cliTestRunnerSingle.js b/tests/cli/cliTestRunnerSingle.js index 7c1b92d1..cf3941f8 100644 --- a/tests/cli/cliTestRunnerSingle.js +++ b/tests/cli/cliTestRunnerSingle.js @@ -81,7 +81,7 @@ if (existsSync(file) && file.endsWith('.json')) { getNewDateTime() - startDate }ms.`; - // If code is 1, it means that export server thrown an error + // If code is 1, it means that Export Server thrown an error if (code === 1) { return console.error(`[Fail] ${endMessage}`.red); } diff --git a/tests/http/httpTestRunner.js b/tests/http/httpTestRunner.js index c7414ee2..abb4ca8a 100644 --- a/tests/http/httpTestRunner.js +++ b/tests/http/httpTestRunner.js @@ -36,7 +36,7 @@ console.log( '(results are stored in the ./tests/http/_results).\n'.green ); -// Url of Puppeteer export server +// Url of Puppeteer Export Server const url = 'http://127.0.0.1:7801'; // Perform a health check before continuing diff --git a/tests/http/httpTestRunnerSingle.js b/tests/http/httpTestRunnerSingle.js index bc563ee8..9700b23d 100644 --- a/tests/http/httpTestRunnerSingle.js +++ b/tests/http/httpTestRunnerSingle.js @@ -30,7 +30,7 @@ console.log( '(results are stored in the ./tests/http/_results).\n'.green ); -// Url of Puppeteer export server +// Url of Puppeteer Export Server const url = 'http://127.0.0.1:7801'; // Perform a health check before continuing diff --git a/tests/other/sideBySide.js b/tests/other/sideBySide.js index 3a2d3679..d0bda3ab 100644 --- a/tests/other/sideBySide.js +++ b/tests/other/sideBySide.js @@ -27,7 +27,7 @@ const resultsPath = join(__dirname, 'tests', 'other', '_results'); // Create results folder for CLI exports if doesn't exist !existsSync(resultsPath) && mkdirSync(resultsPath); -// Urls of Puppeteer and PhantomJS export servers +// Urls of Puppeteer and PhantomJS Export Servers const urls = ['http://127.0.0.1:7801', 'http://127.0.0.1:7802']; // Test message